#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
在情况1中,标识符b被编译为-5,这不是语法错误,因为char仅接受-128〜127的值。那么,第一个问题是标识符b在转换结束之前是否首先保存为int数据类型?(转换结束时,b将保存在char中。)
在情况2中,x,y被提升为int。因此,h具有正确的结果值。但是在case3中,m,n不会被提升为unsigned int(也许)。标识符I没有普通值(2 ^ 30)。 C99表示
如果一个int可以代表原始类型的所有值,则该值将转换为int; 否则,它将转换为无符号整数。
基于C99,h的值很自然,但是处理m * n / o却溢出了。这是不自然的,因为它与C99相对。这是我的第二个查询。
答案 0 :(得分:4)
在情况1中,初始化在平台上签名的char
且值0xfb
大于CHAR_MAX
的{{1}}并不是语法错误。效果由实现定义,毫不奇怪,0xfb
被转换为具有相同8位模式的值-5
。当a
和b
作为变量参数传递给int
时,将提升为printf
,因为在这种情况下这是指定的行为。
在情况2中,x
,y
和z
被提升为int
,因为在您的平台上,类型int
可以表示类型{ {1}}。使用char
算术执行计算,得出int
的结果,其结果在54
类型的范围内,因此可以毫无问题地存储到char
中。如上所述,将h
传递给h
时,将提升为int
。
在第3种情况下,printf
和m
的类型已经为n
,因此不会升级。升级规则适用于每个操作数,而不适用于以任意精度执行的操作结果:int
是用m*n
算术执行的,并在您的平台上溢出,其中int
是大概是32位宽。行为是未定义。碰巧结果是int
,因此将其除以-2147483648
会得到2
,但这不是C标准所保证的。
为使计算准确进行,至少-107341824
或m
必须具有较大的类型或将其强制转换为:n
将在您的平台上产生i = (unsigned)m * n / o;
,但请注意,类型107341824
的位数可能少于32位。对于可移植表达式,您需要对类型int
和类型unsigned long
使用i
类型,将其指定为至少具有32个值位。
最后,main
应该定义为int main()
或int main(void)
或int main(int argc, char *argv[])
或兼容的原型。 void main()
不正确。
答案 1 :(得分:4)
在情况1中,标识符b编译为-5,这不是语法错误 以为char只接受-128〜127的值。所以,第一个问题是 标识符b首先在其末尾另存为int数据类型 翻译?(翻译结束时,b将保存在char中。)
在情况1中,变量b
的初始化程序(常数0xfb
)表示类型为int
的值,值为251(十进制)。在C抽象机模型中,该值在初始化程序运行时转换为b
的类型(char
),对于块范围变量而言,该值是在执行到达该声明时(而不是在转换期间) 。如果确实在实现中char
的范围是-128-127,则您已经签名了char
,它们不能代表初始化程序的值,从而导致实现定义的行为。
因此,再次引用抽象机器模型,在翻译结束时, 什么都没有存储在b
中。它是一个块范围变量,因此在执行达到其声明之前不存在。转换后的程序确实需要某种方式来存储b
的初始值,但是C并未指定应以哪种形式存储它。但是,在翻译或执行期间,b
绝对不会包含类型int
的值。
在评估printf
调用的参数时,将读取b
的值,然后将其转换为int
(因为它是可变参数)。那么可以争辩地说,使用%d
字段来打印它是可以的,但是如果您要确定要打印转换前的值,则应该改为使用%hhd
(尽管在您的情况下,几乎可以肯定会显示相同的结果。
在情况2中,x,y被提升为int。因此h具有正确的结果值。
更具体地说,在情况2中,在期间, x
,y
和z
的值被提升为int
对表达式x*y/z
的求值,每个操作都会产生一个int
结果。乘法不会溢出所选类型int
,并且总结果在类型char
的范围内,因此在分配给char
时应用到h
的转换是没什么。
但是在 case3,m,n不提升为unsigned int(也许)。标识符I 没有普通值(2 ^ 30)。
在情况3中,m
,n
和o
已经具有类型int
,因此它们不被提升,并且算术表达式会计算相同类型的结果(int
)。子表达式m*n
的结果不在类型int
的范围内,因此,根据标准paragraph 6.5/5,出现了不确定的行为:
如果评估过程中发生异常情况 表达式(也就是说,如果结果没有在数学上定义,或者 不在其类型的可表示值的范围内) 未定义。
确实是
C99 [和C11]表示
如果一个int可以代表原始类型的所有值,则该值将转换为int;否则,它将转换为无符号整数。
,但这与这里无关。它是“整数提升”描述的一部分,该描述基于表达式的类型适用于表达式的操作数。
基于C99,h的值很自然,但是处理m * n / o是 溢出。这是不自然的,因为它与C99相对。这是我的 第二个查询。
您似乎希望中间表达式m*n
会被计算为产生类型unsigned int
的结果,因此它不会溢出,但是标准不支持。通常的算术转换(包括整数提升)仅基于操作数类型的特征,包括其有符号性和值范围。进行常规算术转换(包括*
)的运算符使用它们来确定所有操作数和结果的通用类型。
您的m
和n
已经是同一类型,并且该类型是int
,因此没有转化/促销活动。如果int
和m
的值不等于未乘积(作为整数),则乘法结果也将是n
。但是,实际上,该操作会溢出类型int
,从而产生未定义的行为。
答案 2 :(得分: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]范围