【发布时间】:2014-02-12 17:38:32
【问题描述】:
void demo()
{
printf("demo");
}
int main()
{
printf("%p",(void*)demo);
return 0;
}
上面的代码打印出函数demo的地址。
所以如果我们可以打印出一个函数的地址,那意味着这个函数存在于内存中并且占用了其中的一些空间。
那么它在内存中占用了多少空间呢?
【问题讨论】:
void demo()
{
printf("demo");
}
int main()
{
printf("%p",(void*)demo);
return 0;
}
上面的代码打印出函数demo的地址。
所以如果我们可以打印出一个函数的地址,那意味着这个函数存在于内存中并且占用了其中的一些空间。
那么它在内存中占用了多少空间呢?
【问题讨论】:
您可以使用objdump -r -d 亲自查看:
0000000000000000 <demo>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: bf 00 00 00 00 mov $0x0,%edi
5: R_X86_64_32 .rodata
9: b8 00 00 00 00 mov $0x0,%eax
e: e8 00 00 00 00 callq 13 <demo+0x13>
f: R_X86_64_PC32 printf-0x4
13: 5d pop %rbp
14: c3 retq
0000000000000015 <main>:
我获取了您的代码并编译(但未链接!)它。使用objdump,您可以看到编译器布置要运行的代码的实际方式。归根结底,没有函数这样的东西:对于 CPU 来说,它只是跳转到某个位置(在这个清单中恰好被标记)。所以“函数”的大小就是包含它的代码的大小。
似乎有些混淆,这不是“真正的代码”。 GDB 是这样说的:
Dump of assembler code for function demo:
0x000000000040052d <+0>: push %rbp
0x000000000040052e <+1>: mov %rsp,%rbp
0x0000000000400531 <+4>: mov $0x400614,%edi
0x0000000000400536 <+9>: mov $0x0,%eax
0x000000000040053b <+14>: callq 0x400410 <printf@plt>
0x0000000000400540 <+19>: pop %rbp
0x0000000000400541 <+20>: retq
这是完全相同的代码,大小完全相同,由链接器修补以使用真实地址。 gdb 以十进制打印偏移量,而objdump 使用更有利的十六进制。如您所见,在这两种情况下,大小都是 21 字节。
【讨论】:
所以如果我们可以打印一个函数的地址,那就意味着这个 函数存在于内存中并占用了一些空间。
是的,您编写的函数会被编译成存储在内存中的代码。(在解释型语言的情况下,代码本身保存在内存中并由解释器执行。) p>
那么它在内存中占用了多少空间呢?
内存量完全取决于函数。您可以编写一个很长的函数,也可以编写一个很短的函数。长的将需要更多的内存。但是,用于代码的空间通常不是您需要担心的,除非您在具有严重内存限制的环境中工作,例如在非常小的嵌入式系统上。在具有现代操作系统的台式计算机(甚至移动设备)上,虚拟内存系统将根据需要将代码页面移入或移出物理内存,因此您的代码也几乎不会消耗很多内存。
【讨论】:
void 的空函数编译为单个 ret 指令(或类似指令)。
(In the case of an interpreted language, the code itself is kept in memory and executed by an interpreter.) 严格来说这不是真的,大多数现代语言编译成一些的较低级别的字节码,而不是在内存中存储文本或 AST。
当然是在内存中占空间,一旦你执行整个程序就会被加载到内存中。通常,程序指令存储在内存空间的最低字节中,称为text section。你可以在这里阅读更多信息:http://www.geeksforgeeks.org/memory-layout-of-c-program/
【讨论】:
是的,您在代码中使用的所有函数都会占用内存空间。但是,内存空间不一定专属于您的功能。例如,inline 函数将占用调用它的每个函数内部的空间。
该标准没有提供一种方法来判断函数在内存中占用了多少空间,因为指针算术,让您计算数据内存中连续内存区域大小的技巧,没有为函数指针定义。此外,ISO C 禁止将函数指针转换为对象指针类型,因此您无法通过将函数指针转换为 char* 来绕过此限制。
printf("%p",demo);上面的代码打印出函数
demo()的地址。
这是未定义的行为:%p 需要 void*,而您传递给它的是 void (*)()。您应该会看到一个编译器警告,告诉您您正在执行的操作无效 (demo)。
【讨论】:
至于确定它占用的内存量,这在运行时是不可能的。但是,还有其他方法可以确定它: How to get the length of a function in bytes?
【讨论】:
这些函数被编译成机器代码,只能在特定的 ISA(x86,如果要在手机上运行,可能是 ARM 等)上运行。由于不同的处理器可能需要更多或更少的指令来运行相同的函数,而且指令的长度也可能不同,在编译之前无法提前知道函数的确切大小。
即使您知道它将针对什么处理器和操作系统进行编译,不同的编译器也会根据它们使用的指令以及优化代码的方式创建不同的、等效的函数表示。
另外,请记住,函数以不同的方式占用内存。我认为您在谈论代码本身,这是它自己的部分。在执行过程中,函数也可以占用栈空间——每次调用函数时,都会以栈帧的形式占用更多的内存。数量取决于函数声明的局部变量和参数的数量和类型。
【讨论】:
what in the function is occupying that memory as it is empty? 只是一个回报。只需按照@cnicutar 的方法进一步探索。但请记住,您将探索 machine 和 compiler 的特定行为,这可能在语言标准中未指定。
是的,但是您可以将其声明为内联,因此编译器将获取源代码并将其移动到您调用该函数的任何位置。或者您也可以使用预处理器宏。虽然请记住使用内联会生成更大的代码,但它会执行得更快,并且编译器可以决定忽略您的内联请求,如果它觉得它会变得很大。
【讨论】: