考虑以下案例:
#include<stdio.h>
int main()
{
char A[5];
scanf("%s",A);
printf("%s",A);
}
Run Code Online (Sandbox Code Playgroud)
我的问题是char是否A[5]
只包含两个字符.说"ab",然后A[0]='a'
,A[1]='b'
和A[2]='\0'
.但是,如果输入是"abcde",那么'\0'
在那种情况下.会A[5]
包含'\0'
?如果是,为什么?
sizeof(A)
将总是返回5作为答案.然后,当阵列是满的,是有保留一个额外的字节'\0'
这sizeof()
不算数?
Joh*_*ica 50
如果键入超过四个字符,则额外字符和空终止符将写在数组末尾之外,覆盖不属于该数组的内存.这是一个缓冲区溢出.
C不会阻止你破坏你不拥有的记忆.这导致未定义的行为.你的程序可以做任何事情 - 它可能会崩溃,它可能会默默地删除其他变量并导致混乱的行为,它可能是无害的,或其他任何东西.请注意,无法保证您的程序可靠运行或可靠运行.你甚至不能立即依赖它崩溃.
这是一个很好的例子,说明为什么scanf("%s")
是危险的,不应该使用.它不知道你的阵列的大小,这意味着没有办法安全地使用它.相反,避免使用scanf并使用更安全的东西,比如fgets():
fgets()从流中读取最多一个小于大小的字符,并将它们存储到s指向的缓冲区中.读数在EOF或换行符后停止.如果读取换行符,则将其存储到缓冲区中.终止空字节('\ 0')存储在缓冲区中的最后一个字符之后.
例:
if (fgets(A, sizeof A, stdin) == NULL) {
/* error reading input */
}
Run Code Online (Sandbox Code Playgroud)
令人讨厌的是,fgets()会在数组的末尾留下一个尾随的换行符('\n').所以你可能也想要代码删除它.
size_t length = strlen(A);
if (A[length - 1] == '\n') {
A[length - 1] = '\0';
}
Run Code Online (Sandbox Code Playgroud)
啊.一个简单的(但破碎的)scanf("%s")
变成了7行的怪物.这是当天的第二课:C不擅长I/O和字符串处理.它可以完成,并且可以安全地完成,但是C会一直踢和尖叫.
正如已经指出的那样 - 你必须定义/分配一个长度为N + 1的数组,以便正确存储N个字符.可以限制scanf读取的字符数量.在您的示例中,它将是:
scanf("%4s", A);
Run Code Online (Sandbox Code Playgroud)
为了读取最大值 来自stdin的4个字符.
c 中的字符数组只是指向内存块的指针。如果您告诉编译器为字符保留 5 个字节,它就会这样做。如果您尝试在其中放入超过 5 个字节,它只会覆盖您保留的 5 个字节之后的内存。
这就是为什么 c 可以有严格的安全实现。你必须知道你只会写4个字符+一个\0。C 会让你覆盖内存,直到程序崩溃。
请不要将 char foo[5] 视为字符串。将其视为放置 5 个字节的位置。您可以在其中存储 5 个不带 null 的字符,但您必须记住您需要执行 memcpy(otherCharArray, foo, 5) 而不是使用 strcpy。您还必须知道 otherCharArray 有足够的空间容纳这 5 个字节。