在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
作为参数传递给scanf
或printf
,表达式将从类型“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));