第七章
有符号整数溢出,程序行为是未定义的
无符号整数运算过程中发生溢出,结果是有定义的:对2^n取模,n适用于存储结果的位数
C标准未说明
char
类型是有符号还是无符号型-
「惯用法」
while(getchar()!='\n');//skip rest of line
while((c=getchar())==' ');//skip blanks
-
C89算术类型
- 整值类型
- 字符类型
char
- 有符号整型
signed char
、short int
、int
、long int
- 无符号整型
unsigned char
、unsigned short
、unsigned int
、unsigned long
- 枚举类型
- 字符类型
- 浮点类型
float
、double
、long double
- 整值类型
-
C99算术类型
- 整数类型
- 字符类型
char
- 有符号整型 包括标准的(
signed char
、short int
、int
、long int
、long long int
)和扩展的 - 无符号整型 包括标准的(
unsigned char
、unsigned short int
、unsigned int
、unsigned long int
、unsigned long long int
、_Bool
)和扩展的 - 枚举类型
- 字符类型
- 浮点类型
- 实数浮点类型
float
、double
、long double
- 复数类型
float_Complex
、double _Complex
、long double _Complex
- 实数浮点类型
- 整数类型
-
以下情况进行隐式转换
- 当算术表达式或逻辑表达式中操作数的类型不相同时(C语言执行所谓的常用算术转换)
- 当赋值运算符的右侧表达式类型与左侧变量的类型不匹配时
- 当函数调用的实参类型与形参类型不匹配时
- 当return语句中表达式的类型与函数返回值的类型不匹配时
-
常用算术转换
整值提升(integral promotion)把字符或短整型转换成int类型(如果
int
类型整数没有大到足以包含所有可能的原始类型值,那么是unsigned int
)任一操作数是浮点类型的情况。
float
->double
->long double
两个操作数均不是浮点类型。首先进行整值提升(保证没有一个操作数是字符类型或者短整型),然后
int
->unsigned int
->long int
->unsigned long int
。有一种特殊情况,在unsigned int
和long int
相同时,如果一个操作数是unsigned int
,另一个是long int
,那么都被转换为unsigned long int
-
当把有符号操作数和无符号操作数组合时,把有符号操作数“转换”成无符号的值。转换过程中需要加上或者减去
n+1
的倍数,n
是无符号类型能表示的最大值。这条规则会导致一些隐蔽的编程错误。例:#include <stdio.h> int main(int argc, char **argv){ unsigned int a=10; int b = -10; printf("yes, i am %d\n", b < a); printf("yes, i am %d\n", -10 < 10); return 0; }
运行结果是:
yes, i am 0 yes, i am 1
结论就是:尽量避免使用无符号整数!
-
赋值过程中的转换
- 常用算术转换不适用于赋值运算。C语言遵循另一条简单的转换规则,那就是把赋值运算符右侧的表达式转换为左侧变量的类型
- 如果变量类型至少和表达式类型一样宽,那么转换没有任何障碍
- 其他情况是有问题的。把浮点数赋值给整型丢掉小数部分。把某种类型的值赋给类型更狭小的变量时,如果该值在变量类型范围之外,那么会得到无意义的结果(甚至更糟)
-
C99中的隐式转换
- 为了定义转换规则,C99允许每个整数类型具有“整数转换等级”。下面是从最高到最低的排序(这里忽略了扩展的整数类型和枚举类型)
-
long long int
、unsigned long long int
-
long int
、unsigned long int
-
int
、unsigned int
-
short int
、unsigned short int
-
char
、signed char
、unsigned char
_Bool
-
- C99用整数提升(integer promotion)取代了C89中的整值提升(integral promotion),可以将任何等级低于
int
和unsigned int
的类型转换为int
或unsigned int
- 任一操作数是浮点类型的情况。只要两个操作数都不是复数类型,
float
->double
->long double
- 两个操作数均不是浮点类型。首先进行整数提升,如果两个操作数类型相同,过程结束。否则一次尝试以下规则,一旦遇到可应用的规则,就不再考虑其他的规则:
- 如果两个操作数均为有符号型或无符号型,将整数转换等级低的转换如为等级高的操作数类型
- 如果无符号操作数的等级高于或等于有符号操作数的等级,将有符号操作数转换为无符号操作数的类型
- 如果有符号操作数的类型可以表示无符号操作数类型的所有制,将无符号操作数转换为有符号操作数的类型
- 否则,将两个操作数都转换为与有符号操作数的类型相对应的无符号类型
- 为了定义转换规则,C99允许每个整数类型具有“整数转换等级”。下面是从最高到最低的排序(这里忽略了扩展的整数类型和枚举类型)
-
强制类型转换
- 有时需要使用强制类型转换来避免溢出,例:
long i; int j = 1000; i = j * j;//overflow may occur
在有些机器上
j*j
的值过大,无法表示为int
型,导致溢出。
可以改为i = (long)j * j;
-
sizeof
运算符- 编译器通常就能确定
sizeof
表达式的值,C99值变长数组不可以。
- 编译器通常就能确定