繁体   English   中英

这里的“ foo”地址是否总是非零?

[英]Will address of `foo' here be always non-zero?

我有以下代码:

#include <stdio.h>
#include <stdlib.h>

extern void foo(double);

int main(void) {
    // printf("address is: %p\n", *foo);

    if (foo)
        puts("indeed");
    else
        puts("not");
    exit(0);
}

它总是编译和印刷品indeed ,如果行号7的评论,但GCC警告我说:

the address of 'foo' will always evaluate as 'true' [-Waddress]

但是,如果第7行未注释,则不会编译:

/tmp/ccWvhcze.o: In function `main':
post.c:(.text.startup+0x5): undefined reference to `foo'
collect2: error: ld returned 1 exit status

好吧,如果根据gcc地址总是非零,为什么链接失败? 当然,这是我所期望的,因为foo没有在此定义,但为什么gcc声称该地址将始终为非零? C标准是否要求翻译单元中的所有标识符始终评估为true?

测试if (pointer_expression)是否检查指针是否为空指针。 保证每个有效的对象和函数都不是空指针。 (请注意,空指针不一定是“零”。)

编译器会在if ()删除测试,因为它保证为true。 因此,链接器没有任何可解析的。 添加fooprintf会增加解析foo的需要。

您必须记住,编译和链接是C程序的两个完全独立的阶段。 编译器说“如果(且仅当)链接并执行此代码时, foo始终为非零”。 但是,然后继续尝试将该对象文件链接到可执行文件,而不提供foo定义 (仅声明)。 那是非法的,因此链接失败。 foo不是NULL或not-NULL,它没有值。

由于我正在通过智能手机编写此代码,因此未经测试,我猜foo可能会变为零。

例如,如果我的想法是正确的,则与此NASM代码链接将使foo为零。

bits 32
absolute 0
global foo
global _foo
foo:
_foo:

更新:

我使用GCC 4.8.1和NASM 2.11.08测试了此代码,并获得了输出

address is: 00000000
indeed

另外,尽管此代码与原始代码不同,但在Ubuntu,GCC 4.6.3上的xv6上的代码在Vagrant VM上运行

#include "types.h"
#include "user.h"

void foo(double a) {
    (void)a;
}

int main(void) {
    printf(1, "address is : %p\n", (void*)*foo);

    if (foo)
        printf(1, "indeed\n");
    else
        printf(1, "not\n");
    exit();
}

发射输出

address is : 0
indeed

(从xv6 MakefileCFLAGS中删除-Werror ,否则会出现编译错误)

还要注意, *foo成为函数foo地址,因为作为运算符*操作数foo被转换为指向函数foo的指针,然后被*取消引用并变为函数foo ,然后再次转换为函数参数的指针。

正如其他人指出的那样,GCC假设'foo'永远不会为零。 如果您具有函数'foo',则它将具有一些地址,如果未定义函数foo,则您的程序将仅不会链接,并且将没有可执行文件运行。 如果您链接到链接时具有功能“ foo”的“ dll”或“ so”文件,但在执行时稍后将其删除,则不会加载可执行文件。 因此,GCC安全地假设'foo'永远不会为零。

但是,在99.(99)%的情况下是正确的。 正如MikeCat指出的那样,函数foo可以位于地址零。 对于用户空间可执行文件来说,这永远不会发生,甚至内核代码也不会采用这种技巧。 唯一可以将对象置于绝对地址的实用程序是引导加载程序和直接在硬件上运行的其他程序。

他们依靠链接脚本将对象放置在正确的地址。 这是一个代码示例,在技术上可以做到这一点,但这是一件非常令人讨厌的事情。 我知道唯一需要采取这种技巧的C程序是引导加载程序代码https://github.com/trini/u-boot/blob/master/arch/arm/mach-at91/armv7/u-boot-spl .lds

但是,即使引导加载程序可以执行此操作,GCC仍然可能是正确的。 大多数CPU或OS出于某些特殊目的保留地址“ 0”。 在大多数情况下,中断向量表都位于该位置,在其他情况下,OS或体系结构本身会为空指针保留地址0,因此任何尝试访问它都会导致异常。 我相信x86保护模式是这种体系结构之一,它是所有流行的OS(Linux,Windows)运行用户程序的模式。

因此,在您的情况下,GCC在优化if语句方面可能是正确的,因为您使用的是x86版本的gcc。 如果您发现未保留地址0的体系结构,则很可能会使用不执行此类优化的GCC版本。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM