2013-07-16 13 views
11
int main() 
{ 
    char arr[5][7][6]; 
    char (*p)[5][7][6] = &arr; 
    printf("%d\n", (&arr + 1) - &arr); 
    printf("%d\n", (char *)(&arr + 1) - (char *)&arr); 
    printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr); 
    printf("%d\n", (unsigned)(p + 1) - (unsigned)p); 

    return 0; 
} 

に配列のアドレスを変換する:私は上記のコードを実行すると、私は、出力以下の取得、他のデータ型

1 
210 
42 
210 

なぜ出力すべてのケースではない1はありますか?

+4

ヒント: '= 42' 6 * 7、' 5 * 7 * 6 = 210' –

+0

@Arminができますあなたが説明してください? – Alex

+1

もう1つのヒント - アドレスが最終的なタイプではなく元のタイプであると仮定してポインタの算術演算を*行った後で*別のタイプに変換していない場合... – twalberg

答えて

5

&arrは完全な3次元文字配列のアドレスですが、arrは2次元文字配列の最初の要素を指します。図中の以下のようなもの:char(*)[5][7][6]ある

0xbf8ce2c6 
+------------------+  ◄-- arr = 0xbf8ce2c6 
| 0xbf8ce2f0 | 
| +------------------+  ◄-- arr + 1 = 0xbf8ce2f0 
| | 0xbf8ce31a | | 
| | +------------------+  ◄-- arr + 2 = 0xbf8ce31a 
| | 0xbf8ce344 | | | 
| | | +------------------+  ◄-- arr + 3 = 0xbf8ce344 
| | 0xbf8ce36e | | | | 
| | | | +------------------+  ◄-- arr + 4 = 0xbf8ce36e 
| | | | | | | | | | 
+---|---|---|--|---+ | | | | Each are 7*6, 2-Dimensional 
    | | | |  | | | | Consists Of 42 bytes 
    +---|---|--|-------+ | | | 
     | | |   | | | 
     +---|--|-----------+ | | 
      | |    | | 
      +--|---------------+ | 
       |     | 
       +------------------+ 

The diagram show: 
1. How a 3-dimensional can be interpreted as series of 2-dimensional arrays 
2. Here (arr + i) points to a 2-D array 
3. Notice difference between: (arr + i + 1) - (arr + i) = 0x2a = 42, where i = [0, 4] 

タイプ&arrの寸法[5][7][6]のチャー3次元アレイのアドレスです。 &arr&arr + 1の間の値の差は、5 * 7 * 6 * sizeof(char) = 210です。
char[5][7][6]のサイズは5 * 7 * 6 * sizeof(char)です。
コードで&arrは3次元配列を指し、&arry + 1次の3次元配列(私たちのコードには存在しません)を指します。

チェックcodepadeで、この作業コード:

int main() 
{ 
    char arr[5][7][6]; 
    printf(" &arr : %p", &arr); 
    printf(" &arr+1: %p", &arr + 1); 

    return 0; 
} 

出力:

&arr : 0xbf5dd7de 
&arr+1: 0xbf5dd8b0 

(&arr + 1) - (&arr) = 0xbf5dd8b0 - 0xbf5dd7de = 0xd2 = 210の違い。 2番目のprintfで

printf("%d\n", (char *)(&arr + 1) - (char *)&arr); 

あなたは、プレーン(char*)にタイプchar(*)[5][7][6]のアドレスを型キャスト、およびsizeof char[5][7][6]210両方のアドレスがはるかに210あるからです。 (sizeof(char) == 1を覚えておいてください)。これが理由です。210

最初のステートメントで述べたように、arrは、2次元の文字配列である最初の要素のアドレスです。タイプarrchar(*)[7][6]です。現在、1つの要素(2次元配列のサイズは6 * 7 * sizeof(char) = 42)です。
(注:3次元配列は、各要素が2次元配列である1次元配列と考えることができます)。あなたの第三のprintfで

:符号なしの値に(ただし、アドレス/ポインタ型へ)

printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr); 

あなた型キャスト。 arr + 1arrの差は42 * sizeof(char) = 42(つまり、char[7][6]のサイズに等しい)です。したがって、printfステートメントは42を出力します。

注:アドレスを値に型キャストしているので、sizeof (int) == sizeof (void*)?を読む必要があります。この変換は完全には定義されていません。 (私の説明はあなたの出力と私が与えた出力です)。作業コードの下に、さらに明確化のチェックのために

codepade:

int main() 
{ 
    char arr[5][7][6]; 
    printf(" arr : %p\n", arr); 
    printf(" arr+1: %p", arr + 1); 

    return 0; 
} 

