考虑以下案例:
#include<stdio.h>
int main()
{
char A[5];
scanf("%s",A);
printf("%s",A);
}
我的问题是char A[5]
是否只包含两个字符。说“ab”,然后是A[0]='a'
,A[1]='b'
和A[2]='\0'
。
但是如果输入是“abcde”,那么在这种情况下'\0'
。 A[5]
会'\0'
吗?
如果是,为什么?
sizeof(A)
将始终返回5作为答案。然后,当数组已满时,是否为'\0'
保留了sizeof()
不计算的额外字节?
答案 0 :(得分:50)
如果键入超过四个字符,则额外字符和空终止符将写在数组末尾之外,覆盖不属于该数组的内存。这是一个缓冲区溢出。
C不会阻止你破坏你不拥有的记忆。这导致 undefined behavior 。你的程序可以做任何事情 - 它可能会崩溃,它可能会默默地删除其他变量并导致混乱的行为,它可能是无害的,或其他任何东西。请注意,无法保证您的程序可靠运行或可靠运行。你甚至不能立即依赖它崩溃。
这是一个很好的例子,说明为什么scanf("%s")
是危险的,永远不应该使用。它不知道你的阵列的大小,这意味着没有办法安全地使用它。相反,请避免使用scanf并使用更安全的内容,例如fgets():
fgets()从流中读取最多一个小于字符的字符,并将它们存储到s指向的缓冲区中。读数在EOF或换行符后停止。如果读取换行符,则将其存储到缓冲区中。终止空字节('\ 0')存储在缓冲区中的最后一个字符之后。
示例:
if (fgets(A, sizeof A, stdin) == NULL) {
/* error reading input */
}
令人讨厌的是,fgets()会在数组的末尾留下一个尾随的换行符('\ n')。所以你可能还想要代码来删除它。
size_t length = strlen(A);
if (A[length - 1] == '\n') {
A[length - 1] = '\0';
}
唉。一个简单的(但破碎的)scanf("%s")
变成了7行怪异。这是当天的第二课:C不擅长I / O和字符串处理。它可以完成,它可以安全地完成,但C会一直踢和尖叫。
答案 1 :(得分:8)
正如已经指出的那样 - 你必须定义/分配一个长度为N + 1的数组,以便正确存储N个字符。可以限制scanf读取的字符数量。在您的示例中,它将是:
scanf("%4s", A);
为了读取最大值来自stdin的4个字符。
答案 2 :(得分:3)
没有保留的字符,因此必须注意不要将整个数组填充到无法终止的位置。 Char函数依赖于null终止符,如果你发现自己处于你描述的情境中,你将会从中获得灾难性的结果。
您将看到的许多C代码将使用strncpy等函数的'n'派生词。从该手册页中,您可以阅读:
strcpy()和strncpy()函数返回s1。 stpcpy()和 stpncpy()函数返回一个 指向s1的终止“\ 0”字符的指针。如果stpncpy()没有用NUL终止s1 它返回一个指向s1 [n]的指针(它不一定是指有效的存储器) ory location。)
strlen还依赖于null字符来确定字符缓冲区的长度。如果您错过了该角色,您将得到不正确的结果。
答案 3 :(得分:3)
您将最终得到未定义的行为。
正如你所说,A
的大小总是5,所以如果你读了5个或更多char
s,scanf
会尝试写入内存,那不是应该修改。
不,\0
符号没有保留的空格/字符。
答案 4 :(得分:3)
任何长度超过4个字符的字符串都会导致scanf
超出数组范围。由此产生的行为是未定义的,如果您很幸运,将导致您的程序崩溃。
如果您想知道为什么scanf
不会停止编写太长而无法存储在数组A
中的字符串,那是因为scanf
无法知道{ {1}}是5.当您将数组作为参数传递给C函数时,数组将衰减到指向数组中第一个元素的指针。因此,无法在函数内查询数组的大小。
为了限制读入数组的字符数,请使用
sizeof(A)
答案 5 :(得分:3)
c中的字符数组只是指向内存块的指针。如果你告诉编译器为字符保留5个字节,那就是。如果您尝试在那里放入超过5个字节,它将覆盖超过您保留的5个字节的内存。
这就是为什么c可以有严格的安全实现。你必须知道你只会写4个字符+一个\ 0。 C将允许您覆盖内存,直到程序崩溃。
请不要将char foo [5]视为字符串。可以把它想象成放置5个字节的点。你可以在那里存储5个字符而不是null,但你必须记住你需要做一个memcpy(otherCharArray,foo,5)而不是使用strcpy。您还必须知道otherCharArray有足够的空间容纳这5个字节。
答案 6 :(得分:-1)
空字符用于终止数组。它位于数组的末尾,表明数组在该点结束。数组自动将最后一个字符作为空字符,以便编译器可以很容易地理解数组已结束。
答案 7 :(得分:-3)
\ 0是一个终结符运算符,它在数组已满时终止 如果数组未满,则\ 0将位于数组的末尾 当你输入一个字符串时,它将从数组的末尾读取