这是有效的C99代码吗?如果是这样,它是否定义了实现定义的行为?
int a;
unsigned long b[] = {(unsigned long)&a+1};
根据我对C99标准的理解,根据ISO C99标准的§6.6,这可能是有效的:
整数常量表达式应具有整数类型,并且仅应具有整数常量的操作数(...)整数常量表达式中的强制转换运算符只能将算术类型转换为整数类型,除非作为操作数的一部分转换为sizeof运算符。
初始化程序中的常量表达式允许更大的自由度。这样的常数表达式应为以下之一或求值:
- 算术常数表达式
- (...)
- 一个对象类型的地址常量,加上或减去一个整数常量表达式。
但是,由于存在加法溢出的可能,因此这可能不被视为常量表达式,因此不是有效的C99代码。
有人可以确认我的推理是否正确吗?
请注意,即使使用,GCC和Clang都接受此代码而不会发出警告-std=c99 -pedantic
。但是,当强制转换为unsigned int
而不是时unsigned long
,即使用以下代码:
int a;
unsigned long b[] = {(unsigned int)&a+1};
然后,两个编译器都抱怨该表达式不是编译时常量。
来自这个clang开发人员线程的一个类似问题是:当转换为long但不是int时,函数指针是编译时常量吗?其理由是该标准不需要编译器支持此方法(此方案未包含在中的项目符号中6.6p7
),尽管允许该方法支持该支持的截断地址将很麻烦:
我假设您的目标上的sizeof(int)<sizeof(void(*)())== sizeof(long)。问题在于,工具链几乎可以肯定不能将截断的地址表示为重定位。
C仅要求实现支持以下初始化程序值:(1)常量二进制数据,(2)某个对象的地址,或(3)或添加到某个对象地址的偏移量。我们被允许(但不是必需)支持更深奥的事情,例如减去两个地址或将一个地址乘以一个常数,或者,如您所愿,将地址的高位截断。这种计算需要从汇编器到装载器的整个工具链的支持,包括一路走来的各种文件格式。该支持通常不存在。
您的案例(将指针转换为整数类型)不符合6.6
段落下的任何案例7
:
初始化程序中的常量表达式允许更大的自由度。这样的常数表达式应为以下之一或求值:
- 算术常数表达式
- 指针指针常量,
- 地址常数,或
- 一个对象类型的地址常量,加上或减去一个整数常量表达式。
但是如后编译器中所述,允许其支持其他形式的常量表达式:
一个实现可以接受其他形式的常量表达式。
但既不接受clang
也不gcc
接受。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句