为什么 char name[1] 可以容纳 1 个以上的字符?

Why char name[1] can hold more than 1 character?

遇到这种情况时,我正在对一个主题进行一些研究。 假设以下 C 代码:

#include <stdio.h>
int main() {
char name[1];
scanf("%s",name);
printf("Hi %s",name);
return 0;
}

我用 -fno-stack-protector 编译并用长于 1 的输入测试它,比如 John,令我惊讶的是,它有效!
当输入长于 1 时,它不应该抛出分段错误吗?
最终它打破了 Alexander 作为输入 (9) 但它适用于任何小于 9.
为什么它使用比名称数组长度更长的输入?
P.S :我正在使用 Ubuntu(64 位),gcc 版本 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04) & CLion 作为 IDE.

您存储输入的变量的大小和类型与scanf无关。

scanf 仅传递一个地址(指针),用于存放从用户那里获得的输入。

如果传递给 scanf 的格式字符串与参数类型不匹配,聪明的编译器现在会警告您,但原则上您甚至可以将 name 声明为整数:

int name;

它可以很好地保存输入字符串,最多三个字符(第四个用于字符串结尾,即零),假设 int 的大小为 32 位,即 4 个字节

它能工作的事实纯属倒霉,因为当输入数据由 scanf 存储时,运行超过了为其分配的缓冲区的末尾 (name)。

注意:只为一个字符串分配一个字符是行不通的,即使对于只有一个字符的输入字符串也是如此。您始终需要考虑用于终止它们的 EOS。所以 name 至少应该声明为 char name[2];

这是未定义的行为。您的程序发生了缓冲区溢出,因为它恰好分配了一个字符,足以存储一个空的以 null 结尾的字符串。

但是,与您的缓冲区相邻的内存尚未分配给您的程序。 scanf 将您的输入放入该内存中,因为它不知道您的字符串缓冲区有多长。这是一个很大的危险,也是无数黑客攻击的根源,当一个预先确定的字节序列被放入您的字符串中,希望覆盖一些重要元素,并最终获得控制权。

这就是为什么在不指定大小的情况下使用 %s 是危险的。您需要始终为 %s 添加适当的大小限制,否则您的程序有缓冲区溢出的危险。

char name[120];
scanf("%119s",name);

这个程序是安全的,因为即使恶意用户键入超过 120 个字符,scanf 也会忽略第 119 个字符之后的所有内容,如 %119s 格式所指定。