关于常用算术转换的问题-GCC编译器
Question on Usual Arithmetic Conversions - GCC Compiler
我正在尝试理解 C 中的隐式数据类型转换。我以为我已经理解了这个主题,但是下面的代码示例仍然让我感到困惑。
具体来说,我之前从 C 标准的草稿中读到了有关常用算术转换和整数提升的内容。
unsigned short int a = 0;
printf("\n%lld", (signed int)a - 1);
我正在使用 GCC 进行编译。
unsigned short int
是 2 个字节。
int
是 4 个字节。
当我运行这段代码时,我得到以下结果:4294967295
我期望结果 -1。
这是我预期会发生的事情:
Typecast优先,-
的LHS变为signed int
.
进行了-
操作。这里没有整数提升或隐式转换发生,因为 LHS 和 RHS 已经都是 signed int
。运算结果为 -1,数据类型为 signed int
.
在printf
语句中,值-1在转换为long long int
时保留,结果显示-1。
谁能解释一下我逻辑上的缺陷在哪里?
它是未定义的行为,因为 %lld
是 int
类型的不合适的格式说明符。
确实 (signed int)a - 1
是一个 int
类型,值为 -1
,但是 printf
调用是未定义的部分。 C 标准中没有任何内容建议转换为 long long
。
Within printf statement, value -1 is retained within the conversion to long long int
没有发生这样的转换。 printf
(函数族)是愚蠢的,需要一个与参数列表类型相对应的格式字符串。
printf
not 像普通函数一样工作 void f (long long int x)
,它会强制隐式转换为参数类型 ("as per assignment"/"lvalue conversion")。这会给你预期的 "sign extension".
值得注意的是,这里还有另一种专门的隐式转换,称为默认参数提升,它只适用于可变参数函数和没有原型的函数。
C17 6.5.2.2/6
If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float
are promoted to double
. These are called the default argument
promotions.
C17 6.5.2.2/7 关于可变参数函数:
The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument
promotions are performed on trailing arguments.
实际上这意味着:
传递给 printf
的 float
在函数调用期间隐式转换为 double
。
- 传递给
printf
的小整数类型在函数调用期间根据整数提升进行隐式转换,最有可能以 int
. 结束
- 传递给
printf
的其他类型在函数调用期间不会隐式提升。
然后传递的和可能转换的参数在内部被视为转换说明符指定的类型。如果那个与实际类型不匹配,则代码具有未定义的行为。
在您的情况下,您传递了一个 int
,它不会被隐式提升,但由于 printf
将其视为 long long
,您会得到未定义的行为。
在这里你可以认为自己很幸运。 a
是一个 short int
,它经过通常的算术转换为“signed int”,即使进行了强制转换,所以
unsigned short int a = 0;
printf("\n%d", (signed int)a - 1);
和
unsigned short int a = 0;
printf("\n%d", a - 1);
将具有相同的行为,if unsigned short
的所有值都可以在 int
中表示(就像您的情况一样)。转换的结果是 int
。现在,对于可变参数,应用默认参数提升,任何小于 int
的整数如果可表示则转换为 int
,否则 unsigned int
。但是 lld
期望 signed long long int
是 8 个字节宽。默认参数提升不会将 int
隐式提升为 long long int
.
现在运气来了——你确实得到了一个错误的值。看,由于 行为未定义 你可能已经得到了你 期望 的值,这次——毕竟在64位处理器上是完全可行的!
我正在尝试理解 C 中的隐式数据类型转换。我以为我已经理解了这个主题,但是下面的代码示例仍然让我感到困惑。
具体来说,我之前从 C 标准的草稿中读到了有关常用算术转换和整数提升的内容。
unsigned short int a = 0;
printf("\n%lld", (signed int)a - 1);
我正在使用 GCC 进行编译。
unsigned short int
是 2 个字节。
int
是 4 个字节。
当我运行这段代码时,我得到以下结果:4294967295
我期望结果 -1。
这是我预期会发生的事情:
Typecast优先,
-
的LHS变为signed int
.
进行了-
操作。这里没有整数提升或隐式转换发生,因为 LHS 和 RHS 已经都是signed int
。运算结果为 -1,数据类型为signed int
.在
printf
语句中,值-1在转换为long long int
时保留,结果显示-1。
谁能解释一下我逻辑上的缺陷在哪里?
它是未定义的行为,因为 %lld
是 int
类型的不合适的格式说明符。
确实 (signed int)a - 1
是一个 int
类型,值为 -1
,但是 printf
调用是未定义的部分。 C 标准中没有任何内容建议转换为 long long
。
Within printf statement, value -1 is retained within the conversion to long long int
没有发生这样的转换。 printf
(函数族)是愚蠢的,需要一个与参数列表类型相对应的格式字符串。
printf
not 像普通函数一样工作 void f (long long int x)
,它会强制隐式转换为参数类型 ("as per assignment"/"lvalue conversion")。这会给你预期的 "sign extension".
值得注意的是,这里还有另一种专门的隐式转换,称为默认参数提升,它只适用于可变参数函数和没有原型的函数。
C17 6.5.2.2/6
If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type
float
are promoted todouble
. These are called the default argument promotions.
C17 6.5.2.2/7 关于可变参数函数:
The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.
实际上这意味着:
-
传递给
float
在函数调用期间隐式转换为double
。- 传递给
printf
的小整数类型在函数调用期间根据整数提升进行隐式转换,最有可能以int
. 结束
- 传递给
printf
的其他类型在函数调用期间不会隐式提升。
printf
的 然后传递的和可能转换的参数在内部被视为转换说明符指定的类型。如果那个与实际类型不匹配,则代码具有未定义的行为。
在您的情况下,您传递了一个 int
,它不会被隐式提升,但由于 printf
将其视为 long long
,您会得到未定义的行为。
在这里你可以认为自己很幸运。 a
是一个 short int
,它经过通常的算术转换为“signed int”,即使进行了强制转换,所以
unsigned short int a = 0;
printf("\n%d", (signed int)a - 1);
和
unsigned short int a = 0;
printf("\n%d", a - 1);
将具有相同的行为,if unsigned short
的所有值都可以在 int
中表示(就像您的情况一样)。转换的结果是 int
。现在,对于可变参数,应用默认参数提升,任何小于 int
的整数如果可表示则转换为 int
,否则 unsigned int
。但是 lld
期望 signed long long int
是 8 个字节宽。默认参数提升不会将 int
隐式提升为 long long int
.
现在运气来了——你确实得到了一个错误的值。看,由于 行为未定义 你可能已经得到了你 期望 的值,这次——毕竟在64位处理器上是完全可行的!