47

私と一緒に働いているインターンは、エンディアンの問題についてコンピューター サイエンスで受けた試験を見せてくれました。ASCII 文字列 "My-Pizza" を示す問題があり、学生はその文字列がリトル エンディアンのコンピューターのメモリでどのように表現されるかを示さなければなりませんでした。もちろん、ASCII 文字列はエンディアンの問題の影響を受けないため、これはひっかけ問題のように思えます。

しかし衝撃的なことに、インターンは、彼の教授が文字列は次のように表現されると主張していると主張しています。

P-yM azzi

私はこれが正しくないことを知っています。どのマシンでも、ASCII 文字列をそのように表現する方法はありません。しかしどうやら、教授はこれを主張している。そこで、私は小さな C プログラムを作成し、インターンに教授に渡すように言いました。

#include <string.h>
#include <stdio.h>

int main()
{
    const char* s = "My-Pizza";
    size_t length = strlen(s);
    for (const char* it = s; it < s + length; ++it) {
        printf("%p : %c\n", it, *it);
    }
}

これは、文字列が「My-Pizza」としてメモリに保存されていることを明確に示しています。1 日後、インターンが私に戻ってきて、その教授は現在、C がアドレスを自動的に変換して適切な順序で文字列を表示していると主張していると教えてくれました。

私は彼に彼の教授は正気ではないと言いましたが、これは明らかに間違っています。しかし、ここで自分の正気を確認するために、これをstackoverflowに投稿して、他の人に自分の言っていることを確認してもらうことにしました.

だから、私は尋ねます:ここにいるのは誰ですか?

4

12 に答える 12

30

間違いなく、あなたは正しいです。

ANSI C 標準 6.1.4 では、リテラル内の文字を「連結」することによって文字列リテラルがメモリに格納されることを指定しています。

ANSI 標準 6.3.6 では、ポインター値に対する加算の効果も指定されています。

整数型の式をポインターに加算またはポインターから減算すると、結果はポインター オペランドの型になります。ポインターオペランドが配列オブジェクトの要素を指し、配列が十分に大きい場合、結果は元の要素からオフセットされた要素を指し、結果と元の配列要素の添え字の差が整数式に等しくなります。

この人のアイデアが正しかった場合、整数が配列インデックスとして使用されている場合、コンパイラは整数演算をいじる必要があります。他にも多くの誤謬が生じますが、それらは想像に任せます。

(文字列イニシャライザとは異なり) 'ABCD'などのマルチバイト文字定数はエンディアン順に格納されるため、混乱する可能性があります。

人がこれについて混乱する理由はたくさんあります。他の人がここで示唆しているように、彼は、int 値を読みやすくするために内容がバイトスワップされているデバッガー ウィンドウに表示されるものを読み間違えている可能性があります。

于 2009-10-14T18:25:30.950 に答える
16

教授は困惑しています。「P-yMazzi」のようなものを見るには、メモリを「4バイト整数」モードで表示すると同時に、各整数の「文字解釈」を高次で提供するメモリ検査ツールを使用する必要がありますバイトから下位バイトへのモード。

もちろん、これは文字列自体とは何の関係もありません。文字列自体がリトル エンディアン マシンでそのように表現されていると言うのは、まったくナンセンスです。

于 2009-10-14T18:45:53.140 に答える
10

文字あたり 8 ビットを使用するシステムについて話している場合、教授は間違っています。

私はよく、実際に 16 ビット文字を使用する組み込みシステムで作業しており、各単語はリトルエンディアンです。そのようなシステムでは、文字列「My-Pizza」は実際には「yMP-ziaz」として保存されます。

ただし、1 文字あたり 8 ビットのシステムである限り、上位レベルのアーキテクチャのエンディアンに関係なく、文字列は常に「My-Pizza」として格納されます。

于 2009-10-14T18:23:31.330 に答える
10

文字列が渡されたことを知らない関数で印刷を行うことにより、コンパイラがそのような「魔法の」変換を行っていないことを非常に簡単に証明できます。

int foo(const void *mem, int n)
{
    const char *cptr, *end;
    for (cptr = mem, end = cptr + n; cptr < end; cptr++)
        printf("%p : %c\n", cptr, *cptr);
}

int main()
{
    const char* s = "My-Pizza";

    foo(s, strlen(s));
    foo(s + 1, strlen(s) - 1);
}

または、アセンブリにコンパイルしてgcc -S、魔法が存在しないことを最終的に判断することもできます。

于 2009-10-14T20:40:06.803 に答える
2

しかし衝撃的なことに、インターンは、彼の教授が文字列は次のように表現されると主張していると主張しています。

P-yM アッツィ

それは何のように表されますか?32 ビット整数ダンプとしてユーザーに表示されますか? または P-yMazzi としてコンピュータのメモリに表示/レイアウトしますか?

コンピューターがリトル エンディアン アーキテクチャであるため、教授が「My-Pizza」はコンピューターのメモリでは「P-yMazzi」として表現/レイアウトされると言った場合は、誰か、その教授にデバッガーの使い方を教えてください! 教授の混乱はすべてそこから生じていると思います。私は教授がコーダーではないという予感を持っています (私が教授を見下しているわけではありません)。エンディアンについて学びました。

