为什么这个添加被默默地忽略了?

问题描述 投票:14回答:2

我有以下C代码:

#include <stdint.h>
#include <stdio.h>

int i;
uint64_t a[] = { (uint64_t)&i, (uint64_t)&i + 0x8000000000000000 };

int main() {
    printf("%p %llx %llx\n", &i, a[0], a[1]);
}

如果我使用Microsoft Visual Studio Community 2015编译它(作为C或C ++)然后运行它,输出类似于以下内容:

013E9154 13e9154 13e9154

似乎代码+ 0x8000000000000000,我期望设置高位a[1],已被默默地忽略。

但是,如果我在a中移动main的初始化,输出就是我所期望的:

00179154 179154 8000000000179154

随着a全球化,为什么增加被默默忽略?如果尝试添加实际上设置了a[1]的高位,还是应该导致编译器错误?

有趣的是,如果上面代码中的+ 0x8000000000000000| 0x8000000000000000取代,我得到“错误C2099:初始化程序不是常数”。

编辑:即使没有强制转换,也会出现类似的问题。编译为x64,以下代码打印相同的值(例如000000013FB8D180)三次:

#include <stdio.h>

int i;
int * a[] = { &i, &i + 0x100000000 };

int main() {
    printf("%p %p %p\n", &i, a[0], a[1]);
}
c visual-c++ compiler-errors language-lawyer constant-expression
2个回答
1
投票

初始化程序

(uint64_t)&i + 0x8000000000000000

它不是C中有效的constant expression。它既不是一个算术常量表达式,它只允许整数常量,浮点常量,枚举常量,字符常量和sizeof表达式作为操作数;也不是一个不允许强制转换为整数类型的地址常量。

也就是说,我希望Visual Studio生成“错误C2099:初始化程序不是常量”,就像它与| 0x8000000000000000一样。

不过,我不确定C ++。


1
投票

没有使用的初始化器

uint64_t a[] = { (uint64_t)&i, (uint64_t)&i + 0x8000000000000000 };

是合格的常量表达式。 C中常量表达式的迂腐定义不允许将指针值转换为整数类型,即使指针值满足地址常量的要求也是如此。这意味着正式的(uint64_t)&i在这种情况下已经是非法的。

但是,这个编译器在这种情况下显然接受(uint64_t)&i作为扩展。

之后,当+|运算符替换时它抱怨的事实可能直接根植于语言规范

6.6常量表达式

7初始值设定项中的常量表达式允许更多的纬度。这样的常量表达式应为或评估为以下之一:

- 算术常量表达式,

- 空指针常量,

- 地址常量,或

- 对象类型的地址常量加上或减去整数常量表达式。

同样,这不是完全匹配,因为上面的措辞允许仅将固定偏移量添加到地址常量,但对于在此上下文中接受(uint64_t)&i作为常量表达式的编译器,继续应用“加或”并不罕见减去“限制。在C中向地址常量添加内容(或从中减去某些内容)的能力由在加载时执行地址重定位的加载器的功能定义。加载器可以加或减,但它们不能对地址执行按位运算。

最后,它在运行时没有任何影响的事实显然是由加载器的限制引起的,加载器负责在启动时实现静态的C风格初始化。

© www.soinside.com 2019 - 2024. All rights reserved.