为什么溢出char数组影响另一个数组?

时间:2012-10-16 14:23:06

标签: c

您好。这就是难题。我有这段代码:

#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的值也会发生变化。你会期望它保持&#34; BANG&#34;,但事实并非如此。我想对此有一个解释。谢谢!

7 个答案:

答案 0 :(得分:2)

您正在创建“缓冲区溢出”。该数组的大小只能容纳5个字节(4个字符加上标准的C字符串终结符),如果你把它放在那里,那么剩下的就会溢出。

通常情况下,重要的是让程序崩溃。

有自动工具(例如valgrind)来检测此类错误。

答案 1 :(得分:2)

如果字符串足够长,则会出现缓冲区溢出并且行为未定义,包括覆盖其他阵列甚至导致应用程序崩溃。因为行为是未定义的,所以应该避免使用它,但只是为了理解,编译器在内存中的a数组之后(在编译器的这个特定运行中)布置了b数组。当您写b+sizeof(b)时,您正在写信a[0]

答案 2 :(得分:2)

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

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

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

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

答案 3 :(得分:2)

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

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

所以当你做“bang”的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

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

答案 4 :(得分:1)

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

有效地分配&#39; a&#39;减去当前堆栈指针的5个字节(x86 / x64上的esp / rsp)。分配&#39; b&#39;然后再减去5个字节。

因此,当您进行第一次堆栈分配时,假设您的esp为0x1000。这给出了一个&#39;内存地址0xFB。 &#39; B&#39;然后将得到0xF6,因此0xF6的第6个字节(即索引5)是0xF6 + 5或0xFB,因此您现在正在写入数组中的一个。

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

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

您将看到b的内存地址低于。

答案 5 :(得分:0)

C对内存访问没有边界检查,因此您可以自由地读取和写入数组的声明结尾。 ab可能会在内存中相邻,即使按照其声明的相反顺序排列也是如此,因此除非您的代码注意以读取比例如更多的字符。属于b,您可以损坏a。实际发生的事情是未定义的,并且可能会在不同的运行中发生变化。

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

答案 6 :(得分:0)

b只有5个字母。因此,如果你写一个更长的字符串,你正在写入与b相邻的内存。