执行以下代码时,我一直在检查堆内存:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
struct data {
char name[64];
};
struct fp {
int (*fp)();
};
void winner()
{
printf("level passed\n");
}
void nowinner()
{
printf("level has not been passed\n");
}
int main(int argc, char **argv)
{
struct data *d;
struct fp *f;
d = malloc(sizeof(struct data));
f = malloc(sizeof(struct fp));
f->fp = nowinner;
printf("data is at %p, fp is at %p\n", d, f);
strcpy(d->name, argv[1]);
f->fp();
}
代码编译如下:
gcc winner.c -w -g -fno-stack-protector -z norelro -z execstack -o winner
(稍后会添加更多代码,因此fno-stack-protector
等标记就会存在)
我使用参数“HELLO”执行了项目,并在f->fp()
上设置了一个断点并检查堆内存:
第一个malloc()
之后的所有内容都有意义,但我对第二个malloc()
之后发生的事情感到有些疑惑。第二个块应该只请求4个字节的内存来存储函数指针,而是需要12个字节,这反映了存储在地址0x804a04c
上的内容(4个字节的元数据+ 12个字节的请求内存+状态位1 = 17 =&gt; 0x00000011)。
正如您所看到的,函数指针只占用0x804a050
上的四个字节,地址为nowinner
(0x080484a1
)。
答案 0 :(得分:3)
通过打印指针的sizeof
,可以非常轻松地回答您的初始问题。你不会在这里看到12。
你的问题的答案“为什么函数指针长12个字节?”简单地说:“它不是!”
但您的问题描述了一个不同的基本问题: “为什么在堆上分配4个字节需要12个字节?”
您错误地认为内存分配只需要存储用户数据所需的内容。
这是错的。
内存管理还需要为每个分配存储一些管理数据。
当您调用free
时,运行时库需要知道已分配块的大小。
因此,您可以认为每次分配都会消耗比请求数量更多的内存。
根据堆的实现,这可以在堆本身内或在单独的区域中。 您也不能依赖于为每个分配花费相同的开销。那里有奇怪的实施。
某些实现采用请求的数量并添加固定长度的管理数据。 一些实现使用伙伴系统并遵循一系列斐波那契数来确定最小的合适块大小。