使用arm-none-eabi-gcc
,以下程序根据编译器优化级别提供不同的输出:
#include <stdio.h>
unsigned short p = 100;
void f(signed char o) {
// o is signed, so 0xfc should be interpreted as -4 here, but it is not always
p += o;
}
int main(void) {
void (*fp)(unsigned char o);
fp = (void (*)(unsigned char))f;
printf("%d\n", p);
fp(0xfc);
printf("%d\n", p);
return 0;
}
-O0
输出(所需):
100
96
-O2
输出(不需要的):
100
352
我希望程序在使用96
时输出-O2
。
使用-fwrapv
或-fno-strict-overflow
无效。
我无法更改fp
的类型,我希望f
始终将第一个参数解释为signed char
(因此它将0xfc视为-4)。
答案 0 :(得分:0)
通过不同类型的指针调用函数是未定义的行为。
没有保证。编译器正在做“正确”的事情,因为任何事都是允许的。
您说您无法更改fp
的类型,因此修复方法是更改f
的类型:
void f(unsigned char uo)
{
signed char o = (signed char)uo; // value is implementation-defined :(
p += o;
}
答案 1 :(得分:-2)
这里的问题是未签名的促销。由于C中的某种令人惊讶的行为,无符号值和有符号值的二元运算符将在操作之前将有符号值提升为无符号。
是。这很奇怪。试试这个程序确认(live demo):
#include <stdio.h>
int main(void) { printf("%d",(int)( -1 < 0u )); return 0; }
-1
肯定小于0
,但此程序输出错误。发生的事情是-1
在比较之前被提升为无符号值(因为0
不同,0u
是无符号的)。由于无符号数不能小于无符号零,因此比较结果为false。
我非常强烈怀疑这是你的例子中发生的事情:
signed char o = -4;
unsigned short p = /*...*/;
p += o;
由于p
是无符号的,o
会转换为无符号字符:两个补码中-4
的位模式为0xFC
,即252
{1}}如果被解释为无符号。
由于您现在有两个未签名的操作数,现在可以继续添加,252
添加到p
。
至于做什么呢?好吧,你可以让p
成为一个有符号的变量,这样就不会发生这种情况。您可以减去正值而不是添加负值。如果不看你的用例,很难知道。