質問: sizeof() の出力が配列宣言の両方のタイプで異なるのはなぜですか?
回答: このステートメントは、「Hello」を保持するメモリ位置を指す、char[] 型の q という名前の変数を宣言します。
char q[] = "Hello";
sizeof(q) は 6 です。文字列 "Hello" は 'H'、'e'、'l'、'l'、'o'、'\0' で構成されており、カウントに NULL 文字が含まれているためです。
このステートメントは、p という名前の変数を char[] 型で宣言し、5 つの文字が予約されているメモリ位置を指します。
char p[5];
コンパイラへのメモリ アラインメント フラグによっては、実際には p に予約されている場所に 6、8、またはそれ以上の文字が予約されている場合があることに注意してください。また、p[5] (p[] 配列の序数の 6 番目の文字) を参照または割り当てても、C は文句を言いません。
sizeof(p) は 5 です。これは、p に対して宣言したメモリ位置の大きさがコンパイラによって記録されているためです。p と q は異なる方法で宣言され、異なるエンティティを参照するため、 sizeof(p) と sizeof(q) は異なる値を返します。
質問: strlen() と printf() はいつ停止するかをどのように認識しますか?2 つの配列を宣言するときに null 文字が追加されませんでした。
回答: どちらの strlen() 関数呼び出しも、NULL 以外の文字の数をカウントします。したがって、両方の strlen 関数呼び出しは、NULL ターミネータが見つかるまで char をカウントします。少なくとも p+5 のメモリ位置に別の値が割り当てられるまでは、p と q の両方があります。これは、p と q の両方がスタックに割り当てられているためです。p、q、および整数 i のアドレスを見てください。p と q がどこにあるかを示すのに役立つ追加の変数が追加された関数を次に示します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)<(b))?(b):(a))
int main()
{
char m0 = 'X';
char p[5];
char m1 = 'Y';
char q[]="Hello";
char m2 = 'Z';
int i=0;
strcpy(p,"World");
printf("strlen(p)=%d\n",strlen(p));
printf("sizeof(p)=%d\n",sizeof(p));
printf("strlen(q)=%d\n",strlen(q));
printf("sizeof(q)=%d\n",sizeof(q));
for(i=0;i<6;i++)
{
printf("p[%d]=%c\tq[%d]=%c\n",i,p[i],i,q[i]);
}
printf("m0=%x, %c\n",&m0,m0);
printf(" p=%x\n",p);
printf("m1=%x, %c\n",&m1,m1);
printf(" q=%x\n",q);
printf("m2=%x, %c\n",&m2,m2);
char *x;
for(x=min(&m0,&m2);x<max(&m0,&m2);x++)
{
printf("x[%x]=%c\n",x,*x);
}
return 0;
}
m0、m1、および m2 が配列 p[] および q[] に隣接していることに注意してください。私の Linux システムで実行すると、「World」の strcpy が m0 の値を変更することがわかります (「X」が「\0」に置き換えられます)。
strlen(p)=5
sizeof(p)=5
strlen(q)=5
sizeof(q)=6
p[0]=W q[0]=H
p[1]=o q[1]=e
p[2]=r q[2]=l
p[3]=l q[3]=l
p[4]=d q[4]=o
p[5]= q[5]=
m0=bfbea6a7,
p=bfbea6a2
m1=bfbea6a1, Y
q=bfbea69b
m2=bfbea69a, Z
x[bfbea69a]=Z
x[bfbea69b]=H
x[bfbea69c]=e
x[bfbea69d]=l
x[bfbea69e]=l
x[bfbea69f]=o
x[bfbea6a0]=
x[bfbea6a1]=Y
x[bfbea6a2]=W
x[bfbea6a3]=o
x[bfbea6a4]=r
x[bfbea6a5]=l
x[bfbea6a6]=d
x[bfbea6a7]=
"Hello" や "World" などの AC リテラル文字列は NULL 文字で終了し、その文字を文字列のサイズに含めます。strcpy() 関数は、末尾の NULL 文字を含む文字列全体をコピーします。
strncpy を使用するか、宛先文字列のサイズを確認する必要があります。strcpy(p,q) を使用した場合、p[] が割り当てたよりも多くの文字 (NULL ターミネータ) をコピーしたことに注意してください。それは避けたいことです。C は配列の境界チェックを行わないため、strcpy を実行できます。lint はこのエラーを検出しますが。