当输入的字符串长于指定的大小时,请解释输出
#include<stdio.h>
int main()
{
char name[21],address[31];
puts("enter a name(max 21 characters)");
gets(name);
fflush(stdin);
puts("enter an address(max 31 characters)");
gets(address);
fflush(stdin);
puts("your name is:");
puts(name);
puts("Your address is:");
puts(address);
return 0;
}
答案 0 :(得分:0)
当您输入超过要求的大小时,由于不检查边界,您将获得未定义的行为,并且在保留的空格之后可以(或不能,未定义的行为)写入太多的字符。你应该在C中使用fgets(有边界检查),在C ++中使用std :: getline。
答案 1 :(得分:0)
大多数C编译器会在堆栈中分配name
和address
,在内存中彼此相邻。我在Linux机器上使用GCC进行了测试,address
在内存中name
之后进行了测试;其他C编译器也可以反过来做。出于内存对齐的原因,编译器还可以在name
和address
之间分配几个字节的未使用空间;虽然这对于char数组来说并不常见。
我们假设address
位于name
之后。内存分配如下(最上面的地址):
name[0]
name[1]
name[2]
...
name[19]
name[20]
address[0]
address[1]
address[2]
...
address[29]
address[30]
base pointer
return address
stack frame of caller
正如mafso所指出的,字符串存储有一个尾随的NUL字符,因此名称最多应为20个字符(其最后一个字符位于name[19]
,name[20]
中的NUL终结符),地址不应超过30个字符。
输入一个21个字符的名称,NUL终结符将在address[0]
中,但此后不会被地址覆盖。输入21以外的任何其他字符也将被覆盖。 puts(name)
将打印name
中存储的21个字符,然后是用户在address
中输入的内容;有效地,名称和地址连接在一起。 puts(address)
将以正常方式打印地址,因为它会在address[0]
开始打印。
任何输入超过30个字符的地址的尝试都会覆盖基址指针和/或返回地址,这很可能会使函数main
在返回时崩溃。显然,当您输入超过51个字符的名称时也会发生同样的情况。
通过仔细制作将覆盖返回地址的多余字符,可以使程序执行除崩溃之外的其他操作。熟练的黑客可以利用这个来获得好运;这就是缓冲区溢出漏洞如此臭名昭着的原因。