为什么在我的案例中没有发生整数提升?

Why doesn't integer promotion happen in my case?

#include <stdio.h>

void main()
{
    unsigned char a = 0xfb;
    char b = 0xfb;
    printf("case1 : %d, %d", a, b); // case1

    char x=90, y=60, z=100, h;
    h = x*y/z;
    printf("\ncase2 : %d", h);      // case2

    int m = 32768, n = 65536, o = 2, i;
    i = m*n/o;
    printf("\ncase3 : %d", i);      // case3
}

result

case1 : 251, -5
case2 : 54
case3 : -107341824

在case1中,标识符b被编译为-5,这不是语法错误,认为char只接受-128~127的值。那么,第一个问题是标识符b在翻译结束时首先保存为int数据类型?(翻译结束时,b将保存为char。)

在case2中,x,y被提升为int。所以 h 有正确的结果值。但是在 case3 中,m、n 没有被提升为 unsigned int(可能)。标识符 I 没有普通值 (2^30)。 C99 说

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.

基于C99,h的取值是自然的,但是处理m*n/o就溢出了。这不自然,因为它反对 C99。这是我的第二个查询。

#include <stdio.h>

int main()
{
    unsigned char a = 0xfb;
    char b = 0xfb;
    printf("case1 : %d, %d", a, b); // case1

    char x=90, y=60, z=100, h;
    h = x*y/z;
    printf("\ncase2 : %d", h);      // case2

    unsigned long long int  m = 32768, n = 65536, o = 2,i;
    i = m*n/o;
    printf("\ncase3 : %llu", i);      // case3

}

在这个类型中你可以得到答案 [0, +18,446,744,073,709,551,615] 范围

在情况 1 中,初始化在您的平台上签名的 char,其值 0xfb 大于 CHAR_MAX,这不是语法错误。效果是实现定义的,不出所料 0xfb 被转换为具有相同 8 位模式的值 -5。当作为变量参数传递给 printf 时,ab 被提升为 int,因为这是在这种情况下指定的行为。

在情况 2 中,xyz 被提升为 int,因为在您的平台上类型 int 可以表示所有值输入 char。使用 int 算法执行计算,产生 54 的结果,它在类型 char 的范围内,因此可以毫无问题地存储到 h 中。如上传递给 printf 时,h 被提升为 int

在情况 3 中,mn 不会被提升,因为它们已经具有类型 int。提升规则适用于每个操作数,而不是操作的结果,就好像它是以任意精度执行的:m*n 是用 int 算法执行的,并且在你的平台上溢出,其中 int 是大概是 32 位宽。行为是 未定义。碰巧结果是 -2147483648,所以将它除以 2 得到 -107341824,但这不受 C 标准的保证。

为了准确执行计算,至少 mn 必须具有更大的类型或这样转换:i = (unsigned)m * n / o; 会产生 107341824您的平台,但请注意类型 int 可能少于 32 位。对于可移植表达式,您需要使用类型 unsigned long,它被指定为至少具有 32 个值位,用于类型转换和 i.

的类型

最后,main应定义为int main()int main(void)int main(int argc, char *argv[])或兼容原型。 void main() 不正确。

In case1, identifier b is compiled as -5, which is not syntax error thought char only accept -128~127 value. So, first question is identifier b is firstly saved as int data type by end of its translation?(When translation is end, b will be saved in char.)

在情况 1 中,变量 b 的初始值设定项,常量 0xfb,表示类型 int 的值,值为 251(十进制)。在 C 抽象机模型中,当初始化程序运行时,这个值被转换为 b 的类型(char),对于块范围变量,这是在执行到达该声明时(而不是在翻译期间) .如果您的实现中 char 的范围确实是 -128 - 127,那么您签署的 char 不能表示初始化值,从而导致实现定义的行为。

因此,再次引用抽象机模型,nothing 在翻译结束时存储在 b 中。它是一个块作用域变量,因此在执行到它的声明之前它不存在。翻译后的程序确实需要以某种方式存储 b 的初始值,但 C 没有指定它应该以何种形式存储。但是,在转换或执行期间,b 绝不会包含 int 类型的值。

当计算 printf 调用的参数时,会读取 b 的值,然后将其转换为 int(因为它是可变参数)。可以说,然后,可以使用 %d 字段来打印它,但是如果您想确定打印转换前的值,那么您应该真正使用 %hhd 代替(尽管在您的情况下,那几乎肯定会打印出相同的结果)。

In case2, x, y is promoted as int. So h has right result value.

更具体地说,在情况 2 中, xyz 的值提升为 int 在计算表达式 x*y/z 期间,每个操作都会产生一个 int 结果。乘法不会溢出所选类型 int,并且整体结果在类型 char 的范围内,因此在分配给 h 时应用到 char 的转换是不起眼。

But in case3, m, n aren't promoted as unsigned int(maybe). Identifier I doesn't have ordinary value(2^30).

在情况 3 中,mno 已经具有类型 int,因此它们不会被提升,算术表达式计算结果相同类型 (int)。子表达式 m*n 的结果不在类型 int 的范围内,因此根据标准的 paragraph 6.5/5,会发生未定义的行为:

If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.

这是真的

C99 [and C11] says that

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.

,但这与这里无关。它是 "integer promotions" 描述的一部分,适用于表达式的 操作数 ,基于它们的类型。

Being based on C99, value of h is natural, but dealing with m*n/o is overflowed. It's not natural because it's opposed to C99. This is my second query.

您似乎期望中间表达式 m*n 会被求值以产生类型 unsigned int 的结果,这样它就不会溢出,但这不受标准支持。通常的算术转换,包括整数提升,仅基于操作数类型的特征,包括它们的符号性和值范围。受常规算术转换影响的运算符,包括 *,使用它们来确定所有操作数和结果的通用类型。

您的 mn 已经是同一类型,并且该类型是 int,不适用转换/促销。如果 mn 的值不是这样它们的乘积(作为 int)未定义,则乘法的结果也将是 int。然而,事实上,操作溢出类型 int,产生未定义的行为。