おそらく、教授はエンディアンに関することを 1 週間ほど前に学び、デバッガーを誤って使用しただけで、コンピューターに関する彼の新たな独自の洞察にすぐに喜び、すぐに学生に説教しました。

マシンのエンディアン性がアスキー文字列がメモリ内でどのように表現されるかに関係があると教授が言った場合、彼は自分の行為をクリーンアップする必要があり、誰かが彼を修正する必要があります。

教授が代わりに、マシンのエンディアンに応じてマシンで整数がどのように表現/レイアウトされるかについて例を挙げた場合、彼の学生は彼が教えていることを理解することができます。

于 2009-10-15T07:35:15.890 に答える
1

教授はエンディアン/NUXIの問題について類推して主張しようとしていたと思いますが、それを実際の文字列に適用すると正しいです。彼が学生にポイントと問題について特定の方法で考える方法を教えようとしていたという事実からそれを脱線させないでください.

于 2009-10-14T18:27:30.080 に答える
1

興味があるかもしれませんが、リトル エンディアン アーキテクチャをビッグ エンディアン マシンでエミュレートすることも、その逆も可能です。コンパイラは、ポインターを逆参照するたびに、ポインターの最下位ビットを自動的に混乱させるコードを発行する必要がありchar*ます。32 ビット マシンでは、00 <-> 11 と 01 <-> 10 をマップします。

したがって、0x01020304ビッグエンディアンのマシンで数値を書き込み、このアドレス変更でその「最初の」バイトを読み戻すと、最下位バイト0x04. ハードウェアはビッグ エンディアンですが、C の実装はリトル エンディアンです。

短いアクセスにも同様のトリックが必要です。アライメントされていないアクセス (サポートされている場合) は、隣接するバイトを参照しない場合があります。また、一度に 1 バイトずつ読み戻すと単語が入れ替わったように見えるため、単語より大きい型にはネイティブ ストアを使用できません。

ただし、明らかに、リトルエンディアンのマシンは常にこれを行うわけではありません。これは非常に専門的な要件であり、ネイティブ ABI を使用できません。教授が実際の数値を「実際には」ビッグエンディアンであると考えており、リトルエンディアンアーキテクチャが実際に何であるか、および/またはそのメモリがどのように表現されているかを深く混乱させているように私には聞こえます。

文字列が 32 ビット ファイル マシンで「次のように表現される」のは事実ですが、「表現される」とは、P-yM azzi「アドレスの昇順で表現の単語を読み取り、各単語のバイトをビッグ エンディアンで出力する」ことを意味する場合に限ります。他の人が言ったように、これは一部のデバッガーのメモリ ビューで実行される可能性があるため、実際はメモリの内容を表しています。ただし、個々のバイトを表す場合は、各単語を複数文字リテラルとして表すのではなく、単語が be または le に格納されているかどうかに関係なく、アドレスの昇順でリストする方が一般的です。確かにポインターいじりは行われておらず、もし教授の選択した表現が彼に何かがあると思わせたのなら、それは彼を誤解させた.

于 2009-10-14T21:15:13.417 に答える
0

私はこれに出くわし、それをクリアする必要性を感じました. bytes とs の概念や、それらに対処wordする方法については誰も触れていないようです。1バイトは 8 ビットです。ワードはバイトの集まりです。

コンピューターが次の場合:

  • バイトアドレス可能
  • 4 バイト (32 ビット) ワードで
  • 単語の整列
  • メモリは「物理的に」表示されます (ダンプもバイトスワップもされません)。

それなら確かに、教授は正しいでしょう。彼がこれを示さなかったということは、彼が何について話しているのか正確にはわかっていないことを証明していますが、彼は基本的な概念を理解していました.

ワード内のバイト順: (a) ビッグ エンディアン、(b) リトル エンディアン

ワード内のバイト順: (a) ビッグ エンディアン、(b) リトル エンディアン

ワードの文字および整数データ: (a) ビッグ エンディアン、(b) リトル エンディアン

ワードの文字および整数データ: (a) ビッグ エンディアン、(b) リトル エンディアン

参考文献

于 2013-01-28T20:37:10.290 に答える
0

また、(そして、私はこれを長い間遊んでいなかったので、間違っているかもしれません)彼は、文字列が「パックされた配列」として表されるpascolを考えているのかもしれません.IIRCは4バイトの整数にパックされた文字ですか?

于 2009-10-14T21:52:37.877 に答える
0

教授の考えを読むのは困難であり、確かにコンパイラは BE システムと LE システムの両方で隣接する増加するアドレスにバイトを格納する以外には何もしていませんが、ワード サイズが何であれ、メモリをワード サイズの数値で表示するの普通のことです。千を 1,000 と書きます。000,1 ではありません。

$ cat > /tmp/pizza
My-Pizza^D
$ od -X /tmp/pizza
0000000 502d794d 617a7a69
0000010
$ 

記録として、y == 79、M == 4d です。

于 2009-10-15T00:48:39.337 に答える
0

私の知る限り、エンディアンは、大きな値を小さな値に分割したい場合にのみ意味があります。したがって、C スタイルの文字列は影響を受けないと思います。結局のところ、それらは単なる文字の配列だからです。1 バイトだけを読み取っている場合、左から読み取るか右から読み取るかはどのように問題になるのでしょうか?

于 2009-10-15T05:36:18.010 に答える