这里`foo'的地址总是非零吗?

时间:2016-01-26 00:16:09

标签: c

我有以下代码:

#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);
}

如果第7行被注释,它总是编译并打印indeed,但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标准是否强制要求翻译单元中的所有标识符始终评估为真?

5 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:3)

编译器删除Unable to locate Dependency Microsoft.EntityFrameworkCore.InMemory >= 1.0.0-* Unable to locate Dependency Microsoft.EntityFrameworkCore.SqlServer >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Authentication.Cookies >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Authentication.Facebook >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Authentication.Google >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Authentication.MicrosoftAccount >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Authentication.OpenIdConnect >= 0.1.0-* Unable to locate Dependency Microsoft.AspNetCore.Authentication.Twitter >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Identity.EntityFrameworkCore >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.IISPlatformHandler >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Server.Kestrel >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Tooling.Razor >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Server.WebListener >= 0.1.0-* Unable to locate Dependency Microsoft.AspNetCore.Mvc.TagHelpers >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Session >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.StaticFiles >= 1.0.0-* Unable to locate Dependency Microsoft.AspNetCore.Mvc >= 1.0.0-* 中的测试,因为它保证是真的。因此链接器无需解决。添加if () printf会增加解析foo的必要性。

答案 2 :(得分:2)

您必须记住,编译和链接是C程序的两个完全独立且独立的阶段。编译器说&#34;如果(并且仅当)链接并执行此代码,foo将始终为非零&#34;。但是,您继续尝试将该目标文件链接到可执行文件,而不为foo提供定义(仅声明)。那是非法的,所以链接失败了。 foo不是NULL或不是NULL,它没有值。

答案 3 :(得分:1)

未经测试,因为我是从智能手机上写的,我猜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上的xv6代码,GCC 4.6.3在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 -Werror中的CFLAGS删除Makefile,否则会出现编译错误)

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

答案 4 :(得分:1)

正如其他人所指出的那样,海湾合作委员会会假设&#39; foo&#39;永远不会为零。如果你有功能&#39; foo&#39;然后它会有一些地址,如果没有定义函数foo,那么你的程序根本就不会链接,也就没有可执行文件可以运行。如果您要链接到&#39; dll&#39;或者&#39;所以&#39;有功能的文件&#39; foo&#39;在链接时,但在执行时它被删除,然后您的可执行文件将无法加载。所以海湾合作委员会安全地假设“foo&#39;永远不会为零。

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

他们依靠链接器脚本将对象放在正确的地址。这是一个代码的例子,它在技术上是肯定的,但这是非常恶心的事情。我知道唯一需要采用这种技巧的C程序是启动加载程序代码https://github.com/trini/u-boot/blob/master/arch/arm/mach-at91/armv7/u-boot-spl.lds

但是,即使启动加载器可以做这样的事情,GCC仍然可能是正确的。大多数CPU或操作系统保留地址&#39; 0&#39;出于某些特殊目的。大多数情况下,中断向量表位于那里,在其他情况下,OS或Architecture本身为空指针保留地址0,因此任何访问它的尝试都将导致异常。我相信x86保护模式,这是所有流行的操作系统(Linux,Windows)运行用户程序的模式,是这种架构之一。

所以GCC在你的情况下GCC可能在优化if语句时是正确的,因为你使用的是x86版本的gcc。如果您发现地址0未保留的架构,很可能会有一个不执行此类优化的GCC版本。