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() 如何知道何时停止,声明两个数组时没有添加空字符。
4

7 回答 7

6

您的问题中有多个问题。

strcpy(p,"Hello");
  • 这是非法的,因为p它只有 5 个字符长,所以没有空间给.添加的终止 0 留下空间strcpy。因此,它要么不是 0 终止的,要么是 0 字节被添加到可用空间之外 - 调用strlen它也是未定义的行为或至少是可疑的

  • 调用sizeofp可以的,并产生正确的值 5。

  • 调用strlen(q)产生 5 因为 q 确实包含一个 0 终止符 -通过使用字符串文字初始化隐式添加- 并且在 0 之前有 5 个字符

  • 由于它包含一个 0 终止符,q因此实际上是一个 6 个字符的数组,因此sizeof产生 6。

于 2013-10-03T00:30:37.167 回答
1
char p[5];
strcpy(p,"Hello");

复制 5 个字符p并将终止的空字符 ( '\0') 写入第 6 个位置,即超出此数组的范围,这会产生未定义的行为

从手册页strcpy

“如果 strcpy() 的目标字符串不够大,那么任何事情都可能发生。任何时候程序读取或复制数据到缓冲区,程序首先需要检查是否有足够的空间。”

于 2013-10-03T00:32:58.297 回答
0
   char p[5];

   strcpy(p,"Hello");

这个 strcpy 将 0 写入 p[5]。所以越界了。sizeof(p) 仍然是 5。你已经写到了 p 的末尾。这是不正确的,并导致未定义的行为。在这种情况下,没有发生任何不好的事情,并且没有引起注意。

您拥有的另一个字符串的长度为 5,大小为 6。

于 2013-10-03T00:28:28.107 回答
0

qchar 数组还包含空终止字符。虽然 的固定大小p不允许复制空字符。请注意,strlen它将检查空字符以计算字符串的字符数,因此没有空字符可能会导致未定义的行为。

于 2013-10-03T00:28:36.653 回答
0

sizeof(q) 为 6,因为它包含空终止符。

p 没有为空终止符保留足够的空间 - 所以 strlen(p) 可以是任何随机值。这称为未定义行为。

于 2013-10-03T00:30:26.183 回答
0

C 中的字符串以NUL字符 '\0' 结束;

这就是为什么sizeof(q)返回 6,它有足够的空间来存储'\0'最后的。您自己调整了 p 的大小,使其能够容纳 5 个字符,对于尾随 . 来说还不够'\0'

所以,这段代码是未定义的行为:

strcpy(p, "Hello");

这是复制'\0'into p[5],这是越界的。

于 2013-10-03T00:31:27.527 回答
0

问题:为什么两种数组声明类型的 sizeof() 输出存在差异?

回答:该语句声明了一个名为 q 的变量,类型为 char[],指向保存“Hello”的内存位置。

char q[] = "Hello";

sizeof(q) 是 6,因为字符串 "Hello" 由 'H','e','l','l','o','\0' 组成,其中包括计数中的 NULL 字符。

该语句声明了一个名为 p 的变量,类型为 char[],指向保留 5 个字符的内存位置。

char p[5];

请注意,根据编译器的内存对齐标志,您实际上可能在保留给 p 的位置保留了 6、8 或更多 char。如果您引用或分配 p[5] (这是 p[] 数组中的序数第六个字符),C 不会抱怨。

sizeof(p) 是 5,因为编译器记录了您为 p 声明的内存位置有多大。所以 sizeof(p) 和 sizeof(q) 返回不同的值,因为 p 和 q 的声明不同并且引用不同的实体。

问题:strlen() & printf() 如何知道何时停止,声明两个数组时没有添加空字符。

答案:两个 strlen() 函数调用都计算非 NULL 字符的数量。因此,两个 strlen 函数都调用 count char,直到它们找到 NULL 终止符。p 和 q 都具有,至少在 p+5 处的内存位置被分配另一个值之前。这是因为 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) 时,您复制的字符(NULL 终止符)比 p[] 分配的要多。这是你想要避免的事情。C 不对数组进行边界检查,所以它会让你执行 strcpy。虽然 lint 会检测到这个错误。

于 2013-10-03T01:45:20.950 回答