我在 mac 和 linux 上运行了这段代码:
#include <stdio.h>
#include <string.h>
int main (int argc, char *argv[]){
int value = 5;
char buffer_one[8], buffer_two[8];
strcpy(buffer_one, "one");
strcpy(buffer_two, "two");
printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
printf("[BEFORE] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);
printf("[BEFORE] value is at %p and is %i (0x%08x)\n", &value, value, value);
printf("\n[STRCPY] copying %i bytes into buffer two\n\n", strlen(argv[1]));
strcpy(buffer_two, argv[1]);
printf("[AFTER] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
printf("[AFTER] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);
printf("[AFTER] value is at %p and is %i (0x%08x)\n", &value, value, value);
}
在 mac 上,如果我输入“1234567890”作为命令行参数,90 就会溢出到缓冲区 1,因为 8 字节的缓冲区超出了 2。
但是,如果我在我的 Linux 系统上运行它,需要更多的字符才能溢出缓冲区。为什么我可以在 Linux 中执行缓冲区?
另请注意,在两个系统上,整个字符串仍将打印在缓冲区二中,只有溢出的项目打印在缓冲区一中。为什么会这样?为什么其他角色不直接去下一个?如果这个问题措辞不当,请举个例子:
如果我在我的 mac 上输入 1234567890,1234567890 将打印在缓冲区二中,90 将打印在缓冲区一中。即使 90 已经溢出,它怎么能仍然适合缓冲区二。 (在 linux 上是相同的概念,但需要超过 10 个字节才能溢出)
最佳答案
第一个问题的答案是内存中变量的对齐是实现定义的。 (参见 C11 draft 中的第 6.2.8 节“对象对齐”。)基本上,不同的编译器可能要求内存中两个对象之间的最小字节数不同。您在 Mac 上使用的编译器将堆栈中的两个 8 字节缓冲区紧挨着打包,可能是因为 char[]
的对齐方式为 8 字节或更少字节。您在 Linux 上使用的编译器在两个地址之间留下了更多字节,可能是因为 char[]
的对齐方式是 16 字节。
对于第二个问题,buffer_one
和 buffer_two
只是您的程序可以访问的连续内存块中的两个地址。这种情况下,由于栈的实现,buffer_two
出现在内存中比buffer_one
低的地址,所以写入buffer_two
的数据溢出到 buffer_one
。您从 buffer_two
打印“1234567890”和从 buffer_one
打印“90”的原因是 printf()
开始读取您提供的地址处的字节直到它读取一个空终止符 (0x00
)。
所以,当你strcpy()
“1234567890”到buffer_two
时,你实际上写了11个字节,包括空终止符(0x00
) 在字符串的末尾。在您的 Mac 上,buffer_two
和 buffer_one
相隔 8 个字节,因此当 printf()
从 buffer_two
读取时,它在看到空终止符之前读取 10 个字符,空终止符恰好在 buffer_one
指向的地址之后。当 printf()
从 buffer_one
读取时,它会在看到空终止符之前读取 2 个字符。
关于c - 为什么这个字符串不会溢出缓冲区?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34036389/