14

我有以下 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我希望设置高位的 codea[1]已被默默地忽略。

a但是,如果我移动inside的初始化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]);
}
4

2 回答 2

1

没有使用任何初始化程序

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

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

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

+之后,它抱怨 when被替换为operator的事实|可能直接植根于语言规范

6.6 常量表达式

7初始化器中的常量表达式允许更大的自由度。这样的常量表达式应为或评估为以下之一:

— 算术常数表达式,

— 一个空指针常量,

— 地址常数,或

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

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

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

于 2016-10-07T16:30:02.273 回答
1

初始化器

(uint64_t)&i + 0x8000000000000000

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

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

不过,我不确定 C++。

于 2016-10-07T13:36:57.757 回答