Apple Secure Coding Guide说明以下内容(第27页):
此外,任何溢出超过整数变量长度的位(无论是有符号还是无符号)都将被删除。
但是,有问题的整数溢出C standard (89)表示:
未定义行为的一个示例是整数溢出的行为。
和
如果在评估表达式期间发生异常(即,如果结果未在数学上定义或无法表示),则行为未定义。
编码指南错了吗?这里有什么我不懂的东西吗?我不相信Apple 安全编码指南可能会出错。
答案 0 :(得分:3)
以下是第二种意见,来自静态分析器described,用于检测未定义的行为:
int x;
int main(){
x = 0x7fffffff + 1;
}
运行分析仪:
$ frama-c -val -machdep x86_32 t.c
它产生:
[kernel] preprocessing with "gcc -C -E -I. t.c"
[value] Analyzing a complete application starting at main
...
t.c:4:[kernel] warning: signed overflow. assert 0x7fffffff+1 ≤ 2147483647;
...
[value] Values at end of function main:
NON TERMINATING FUNCTION
这意味着程序t.c包含未定义的行为,并且在没有导致未定义的行为的情况下,它的执行不会终止。
答案 1 :(得分:2)
我们来看这个例子:
1 << 32
如果我们假设32位int
,C明确表示它是未定义的行为。周期。
但是任何实现都可以定义这种未定义的行为。
例如, gcc
(虽然在定义行为时不是很明确):
GCC不使用C99中给出的宽容度来处理签名'&lt;&lt;'的某些方面如未定义,但这可能会有所变化。
http://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html
我不知道clang但是我怀疑对于gcc
,对1 << 32
这样的表达式的评估并不会让人感到意外(即评估为0
)。
但即使它是在Apple操作系统中运行的实现上定义的,便携式程序也不应该使用在C语言中调用未定义行为的表达式。
编辑:我认为Apple的句子只处理按位<<
运算符。看起来它更普遍,在C语言的情况下,它们是完全错误的。
答案 2 :(得分:2)
这两个陈述并非相互矛盾。
作为程序员,建议您将行为视为未定义,因为您的代码可能需要移动到行为不同的其他平台,并且可能因为Apple理论上可以在未来改变主意并且仍然符合标准。
答案 3 :(得分:0)
考虑代码
void test(int mode)
{
int32_t a = 0x12345678;
int32_t b = mode ? a*0x10000 : a*0x10000LL;
return b;
}
如果在mode
值为零的情况下调用此方法,则代码将计算长long值0x0000123456780000并将其存储到a
中。这个行为完全由C标准定义:如果结果的第31位是清除的,它将丢弃除底部32位以外的所有数据并将得到的(正)整数存储到a
中。如果设置了第31位并且结果存储到32位int
而不是类型int32_t
的变量,则实现将具有一定的自由度,但实现仅允许定义{{1如果他们根据两个补码数学的规则进行这种缩小的转换。
如果使用非零int32_t
值调用此方法,则数值计算将产生超出临时表达式值范围的结果,因此会导致未定义的行为。虽然规则规定如果对较长类型执行的计算存储在较短的类型中会发生什么,但它们并不表示如果计算不适合执行它们的类型会发生什么。标准中的一个相当讨厌的差距(应该插入恕我直言)发生在:
mode
对于uint16_t multiply(uint16_t x, uint16_t y)
{
return x*y;
}
和x
值的所有组合,其中标准说明了此函数应执行的操作,标准要求它计算并返回产品mod 65536.如果标准要授权对于y
和x
值0-65535的所有组合,此方法必须返回(x * y)mod 65536的算术值,这将是99.99%符合标准的编译器的强制行为已经符合要求了。不幸的是,在y
为32位的机器上,标准目前对算术产品在大于2147483647的情况下的行为没有要求。即使中间结果的任何部分也是如此超出底部16位将被忽略,代码将尝试使用32位有符号整数类型来评估结果;如果编译器认识到产品将溢出该类型,则标准不对要发生的情况施加任何要求。