で出力は次のようになります。(arr + 1) - (arr) = 0xbf4836a8

arr : 0xbf48367e 
arr+1: 0xbf4836a8 

テイク差 - 0xbf48367e = 0x2a = 42

最終のprintf:p 3-Dの文字列(= &arr)へのポインタであるため

printf("%d\n", (unsigned)(p + 1) - (unsigned)p); 

ジャスト(第2のprintfに類似)&arr+1&arr = 210間の差を取ります。そしてあなたは値型(ポインタ型ではない)に型キャストしています。

はさらに、が(ただ、目的を理解するための追加、私は読者がそれが役に立つでしょう推測)、

は私達があなたのより深い概念を理解するのに役立ちますsizeof演算子を使用してarr&arrの間に1つの以上の違いを学ぶことができます。この最初の読み取りのために:sizeof Operator

あなたは配列識別子にsizeofオペレータを適用すると、結果は、配列全体ではなく 配列識別子によって表されるポインタのサイズの大きさです。

チェックがcodepadeで、この作業コード:

int main() 
{ 
    char arr[5][7][6]; 
    printf(" Sizeof(&arr) : %lu and value &arr: %p\n", sizeof(&arr), &arr); 
    printf(" Sizeof(arr) : %lu and value arr : %p\n", sizeof(arr), arr); 
    printf(" Sizeof(arr[0]): %lu and value a[0]: %p\n",sizeof(arr[0]), arr[0]); 
    return 0; 
} 

その出力:

Sizeof(&arr) : 4 and value &arr: 0xbf4d9eda 
Sizeof(arr) : 210 and value arr : 0xbf4d9eda 
Sizeof(arr[0]): 42 and value a[0]: 0xbf4d9eda 
  • ここ

    &arrは単なるアドレスであり、システム内のアドレスは4バイトでありますこれは完全な3次元char配列のアドレスです。

  • arrは3次元配列の名前であり、sizeof演算子は210 = 5 * 7 * 6 * sizeof(char)という配列の合計サイズを与えます。 Iは2次元配列である第一の要素に自分の図でarr点を示すように

。だからarr = (arr + 0)。現在、*を使用しています。(arr + 0)の参照演算子は、アドレスに値を与えます。

  • お知らせsizeof(arr[0])は= 7 * 6 * sizeof(char)42を与えます。そして、この証明は概念的には3次元配列ではなく、2次元配列の配列であるということです。

以上の多くの時に私の答えに、私は次のように書かれているので:は「char[5][7][6]のサイズは5 * 7 * 6 * sizeof(char)です。」は、私はcodepade @以下興味深いのコードを追加してい:

int main(){ 
printf(" Char   : %lu \n", sizeof(char)); 
printf(" Char[5]  : %lu \n", sizeof(char[6])); 
printf(" Char[5][7] : %lu \n", sizeof(char[7][6])); 
printf(" Char[5][7][6]: %lu \n", sizeof(char[5][7][6])); 

return 1; 
} 

出力:

Char   : 1 
Char[5]  : 6 
Char[5][7] : 42 
Char[5][7][6]: 210 
+0

これを確認してください(図表)(http://alibad.files.wordpress.com/2010/05/array3d-jagged.jpg?w=369&h=590) –

+0

@Alex今回はそれぞれのタイプコードでコードを試してください: ' (char(*)[5] [7] [6]) '。 printf( "%d \ n"、(char *)[5] [7] [6])(arr + 1) - ((char(*)[5] [7] [6] )arr); 'と出力の理由を教えてください。 –

6

髪を分割したい場合:最初に、コードはprintf()ステートメント全体で未定義の動作を呼び出します。 2つのポインタの違いはタイプptrdiff_tであり、そのために正しい変換指定子は%tdであり、%dではありません。

残りは投機的なものです。あなたのシステムが妥当で、数値的にのポインタ値&arrは、どのタイプに変換されても常に同じであるとしましょう。

ここで、(&arr + 1) - &arrは、もちろん、ポインタ演算の規則に従って1です。 (結果はTポインタの基本型であるサイズsizeof(T)の単位で与えられている理由だと、二つのポインタの間の実際の差が210 * sizeof(int)バイトであるが、これは学校の数学が、ポインタ演算ではない。)

そして(char *)(&arr + 1) - (char *)&arrchar *へのポインタをキャストし、charのサイズは1であるため、これは差異をバイト単位で出力します。あなたは事実上、ここでポインタ算術を欺く/乱用しています。

さらに:printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr)は、タイプint (*)[7][6]の2つのポインタを減算しています。それはarrが崩壊するところです。もちろん、7 * 6 = 42なので、arr + 1arrのサイズの差は42要素です。

