空数字符串终止

g4u*_*r4v 35 c null char

考虑以下案例:

#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会一直踢和尖叫.


har*_*pun 8

正如已经指出的那样 - 你必须定义/分配一个长度为N + 1的数组,以便正确存储N个字符.可以限制scanf读取的字符数量.在您的示例中,它将是:

scanf("%4s", A);
Run Code Online (Sandbox Code Playgroud)

为了读取最大值 来自stdin的4个字符.


Dou*_*oug 5

c 中的字符数组只是指向内存块的指针。如果您告诉编译器为字符保留 5 个字节,它就会这样做。如果您尝试在其中放入超过 5 个字节,它只会覆盖您保留的 5 个字节之后的内存。

这就是为什么 c 可以有严格的安全实现。你必须知道你只会写4个字符+一个\0。C 会让你覆盖内存,直到程序崩溃。

请不要将 char foo[5] 视为字符串。将其视为放置 5 个字节的位置。您可以在其中存储 5 个不带 null 的字符,但您必须记住您需要执行 memcpy(otherCharArray, foo, 5) 而不是使用 strcpy。您还必须知道 otherCharArray 有足够的空间容纳这 5 个字节。