【问题标题】:Difference between variables' addresses变量地址之间的差异
【发布时间】:2017-10-13 05:46:16
【问题描述】:

为什么每次我运行程序时变量地址都会有特定的差异(如“printf(”%d %d\n“,&a,&b);”。它会在一次运行中打印“1000 988” ,“924 912”在另一个,“1288 1276”,等等等等)?编译器是否在每个变量声明后都占用了一定数量的内存,无法写入任何内容?如果是,它取决于什么?在我的一个程序中使用一些变量,它们之间的最小差异是 12 个字节,它达到了 212。这是唯一一个差异不是 12 的倍数的情况(在其他情况下是 24、36 或 48字节)。这背后有什么原因吗?由于我的变量是 int 类型(在我的系统中占用 4 个字节),我的变量地址之间的差异会小于 12(例如 4)吗?这些地址差异是否取决于变量类型?如果是,以什么方式?提前谢谢!

【问题讨论】:

  • 您的操作系统提供了所有应用程序运行的虚拟内存地址空间。当您的代码执行时,您的操作系统的内存管理器会填充应用程序对内存的请求,并在您的代码完成时释放内存(以便在您的操作系统需要的任何地方重用)。有时您可以在连续运行时为每个变量获得完全相同的地址,有时则不能,这取决于您的操作系统在启动时为应用程序提供的内容。 (从你的应用的角度来看——“这就像一盒巧克力......”)
  • 你知道用%d打印一个指针是UB吗?您的编译器可能会警告您。无论如何,正确的格式是%p
  • @Deduplicator 我知道这一点。我只使用它是因为它更容易在小数之间进行算术,而不是十六进制。在这种情况下,打印的结果应该是正确的。
  • @DavidC.Rankin:感谢您的澄清!

标签: c memory memory-address


【解决方案1】:

当今的大多数操作系统都使用 地址空间布局随机化,以便更难编写某些类型的恶意软件。 (将代码写入内存然后试图让程序将控制权交给它的那种;现在必须猜测让程序跳转到哪个地址。)结果,变量不会位于相同的地址每次运行程序时。

根据变量的类型、分配方式以及您运行的操作系统和架构,变量的大小和对齐方式会有所不同。编译器和运行时可能会也可能不会总是将它们放在 4、8 或 16 字节的边界上。例如,x86_64 函数调用 ABI 总是在 16 字节边界上开始函数的堆栈帧,malloc() 的某些实现总是返回可被 16 整除的地址,因为这是在某些 CPU 上存储向量所必需的。

如果你想知道编译器在做什么,你可以尝试编译成汇编。在 gcc 或 clang 上,您可以使用 -S 标志来执行此操作。

【讨论】:

  • 明白。谢谢!
【解决方案2】:

如果您要问为什么不同的可执行文件运行时变量的内存地址不同,答案是 ASLR,它的存在是为了更难利用代码中的安全问题(请参阅https://en.wikipedia.org/wiki/Address_space_layout_randomization)。

如果禁用 ASLR,每次运行可执行文件时,您将获得给定变量的相同地址。

另见Difference between gdb addresses and "real" addresses?

【讨论】:

  • 我在问为什么,如果我声明 2 个像这样的变量“int a, b;”并继续打印它们的地址,如 "printf("%d %d\n", &a, &b);",它们的地址总是相差相同的数量。例如,在一次运行中, printf 将打印 (500 488) 并在另一次运行中打印 (612 600) 等等。再看一下我上面的问题,我确实没有说清楚。不过,还是谢谢你的回答。我也有兴趣知道这一点。
  • 如果你声明“int a,b”,它们都在堆栈上,它们之间的偏移量应该总是 sizeof(int)。
  • printf("%d\n", sizeof(int));打印 4。但是,偏移量不是 4。它是 12 或更多。我运行代码的每个案例。
  • 我在 OS X 和 Linux 上分别使用 llvm 和 gcc 得到 4。代码是int a,b; printf("%p %p\n", &a, &b);
  • 使用与您完全相同的代码,我得到:006FFDD4 006FFDC8,因此十六进制的差异也相同。这就是我好奇的原因。为什么占用的空间大于变量的大小?正如上面戴维斯勒所说,在某些情况下可能会发生这种情况。
【解决方案3】:

您的链接器(以及在某种程度上,您的编译器)布置应用程序的地址空间。链接器通常基于某个地址(例如,零)构建可重定位映像。显然,您的加载程序在运行时将可重定位图像放置在不同的位置。

编译器是否在每个变量声明后都占用了一定数量的内存,无法写入任何内容?

通常没有,除非下一个变量需要对齐。变量通常与变量大小的倍数的地址对齐。

听起来你的编译器正在为你根本没有考虑的东西分配内存。

【讨论】:

    猜你喜欢
    • 2015-06-07
    • 1970-01-01
    • 2012-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-18
    • 1970-01-01
    • 2014-10-20
    相关资源
    最近更新 更多