3

次のコードがあります。

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

int main()
{
   char p[5];
   char q[]="Hello";
   int i=0;

   strcpy(p,"Hello");
   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]);
   }
   return 0;
}

私が得る出力は次のとおりです。

strlen(p)=5
sizeof(p)=5
strlen(q)=5
sizeof(q)=6
p[0]=H  q[0]=H
p[1]=e  q[1]=e
p[2]=l  q[2]=l
p[3]=l  q[3]=l
p[4]=o  q[4]=o
p[5]=   q[5]=
  1. q[]="some string" のように配列を宣言すると、配列のサイズが文字列 const の文字数に等しく設定されることはわかっていますが、配列宣言の両方のタイプで sizeof() の出力に違いがあるのはなぜですか?
  2. strlen() と printf() はいつ停止するかをどのように認識しますか?2 つの配列の宣言中に null 文字が追加されませんでした。
4

7 に答える 7

1
char p[5];
strcpy(p,"Hello");

に 5 文字をコピーしp、終端のヌル文字 ( '\0') を 6 番目の位置、つまりこの配列の境界外に書き込みます。これにより、未定義の動作が発生します。

のマニュアルページからstrcpy

「strcpy() の宛先文字列が十分に大きくない場合、何かが起こる可能性があります。プログラムがデータをバッファーに読み取ったりコピーしたりするときはいつでも、プログラムは最初に十分なスペースがあることを確認する必要があります。」

于 2013-10-03T00:32:58.297 に答える
0

質問: 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 はこのエラーを検出しますが。

于 2013-10-03T01:45:20.950 に答える
0
   char p[5];

   strcpy(p,"Hello");

この strcpy は p[5] に 0 を書き込みます。だから、それは範囲外です。ただし、 sizeof(p) はまだ 5 です。p の最後に上書きしました。これは正しくなく、未定義の動作が発生します。この場合、悪いことは何も起こらず、気付かれませんでした。

もう 1 つの文字列は、長さが 5 でサイズが 6 です。

于 2013-10-03T00:28:28.107 に答える
0

C の文字列はNUL文字 '\0' で終了します。

これが 6 を返す理由です。最後sizeof(q)に を格納するのに十分なスペースがあり'\0'ます。末尾の'\0'.

したがって、このコードは未定義の動作です。

strcpy(p, "Hello");

これは範囲外の に をコピーし'\0'p[5]います。

于 2013-10-03T00:31:27.527 に答える
0

qchar 配列には、ヌル終了文字も含まれます。の固定サイズでpはヌル文字をコピーできませんがstrlen、文字列の文字数をカウントするためにヌル文字をチェックすることに注意してください。したがって、ヌル文字がない場合、未定義の動作が発生する可能性があります。

于 2013-10-03T00:28:36.653 に答える