在哪个内存段中我们将找到C中函数的内存地址

In Which memory segment we shall find the memory address of a function in C

函数指针指向C中函数的地址。这意味着函数有一个内存地址,它将成为任何内存段的一部分。我刚刚打印了函数地址并指向代码段。是不是所有用C写的函数在代码段都有地址?

#include <stdio.h>

void func()
{
        printf("hi!!!\n");
}

int main()
{
        void (*fptr)();
        fptr = func;
        printf("fptr pointing func addr : %p and &fptr : %p\n", fptr, &fptr);
        fptr();
        while (1) {
                sleep(1); /* Sleep for accessing procfs of the process */
        }
        return 0;
}

程序输出:

[revarath@bgl-vms-vm0251 basic]$ ./a.out &
[1] 20168
[revarath@bgl-vms-vm0251 basic]$ fptr pointing func addr : 0x4005ad and &fptr : 0x7fff301e6b80
hi!!!
c = 10

正在查找 PID 以访问 procfs:

[revarath@bgl-vms-vm0251 basic]$ ps -ef | grep a.out
revarath 20168 24339  0 04:55 pts/2    00:00:00 ./a.out
revarath 20180 24339  0 04:56 pts/2    00:00:00 grep --color=auto a.out

内存映射输出:

[revarath@bgl-vms-vm0251 basic]$ cat /proc/20168/maps
00400000-00401000 r-xp 00000000 00:2c 124964755                          /ws/revarath-bgl/backup/tests/basic/a.out
00600000-00601000 r--p 00000000 00:2c 124964755                          /ws/revarath-bgl/backup/tests/basic/a.out
00601000-00602000 rw-p 00001000 00:2c 124964755                          /ws/revarath-bgl/backup/tests/basic/a.out
.......
7fff301c8000-7fff301e9000 rw-p 00000000 00:00 0                          [stack]

我的函数地址:0x4005ad 位于内存区域 00400000-00401000

我的Cpu信息:多核英特尔

[revarath@bgl-vms-vm0251 basic]$ lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                8

编译器:

gcc
gcc version 4.8.5 20150623 (Red Hat 4.8.5-36)

函数的内存地址是OR指向代码段的说法正确吗?

这里有更多语言律师的答案(比如,不要依赖代码中的系统实现,请参阅下面的免责声明),但基本上,是的,这就是它的工作原理。

您程序中的所有已编译代码,包括您所说明的函数,都是生成的二进制映像中代码段(也容易混淆地称为 "text" 段)的一部分。 OS 的加载程序会将那些编译后的代码字节放入进程中的内存段,如果您以这种方式获取静态函数的地址,您会看到其对应的代码位于该段的某处。 main 等也是如此。


编译后的程序进入内存并执行的方式远没有看起来那么神奇。撇开现代 OS 的安全机制等不谈,它有点像这样:

  1. 工具链将所有 C 代码转换为可执行字节流。每个编译函数都必须从该字节流中的某个特定偏移量开始。
  2. "image"(可执行文件)在其中包含不同的段:一个代码段,a set of global variables with their initialized values, a segment indicating some initially-zero 需要在程序启动时出现但不需要明确包含在中的值图片等
  3. OS 中的加载程序将图像文件分开,并使用适当的 executable/read/write 标志将不同的部分以它想要的方式放入内存,修补静态引用,然后跳转控制你的 main 函数的第一个字节(通过对你隐藏的 C 运行时东西)。

当您获得函数的地址时,那里没有魔法,它是指向该函数编译指令的第一个字节的指针。

专业软件工程免责声明 正如对该答案和问题的评论所表明的那样,在现实世界中,链接器和加载器的工作方式有无数种变体;有些系统并没有真正这样做。当然,您永远不会编写对内存段进行假设的广泛可移植的 C 代码。但是出于教学目的,如果您使用的是大牌传统桌面py OSs 之一,它*squints* 基本上或多或少会像您想象的那样工作。 (如果你能找到 Tanenbaum 的 "Modern Operating Systems" 的旧副本,即使是旧的第一版,它也会阐明很多看似不透明的主题。)