【问题标题】:How to print the address value of a pointer in a C program?如何在 C 程序中打印指针的地址值?
【发布时间】:2018-01-17 20:01:03
【问题描述】:

我正在尝试学习如何在 C 程序中使用指针;我的例子如下:

   #include <stdio.h>
   #include <stdlib.h>
   
   int main(int argc, char *argv[]) {
        int * tab = (int*)malloc(sizeof(int)*5);
        int a = 10;
        int *p;
        p  = &a;
        printf("the address of a is %d \n",&a);    
        printf("the address of a using the pointer is %p \n",p);    
        printf("the address of tab is %d \n",tab);    
    }

我正在尝试打印a 的地址、p 内部的地址值以及tab 的第一个字节开始的位置。

使用"%p"时我可以得到十六进制值,但我愿意使用十进制值。

编辑:在这个Videoimage上,有人用"%d"为指针地址打印了一个十进制值,这让我很困惑。

【问题讨论】:

  • 首先——地址的小数点是多少?
  • "%d"int 一起使用。它可能不适用于&amp;a。建议printf("%p\n",(void*) &amp;a);
  • 要打印指针的地址,你应该使用%p。忘记指针的十进制解释吧,十六进制表示要好得多。
  • 稍后我将尝试操作 tab 变量,并且我将测试 tab 中的每个单元格是否都是 4 字节,所以我可能会使用 tab+8 字节来获取特定单元格的值,这里是一个简单的例子,小数对我来说很容易。
  • @zakzak %p 的输出格式是实现定义的,但它是定义的。您的代码给出了十进制输出,但不一定是正确的输出。您的目标似乎想要任何简单的十进制输出,而不一定是正确的十进制输出。

标签: c pointers output


【解决方案1】:

要打印对象指针的十进制版本,首先将指针转换为整数。最好使用可选类型uintptr_t

以下类型 (uintptr_t) 指定无符号整数类型,其属性是任何指向 void 的有效指针都可以转换为此类型 (uintptr_t) ... C11dr §7.20.1.4 1

#include <inttypes.h>
uintptr_t d = (uintptr_t)(void *) &a;

然后打印出来。
关于PRIuPTR的一些信息

printf("%" PRIuPTR "\n", d);

对于 C99 之前的代码,请考虑直接转换为 unsigned long@Barmar


OP 后来评论了

"...我将尝试操作选项卡变量,并且我将测试选项卡中的每个单元格是否都是 4 字节,所以我可能会使用制表符 + 8 字节来获取特定单元格的值,这里这是一个简单的例子,小数对我来说很容易。”

这是一个有问题的方法,因为指针的十进制化可能无法提供预期的连续数字模型。这篇文章可能对学习有好处,但是随着 OP 在另一篇文章中提供更多细节,可以为更高级别的问题提供更好的想法。

【讨论】:

    【解决方案2】:

    首先,请注意地址通常是“类似数字的”,但也可能不是(例如,请记住 1980 年代 16 位 x86 PC 上远地址的段+偏移量概念)。

    那么,与指针最相似的整数类型是intptr_t(有符号)或uintptr_t(无符号)——都来自&lt;stdint.h&gt;

    您可以将其转换为 long long(希望它们不小于指针),例如

     printf("address of a in decimal is %lld\n", (long long) (intptr_t) (&a));
    

    但是说真的,为什么要以这种方式显示地址?我认为不需要它(例如,调试器或链接器通常以十六进制显示地址,这可能是 %p 所做的)。

    请注意,C11 标准(请参阅 n1570,§7.21.6.2,p315)对打印指针的 %p 没有太多说明:

    参数应为指向void 的指针。指针的值以实现定义的方式转换为打印字符序列。

    我理解上述内容的方式是,为任何指针打印Hello ⭔ 将是可接受且符合标准的行为。在实践中,实现打印指针的方式类似于链接器和调试器所做的,并且不会以愚蠢的方式表现(但我知道他们可以)。

    最后,指针的实际具体“数字”值并不是很重要,并且从一次执行到下一次执行可能会有很大差异(例如,因为ASLR)。

    【讨论】:

      【解决方案3】:

      对于打印“地址”(实际上是一个指针)"%p" 是最常用的方法(注意必须强制转换为void*)。如果您需要十进制而不是十六进制的地址,您可以将指针转换为整数值;这在 C 中有很好的定义(参见this 在线 C 标准草案):

      6.3.2.3 指针

      (6) 任何指针类型都可以转换为整数类型。除了作为 之前指定的,结果是实现定义的。如果 结果不能用整数类型表示,行为是 不明确的。结果不必在任何值的范围内 整数类型。

      所以代码可能如下所示:

      int main(int argc, char *argv[]) {
          int a =10;
          int *p = &a;
          p  = &a;
      
          unsigned long long addrAsInt = (unsigned long long)p;
      
          printf("the adress of a is %llu \n",addrAsInt);
          printf("the adress of a using the pointer is %p \n",(void*)p);
      }
      

      【讨论】:

      • 我会使用上面的unsigned long long 而不是uint64_t,即使它们通常相同。
      • 为什么不#include 并使用size_t?似乎比 uint64_t 或 unsigned long long 更可取。 size_t 很可能与其中任何一个相同,但 uint64_t 很可能与 unsigned long long 相同,而 size_t 很可能相同。
      • @SornelHaetir size_t 可能和uint16_t 一样小。转换为像 unsigned long longuintmax_t 这样的宽类型,它们都至少是 64 位,可以减少信息丢失的机会。
      【解决方案4】:

      如果要将指针视为十进制整数,请将其转换为 unsigned long 并以 %lu 格式打印。虽然标准不保证这一点,但它应该适用于大多数实现。

      printf("the address of a is %lu\n", (unsigned long)&a);    
      

      【讨论】:

      • 你的解决方案对我有用,我更喜欢这个,因为它很简单。我要感谢每一个人。
      • @zakzak 这很简单 - 如果指针比 long 更宽,可能会提供令人困惑的结果。
      • chux 的答案是正确的,不要喜欢 简单 的答案而不是正确的答案...
      【解决方案5】:

      p 是打印指针的转换说明符。使用这个。

      int a = 42;
      
      printf("%p\n", (void *) &a);
      

      请记住,省略强制转换是未定义的行为,使用 p 转换说明符的打印是以实现定义的方式完成的。

      【讨论】:

        猜你喜欢
        • 2010-11-06
        • 2012-01-26
        • 1970-01-01
        • 1970-01-01
        • 2021-07-07
        • 1970-01-01
        • 2020-06-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多