在c中声明可变长度的字符串 – 显然是动态的?

我试图理解一些关于如何在c中定义“字符串”的基本知识。

char s[2]; scanf("%s", s); printf("%s", s); printf("sizeof s %d", sizeof(s)); 

我不是程序员。 我知道使用scanf获取用户输入存在问题,我甚至没有检查其返回值等。这只是为了理解声明字符串的基本内容。

鉴于上面的代码,如果我输入’helloworld’,它的作用是print’helloworld’。 好。 但我想通过说char s [2]我说的是’s是一个长度为2的数组,其中每个元素都是char’类型。

因此,我期待看到’他’打印。 不是’helloworld’。 因为我的arrays只有2个字符的空间。

sizeof仍然返回2.但看起来我的数组已经增长到用户输入的大小。

怎么了?

两个问题:

  • C不对数组访问进行任何边界检查;
  • 在大多数情况下,数组类型的表达式将转换为指针类型的表达式,表达式的值将是数组的第一个元素的地址。 这包括作为参数传递给函数的数组表达式。

%s转换规范告诉printf打印从指定地址开始的字符序列 ,直到它在字符串末尾看到0终止符。 类似地,它告诉scanf存储从指定地址开始的字符序列,直到它看到空白字符或EOF。

当您将表达式 s作为参数传递给scanfprintf ,表达式将从类型“2-element array of char ”转换为“指向char指针”,并且表达式的值是第一个元素的地址。数组(它相当于传递表达式&s[0] )。

所有scanf接收的都是指针值 – 它不知道从该地址开始的数组有多大。 所以它不知道s只有大到足以包含两个字符。 相反,它愉快地将那些额外的字符写在数组的末尾。 类似地, printf不知道该数组只有2个字符宽 – 它只是保持打印直到它看到0终止符。

您可以指定字段宽度作为转换的一部分:

 scanf( "%1s", s ); 

这将从标准输入中读取最多 1个字符并将其存储到s 。 请记住, 字符串是一个字符序列,后跟一个0终结符,因此要存储一个N字符的字符串,您需要预留一个N + 1元素数组来存储它。

这显示了为什么scanf如此危险的完美原因。 你已经覆盖了其他不适用于此的内存。 读取常量长度字符串的更安全的方法是执行类似的操作

 char a[2]; fgets(a, sizeof(a), stdin); printf("%s\n", a); 

如果你这样做并且输入了helloworld那么你只能得到hdd(因为字符串有sizeof(a) - 1由于null终止符而有sizeof(a) - 1字符。这意味着数组a实际上是{'h', '\0'} ) 。 读取常量大小的字符串比使用scanf更安全。

我认为还没有人真正回答过这个问题。 这里有两个问题……

首先,数组“s”只有2个字节长,因此任何超过1个字符的字符串的scanf都会产生不希望的结果。

 char s[255]; // was char s[2]; 

另外,如前所述, scanf是一种输入字符串的可怕方式。 请改用fgets

 fgets(s, sizeof(s), stdin); // was scanf("%s", s); 

其次,你不能使用sizeof来获取字符串的长度。 它会返回包含字符串的数组的长度(不是你想要的,相信我)。 相反,使用strlen

 printf("strlen s %d", strlen(s)); // was printf("sizeof s %d", sizeof(s));