pは、配列の最初の要素へのポインタではありませんが、配列自体へのポインタです。そのタイプは正しくint (*)[5][6][7]と表示されます。あなたはそのタイプを使用して違いを印刷するが、場合今、あなたは、コンパイラは、ポインタがちょうどunsignedであることにそれをだますことで除算を行い、その後、あなたは(&arr + 1) - &arrで210

+1

誰でも落札して、コメントを残してください。私はdownvoteの理由が表示されません。 –

+3

答えはよく書かれておらず、関係するタイプを説明していません。答えは真の声明を出すような方法で書かれていますが、それを理解していない人には概念を教えてはいけません。また、 'char *'に変換して引くことは、ポインタ算術を欺くことや悪用することではありません。それはC標準によって完全に定義されています。 –

6

ある5 * 6 * 7を、取得することはできません。 :

&arrは、6つのアレイの5つのアレイのアドレス6 charです。 1つを追加すると、の次のの7つの配列の5つの配列がの配列になります。元のアドレスである&arrを差し引くと、2つのアドレスの間に差異が生じます。 C標準では、この差は2つのアドレス間の要素数で表されます。要素型は、指し示されているオブジェクトの型です。そのタイプは6 charの7つの配列の5つの配列の配列であるため、2つのアドレス間の距離は1つの要素です。言い換えれば、&arrから(&arr + 1)までの距離は、であり、7アレイの5アレイのアレイは、である。(char *)(&arr + 1) - (char *)&arr

&arr + 1は再び6 charの7列の5列の次配列があろう場所へのポインタです。 char *に変換されると、結果はその次の配列の最初のバイトとなるものへのポインタになります。同様に、(char *)&arrは、最初の配列の最初のバイトへのポインタです。次に、2つのポインタを減算すると、要素の差が得られます。これらのポインタはcharを指すポインタなので、その差はcharの数として生成されます。その違いは、配列が6 である5つの配列の5つの配列のバイト数です。これは5•7•6 char、または210 charです。

(unsigned)(arr + 1) - (unsigned)arr

で:arrので

&(又はsizeofまたは他の例外的な場合)で使用されず、自動的に最初の要素へのポインタに6 charの7つの配列の5つの配列の配列から変換されます。したがって、6 charの7つの配列の配列へのポインタです。次にarr + 1は、次の7つの配列の次の配列へのポインタです。char。このアドレスがunsignedに変換されると、使用しているC実装の結果が実際にオブジェクトのメモリアドレスになります。 (これは一般的ですが、C標準では保証されておらず、アドレスが64ビットであるが、unsignedが32ビットのときは確実に中断します。)同様に、(unsigned)arrは最初のオブジェクトのアドレスです。アドレスが減算されると、結果はバイト間の距離になります。したがって、7つの配列の配列のバイト数は6 charです。これは7 6バイトまたは42バイトです。この場合の重要な違いに注意してください。&arrは、6 charの7つの配列の5つの配列の配列へのポインタですが、arrは、6 charの7つの配列の配列へのポインタです。 (unsigned)(p + 1) - (unsigned)p

の7列の5列の配列へのポインタです。次に、p+1は、次の配列の位置へのポインタです。 unsignedに変換すると、上記のように動作します。減算すると、その差はバイトになります。したがって、7つの配列の5つの配列のサイズは6 charです。したがって、210バイトになります。

(&arr + 1) - &arrのタイプはptrdiff_t%tdで印刷されるべきである。余談として

(char *)(&arr + 1) - (char *)&arrのタイプはptrdiff_tであり、%tdで印刷する必要があります。

(unsigned)(arr + 1) - (unsigned)arrのタイプはunsigned intであり、%uで印刷する必要があります。

(unsigned)(p + 1) - (unsigned)pのタイプはunsigned intであり、%uで印刷する必要があります。

+0

コードは実行時に未定義の動作を引き起こすと思いますか? –

+0

@GrijeshChauhan:このコードの 'char * 'に対する加算、減算、および変換が定義されています。 'unsigned'への変換は完全には定義されていませんが、典型的なC実装では、結果のアドレスが' unsigned'型の範囲内にあれば、適切に動作します。間違った書式指定子の使用は定義されていません。 –

+0

ありがとう!!私は今それを得て、私はあなたの答えのその部分をもう一度読んだ(私は第2投票した)。私のコメント投票の限界を超えました。ありがとう –

関連する問題