C语言中未指定行为的一个示例是函数的参数求值顺序.可能是从左到右,也可能是从右到左,只是你不知道.这将影响判断foo(c++, c)
或foo(++c, c)
的方式.
还有什么其他未指明的行为会让不知情的程序员感到惊讶?
C语言中未指定行为的一个示例是函数的参数求值顺序.可能是从左到右,也可能是从右到左,只是你不知道.这将影响判断foo(c++, c)
或foo(++c, c)
的方式.
还有什么其他未指明的行为会让不知情的程序员感到惊讶?
语言律师问题.嗯,凯.
我个人的top3:
违反了严格的别名规则
违反了严格的别名规则
违反了严格的别名规则
:-)
Edit这里有一个小例子,它做错了两次:
(假设32位整数和小尾数)
float funky_float_abs (float a)
{
unsigned int temp = *(unsigned int *)&a;
temp &= 0x7fffffff;
return *(float *)&temp;
}
该代码试图通过直接在浮点表示中旋转符号位,逐位获得浮点的绝对值.
然而,通过从一种类型转换到另一种类型来创建指向对象的指针的结果是无效的C.编译器可能会假设指向不同类型的指针不指向同一内存块.这适用于除void*和char*之外的所有类型的指针(符号性并不重要).
在上面的例子中,我做了两次.一次获取浮点a的int别名,一次将值转换回浮点.
有三种有效的方法可以做到这一点.
在转换过程中使用char或void指针.这些都是任何东西的别名,所以它们是安全的.
float funky_float_abs (float a)
{
float temp_float = a;
// valid, because it's a char pointer. These are special.
unsigned char * temp = (unsigned char *)&temp_float;
temp[3] &= 0x7f;
return temp_float;
}
使用memcopy.Memcpy使用空指针,因此它也会强制使用别名.
float funky_float_abs (float a)
{
int i;
float result;
memcpy (&i, &a, sizeof (int));
i &= 0x7fffffff;
memcpy (&result, &i, sizeof (int));
return result;
}
第三种有效方法:使用联合体.这显然是not undefined since C99:
float funky_float_abs (float a)
{
union
{
unsigned int i;
float f;
} cast_helper;
cast_helper.f = a;
cast_helper.i &= 0x7fffffff;
return cast_helper.f;
}