static int arr [10]存储器地址始终以060结尾


17

我有一个看起来像这样的交流程序

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编译了程序


2
什么操作系统?什么编译器?
安德鲁·亨利

2
该变量不在堆中,而是在程序地址空间的data或bss部分中,请参见en.wikipedia.org/wiki/Static_variable。我的猜测是,程序将始终放置在某个边界处的内存地址,例如可以除以0x1000,并且编译器将变量放置在程序地址空间中的固定偏移量处。
博多

Answers:


15

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

heap与名称相反,该变量不是位于堆上,而是位于上bss。因此,地址空间中的偏移量是恒定的。

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

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


我记得,ASLR随机加载基础。部分地址基于该地址。
阿夫申

我正在使用Axel-Thobias Schreiner撰写的有关面向对象的ANSI-C编程的书。这本书的写法类似于1993年。您知道当时的内存布局是否有所不同吗?如果不是,为什么heap当不在堆中时,他为什么要命名该变量?
linuxlmao

4096是否以某种方式转换为060或0x1000转换为060,否则我不明白您的意思是结束的原因?我认为这可能与数组的大小有关,该数组的大小从十六进制转换为060,例如十进制
linuxlmao

2
@linuxlmao偏移量例如为14060,因此,当您添加页面大小的倍数(0x1000)时,将保留最后三位数字060
Ctx

4

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

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

例如,这是代码的已编译Windows版本的表: 部分.text部分是您的已编译代码所.data包含的heap变量。当您的PE加载到内存中时,段会加载到不同的地址,并且由返回,VirtualAlloc()并将为页面大小的倍数。但是每个变量的地址都相对于部分的起始位置,即页面大小。因此,您始终会在低位看到固定数字。由于heap从本节开始的相对地址是基于编译器,编译选项等的,因此您将看到相同代码,不同编译器的编号不同,但是每次打印的内容都是固定的。

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


我正在使用Axel-Thobias Schreiner撰写的有关面向对象的ANSI-C编程的书。这本书的写法类似于1993年。您知道当时的内存布局是否有所不同吗?如果不是,为什么heap当不在堆中时,他为什么要命名该变量?
linuxlmao

2
@linuxlmao可能完全不同。在1993年,Windows是一个16位操作系统,具有内存分段和各种令人困惑的内容。它不是现在的32位平面内存体系结构。但是,这些类型的问题是为什么询问/回答有关程序二进制文件在内存中的布局的常规问题没有用的原因。大致了解C语言标准对您的保证,这就是您需要知道的全部。如果您要调试特定的问题,则只需担心实际布局,然后使用调试器
Cody Gray

不,即使在较旧的系统上也不应该在堆上创建该变量,因为该变量未分配给malloc且具有静态存储持续时间
phuclv

@Afshin我在上面对OP的评论
phuclv

@phuclv对不起,因为您没有提到他,我以为您在跟我讲话。:)
阿夫申

4

编译器碰巧heap在其数据段中放置了偏移量0x60字节,这可能是因为编译器在前0x60字节中还有其他内容,例如启动main例程的代码所使用的数据。这就是为什么您看到“ 060”的原因;它只是发生的地方,对它没有太大的意义。

地址空间布局随机化会更改用于程序存储器各个部分的基址,但始终以0x1000字节为单位进行更改(因为这样可以避免引起对齐问题和其他问题)。因此,您会看到地址以0x1000的倍数波动,但最后三位数字不变。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.