3
#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 说

如果一个 int 可以表示原始类型的所有值,则将该值转换为 int;否则,它将转换为无符号整数。

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

4

3 回答 3

4

在情况 1 中,使用大于char的值初始化在您的平台上签名的a 不是语法错误。效果是实现定义的,并且毫不奇怪地转换为具有相同 8 位模式的值。并且在作为变量参数传递给时被提升为,因为这是在这种情况下的指定行为。0xfbCHAR_MAX0xfb-5abintprintf

在情况 2 中xyz被提升为int,因为在您的平台上 typeint可以表示 type 的所有值char。计算是用int算术执行的,产生的结果54在 type 的范围内char,因此可以h毫无问题地存储。如上所述传递到时h被提升到。intprintf

在第 3 种情况下,因为它们已经有类型而没有被m提升。提升规则适用于每个操作数,而不是像以任意精度执行的操作结果:在您的平台上使用算术执行并溢出,大概是 32 位宽。行为未定义。碰巧结果是除以产量,但 C 标准不能保证这一点。nintm*nintint-21474836482-107341824

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

最后,main应该定义为int main()or int main(void)or or int main(int argc, char *argv[])or 一个兼容的原型。void main()是不正确的。

于 2019-12-26T06:59:39.600 回答
4

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

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

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

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

在 case2 中,x,y 被提升为 int。所以 h 有正确的结果值。

更具体地说,在情况 2 中,在表达式 的求值过程中将、和的值 提升为,并且每个操作都会产生一个结果。乘法不会溢出所选类型,并且总体结果在 type 的范围内,因此在分配 to 时应用的转换是不起眼的。xyzint x*y/zintintcharcharh

但在 case3 中,m、n 不会被提升为 unsigned int(也许)。标识符 I 没有普通值(2^30)。

在情况 3 中mn、 和o已经有 type int,因此它们不会被提升,并且算术表达式计算相同类型 ( int) 的结果。子表达式的结果m*n不在 type 的范围内int,因此会出现未定义的行为,根据标准的第 6.5/5 段

如果在计算表达式期间出现异常情况(即,如果结果未在数学上定义或不在其类型的可表示值范围内),则行为未定义。

的确如此

C99 [和 C11] 说

如果一个 int 可以表示原始类型的所有值,则将该值转换为 int;否则,它将转换为无符号整数。

,但这在这里无关紧要。它是“整数提升”描述的一部分,它根据类型应用于表达式的操作数。

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

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

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

于 2019-12-26T07:06:59.480 回答
0
#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] 范围

于 2019-12-26T04:04:02.720 回答