这是有效的C99代码吗?如果是的话,它是否定义了实现定义的行为?
int a;
unsigned long b[] = {(unsigned long)&a+1};根据我对C99标准的理解,根据ISO C99标准第6.6节,这可能是有效的:
- an arithmetic constant expression,
- (...)
- an address constant for an object type plus or minus an integer constant expression.
但是,由于存在加法溢出的可能性,这可能不是一个常量表达式,因此不能使用有效的C99代码。
有人能确认一下我的推理是否正确吗?
请注意,GCC和Clang都不带警告地接受此代码,即使在使用-std=c99 -pedantic时也是如此。但是,当转换为unsigned int而不是unsigned long时,即使用以下代码:
int a;
unsigned long b[] = {(unsigned int)&a+1};然后,两个编译器都抱怨表达式不是编译时常量。
发布于 2015-04-15 14:20:11
从这个clang开发人员线程中可以看到类似的问题:函数指针在转换为长时是编译时间常数,而不是int?,其基本原理是,标准不要求编译器支持这个(这个场景不包括在6.6p7中的任何一个符号中),尽管允许它支持这个支持截断的地址将是很麻烦的:
--我假设在目标上,sizeof(int) < sizeof(void(*)()) == sizeof(long)。问题是工具链几乎肯定不能将截断的地址表示为重新定位。 C只需要实现支持初始化值,这些值要么是(1)常量二进制数据,(2)某个对象的地址,要么是(3)或添加到某个对象地址的偏移量。我们允许(但不需要)支持更复杂的事情,比如减去两个地址或将一个地址乘以一个常量,或者,就像在您的例子中那样,截断地址的顶部位。这种计算需要从汇编程序到加载程序的整个工具链的支持,包括各种文件格式。这种支持一般不存在。
您的情况(它正在转换指向整数类型的指针)不适合6.6段落7下的任何情况。
对于初始化器中的常量表达式,允许有更大的空间。这种常量的表述应是或评估以下之一:
但是,正如post编译器中所提到的,允许支持其他形式的常量表达式:
实现可以接受其他形式的常量表达式。
但clang和gcc都不接受这一点。
发布于 2015-04-15 15:05:06
符合规范的实现不需要接受此代码。你在你的问题中引用了有关的段落:
(unsigned long)&x不是那种东西。它不是算术常量,因为C11 6.6/8:
算术常量表达式中的转换运算符只能将算术类型转换为算术类型。
(指针类型不是算术类型,6.2.5/18);它不是地址常量,因为所有地址常量都是指针(6.6/9)。最后,一个指针加或减一个ICE是另一个指针,所以它也不是。
然而,6.6/10说,实现可以接受其他形式的常量表达。我不确定这是否意味着原始代码应该被称为格式错误(格式错误的代码需要诊断)。显然,您的编译器在这里接受一些其他常量表达式。
下一个问题是从指针到整数的转换是实现定义的.如果没有对应于特定指针的整数表示,它也可能是未定义的。(6.3.2.3/6)
最后,最后的+ 1没有什么区别。unsigned long算法在加法和减法上都有很好的定义,所以它是好的当且仅当(unsigned long)&x是OK的。
发布于 2015-04-15 14:23:31
首先,初始化器不一定是一个常量表达式。如果a有本地作用域,那么当它被推到堆栈上时,会在运行时为它分配一个地址。C11 6.6/7说,要使指针成为常量表达式,它必须是地址常量,在6.6/9中定义为:
地址常量是空指针,是指向指定静态存储期限对象的值的指针,或者是指向函数指示符的指针;它应该使用一元运算符或整数常量转换为指针类型显式创建,或者通过使用数组或函数类型的表达式隐式创建。
(强调地雷)
至于您的代码是否是标准C,是的。指针转换到整数是允许的,尽管它们可能伴随着各种形式的错误指定的行为。6.5/6中具体说明:
任何指针类型都可以转换为整数类型。除前面指定的结果外,结果是实现定义的。如果结果不能用整数类型表示,则行为未定义。结果不需要在任何整数类型的值范围内。
要安全地确保指针符合整数,您需要使用uintptr_t。但是我不认为指向整数转换的指针是你发布这个问题的原因。
关于整数溢出是否会阻止它成为编译时间常量,我不知道您是从哪里得到这种想法的。我不相信您的推理是正确的,例如,(INT_MAX + INT_MAX)是一个编译时间常数,而且它也保证会溢出。(GCC给你一个警告。)如果它溢出,它将调用未定义的行为。
至于为什么会出现关于表达式不是编译时常量的错误,我不知道。我不能在gcc 4.9.1上复制它。我尝试声明具有静态和自动存储时间的a,但没有区别。
听起来像是您意外地编译为C90,在这种情况下gcc会告诉您“错误:初始化器元素在加载时是不可计算的”。或者可能是我的gcc版本中有一个编译错误。
https://stackoverflow.com/questions/29652323
复制相似问题