【问题标题】:Which is the address printed by printf() with a %p format in c?哪个是 printf() 在 c 中以 %p 格式打印的地址?
【发布时间】:2013-03-27 19:55:51
【问题描述】:

我的简单代码如下:

#include<stdio.h>

int glob;

int main(void)
{
   int a;
   printf("&a is : %p \n", &a);
   printf("glob is : %p \n", &glob);
   return 0;
}

上述程序的输出是: 第一次运行:

&a is : 0x7fff70de91ec
glob is : 0x6008f4

第二次运行:

&a is : 0x7fff38c4c7ac
glob is : 0x6008f4

我正在研究虚拟地址和物理地址。我有以下问题:

  1. 变量“a”的打印地址(物理/虚拟)是什么?
  2. 如果它是虚拟的,那么它在同一程序的每次运行中如何变化? 据我了解,编译器在编译时为变量提供虚拟地址?
  3. 为什么每次程序运行时全局变量的地址都是不变的?

在 Linux 上执行此程序:2.6.18-308.el5 x86_64 GNU/Linux

编译使用:gcc 版本 4.1.2 20080704 (Red Hat 4.1.2-52)

【问题讨论】:

  • 您的程序调用了未定义的行为%p 必须指定一个 ptr-to-void,因此您必须在两个 printfs 中转换为 (void*)
  • @Jens 参数不会被隐式转换为void *吗?
  • @VilhelmGray 隐式转换发生在需要类型但可变参数函数中没有类型时。

标签: c linux gcc virtual-address-space


【解决方案1】:

两个地址都是虚拟的。

现代系统使用堆栈随机化来防止所谓的堆栈粉碎攻击,这就是为什么局部变量可以在每次运行时更改其位置的原因。但是,全局变量存储在可执行文件中,并且每次都以相同的偏移量加载。

【讨论】:

  • 值得注意的是,某些其他操作系统和一些更注重安全的 Linux 发行版具有 PIE(与位置无关的可执行文件),并且在它们上,全局变量的地址也会发生变化。
  • @Joachim,是的,我同意局部变量存储在堆栈中,但为什么说变量在编译时由编译器赋予特定地址?
  • @BSalunke 当调用函数时,局部变量被放置在距堆栈指针的特定偏移处。所以虽然编译器没有给它一个固定的地址,但变量仍然有一个编译器给定的地址。
  • @BSalunke 每个变量必须映射到一个地址,否则编译器应该如何生成指令来访问它?在某些情况下,对于全局地址,它可以是绝对地址(在进程的虚拟空间中),对于本地地址,它通常是堆栈相对地址。
【解决方案2】:

您的程序将始终只看到虚拟地址。

真实地址仅对内核模式下的虚拟内存管理器可用。

Globals 具有相同的地址(除非您在其前面放置其他变量),因为它是在数据段中创建的。

局部变量总是在堆栈上创建。

【讨论】:

  • 局部变量也可以是static ... :)
  • @unwind :是的,你是对的。我只是描述了 OP 提供的代码。
【解决方案3】:

在程序中看到的地址始终是虚拟的,OP 描述的行为是一种避免buffer overflow attacks 的 Linux 对策。

只是为了尝试,你可以禁用它

sysctl -w kernel.randomize_va_space=0

然后再次运行您的程序并观看。

全局变量位于另一个内存空间中,从骇人听闻的角度来看,这不会是有害的。那是因为它不是每次都随机的。

【讨论】:

    【解决方案4】:

    程序看到的所有地址都是虚拟的。 然而,局部变量在称为数据段的特殊区域上进行堆栈和全局。 虽然变量的相对位置是在编译时确定的,但每次运行时堆栈可能会有所不同。

    【讨论】:

      猜你喜欢
      • 2014-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多