如何在C语言中将字面量转换类型

6

我有一个小的样本函数:

#define VALUE 0

int test(unsigned char x) {
  if (x>=VALUE)
    return 0;
  else
    return 1;
}

我的编译器警告我比较语句(x>=VALUE)在所有情况下都为真,这是正确的,因为x是一个无符号字符,而VALUE被定义为值0。所以我修改了我的代码:

if ( ((signed int) x ) >= ((signed int) VALUE ))

但是警告又出现了。我用了三个GCC版本进行测试(所有版本>4.0,有时需要启用-Wextra)。

在这种情况下,我有一个显式的转换,应该是有符号整数比较。为什么它声称比较总是为真?


1
那么,你想要实现什么目标?在什么条件下,你希望它返回1而不是0? - David Thornley
1
我的意图是避免这个警告。 - Günther Jena
6
不要忽略这个警告,试着去除你想知道一个无符号数是否大于等于0的需求。它总是大于等于0的——编译器会告诉你这一点。 - xtofl
我想要有可能将VALUE的定义更改为0以外的其他值。如果VALUE不是0,那就没有问题。 - Günther Jena
2
听起来对我来说是一个很好的时机来应用YAGNI。等到你真正需要这个功能再考虑它。或者如果你确实需要它,那么它可能不应该是一个编译时常量,而应该是一个配置参数、命令行参数或其他类似的东西。 - jpmc26
我本来想建议使用 if constexpr 或类似的东西。但是 gcc 对警告非常敏感。 :-/ 我可能会对 gcc 提交一个错误报告,因为确实应该有一些方法可以在不使用预处理器的情况下明确您的意图。 - Omnifarious
4个回答

12
即使进行了类型转换,只要行为定义良好,比较在所有情况下仍然成立。编译器仍然确定 (signed int)0 的值为0,并且如果程序有定义明确的行为(从无符号类型转换为有符号类型时,如果值超出有符号类型的范围,则行为未定义),则仍然确定(signed int)x是非负的。
因此,编译器继续警告,因为它会完全消除else分支。 编辑:为了消除警告,请将代码编写为:
#define VALUE 0

int test(unsigned char x) {
#if VALUE==0
  return 1;
#else
  return x>=VALUE;
#endif
}

3
一个unsigned char值永远不可能超出signed int的范围。即使符号位被设置,该值也始终在signed int的范围内,这个操作总是被定义明确的(结果总是正数)。 - Konrad Rudolph
3
哎呀,我错过了它是从字符转换为整数的。尽管如此,在一个(理论上的)实现C语言中,如果int类型大小为1字节,这种情况是可能发生的。考虑到int类型必须至少有2个字节,这在一个char类型也是两个字节的实现中是可能的。我同意在现实平台上,这里不会发生溢出。 - Martin v. Löwis
@Martinv.Löwis:一些实际的字地址可寻址DSP在其C实现中char = int = 32位。 - Peter Cordes

7

x 是一个 unsigned char,意味着它的值在0和256之间。由于 intchar 大,将 unsigned char 强制转换为 signed int 仍然保留了 char 的原始值。由于这个值总是大于等于0,你的 if 语句总是为真。


3

所有unsigned char的值都可以完美地适配到你的int中,所以即使进行强制转换,你也永远不会得到负值。你需要的转换是到signed char,然而,在这种情况下,你应该在函数签名中将x声明为signed。没有必要对客户撒谎,告诉他们你需要一个无符号值,而实际上你需要一个有符号的值。


1

#define VALUE 0 的意思是你的函数被简化为:

int test(unsigned char x) {
  if (x>=0)
    return 0;
  else
    return 1;
}

由于x始终作为unsigned char传入,因此它的值始终在0255之间(包括边界),无论你是否在if语句中将x0强制转换为有符号整数signed int。因此编译器警告您x始终大于或等于0,并且else子句永远无法执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接