【问题标题】:static int arr[10] memory address always ends in 060static int arr[10] 内存地址总是以 060 结尾
【发布时间】:2026-01-25 14:40:01
【问题描述】:

我有一个像这样的 c 程序

main.c

#include <stdio.h>
#define SOME_VAR 10

static int heap[SOME_VAR];


int main(void) {
    printf("%p", heap);
    return 0;
}

当我运行编译的程序几次时输出这个

0x58aa7c49060
0x56555644060
0x2f8d1f8e060
0x92f58280060
0x59551c53060
0xd474ed6e060
0x767c4561060
0xf515aeda060
0xbe62367e060

为什么总是以 060 结尾?并且数组存储在堆中吗?

编辑:我在 Linux 上,我开启了 ASLR。我使用 gcc 编译了程序

【问题讨论】:

  • 什么操作系统?什么编译器?
  • 变量不在堆中,在程序地址空间的数据或bss部分,见en.wikipedia.org/wiki/Static_variable。我的猜测是程序将始终放置在某个边界的内存地址,例如可被 0x1000 整除,并且该变量由编译器放置在程序地址空间中的固定偏移处。

标签: c arrays memory


【解决方案1】:

地址因 ASLR(地址空间布局随机化)而不同。使用它,二进制文件可以映射到虚拟地址空间中的不同位置。

变量heap - 与它的名字相反 - 不在堆上,而是在bss 上。因此,地址空间中的偏移量是恒定的。

页面以页面粒度进行映射,在许多平台上为 4096 字节(十六进制:0x1000)。这就是为什么地址的最后三个十六进制数字相同的原因。

当您对 stack 变量执行相同操作时,在某些平台(即具有最新内核的 linux)上,地址的最后一位甚至可能会有所不同,因为堆栈不仅映射到其他地方,而且启动时也会收到一个随机偏移量。

【讨论】:

  • ASLR 随机加载我记得的基础。部分地址基于该地址。
  • 我正在使用 Axel-Thobias Schreiner 的一本关于面向对象的 ANSI-C 编程的书。这本书写于 1993 年左右。你知道当时的内存布局是否不同吗?如果不是,为什么他会在变量不在堆中时将其命名为heap
  • 4096 是否以某种方式转换为 060,或者 0x1000 是否转换为 060,否则我不明白你的意思是什么意思,这是结束的原因?我认为这可能与数组的大小有关,该数组的大小从十六进制转换为 060,例如十进制
  • @linuxlmao 偏移量例如14060,所以当你添加一个页面大小的倍数(0x1000)时,最后三位仍然是060
【解决方案2】:

如果你使用的是Windows,原因是PE结构。

您的heap 变量存储在文件的.data 部分中,其地址是根据该部分的开头计算的。每个部分都独立加载到一个地址中,但其起始地址是页面大小的倍数。因为你没有其他变量,它的地址可能是.data部分的开始,所以它的地址将是块大小的倍数。

例如,这是您的代码的已编译 Windows 版本的表格: .text 部分是您的编译代码,.data 包含您的 heap 变量。当你的 PE 被加载到内存中时,部分被加载到不同的地址并且由VirtualAlloc() 返回并且将是页面大小的倍数。但是每个变量的地址都相对于现在是页面大小的节的开头。所以你总是会在较低的数字上看到一个固定的数字。由于heap 从节开始的相对地址是基于编译器、编译选项等。你会看到来自相同代码但不同编译器的不同数字,但每次打印的内容都是固定的。

当我编译代码时,我注意到heap.data 部分开始后被放置在0x8B0 字节上。所以每次我运行这段代码时,我的地址都以0x8B0结尾。

【讨论】:

  • 我正在使用 Axel-Thobias Schreiner 的一本关于面向对象的 ANSI-C 编程的书。这本书写于 1993 年左右。你知道当时的内存布局是否不同吗?如果不是,为什么他会在变量不在堆中时将其命名为heap
  • @linuxlmao 很可能会有所不同。 1993 年,Windows 是一个 16 位操作系统,有memory segmentation 和各种令人困惑的东西。它不是像现在这样的 32 位平面内存架构。但是这些类型的事情是为什么询问/回答有关内存中程序二进制文件布局的一般问题没有用的原因。了解 C 语言标准一般向您保证什么,这就是您需要知道的全部内容。如果您正在调试特定问题,只需担心实际布局,然后使用调试器
  • 不,即使在旧系统上也不应该在堆上创建变量,因为它没有分配 malloc 并且具有静态存储持续时间
  • @Afshin 我正在处理上面的 OP 评论
  • @phuclv 对不起,因为你没有提到他,我以为你在跟我说话。 :)
【解决方案3】:

编译器碰巧将heap放在它拥有的数据段的偏移量0x60字节处,可能是因为编译器在前0x60字节中有一些其他内容,例如启动main例程的代码使用的数据.这就是你看到“060”的原因;它只是它发生的地方,并没有太大的意义。

地址空间布局随机化会更改用于程序存储器各个部分的基地址,但它总是以 0x1000 字节为单位进行更改(因为这样可以避免导致对齐问题和其他问题)。所以你会看到地址以 0x1000 的倍数波动,但最后三位数字没有变化。

定义static int heap[SOME_VAR]; 定义了具有静态存储持续时间的heap。典型的 C 实现将其存储在通用数据部分中,而不是堆中。 “堆”是用于动态分配的内存的误称。 (这是用词不当,因为malloc 实现可能使用多种数据结构和算法,不限于堆。它们甚至可能在一个实现中使用多种方法。)

【讨论】: