我正在读关于缓冲区溢出的内容。我在堆栈上找到了关于局部变量的内存赋值的一个奇怪的事情
int f1 ()
{
char string1[12];
char string2[4];
}
这里的分配发生在堆栈上。
现在,在 GCC 中,string2被分配了4个字节,但是如果我声明除了2的幂(最多16个)之外,那么它由编译器分配16个字节。这意味着如果我在3,5,6,7,....,15字节中分配string2然后由编译器分配16个字节,但如果我分配2的幂,如1,2,4,8 ...然后它被分配完全相同的大小。如果我分配超过16个字节(不是2的幂),那么它分配32个字节(我估计最多32个字节)。
在Visual Studio中,如果我分配1个字节,则分配9个字节,如果从2-4个字节分配,则分配12个字节,如果从5-8个字节开始,则编译器分配16个字节。
任何人都知道为什么会这样的任务?
Atleast在Visual Studio中,如果有缓冲区溢出,我会收到调试错误,但在gcc中没有任何反应。只有在发生过大的溢出时,GCC才会提供分段错误。
答案 0 :(得分:12)
堆栈帧大小受内存对齐选择的影响,对于32位代码通常是4的倍数,对于64位代码通常是8的倍数。
两个编译器都可以包含使用 canary 进行堆栈帧损坏检查,堆栈顶部的额外32位值在函数入口处初始化并在函数出口处检查。如果更改了canary值,则会发生程序中止,因为堆栈帧可能被恶意代码损坏,可能会更改函数返回地址并使其返回到任意位置。一种非常流行的恶意软件注入媒介。
MSVC具有/ RTC选项,默认情况下在Debug配置中启用。在每个局部变量之间添加这些金丝雀。因此,它可以检测每个变量上的缓冲区溢出问题。
这些金丝雀当然会占用额外的空间,影响堆栈的帧大小。
答案 1 :(得分:5)
那是由于memory-aligment。 CPU更容易访问内存地址,这些内存地址是所请求数据大小的倍数 因此,编译器使用明确的字节填充结构以对齐结构。例如:
struct foo
{
char a
int b , c;
short d;
bool e;
double f;
};
理论上,该结构的大小是(假设bool
的大小是1个字节,char
1个字节,int
4个字节,short
2个字节,{ {1}} 8个字节)20个字节。但实际上,编译器会在结构中添加“空洞”以对齐内存:
double
答案 2 :(得分:0)
我记得这是优化的原因,也许是因为要解决这个问题。像这样的另一个例子是布尔类型,它通常消耗8位,可能只是一个。这可能与不同的编译器有很大不同。您可以在Why is a char and a bool the same size in c++?
中找到有关布尔值的更多信息