我有以下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]);
}
答案 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风格初始化。