【问题标题】:memory address positive or negative value in c?c语言中的内存地址是正值还是负值?
【发布时间】:2009-11-06 18:29:48
【问题描述】:

在 c 中,我试图打印出变量的地址和某个函数的地址。 我得到一个是负值,另一个是正值。 我的问题是:为什么 C 不代表所有负值或所有正值?

这是我的代码:

int foo() { 
     return 0;
}

int main() {

    int a;
    printf("%d\n",&a);

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

    return 0;
}

结果如下:

-1075908992 134513684

【问题讨论】:

    标签: c memory-address


    【解决方案1】:

    内存地址不应以这种方式解释为有符号整数。数字的符号取决于最高位集(假设二进制补码表示,目前绝大多数系统都使用这种表示),因此 32 位系统上 0x80000000 以上的内存地址将为负数,而内存低于 0x80000000 的地址将为正数。这并没有真正的意义。

    您应该使用%p 修饰符打印内存地址;或者,有些人使用%08x 打印内存地址(或%016llx 在64 位系统上)。这将始终打印为十六进制的无符号整数,这比有符号的十进制整数有用得多。

    int a;
    printf("%p\n", &a);
    

    【讨论】:

    • %08x 是不可移植的(例如,实际上会在 LLP64 平台上搞砸)。
    • 是的,这就是你应该使用%p的原因,但仍然值得一提的是,很多人在32位机器上使用%08x。 99% 的情况下,如果你打印出一个指针值,你就是在调试一些东西,所以你确切地知道你在什么架构上运行。
    【解决方案2】:

    要尽可能符合标准,请将格式字符串中的“%p”传递给printf(),并将参数转换为void*

    printf("%p\n", (void*)&a);
    printf("%p\n", (void*)foo);
    

    引自标准草案 (n1401.pdf)

    7.20.6.1 fprintf 函数 8 转换说明符及其含义是: p 参数应该是一个指向 void 的指针。指针的值 转换为打印字符序列,在 实现定义的方式。

    【讨论】:

    • 我建议您按照自己的建议在示例代码中将 %d 替换为 %p
    • +1 用于提及转换为 void * 作为默认参数提升不涉及任何指针转换,即在指向不同类型的指针具有不同表示的平台上,您将获得未定义的行为
    【解决方案3】:

    你应该传递指针格式化程序:

    printf("%p\n",foo);
    

    【讨论】:

      【解决方案4】:

      您正在以十进制打印。您可能希望以十六进制打印内存位置(使用“%p”)(大多数内存地址按照惯例以十六进制显示)。

      【讨论】:

        【解决方案5】:

        指针没有符号,但是当转换为有符号整数时,它可能是正数或负数。

        顺便说一句,将一个指向printf 的指针传递给一个旨在打印整数的格式变量会导致未定义的行为。您应该始终显式转换为正确的类型以获得未指定或实现定义的行为(取决于转换)。

        【讨论】:

        • 对;这可能会在指针和整数大小不同的系统(如大多数 64 位系统)上导致真正的问题。
        【解决方案6】:

        指针不是有符号整数,更像是无符号整数。但是,您将它们打印出来,就好像它们是有符号整数一样。在 32 位系统上,有几件事可能是 32 位长:intunsigned intfloat 和指针。您通常无法仅通过查看它们来判断它们,但它们都意味着不同的东西。 (作为一个实验,你可以传入各种整数并像浮点数一样打印它们,反之亦然:这通常是不好的做法,但它可以告诉你不同的数据类型确实意味着不同的东西。)

        采用可变数量参数的函数(“可变参数”函数)以一种复杂的方式执行此操作。特别是,它们不检查参数类型。你可以将任何你喜欢的参数传递给printf() 和类似的函数,但是让它们与格式说明符一致是你的问题。这意味着该函数无法获取正确的类型(就像您将 float 传递给类似 int foo(int) 的函数一样)。

        传递错误类型的参数会导致未定义的行为,如果传递大小错误的参数,则尤其可能导致问题。在大多数 64 位系统上,指针是 64 位,ints 是 32。

        因此,您需要在格式字符串中使用正确的东西。 printf("%p", &a); 会打印出a 的地址作为指针,这就是你想要的。该标准需要printf("%p", (void *)&a); 之类的东西,但实际上这在您可能遇到的任何计算机上都是不必要的。

        【讨论】:

          【解决方案7】:

          您也可以使用printf("%p", &a)%p 是指针格式说明符,以实现定义的方式输出指针的值。实际上,它与 %x 几乎相同,但会处理指针大于 int 的情况。

          【讨论】:

          • 好点。你必须有一个最近的编译器才能工作。 (别笑,我不得不经常令人沮丧地使用几十年前的编译器)
          【解决方案8】:

          printf 不是什么神奇的解释工具。如果你给它“%d”,它所做的就是向你展示位于该变量中的位模式是什么样的整数。

          至于为什么有些指针是负数而有些是正数,这意味着有些指针设置了高位,有些则没有。为对象分配的地址实际上只是您的操作系统的业务(通常有一些硬件帮助)。编程语言在其中没有真正的发言权。

          如果我是你,我不会那样打印地址。如果您真的觉得有必要查看它们,请使用“%x”而不是“%d”。这将以十六进制打印它们,这样可以更容易地确定哪些位在位,哪些位不在位。一般来说,您只关心地址是否为 0,以及它们是否与其他地址的值匹配。

          【讨论】:

            【解决方案9】:

            除了使用%p,您还可以强制转换为uintptr_t 以确保获得一个无符号整数。

            使用PRIuPTR(或PRIXPTR 用于十六进制)形式<inttypes.h> 来获得正确的格式说明符:

            printf("%" PRIuPTR "\n", (uintptr_t)(void *)&foo);
            

            【讨论】:

              【解决方案10】:

              使用以下代码:

              printf("%u",&a);
              

              【讨论】:

              • 这是不正确的。 %u 用于无符号整数,而不是指针。
              • 我喜欢这个主意。它强制地址为正数。
              猜你喜欢
              • 2018-10-22
              • 1970-01-01
              • 1970-01-01
              • 2012-03-15
              • 2012-12-29
              • 1970-01-01
              • 1970-01-01
              • 2020-03-13
              • 1970-01-01
              相关资源
              最近更新 更多