title: 第三章 数据和C
author: HardyDragon
tags: C Notes
第三章 数据和C
3.1 示例程序
3.2 变量和常量数据
3.3 数据:数据类型关键字
3.3.1 整数和浮点数
3.3.2 整数
3.3.3 浮点数
3.4 C语言基本数据类型
int类型
其他数据类型
使用其他字符:char类型
_Bool类型
可移植类型:stdint.h和inttypes.h
float、double、和long、double
复数和虚数类型
其他类型
类型大小
3.5 使用数据类型
3.6 参数和陷阱
3.7 转义序列示例
3.7.1 程序运行情况
3.7.2 刷新输出
3.8 关键概念
3.9 本章小结
3.10 复习题
3.11 编程练习
3.1 示例程序
/* platinum.c -- your weight in platinum*/
#include <stdio.h>
int main()
{
/* code */
float weight;
float value;
printf("Are you worth your weight in platinum?\n");
printf("Let's check it out.\n");
printf("Please enter your weight in pounds:");
/* 获取用户输入*/
scanf("%f", &weight);
/* 假设白金的的价格是每司¥1700*/
/* 14.5833 用于换算 */
value = 1700.0 * weight * 14.5833;
printf("Your weight in platinum is worth $%.2f.\n", value);
printf("You are easily worth that !If plati...");
printf("eat more to maintain your value.\n");
return 0;
}
result:
Are you worth your weight in platinum?
Let's check it out.
Please enter your weight in pounds:156
Your weight in platinum is worth $3867491.25.
You are easily worth that !If plati...eat more to maintain your value.
程序中的新元素:
- float 浮点数类型 可以处理更大范围的数据,且可以存储带小数点的数字。
- 为了打印新类型的变量,在printf()中使用 %f 来处理浮点值,%.2f 中的 .2 用于精确控制输出,指定输出的浮点数只显示小数点后两位。
- scanf() 函数用于读取键盘的输入,。%f 说明scanf() 要读取用户从键盘输入的浮点数,&weight 告诉 scanf() 把输入的值赋给名为weight 的变量。& 取地址符。
3.2 变量和常量数据
在整个程序运行中没有变化的数据类型就成为常量,反之为变量。
3.3 数据:数据类型关键字
对于变量需要在声明时候指定其类型。
最初的数据类型关键字 | C90添加的关键字 | C99添加的关键字 |
---|---|---|
int | signed | _Bool |
long | void | _Complex |
short | _Imaginary | |
unsigned | ||
char | ||
float | ||
double |
- int 表示基本的整数类型,后边的3个关键字 long、short、unsigned以及C90新增的signed用于提供基本整数类型的变式,例如,unsigned short int 和 long long int 。
- char关键字用于指定字母和其他字符,另外,char类型也可以表示较小的整数。
- float、double和long double 表示带小数点的数。
- _Bool 表示 true 或 false
- _complex 和 _Imaginary 分别表示复数和虚数
通过这些关键字创建的类型,按照计算机的存储方式可以分为两大基本类型,整数类型和浮点数类型。
位、字节、字
8位 = 1 字节
字(word) 是设计计算机是给定的自然存储单位,对于8位的微型计算机,1个字长只有8位。之后的个人计算机字长增至16位、32位,直到目前的64位。计算机的字长越大,其数据转移越快,允许的内存访问也更多。
-
整数
计算机以二进制存储整数,例如7的二进制写是111.因此要在8位字节中存储该数字,需要把前五位都设置为0,后三位设置为1。
-
浮点数
2.75、3.16E7、7.00、2e-8 都是浮点数。7 是整数,7.00是浮点数,即在一个值后加一个小数点,这个值就是浮点值。浮点数有许多计数方法,主要是e计数法 3.16E7 = 3.16*10^7
-
浮点数的存储方式
浮点数和整数的存储方案不同,计算机把浮点数分为小数部分和指数部分表示。而且分开存储这两部分,因此虽然7.00和7在数值上相同,但是由于存储方式不同,在十进制下,可以将7.0写成0.7E1。这里的0.7是小数部分,1是指数部分
-
整数和浮点数的区别
- 有无小数
- 浮点数表达范围大于整数
- 对于相同的算术运算,浮点数损失的精度更多
- 计算机的浮点数不能表示所有的值,浮点数通常是实际值的近似值,例如,7.0可能被存储为浮点值6.99999
- 过去的浮点数运算比整数慢,不多由于现在的CPU基本都包含浮点处理器,速度便差不多了。
3.4 C语言基本数据类型
-
int 类型
有符号整型,即int可以有正负,其取值范围根据计算机系统而异,早期的16位PC用16位来存储一个int值,所以它的取值范围为 -32768 ~ 32767,目前的计算机是32/64位,即32/64位存储一个int值,那就更大了。
声明int 类型的整型变量,可以一次声明多个变量,以逗号分隔,最后一分号;结束。
int hogs,cows,goats;
声明后为变量分配内存空间。但是声明后并未给变量提供值。
变量获取值可以通过赋值:
cows = 112;
亦可以通过scanf() 获得值;
还可以初始化变量,就是在声明的时候给变量赋值一个初始值。
int hogs = 21;
最好不要把初始化的变量和未初始化的变量写在一行,容易让人误解。
-
打印int值
可以使用printf()打印int类型的值, %d 指明了在一行中打印整数的位置,%d称为转换说明,它指定了printf() 以什么格式来显示一个值。
-
八进制和十六进制
0x 或0X 前缀表示十六进制值,例如十进制16 表示成十六进制 是 0x10 或0X10
o 前缀表示八进制,例如十进制16 表示八进制是 o20
-
/* bases.c -- 以十进制、八进制、十六进制 打印十进制数100*/ # include<stdio.h> int main() { /* code */ int x = 100; printf("dec = %d;octal = %o;hex = %x\n",x,x,x); printf("dec = %d;octal = %#o;hex = %#x\n",x,x,x); // 要显示进制前缀需要在%后加个# return 0; }
result:
dec = 100;octal = 144;hex = 64 dec = 100;octal = 0144;hex = 0x64
-
其他整数类型
int 类型可以满足大部分程序的整数要求,当然,可以使用C提供的3个附属关键字来修饰基本的整数类型 int 达到int 的变式。
- short int 类型(或者简写为short)占用的存储空间 可能 比int 的小,适用于较小数值的场合以节省空间。short是有符号类型的,即可正负。
- long int(简写 long)占用空间可能比 int 多,但是取值范围更大,且有符号。
- long long int (C99标准加入) 占用的空间比long更大,这个类型至少占用64位,也是有符号。
- unsigned int (简写 unsigned) 无符号,只有正数,例如16位的 unsigned 其取值范围为 0 ~ 65535,将用于正负号的一位用于表示另一个二进制位,可以表达更大的数。
- 在任何有符号类型面前添加关键字 signed ,可以强调有符号类型的意图;例如 short 和 signed short 是同一种类型。
- C99 新加了 long long ,不是所有的编译器都支持。
-
使用多种整数类型的原因
C语言只是规定了short 所占空间不能多于int,long占用空间不少于int,这样规定是为了适应不同的机器。移植时候可能也要注意其数据类型的取值范围。如果在long和int类型占用相同空间的机器上编码,应使用long,以便之后移植到16位机器后也可以正常使用。
-
打印short、long、long long 和unsigned 类型
打印unsigned int 使用 %u转换说明,打印long类型,使用%ld,如果系统的long和int大小相同,使用%d即可。在x和o前可以使用l前缀,%lx表示以十六进制格式打印long类型整数,%lo表示八进制格式打印long类型整数。注意在转换说明中的x和o只能使用小写。
对于short可以使用%h表示,%ho表示以八进制显示short类型的整数。,l 和 h 前缀都可以和 u一起使用。例如,%lu 表示打印 unsigned long 类型的值。
/* print2.c -- 更多printf()特性 */ #include<stdio.h> int main(void) { /* code */ unsigned int un = 300000000; /* int 为32位和short为16位的系统*/ short end = 200; long big = 65537; long long verybig = 12345678908642; printf("un = %u and not %d\n",un,un); printf("end = %hd and %d\n",end,end); printf("big = %ld and not %hd\n",big,big); printf("verybig = %lld and not %ld\n",verybig,verybig); return 0; }
result:
un = 300000000 and not 300000000 end = 200 and 200 big = 65537 and not 1 verybig = 12345678908642 and not 1942899938
对于第二行输出,在printf()中无论指定以short还是int类型打印,其打印出的值都是相同的。因为在给函数传递参数时,编译器将short的值自动转化为int类型的值,因为int被计算机认为是处理整数类型最高效的类型。那这个%h 修饰符作用是为了显示较大的整数被截断成short的情况,第三行的输出就是这种情况。65537 若使用%hd就只会查看其后16位,刚好只有一个1,第十七位的1没转化过来。最后一行也是如此情况,%ld只显示最后32位的值。
使用printf() 函数时,要注意检查每个待打印的值都有对应的转换说明以及转换说明的类型是否与被打印的值的类型相匹配。
-
使用字符:char类型
char类型用于存储字符,但是char是整数类型,因为char类型实际上存储的是整数而不是字符,计算机使用数字编码来处理字符,一般是ASCII编码,例如65表示A,97表示a。
C语言将1字节定义为char类型所占用的位数。
-
声明char类型,和声明其他类型变量一样。
-
字符常量和初始化
char grade = ‘A’;
用单引号括起来的单个字符称为字符常量。编译器一发现’A’ 就会将其转换为对应的代码值,所以单引号必不可少。
双引号括起来的是字符串,以下写法是错误的。
char grade = “A”;
双引号括起来的编译器会认为是字符串。
实际上,字符以数值的形式存储,所以亦可以用数字代替字母来赋值。但是不推荐。
值得注意的是C语言将char类型视为int来存储值,例如:
char grade = ‘FATE’;
其将4个独立的8位ASCII码存储在一个32位的存储单元中,如果这样赋值,只有最后八位有效,所以grade的值是’E’。
-
-
非打印字符
有些特殊字符打印不出来,有以下3中方法:
- 使用ASCII码 char beep = 7;
- 使用转移字符\ ,即转椅序列,把转义序列赋值给字符变量时,需要用单引号将其括起来, char nerf = ‘\n’;
\a 警报;\b 退格; \f 换页;\n 换行;\r 回车;\t 制表符;\ \ 反斜杠;\“ 双引号;
? 问好;\o00 八进制值;\xhh 十六进制;
C90开始,不仅可以使用十进制、八进制表示字符常量,还可以使用十六进制表示字符常量。
字符串内的转义序列无需使用单引号括起来。 -
打印字符
可以用%d和%c打印字符对应的整数和字符。
/* charcode.c -- 显示字符的代码编号*/ #include<stdio.h> int main() { /* code */ char ch; printf("Please enter a character.\n"); scanf("%c",&ch); printf("The code for %c is %d.\n",ch,ch); getchar(); getchar(); return 0; }
result:
Please enter a character. C The code for C is 67.
注意printf() 函数里面的转换说明决定了数据的显示方式,而不是数据的存储方式。
-
有些编译器把char类型实现为有符号类型,有些又是无符号类型,其取值范围不同,使用时应查看对应的编译器手册,确定编译器使用的char类型,或者查看limits.h 头文件。
根据C90标准,C语言允许在关键字char前使用signed或unsigned,这样,无论编译器默认char是什么类型,可以自己规定有无符号的char类型,在使用从char表示处理小整数时很有用,但是如果只用char处理字符,就需要在char前面添加任何修饰符。
-
_Bool 类型
C99添加了_Bool 类型,用于表示布尔值,true 和 false,C语言使用1表示true,0表示false,所以 _Bool 实际上也是一种整数类型,但是其原则上只占用1位的存储空间。
-
可移植类型:stdint.h 和 inttypes.h
C99 新增加两个头文件stdint.h 和 inttypes.h,便于移植时确保C语言的类型在不同系统的功能相同。在包含头文件stdint.h 头文件时,编译器会把int或long根据系统位数类匹配。
C99和C11不仅提供可移植的类型名,还提供对应的输入和输出,如果要打印int32_t 类型的值,有些定义使用%d,有些使用%ld ,为了应对这种情况,C提供了PRId32字符串宏在inttypes.h 头文件中,代表打印32位有符号值的合适转换说明(如d或l)
/* altnames.c -- 可移植整数类型名*/ #include<stdio.h> #include<inttypes.h> int main() { /* code */ int32_t me32; me32 = 45933945; printf("First,assume int32_t is int:"); printf("me32 = %d\n",me32); printf("Next,let's not make any assumptions.\n"); printf("Instead,use a \"macro\" from inttypes.h:"); // printf("me32=%" PRId32 "\n",me32); printf("me32=%" "d" "\n",me32); getchar(); return 0; }
result
First,assume int32_t is int:me32 = 45933945 Next,let's not make any assumptions. Instead,use a "macro" from inttypes.h:me32=45933945
-
float、double、和long double
C标准规定,float类型必须至少能表示6位有效数字,即至少精确到小数点后六位;一般系统存储一个浮点数要占用32位,其中8位用于表示指数的值和符号,剩下24位用于表示非指数部分及其符号。
C语言提供的另一种浮点类型是double(双精度),double和float类型的最小取值范围相同,且至少需要能表示10位有效数字。一般情况下double占用64位,将比float多出的32位用于表示非指数部分,增加了有效位数以及减少了舍入误差。 -
声明浮点型常量
在代码中可以使用多种形式书写浮点型变量,浮点型变量的基本形式是:有符号的数字(包含小数点)后面紧跟e或者E,最后一个是有符号数表示10的指数。例如:
-1.56E+12
2.87e-3
其中正号可以省略,小数点也可以省略例如(2E5)或省略指数部分,就是习惯写的小数形式(2.0),但是这两种形式不可以同时使用,即不可以同时省略小数点和指数部分。
当然也可以省略小数部分例如(3.E16)或者省略整数部分(.45E-6)但也是不能同时省略,即e前需要有数值。需要注意的是不能再浮点型常量中间加空格。
默认情况下编译器假定浮点型常量是double类型的精度,例如:
some = 4.0 * 2.0;
通常,4.0和2.0被存储为64位的double类型,使用双精度进行乘法运算,然后将乘积截断成float类型的宽度,虽然计算的精度高,但是会减慢程序运行的速度。
在浮点数后面加上f或者F后缀可以覆盖默认的设置,编译器会将浮点型常量看做float类型,例如2.3f和9.11E9F。使用 l 或 L 后缀使得数字成为 long double 类型,如 54.3l 和 4.32L。这里建议使用L后缀,因为英文 l 和数字 1 比较容易弄混淆。没有后缀的浮点数常量是double 类型。
C99 又添加了 十六进制表示浮点数常量,即在十六进制数前加上十六进制前缀0x,用p或P代替e或E,用2的幂代替10的幂(即 p计数法),例如:
0xa.1fp10
其中a表示十六进制的 10 ,.1f 是 1/16 加上 15/256 (十六进制f等于15),p10是2^10 或1024。
注意,并不是所有的C99都支持这一特性。
-
打印浮点值
printf() 函数使用%f 转换说明 打印十进制记数法的 float 和double 类型浮点数,用%e打印指数记数法的浮点数,如果系统支持C99的十六进制浮点数可以用a或A代替e和E,打印long double 时候要使用%Lf、%Le或%La转换说明。
/* showf_pt.c -- 以两种方式显示float 类型的值*/ #include<stdio.h> int main(int argc, char const *argv[]) { /* code */ float aboat = 32000.0; double abet = 2.14e9; long double dip = 5.32e-5; printf("%f can be written %e\n",aboat,aboat); // 下一行要求编译器支持C99的新特性十六进制表示浮点数 printf("And it's %a in hexadecimal ,power of 2 notation.\n",aboat); printf("%f can be written %e\n",abet,abet); printf("%Lf can be written %Le\n",dip,dip); getchar(); return 0; }
result:
32000.000000 can be written 3.200000e+004 And it's 0x1.f40000p+14 in hexadecimal ,power of 2 notation. 2140000000.000000 can be written 2.140000e+009 0.000000 can be written 3.172882e-317
tips:浮点数的舍入错误
给定一个数,加上1,再减去原来给定的数,结果不一定是1,因为有些数计算机缺少位数来运算,其计算结果一定是错误的。
-
复数和虚数类型
C99标准支持复数和虚数类型,C11标准将整个复数软件包作为可选项。
C语言由3种复数类型:float_Complex 、double_Complex和 long double _Complex。例如float _Complex 类型的变量应当包含两个float 类型的值,分别表示复数的实部和虚部。类似的,C语言也有3种虚数类型:float _Imaginary、double _Imaginary 和 long double _Imaginary
有专门的的复数头文件 complex.h -
其他类型
C语言虽然没有字符串类型,但是也刻意很好的处理字符串(doge)。C语言还有一些从基本类型衍生的其他类型,如数组、指针、结构和联合。
tips:scanf() 函数中用到的前缀 & 便创建了一个指针,告诉scanf() 把数据放在何处。
int 不小于16位
short或short int 至少占16位,比int小或等于
long或long int 至少占32位
long long 至少占64位
-
类型大小
/* typesize.c -- 打印类型大小*/ #include<stdio.h> int main(int argc, char const *argv[]) { /* code */ printf("Type int size: %zd bytes.\n",sizeof(int)); printf("Type char size: %zd bytes.\n",sizeof(char)); printf("Type short size: %zd bytes.\n",sizeof(short)); printf("Type long size: %zd bytes.\n",sizeof(long)); printf("Type long long size: %zd bytes.\n",sizeof(long long)); printf("Type double size: %zd bytes.\n",sizeof(double)); printf("Type long double size: %zd bytes.\n",sizeof(long double)); getchar(); return 0; }
result:
Type int size: 4 bytes. Type char size: 1 bytes. Type short size: 2 bytes. Type long size: 4 bytes. Type long long size: 8 bytes. Type double size: 8 bytes. Type long double size: 16 bytes.
3.5 使用数据类型
使用变量前需要先声明并选择有意义的变量名。有一些规定的系统化的命名约定,在变量名中体现其类型,用 i_ 前缀表示 int 类型,us_ 表示 unsigned short 。
3.6 参数和陷阱
printf()函数字符串为一个参数,scanf("%d",&weight) 有两个参数。 printf() 和scanf() 函数的参数个数是可变的,参数个数和第一个参数的转换说明个数要对应。
3.7 转义序列示例
/*escape.c -- 使用转义序列*/ #include<stdio.h> int main(int argc, char const *argv[]) { /* code */ float salary; printf("\aEnter your desired monthly salary:"); printf("$_______\b\b\b\b\b\b\b"); scanf("%f",&salary); printf("\n\t$%.2f a month is $%.2f a year.",salary,salary*12.0); printf("\rGee!\n"); getchar(); getchar(); return 0; }
result
Enter your desired monthly salary:$_______ Enter your desired monthly salary:$4000.00
Enter your desired monthly salary:$4000.00 $4000.00 a month is $48000.00 a year.
Enter your desired monthly salary:$4000.00 Gee! $4000.00 a month is $48000.00 a year.
首先 \a 发出一声警报,光标在冒号后面:输入的字符替换了下划线字符,按下enter后光标移至下一行,输出第二个printf后光标在最后,然后 \r 让光标返回当前行的开头/起始处,接着 \n 使得光标移动到下一行的起始处。
-
刷新输出
一开始,printf() 语句会把语句输出发送到 缓冲区 的中间存储区域,然后缓冲区中的内容不断被发送到屏幕上,C标准明确了何时把缓冲区的内容发送到屏幕中:当缓冲区满了,遇到换行字符或需要输入时(从缓冲区把数据发送到屏幕或文件称为刷新缓冲区)。也可以使用 fflush() 函数刷新缓冲区。
3.8 关键概念
计算机的浮点数和整数在本质上不同,其存储方式和运算过程有很大区别,即使两个32位存储单元的存储的位组合完全相同,但是一个解释为float,另一个解释为long,这两个相同位组合表示的值也完全不同。例如,假设一个位组合表示的float的数为256.0,如果将其解释为long 得到的值是113246208。
3.9 本章小结
C的基本数据类型分为两大类整数类型和浮点数类型。最小的整数类型为char,注意char可以是有符号的char。浮点类型有3种,float、double以及C90新加的long double。浮点数可以写成固定小数点的形式(9393.912)或指数形式(7.38E10)。C99和C11新提供了十六进制和2的幂表示。
printf() 根据转换说明来打印对应的值。
3.10 复习题
使用 int32_t 作为标准的32位整型数据, int_least32_t 获得至少32位整型数据,int_fast32_t 获得32整型数据的最快计算速度。
’\b‘ 表示转义的 退格
0xAA 表示十六进制 unsigned int 数据类型,对于八进制和十六进制系统优先解释为无符号类型。
2.0e30 表示双精度浮点类型数据常量,虽然改值没有超过float 的取值范围,但是编译器默认将浮点型常量统一为double类型,除非自定义为float类型,在其后边加个f或F表示为float。
使用e指数表示浮点数时,注意e前要有数值。以下写法错误
g = e12;
正确应该是:
g = 1e12; 或者 g = 1.0e12; (小数点)
常量 | 类型 | 转换说明符 |
---|---|---|
12 | int | %d |
0X3 | unsigned int | %#X |
‘C’ | char | %c |
2.34E07 | double | %e |
‘\040’ | char | %c |
7.0 | double(编译器默认) | %f |
6L | long (int) | %ld |
6.0f | float | %f |
0x5.b6p12 | float (c99/c11 p记数法) | %a |
o12 | unsigned int | %#o |
2.9e05L | long double(long和e指数的组合) | %Le |
100000 | long | %ld |
‘\n’ | char | %c |
赋值回车符给ch变量的几种方式
char ch = '\r';
char ch = 13;
char ch = '\o15';
char ch = '\xd';
scanf() 函数给变量赋值时记得 &
3.11 编程练习
| double(编译器默认) | %f |
| 6L | long (int) | %ld |
| 6.0f | float | %f |
| 0x5.b6p12 | float (c99/c11 p记数法) | %a |
| o12 | unsigned int | %#o |
| 2.9e05L | long double(long和e指数的组合) | %Le |
| 100000 | long | %ld |
| ‘\n’ | char | %c |
赋值回车符给ch变量的几种方式
char ch = '\r';
char ch = 13;
char ch = '\o15';
char ch = '\xd';
scanf() 函数给变量赋值时记得 &
3.11 编程练习