2

次のコード例では、str の名前が str の最初の要素へのポインターである場合、string を配列​​として使用します。しかし、代わりに 6 が返されます。

#include <stdio.h>

char str[] = "string";
char *ptr = "string";

int main(void){
    int first = sizeof(str);
    int second = sizeof(str[0]);
    int third = sizeof(ptr);
    int size_of_array = sizeof(str)/sizeof(str[0]);
    printf("the size is %d, %d, %d, %d", size_of_array, first, second, third);
    return 0;
}

str はポインタではないようですが、str のサイズは ptr のサイズではありませんか? 私はtについて混乱しています

4

4 に答える 4

5

まず、あなたの質問の要点:コンパイラは sizeof を宣言サイズに関して厳密に扱います。次のコードでは、ポインター変数が 32 ビット (4 バイト) アドレスを保持していると仮定します。

char str1[] = "abcd";
char *pstr = str1;

// str1 is declared to be a fixed array of 5 chars (4+nul)
size_t str1size = sizeof(str1); // str1size = 5;

// pstr is declared to be a 32 bit pointer to a memory address.
size_t pstrSize = sizeof(pstr) // size of a 32 bit pointer; 4.

表示されている結果を把握するには、配列とポインター変数の違いを確認してください (ほとんどの人が考えているよりも少ないものです)。

C における配列とポインター変数の主な違いは次のとおりです。

  • ポインター変数はアドレスを保持します。宣言された配列アドレスです。
  • したがって、宣言された配列の「アドレス」を変更することはできません(これらアドレスであるため、ポインター変数のようにアドレスを保持しません)。

これらのうちの最初のものは、C の配列とポインターに頭を悩ませたいのであれば、十分に強調することはできません。それ以外は、それらの関係はせいぜい近親相姦です。ポインター変数は配列 (またはその他の変数) のようなアドレスにあります。ただし、配列とは異なり、としてアドレスも保持します。配列まさにそれらのアドレスです。

1つ目は、次の例で最もよく示されています。

char str1[] = "abcd";
char *pstr = NULL;

pstr = str1;          // ok. pstr holds the address *at* str1.
str1[0] = *pstr;      // ok. copies 'a' on to 'a'.
pstr++;               // ok. increment the address held in pstr by one item (char) size.
str1[0] = *pstr;      // ok. copies 'b' over 'a'.
pstr = str1+1;        // ok. pstr holds the address *at* str1 + one item (char) size.
str1 = pstr;          // does not compile. str1's address cannot be changed.

ポインター変数自体がアドレスを保持するのに対し、固定配列アドレスであることに注意してください。したがって、逆の最後のステートメントは完全に問題ありません。

pstr = str1;          // ok. put the str1 address in pstr, a pointer variable.
于 2012-10-22T05:49:54.543 に答える
5

簡単に言えば。配列はポインターではなく、ポインターは配列ではありませ

ポインターを必要とするコンテキストで配列を使用できるように見える暗黙の変換のため、 多くの初心者 (そして悲しいことに経験豊富な) 開発者は 2 つを混同します

あなた自身のスニペットは、配列がポインターではなく、ポインターが配列ではないという事実の完璧な例です。

1.「多くの状況で配列がポインターに崩壊することに注意することが重要です。これは混乱の絶え間ない原因です。」– エド S.


sizeofは、変数の「実際の」型で動作します。そのため、取得している数値は、配列名がポインターと同等であるという仮定の下で考えたものと一致しません。


この質問の読者に自分自身を明確にするために。配列はポインターではなく、ポインターは配列ではありません。

于 2012-10-22T04:50:02.227 に答える
1

奇妙なことに、以前の回答のどれも、タイトルで尋ねられた質問に直接対処していません.

いいえ、配列の名前は最初の要素へのポインタではありません。

ほとんどの式では、配列である式は最初の要素へのポインターに変換されます。これはほとんどの式で発生するため、人々は配列をポインターと考える傾向があります。ただし、4 つの例外があります。sizeof配列が、_Alignof、または&配列の初期化に使用される文字列リテラルである場合、配列はポインターに変換されません。例の 1 つが のオペランドとして配列をsizeof使用しているため、最初の要素へのポインターではなく、配列を使用しています。

したがって、 insizeof(str[0])str[0]最初に解析されます。この部分式では、strは のオペランドではないsizeofため、ポインターに変換されます。次に、そのポインターが添字演算子と共に使用され、結果が配列の最初の要素になります。次にsizeof、その要素のサイズ (1) に評価されます。

ではsizeof(str)strのオペランドですsizeof。したがって、ポインタには変換されません。それは配列です。次にsizeof、その配列のサイズ (7) に評価されます。

この変換は、識別子 (名前) に対してだけ行われるわけではないことに注意してください。任意の式で発生します。したがって、pが で宣言されているように配列へのポインタである場合float (*p)[4];*pは 4 つの float の配列であるため、float へのポインタに変換されます ( sizeof_Alignof、またはのオペランドでない場合&)。

于 2012-10-22T11:40:48.903 に答える
1

はい、いいえ、str は配列ですが、式に現れると、配列の最初のメンバーへのポインターに変換されます。

奇妙なことに、実際に sizeof(str) を実行すると、NUL ターミネータが含まれているため、6 ではなく 7 が返されるはずです。

文字列 \0

ただし、strlen() を使用すると、6 が返されます。

于 2012-10-22T04:53:28.377 に答える