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

时间:2016-10-07 12:59:27

标签: c visual-c++ compiler-errors language-lawyer constant-expression

我有以下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]);
}

2 个答案:

答案 0 :(得分:1)

初始化程序

(uint64_t)&i + 0x8000000000000000

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

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

但我不确定C ++。

答案 1 :(得分:1)

中没有使用任何初始值设定项
uint64_t a[] = { (uint64_t)&i, (uint64_t)&i + 0x8000000000000000 };

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

但是,此编译器显然在此上下文中接受(uint64_t)&i作为扩展。

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

  

6.6常量表达式

     

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

     

- 算术常量表达式

     

- 空指针常量,

     

- 地址常量或

     

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

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

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