第七章:基本类型
请别搞错:计算机处理的是数而不是符号。我们用对活动算术化的程度来衡量我们的理解力(和控制力)。
六种不同的类型:
short (int)
int
long (int)
unsigned short (int)
unsigned (int)
unsigned long (int)
十进制常量包含数字0--9,一定不能以0开头
八进制常量包含数字0--7,一定要以0开头
十六进制常量包含数字0--9和字母a--f,一定要以0x开头
八进制和十六进制只是数书写的另一种方式;他们不会对数实际存储的方式产生影响。任何时候都可以从一种书写方式切换到另一种书写方式,甚至是混合使用。
当程序中出现整型常量时,如果它属于int类型的取值范围,那么编译器会把此常量作为普通整数来处理,否则会作为长整型数来处理。为了强制编译器把常量作为
长整型数来处理,只需要在后边加上一个字母L或l;同样,无符号常量加U或u;表示无符号长整型数加UL或者LU
读写整数:
printf和scanf中%d只针对int型;
读写无符号整数,%u,%o,%x,分别针对无符号十进制数,八进制数,十六进制。
读写短整型数,在d,o,u,x前加h;读写长整型数,在前面加L
浮点数(小数):
float:单精度浮点数
double:双精度浮点数
long double:扩展双精度浮点数
浮点型的特征(IEEE标准)
类型 最小正值 最大值 精度
float 1.17e-38 3.40e38 6个数字
double 2.22e-308 1.79e308 15个数字
浮点常量57.0的一些书写方式:
57.0 57. 57.0e0 57E0 5.7e1 5.7e+1 .57e2 570.e-1
默认情况下,浮点常量都以双精度数的形式存储。在需要的时候double可以随时转换成float。
强制单精度:57.0F或57.0f,
强制扩展双精度:57.0L或57.0l
读取double类型的数值是,在e、f或g前放置字母l;sanf(“%ld”,&d);
只能在scanf格式字符串中使用l,不能在printf中使用,因为printf中e、f、g对float和double都适用。
当读或写long double 在e、f、g前放置 L;
字符型常量:
C语言标准没有说明普通char型数据是有符号还是无符号型;各种编译器对此不一样。我们最好用signed和unsigned来修饰char类型:
singed char sch;
unsigned char uch;
由于字符和整数之间有密切关系,本书将采用 整值类型 来(统称)包含整型和字符型。
读写字符:
char ch;
scanf(“%c”,&ch);
printf(“%c”,ch);
在读入字符前,scanf函数不会跳过空白字符。如果下一个未读字符是空格,那么前面例子中,scanf函数返回后变量ch将包含一个空格。为了强制scanf函数在读入字符前跳过空白字符,
需要在格式串转换说明%c前加一个空格:
scanf(“ %c”,&ch);
scanf函数格式串中的空白意味着“跳过零个或多个空白字符”。
几种技巧:
do
{
scanf("%c",&ch);
} while (ch != '/n');
这个循环将读入并且忽略掉所有当前输入行中其余的字符,当下次调用scanf函数时,将读入下一输入行中的第一个字符。
如果在同一个程序中混合使用getchar和scanf,,请一定注意scanf函数有一种留下后边字符的趋势,即对于输入后面的字符(包括换行符)只是“看了一下”,并没有读入。如:
printf(“enter an integer:”);
scanf("%d",&i);
printf("enter a command:");
command = getchar();
在读入i的同时,scanf函数调用将会留下后面没有消耗掉的任意字符,包括换行符(但不仅限于换行符)。getchar函数随后将取回第一个剩余字符,但这不是我们所希望的结果。
类型转换:
编译器可以自动处理而无需程序员介入的转换:隐式转换(implicit conversion)
程序员通过强制运算符可以执行显式转换(explicit conversion)。
当发生下列情况时会进行隐式转换:
1、当算术表达式或逻辑表达式中操作数的类型不同时。
2、当赋值运算符右侧表达式的类型和左侧变量的类型不匹配时。
3、当函数调用中使用的参数类型与其对应的参数的类型不匹配时。
4、当return语句中表达式的类型和函数返回值的类型不匹配时。
第一种:常用算术转换
常用算术转换的策略:
第一:安全
第二:使用于两个数值的“最狭小的”数据类型。
为了统一操作数的类型,通常可以将相对较狭小类型的操作数转换成另一个操作数的类型来实现(这就是所谓的提升)。
最常用的提升是整型提升(integral promotion),它把字符或短整数转换成int类型(或某些情况下是unsigned int 类型)。
两种情况:
1、任一操作数的类型是浮点型的情况。
float ——》 double ——》 long double
2、两个操作数的类型都不是浮点型的情况。
int ——》 unsigned int ——》 long int ——》 unsigned long int
有一种特殊情况,只有在long int类型和unsigned int类型长度相同(比如32位)时才会发生。在这种情况下,如果一个操作数的类型是long int,而另一个是unsigned int,那么两个操作数都会转换成unsigned long int 类型。
例子:
char c;
short int s;
int i;
unsigned int u;
long int l;
unsigned long int ul;
float f;
double d;
long double ld;
i = i + c;
i = i + s;
u = u + i;
l = l + u;
ul = ul + l;
f = f + ul;
d = d + f;
ld = ld + d;
把浮点数赋值给整型变量会去掉该数的小数部分。
如果取值在变量类型范围之外,那么把值赋给一个较狭小类型的变量将得到无意义的结果(甚至更糟)。
c = 10000;
i = 1.0e20
f = 1.e100
强制类型转换
[强制转化表达式] (类型名) 表达式
如:float f,frac_part;
fract_part = f - (int) f;
quotient = (float) dividend / divisor;
变量divisor不需要进行强制类型转换,因为把变量dividend强制类型转换成float类型会迫使编译器把divisor也转换成float类型。
C语言把(类型名)视为一元运算符。一元运算符的优先级高于二元运算符,所以编译器会把表达式
(float) dividend / divisor
解释为
((float) dividend) / divisor
有时需要使用强制类型转换来避免溢出。如:
long int i;
int j = 1000;
i = j * j;
这条语句是有问题的。 当两个int型值相乘时,结果也应该是int类型的,但是j * j的结果太大,以至于在某些机器上无法表示成int类型。在这样的机器上,
会给变量i赋一个无意义的值。可以使用强制类型转换来避免这种问题的发生:
i = (long int) j * j;
因为强制运算符的优先级高于*,所以第一个变量j会被转换成long int类型,同时也迫使第二个j进行转换。而语句:
i = (long int) (j * j);
是不对的,因为溢出在强制类型转换之前就已经发生了。
类型定义
可以使用 #define 创建一个宏,来定义布尔型数据:
#define BOOL int
但是一个更好的方法是利用所谓的类型定义(type definition)的特性:
typedef int BOOL;
注意所定义的类型名字放在最后,这和#define宏定义相反。而且末尾要有分号;
类型定义比宏定义功能更强大。特别是,数组和指针类型是不能定义为宏的。如:
#define PTR_TO_INT int *
声明
PTR_TO_INT p,q,r;
在处理后,将会变成
int * p, q, r;
只有p是指针,q和r都成了普通的整型变量。因为#include只是简单的原样替换。typedef不会这样。