1

面接の質問で最初の問題を出しましたが、質問に対する適切な説明が必要です。自宅でこれを試してみると、他のいくつかの混乱も生じています。

#include <stdio.h>
int main()
{
  int arr[4]={10,20,30,40};
  int i;
  for(i=0;i<=4;i++)
  printf("%d,",arr[i]);
  printf("\n");
  return 0;
}

OUTPUT
10,20,30,40,4,

最後の出力は 4 でしたが、配列インデックスから外れています。メモリ変数では、配列要素の後に存在すると思います。したがって、この答えが得られます。

しかし、再び私はこれと混同します

 #include <stdio.h>

int main()
 {
   char arr[4]={10,20,30,40};
   int i;
    for(i=0;i<=4;i++)
       printf("%d,",arr[i]);
    printf("\n");
   return 0;
 }
OUTPUT
10,20,30,40,0,

再び以下と混同します

#include <stdio.h>
int main()
 {
   int arr[4]={10,20,30,40};
   char i;
   for(i=0;i<=4;i++)
      printf("%d,",arr[i]);
   printf("\n");
   return 0;
}

OUTPUT
10,20,30,40,74743796,

このタイプの出力の変動の理由を説明できる機関はありますか?

Intel CPU、Ubuntu OS、Gcc コンパイラを使用しています。

コンパイラ固有またはアーキテクチャ固有の場合は、回答にも記載してください。

4

4 に答える 4

1

これは未定義動作と呼ばれます。範囲外の配列にアクセスしているため、何かが発生する可能性があり、結果は意味をなさなくてもかまいません。

于 2012-08-07T07:15:30.713 に答える
1

範囲外の配列メモリにはアクセスしないでください。範囲外の配列アクセスの値は何でもかまいません-それに期待される値はなく、未定義の動作があります。配列の後にローカルで作成したchar変数とint変数が、配列変数の後にメモリに配置されることを期待しているようです。この場合でも、これらの変数を初期化していないため、それらの値は何でもかまいません。単に範囲外のメモリにアクセスしないでください。また、アクセスの結果を予測しようとしないでください。

于 2012-08-07T07:15:53.647 に答える
0

明確にするために、表示されているものは「未定義の動作」に分類されますが、何が起こっているのかを知るのに十分な情報が回答に含まれています.

最初に、あなたのマシンのアーキテクチャに関するあなたの質問からわかることの詳細について説明します。

CPUについて

  • CPUはリトルエンディアン

OSについて

  • スタックは下方に成長します

コンパイラについて

  • intサイズは 32 ビットです
  • charサイズは8ビットです
  • ローカル変数は、コード内と同じようにスタック上で順序付けられます
  • 実行char時間を高速化するために必要なサイズよりも大きなブロックに入れます
  • メモリ パディングを使用してコードを高速化

これらすべてが組み合わさって、あなたが目にしている結果が得られます。ここから何が起こるかを示すには、視覚的なデモンストレーションが最適です。

最初のケース

ループの最後で、スタックは次のようになります。

| 10 | 0 | 0 | 0 | a[0]
| 20 | 0 | 0 | 0 | a[1]
| 30 | 0 | 0 | 0 | a[2]
| 40 | 0 | 0 | 0 | a[3]
|  4 | 0 | 0 | 0 | i (a[4])

の値は、あるべきi場所と同じ場所にあります。a[4]Soa[4]は と同じ値iです。

3番目のケース

どのiように見えるかは、多くの変数に依存します。しかし、これが最も理にかなっているレイアウトのようです。

| 10 | 0 | 0 | 0 | a[0]
| 20 | 0 | 0 | 0 | a[1]
| 30 | 0 | 0 | 0 | a[2]
| 40 | 0 | 0 | 0 | a[3]
|  X | X | X | 4 | i (a[4])

これは、表示される値に反映されます。74743796リトルエンディアンバイナリでは

11110100 01111111 01110100 00000100 

最後のバイトは4. コードで参照する場合i、コンテキストは char であり、最後のバイトのみが使用されます。を参照a[4]すると、コンテキストは でありint、4 バイトすべてが使用されます。

これは、コンパイラが文字を最後のバイトに押し込み、残りをガベージでパディングしていることを示しているため、重要です。

2番目のケース

他の2つに基づいて、メモリは次のようになると思います

| X | X | X | 10 | a[0]
| X | X | X | 20 | a[1]
| X | X | X | 30 | a[2]
| X | X | X | 40 | a[3]
| 4 | 0 | 0 |  0 | i (a[4])

魔法は最後の記憶行で作用します。としてアクセスした場合intは正常に処理されます。ただし、 としてアクセスするcharと、コンパイラは最初の 3 バイトがパディングであると想定し、最後のバイトを返します。0

于 2012-08-07T08:20:34.457 に答える
0

あなたが扱っているのは未定義の動作です。

最初のケースでは、変数がスタック上の配列iの直後に配置されているため、配列の境界から出て変数に触れています。との型は同じ ( ) なので、配列の一部であるかのように出力しても問題ありません。arriiarrinti

i2 番目のケースでは、変数であるかのようにアクセスしようとしているcharため、ゼロの部分しか取得できません。

コンパイラはスタック上の変数を自由に再配置できるため、変数が直後に従うべきであるという仮定はi(たとえその順序で宣言されていても) 正しくないことに注意してください。

コンパイラにとって、arriは 2 つの別個のローカル変数にすぎません。のサイズはarrメモリのどこにも保持されませんarr。開始アドレスを持つ単なるメモリの一部です。sizeofstatic演算子を使用して、コンパイル時にそのサイズを調べることができます。

于 2012-08-07T07:19:50.090 に答える