1

你好。这是难题。我有这个代码:

#include<stdio.h>
#include<conio.h>
#include<string.h>
int main(){

char a[5];
char b[5];

memset(a, 0, 5); 
memset(b, 0,5);

strcpy(a, "BANG");

printf("b = "); 

scanf("%s", &b);
printf("a = %s\n", a);  
getch();
}

当你运行它时,你会注意到如果你将足够长的字符串读入b, 的值a也会改变。你会期望它保持“BANG”,但事实并非如此。我想对此作出解释。谢谢!

4

7 回答 7

2

恭喜,您遇到了第一次缓冲区溢出(首先您知道 :))。

数组将分配在程序的堆栈中,并且这些数组是相邻的。由于 C 不检查是否违反数组边界,因此您可以将内存的任何允许部分作为任何数组的单元来访问。

让我们回顾一个非常常见的运行时示例,这个程序在 x86 上运行。x86 上的堆栈正在增长到最少的地址,因此通常编译器将其放在堆栈a[]之上。b[]当您尝试访问b[5]时,它的地址将与a[0]b[6]isa[1]等相同。

这就是缓冲区溢出漏洞的工作原理:一些粗心的程序员没有检查缓冲区中的字符串大小,然后一个邪恶的黑客将他的恶意代码写入堆栈并运行它。

于 2012-10-16T14:32:00.150 回答
2

根据程序的内存来考虑它。 a是 5 个字符b的数组, 是 5 个字符的数组。你的堆栈上有这样的东西:

[0][0][0][0][0][0][0][0][0][0]
 ^              ^
 |              +--"a" something like 0xbfe69e52
 +-----------------"b" something like 0xbfe69e4d

因此,当您执行“砰”的 strcpy 时:

[0][0][0][0][0][B][A][N][G][0]
 ^              ^
 |              +--"a" something like 0xbfe69e52
 +-----------------"b" something like 0xbfe69e4d

现在,如果您将“长”字符串放入b

[T][h][i][s][I][s][l][o][n][g]
 ^              ^
 |              +--"a" something like 0xbfe69e52
 +-----------------"b" something like 0xbfe69e4d

哎呀,刚刚输了a。这是一个“缓冲区溢出”,因为您溢出b了(a在这种情况下)。C不会阻止你这样做。

于 2012-10-16T14:42:45.693 回答
2

您正在创建“缓冲区溢出”。该数组的大小只能容纳 5 个字节(4 个字符加上标准 C 字符串终止符),如果放得更多,其余的就会溢出。

通常,进入一些重要的事情,使你的程序崩溃。

有自动化工具(例如valgrind)来检测这种错误。

于 2012-10-16T14:25:45.750 回答
2

如果字符串足够长,您将遇到缓冲区溢出并且行为未定义,包括覆盖另一个数组甚至使应用程序崩溃。因为行为是未定义的,你应该避免它,但只是为了理解,编译器已经在内存中的a数组之后布置了数组b(在编译器的这个特定运行中)。当您写信时,b+sizeof(b)您正在写信给a[0].

于 2012-10-16T14:26:29.977 回答
1

上面每个人似乎都忘记提及的一件事是堆栈通常以与您期望的相反方向处理。

从当前堆栈指针(esp/rsp on x86/x64)有效地分配“a”减去 5 个字节。'b' 的分配然后再减去 5 个字节。

因此,当您进行第一次堆栈分配时,假设您的 esp 为 0x1000。这为“a”提供了内存地址 0xFB。'b' 然后将得到 0xF6,因此 0xF6 的第 6 个字节(即索引 5)是 0xF6 + 5 或 0xFB,因此您现在正在写入 a 的数组。

这可以通过以下代码轻松确认(假设为 32 位):

printf( "0x%08x\n", a );
printf( "0x%08x\n", b );

你会看到 b 的内存地址比 a 低。

于 2012-10-16T14:49:00.697 回答
0

b 只有 5 个字母。所以如果你写一个更长的字符串,你写的是与 b 相邻的内存。

于 2012-10-16T14:30:34.830 回答
0

C 对内存访问没有边界检查,因此您可以在数组声明的末尾自由读写。a并且b可能最终在内存中相邻,即使与它们的声明顺序相反,所以除非你的代码注意不要读取比 eg 更多的字符 ,否则b你可能会损坏a. 实际发生的事情是未定义的,并且可能会随着运行而改变。

在这种特殊情况下,请注意您可以通过scanf在格式字符串中使用宽度来限制读取的字符数:scanf("%4s", &b);

于 2012-10-16T14:26:06.270 回答