blaze兄,实在久等。现在恢复更新了,你也要恢复加油哦_
一、 C语言基本数据类型
1. int类型
C语言提供了许多不同的整数类型,以便程序员恰当地针对不同的情况使用不同的整数类型。例如,在已知变量取值范围处于一个较小的区间(如-32767~32767)时,使用short int
(每个变量占2字节)相比使用int
(占4字节)明显要更省空间。
我们已经在前面了解过怎么声明int变量并为它们赋值。
int num;//声明一个int变量
num=1;//为这个变量赋值
int i=1;//声明变量的同时赋值,这个操作也被称作“初始化”
int num1,num2;//一次声明多个变量
int num3=3,num4;//编译器不会报错,但容易引起混淆
我们可以使用printf()函数来打印int变量。这里写一个有问题的函数以演示printf()的特性:
//printf.c 演示printf()函数的性质
#include <stdio.h>
int main()
{
int num1=1;
int num2=2;
printf("%d减去%d等于%d\n",num2,num1,num2-num1);
printf("%d加上%d等于%d",num1,num2);//占位符个数与变量个数不匹配
return 0;
}
程序的运行结果如下:
2减去1等于1
1加上2等于0
程序的输出结果有错误。这是因为,在printf("%d加上%d等于%d",num1,num2)
这个语句中,占位符个数与变量个数不匹配。这导致最后一个占位符找不到与之相匹配的变量,于是就会匹配内存中的任意值并输出(这里是0);如果占位符个数比变量个数少,则不会有报错(同时你会发现中间有数字没有打印出来)
八进制与十六进制
程序由0和1组成。因此,我们会希望计算机采用的进制也是2的整数次幂,于是,八进制和十六进制应运而生(为什么?)。使用这两种进制可以使二进制数的表达更为紧凑。节省空间。八进制每一位可以表示3个二进制位,十六进制每一位可以表示4个二进制位。
C语言默认整数为十进制。但是,使用特定的前缀可以表示八进制和十六进制数。十六进制数的前缀是0x/0X。因此,"0x16"事实上表示了十六进制下的16,也即十进制下的22;八进制的前缀是0。例如:”012“事实上表示的是十进制下的10。
必须要清楚的一点是:无论整数写成什么样子,存储这个数的方式都相同。16,0x10和020本质上是同一个数字的不同表达。、
那么,如何显示八进制和十六进制数呢?
在C语言中,我们用”%d“表示十进制数字,用”%o“表示八进制数字,用”%x/%X“表示十六进制数字。
如果要显示诸如”0“,”0x/0X“之类的前缀,需要把占位符改写成”%#o“,”%#x/%#X“的形态**(转换说明中只能用小写)**。
下面以打印十进制整数100为例:
//bases,c 以不同进制打印同一个数字
#include <stdio.h>
int main()
{
int num=100;
printf("%o等于%x等于%d\n",num,num,num);
printf("%#o等于%#x等于%d\n",num,num,num);
return 0;
}
输出结果如下:
144等于64等于100
0144等于0x64等于100
我们发现:输出的结果取决于占位符的类型。占位符告诉电脑该用什么方式打印数据,不同的打印方式呈现出来的结果不同,但并不会影响到原变量。在之后,我们会进一步探讨这个问题。
不同整数类型的问题
有符号/无符号:在C程序中,我们通常使用unsigned
来表示 “无符号” 类型。无符号类型的数据一定是非负值。它的意义在于:能够用同样的存储空间储存更大的正数。例如,同样要储存一个16二进制位的正整数,使用int
类型只能取值在032767的数(-32768-1被占据),而unsigned int
类型则能存储取值在0~65535的数。
我们使用**“%u”**来提示系统打印unsigned int
型变量。
整数溢出:当我们为变量赋的值超过了其类型的存储空间时,就会发生溢出。printf()
函数会打印出**“最大值后重新计数”**的数。例如:
//flow.c 演示十六位系统下的整数溢出
#include <stdio.h>
int main()
{
int num=32767;
unsigned int num2=65535;
printf("num+1=%d, num2+1=%u",num+1,num2+1);
return 0;
}
代码的输出结果如下:
num+1=-32768, num2+1=0
当我们为变量赋予的值超过了它本身的取值范围时,变量的值就会”从头计起“,num的值被重置为-32767,num2被重置为0。在被打印的数超出了变量本身能够表示的范围时,就会发生这种情况。
不同类型整数打印时的占位符表
整数类型 | 占位符 |
---|---|
unsigned int | %u |
short/short int | %hd |
long/long int | %ld |
long long | %lld |
printf()函数的更多特性
我们看这个程序:
//flow2.c 演示printf()函数的另一些特性
#include <stdio.h>
int main()
{
short small=200;
long big=65537;
printf("small=%hd, =%d, =%ld\n",small,small);
printf("big=%ld, ≠%hd\n",big,big);
return 0;
}
它的输出结果如下:
small=200, =200, =200
big=65537, ≠1
自动转换首先看第一行:计算机在处理范围在*(-32768到32767,十六位系统下/-2147483648到2147483647,三十二位系统下)*的整数时,会优先转换成int类型的值进行处理。当我们使用%ld占位符时,计算机同样会把它转换成int类型处理。所以第一行的输出是没有问题的。
在第二行中,我们使用了%ld和%hd类型。在使用%hd占位符时,由于short类型本身只占有2字节的空间,所以计算机只会读取后十六位。我们把65537写成二进制形式,是00000000000000010000000000000001
(三十二位数形式),而计算机对此进行了截断,只取后十六位数,也就是1。
因此,在使用printf()函数时,一定要使占位符与变量类型相匹配。
2. char类型
char类型用于存储字符。但是实际上,char也是一种整数类型。计算机使用数字编码来处理字符。在C语言中,这套编码通常是**ASCII编码**。例如,在ASCII编码中,65对应大写字母A。存储字母A,实际上存储的就是整数65。标准ASCII编码的范围是0~127。
C语言把1字节定义为char类型占用的位数,所以char类型的变量占用1字节。
我们可以像生明int变量一样声明char变量,如果需要向一个变量赋初始值,可以这么做:
char letter = 'A';
char letter = A ;//错误,此时A是一个未声明的变量
char letter = "A";//错误,此时A是一个字符串而非字符
char letter = 65 ;//不会报错,但不建议这么做(易使得代码晦涩难懂)
注意:所有字符都必须用单引号括起来,否则计算机无法确认它们是否为字符。用单引号括起来的字符被称为字符常量。编程器一发现'A'
,就会把它当成65来储存。
扩展:字符常量的数据类型
在C语言中,字符常量被以int类型存储,而非char型。例如,对于char letter = 'B’
而言,B对应的整数66原本是存储在32位的存储单元中的*(int形式)*,但是现在却能存储在8位的存储单元中。所以我们可以定义像如下类型的字符常量:‘BIRD’
。它是把四个独立的8位ASCII码存储在一个32位的单元里。当把它赋给某个char类型的变量时,计算机只会读取后面8位数字,所以变量的值会是D。
非打印字符与转义序列
单引号只适用于字符,数字和标点符号。但是ASCII表上还有一些非打印字符。这些字符代表了一些行为(例如,“\n”代表换行),C语言提供了3种方法来表示它们。
第一种是直接使用ASCII码。例如蜂鸣字符的ASCII码是7,那么char beep = 7
就可以把为变量beep赋值。当打印beep
时,你的计算机会发出一声蜂鸣音。(需要计算机的硬件支持,如果没有扬声器可不行)
第二种是使用转义序列。通过使用特殊的符号序列表示非打印字符。如果我们要把转义序列赋值给变量,就必须把它用单引号给括起来。
下面列出了一些常见的转义序列:
转义序列 | 含义 |
---|---|
\a | 警报 |
\b | 退格 |
\n | 换行 |
\r | 回车 |
\\ | 反斜杠 |
\’ | 打印单引号 |
\" | 打印双引号 |
关于转义序列的更多内容参考这里。
我们来详细分析一下转义序列。我们将\b, \n, \r
这类的转义序列称为设备控制输出字符。它们会影响活跃位置。什么是活跃位置?就是指下一个字符应该出现的地方。也就是屏幕光标所在的地方。例如:“\n
”的实际含义是:把屏幕光标移到下一行的开始处。\r
则会把光标移到当前行的开始处。
像\\, \', \"
之类的转义序列是为了打印\ ' "
这样的字符用的。这些字符本身是printf()函数的一部分,强行使用它们会造成混乱。
例如,如果我们想要打印这个句子:
He said : "Can you hear me? "
就应该这么写:
printf("He said : \"Can you hear me? \"");
printf("He said : "Can you hear me? "");//错误的写法,你可以注意到:未处在两个双引号中间的部分不会被识别为打印内容
在字符串中(被两个双引号括起来的部分),我们无需再用单引号将转义序列括起来。而且,由于中文引号与英文引号不同,我们无需担心此问题。
打印字符
我们来看这个程序:
//charcode.c 根据用户输入显示字符的编号
#include <stdio.h>
int main()
{
char ch;
printf("请输入一个字符:\n");
scanf("%c",&ch);
printf("%c的ASCII编码是%d。",ch,ch);
return 0;
}
运行结果如下:
请输入一个字符:
A
A的ASCII编码是65。
在运行这个程序时,输入字符后要按下Enter键。随后,scanf()函数会将用户输入的字符赋给变量ch。由于一个字符变量事实上被存储为一个八位的二进制整数,所以在打印时,第一次打印对应的字符A(对应占位符%c
),第二次打印一个整数(对应%d
,也是A的ASCII码)。这说明:printf()函数中的占位符是转换说明。它决定了数据的显示方式,但不能决定数据的存储方式。
3.bool_类型
bool_类型用来表示逻辑值(布尔值)。它实际上也是一种整数类型,不过它只存储两种值:值0表示false
,值1表示true
。我们将在后面详细讨论这个问题。