为什么将 `uint8_t` 赋值给 `uint8_t` 会产生转换警告?

3

为什么这段C代码会出现:

#include <stdint.h>

uint8_t do_foo() {
  uint8_t int8;
  int8 = 0x80;
  int8 = int8 + (uint8_t) 1;
  return int8;
}

给这个编译器警告?
$ gcc --version
gcc (Debian 4.9.2-10) 4.9.2
$ gcc -c -Wall -Wextra -Wconversion test.c
test.c: In function ‘do_foo’:
test.c:6:10: warning: conversion to ‘uint8_t’ from ‘int’ may alter its value [-Wconversion]
   int8 = int8 + (uint8_t) 1;
          ^

据我所见,右侧表达式肯定是uint8_t类型,而不是int类型。

4
问题出在do_foo()的返回值。它返回int类型,这就是引发警告的原因。所以在返回语句中,uint8_t会被隐式转换成do_foo()的返回类型,也就是int类型!编辑:抱歉,那是另一个问题,数字1是int类型,并且int类型是32位的。所以这个int类型将被转换为uint8,但很遗憾我现在无法使用编译器,但我猜这就是问题所在。 - mvdwerve
@mvdwerve:抱歉,但我认为你错了。gcc知道字面值(1)的值,因此没有理由相信转换(“向下转换”)会改变该值。 - undur_gongor
@undur_gongor 你可能是对的,我看到了你关于整数提升的答案,这甚至没有在我的考虑范围内!忘记了 C 语言中那些更棘手的问题。抱歉,我错了。 - mvdwerve
1
你的代码没有问题。gcc 在警告方面过于激进,转换可能不会改变你的代码中的值。 - M.M
2个回答

6
这是由于整数提升所致。由于+的运算对象比int小,因此它们被提升为int,并在int中执行加法。
对于分配给int8的情况,结果必须转换回uint8_t,这导致警告,因为值可能不适合。

换句话说,我永远不能将任何东西分配给类型为uint_8的变量而不发出警告,因为任何表达式都将被提升为(至少)int - Nikratio
@Nikratioжү§иЎҢint8 = (uint8_t)(int8 + 1);дјҡжңүиӯҰе‘Ҡеҗ—пјҹ - user253751
@immibis 不,但那肯定也是一个错误。 - Nikratio
3
为什么会是一个错误? - user253751

5
为了简化编译器,C语言最初只允许在五种数字类型上进行算术运算:intunsigned intlongunsigned longdouble。这意味着编译器只需要知道执行每个算术运算符的五种方法。此外,在二进制补码系统中,许多运算符可以同时用于有符号和无符号整数类型,并且因此只需要三种实现。如果尝试在以上列表之外的任何类型或任何类型组合上执行算术运算,将会导致“两个”操作数都被转换为以上列表中相同的类型[左移和右移类型的右操作数独立于左操作数]。
尽管标准后来改变了,增加了可执行算术运算的类型列表,包括float,但您的示例使用的是uint8_t类型,该类型不在列表中。总的来说,不在列表中的类型值将被转换为可以容纳对应类型所有值的第一个类型,而在您的情况下将是int。提升操作数以便与类型int执行加法运算后,编译器在尝试将结果赋回到较小类型时发出警告。

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