C语言学习(二)数据

对我们而言,整数和浮点数的区别是它们的书写方式不同。对计算机而言,它们的区别是储存方式不同。
计算机以二进制数字储存整数,例如,整数7以二进制写是111。因此,要在8位字节中储存该数字,需要把前5位都设置成0,后3位设置成1。

书写浮点数有多种形式。
e记数法:3.16E7 表示3.16 乘以10的7次方。其中,7被称为10的指数。
当然,计算机在内部使用二进制和2的幂进行储存,而不是10的幂。

整数没有小数部分,浮点数有小数部分。
浮点数可以表示的范围比整数大。
对于一些算术运算(如,两个很大的数相减),浮点数损失的精度更多。
因为在任何区间内(如,1.0 到 2.0 之间)都存在无穷多个实数,所以计算机的浮点数不能表示区间内所有的值。浮点数通常只是实际值的近似值。例如,7.0可能被储存为浮点值6.99999。

int

int类型是有符号整型,即int类型的值必须是整数,可以是正整数、负整数或零。其取值范围依计算机系统而异。一般而言,储存一个int要占用一个机器字长。
C语言把不含小数点和指数的数作为整数。因此,22和-44都是整型常量,但是22.0和2.2E1则不是。C语言把大多数整型常量视为int类型,但是非常大的整数除外。

%d称为转换说明,它指定了printf()应使用什么格式来显示一个值。

printf(“%d minus %d is %d\n”, ten); // 遗漏2个参数
在第二行输出中,第1个%d对应ten的值,但是由于没有给后两个%d提供任何值,所以打印出的值是内存中的任意值。

通常,C语言都假定整型常量是十进制数。然而,许多程序员很喜欢使用八进制和十六进制数。
例如,十进制数65536经常出现在16位机中,用十六进制表示正好是10000。另外,十六进制数的每一位的数恰好由4位二进制数表示。例如,十六进制数3是0011,十六进制数5是0101。

0x或0X前缀表示十六进制值,所以十进制数16表示成十六进制是0x10或0X10。与此类似,0前缀表示八进制。例如,十进制数16表示成八进制是020。

要清楚,使用不同的进制数是为了方便,不会影响数被储存的方式。也就是说,无论把数字写成16、020或0x10,储存该数的方式都相同,因为计算机内部都以二进制进行编码。

以十进制显示数字,使用%d;以八进制显示数字,使用%o;
以十六进制显示数字,使用%x。另外,要显示各进制数的前缀0、0x和0X,必须分别使用%#o、%#x、%#X。
%u说明显示unsigned int类型

short int类型(或者简写为short)占用的存储空间可能比int类型少。

long int或long占用的存储空间可能比int多,适用于较大数值的场合。

long long int或long long(C99标准加入)占用的储存空间可能比long多,适用于更大数值的场合。该类型至少占64位。

unsigned int或unsigned只用于非负值的场合。用于表示正负号的位现在用于表示另一个二进制位,所以无符号整型可以表示更大的数。

在任何有符号类型前面添加关键字signed,可强调使用有符号类型的意图。例如,short、short int、signed short、signed short int都表示同一种类型。

整数溢出
如果整数超出了相应类型的取值范围会怎样?
在超过最大值时,unsigned int 类型的变量 j 从 0开始;而int类型的变量i则从−2147483648开始。注意,当i超出(溢出)其相应类型所能表示的最大值时,系统并未通知用户。因此,在编程时必须自己注意这类问题。

打印unsigned int类型的值,使用%u转换说明;
打印long类型的值,使用%ld转换说明。
如果系统中int和long的大小相同,使用%d就行。但是,这样的程序被移植到其他系统(int和lolong类型的大小不同)中会无法正常工作。

在x和o前面可以使用l前缀,%lx表示以十六进制格式打印long类型整数,%lo表示以八进制格式打印long类型整数。
注意,虽然C允许使用大写或小写的常量后缀,但是在转换说明中只能用小写。

C语言有多种printf()格式。对于short类型,可以使用h前缀。%hd表示以十进制显示short类型的整数,%ho表示以八进制显示short类型的整数。h和l前缀都可以和u一起使用,用于表示无符号类型。例如,%lu表示打印unsigned long类型的值。
对于支持long long类型的系统,%lld和%llu分别表示有符号和无符号类型。

使用错误的转换说明会得到意想不到的结果。

对于无符号变量 un,使用%d会生成负值!其原因是,无符号值 3000000000
和有符号值−129496296 在系统内存中的内部表示完全相同。对于较小的正数(如96),有符号和无符号类型的存
储、显示都相同。

对于short类型的变量end,在printf()中无论指定以short类型(%hd)还是int类型(%d)打印,打印出来的值都相同。这是因为在给函数传递参数时,C编译器把short类型的值自动动转换成int类型的值。
你可能会提出疑问:为什么要进行转换?h修饰符有什么用?
第1个问题的答案是,int类型被认为是计算机处理整数类型时最高效的类型。因此,在short和int类型的大小不同的计算机中,用int类型的参数传递速度更快。
第2个问题的答案是,使用h修饰符可以显示较大整数被截断成 short 类型值的情况。把 65537 以二进制格式写成一个 32 位数是00000000000000010000000000000001。使用%hd,printf()只会查看后 16位,所以显示的值是 1。与此类似,输出的最后一行先显示了verybig的完整值,然后由于使用了%ld,printf()只显示了储存在后32位的值。


char类型

char response;
声明char类型变量

char broiled; /* 声明一个char类型的变量 */
broiled = 'T'; /* 为其赋值,正确 */
broiled = T; /* 错误!此时T是一个变量 */
broiled = "T"; /* 错误!此时"T"是一个字符串 */

实际上,字符是以数值形式储存的,所以也可使用数字代码值来赋值:

char grade = 65;           /* 对于ASCII,这样做没问题,但这是一种不好的编程风格 */

奇怪的是,C语言将字符常量视为int类型而非char类型。例如,在int为32位、char为8位的ASCII系统中,有下面的代码:

char grade = 'B';

本来’B’对应的数值66储存在32位的存储单元中,现在却可以储存在8位的存储单元中(grade)。利用字符常量的这种特性,可以定义一个字符常量’FATE’,即把4个独立的8位ASCII码储存在一个32位存储单元中。如果把这样的字符常量赋给char类型变量grade,只有最后8位有效。因此,grade的值是’E’。

非打印字符

C语言提供了3种方法表示这些字符。

第1种方法前面介绍过——使用ASCII码。例如,蜂鸣字符的ASCII值是7,因此可以这样写:

char beep = 7;

第 2 种方法是,使用特殊的符号序列表示一些特殊的字符。这些符号序列叫作转义序列。
把转义序列赋给字符变量时,必须用单引号把转义序列括起来。例如,假设有下面一行代码:

char nerf = '\n';

警报字符(如蜂鸣):\a
换页符(\f)把活跃位置移至下一页的开始处;
换行符(\n)把活跃位置移至下一行的开始处;
回车符(\r)把活跃位置移动到当前行的开始处;
水平制表符(\t)将活跃位置移至下一个水平制表点(通常是第1个、第9个、第17个、第25个等字符位置);
垂直制表符(\v)把活跃位置移至下一个垂直制表点。

(\、\’、\”)用于打印\、’、”字符
如果打印下面一行内容:

Gramps sez, "a \ is a backslash."

应这样编写代码:

printf("Gramps sez, \"a \\ is a backslash.\"\n");

如果要用八进制ASCII码表示一个字符,可以在编码值前面加一个反斜杠(\)并用单引号括起来。
例如,如果编译器不识别警报字符(\a),可以使用ASCII码来代替:beep = ‘\007’; 可以省略前面的 0,即’\07’甚至’\7’都可以。即使没有前缀 0,编译器在处理这种写法时,仍会解释为八进制。

这里写图片描述

使用ASCII码时,注意数字和数字字符的区别。例如,字符4对应的ASCII码是52。’4’表示字符4,而不是数值4。

printf("Hello!\007\n");

将打印Hello!并发出一声蜂鸣,而 printf(“Hello!7\n”);则打印 Hello!7。

printf()函数用%c指明待打印的字符。前面介绍过,一个字符变量实际上被储存为1字节的整数值。因此,如果用%d转换说明打印 char类型变量的值,打印的是一个整数。而%c转换说明告诉printf()打印该整数值对应的字符。

scanf("%c", &ch);        /* 用户输入字符 */
printf("The code for %c is %d.\n", ch, ch);

注意,printf()函数中的转换说明决定了数据的显示方式,而不是数据的储存方式

无论编译器默认char是什么类型,signed char表示有符号类型,而unsigned char表示无符号类型。这在用char类型处理小整数时很有用。如果只用char处理字符,那么char前面无需使用任何修饰符。

C99标准添加了 _Bool类型,用于表示布尔值,即逻辑值true和false。
因为C语言用值1表示true,值0表示false,所以_Bool类型实际上也是一种整数类型。但原则上它仅占用1位存储空间,因为对0和1而言,1位的存储空间足够了。


char类型用于储存字符(如,字母或标点符号),但是从技术层面看,char是整数类型。因为char类型实际上储存的是整数而不是字符。计算机使用数字编码来处理字符,即用特定的整数表示特定的字符。例,储存字母A实际上储存的是整数65。

C语言把1字节定义为char类型占用的位(bit)数

标准ASCII码的范围是0~127,只需7位二进制数即可表示。通常,char类型被定义为8位的存储单元,因此容纳标准ASCII码绰绰有余。

许多字符集都超过了127,甚至多于255。例如,汉字 字符集。商用的统一码(Unicode)创建了一个能表示世界范围内多种字符集的系统,目前包含的字符已超过110000个。国际标准化组织(ISO)和国际电工技术委员会(IEC)为字符集开发了ISO/IEC 10646标准。统一码标准也与ISO/IEC 10646标准兼容。

有些C编译器把char实现为有符号类型,这意味着char可表示的范围是-128~127。而有些C编译器把char实现为无符号类型,那么char可表示的范围是0~255。请查阅相应的编译器手册,确定正在使用的编译器如何实现char类型。或者,可以查阅limits.h头文件。

C 语言提供了许多有用的整数类型。但是,某些类型名在不同系统中的功能不一样。C99 新增了两个头文件stdint.h和inttypes.h,以确保C语言的类型在各系统中的功能相同。
C语言为现有类型创建了更多类型名。这些新的类型名定义在stdint.h头文件中。例如,int32_t表示32位的有符号整数类型。
更多请查阅相关资料,因为基本用不到我也懒得写。


C语言学习(三)数据-浮点类型


复数和虚数类型

许多科学和工程计算都要用到复数和虚数。C99 标准支持复数类型和虚数类型,但是有所保留。一些独立实现,如嵌入式处理器的实现,就不需要使用复数和虚数(VCR芯片就不需要复数)。一般而言,虚数类型都是可选项。

简而言之,C语言有3种复数类型:float_Complex、double_Complex和long double _Complex。例如,float _Complex类型的变量应包含两个float类型的值,分别表示复数的实部和虚部。类似地, C语言的3种虚数类型是float_Imaginary、double _Imaginary和long double _Imaginary。

如果包含complex.h头文件,便可用complex代替_Complex,用imaginary代替_Imaginary,还可以用I代替-1的平方根。

C语言还有一些从基本类型衍生的其他类型,包括数组、指针、结构和联合。例如,在scanf()函数中用到的前缀&,便创建了一个指针,告诉 scanf()把数据放在何处。

类型大小

如何知道当前系统的指定类型的大小是多少?运行以下程序清单,会列出当前系统的各类型的大小。

程序清单3.8 typesize.c程序

//* typesize.c -- 打印类型大小 */
#include <stdio.h>
int main(void)
{
/* C99为类型大小提供%zd转换说明 */
printf("Type int has a size of %zd bytes.\n", sizeof(int));
printf("Type char has a size of %zd bytes.\n", sizeof(char));
printf("Type long has a size of %zd bytes.\n", sizeof(long));
printf("Type long long has a size of %zd bytes.\n",
sizeof(long long));
printf("Type double has a size of %zd bytes.\n",
sizeof(double));
printf("Type long double has a size of %zd bytes.\n",
sizeof(long double));
return 0;
}

sizeof是C语言的内置运算符,以字节为单位给出指定类型的大小。C99和C11提供%zd转换说明匹配sizeof的返回类型。一些不支持C99和C11的编译器可用%u或%lu代替%zd。

该程序的输出如下:
Type int has a size of 4 bytes.
Type char has a size of 1 bytes.
Type long has a size of 8 bytes.
Type long long has a size of 8 bytes.
Type double has a size of 8 bytes.
Type long double has a size of 16 bytes.

该程序列出了6种类型的大小,你也可以把程序中的类型更换成感兴趣的其他类型。注意,因为C语言定义了char类型是1字节,所以char类型的大小一定是1字节。而在char类型为16位、double类型为64位的系统中,sizeof给出的double是4字节。在limits.h和float.h头文件中有类型限制的相关信息。

使用数据类型

初始化变量应使用与变量类型匹配的常数类型。例如:
int apples = 3;    /* 正确 */
int oranges = 3.0;  /* 不好的形式 */

把一个类型的数值初始化给不同类型的变量时,编译器会把值转换成与变量匹配的类型,这将导致部分数据丢失。

例如,下面的初始化:
int cost = 12.99;   /* 用double类型的值初始化int类型的变量 */
float pi = 3.1415926536;  /* 用double类型的值初始化float类型的变量 */

第1个声明,cost的值是12。C编译器把浮点数转换成整数时,会直接丢弃(截断)小数部分,而不进行四舍五入。
第2个声明会损失一些精度,因为C只保证了float类型前6位的精度。编译器对这样的初始化可能给出警告。

程序要知道函数的参数个数才能正常工作。printf()和scanf()函数用第1个参数表明后续有多少个参数

printf("%d cats ate %d cans of tuna\n", cats, cans);

-

注意,用%d显示float类型的值,其值不会被转换成int类型。在不同的平台下,缺少参数或参数类型不匹配导致的结果不同。

转义序列示例

程序清单3.10 演示了退格(\b)、水平制表符(\t)和回车(\r)的工作方式。

程序清单3.10 escape.c程序

#include <stdio.h>
int main(void)
{
float salary;
printf("\aEnter your desired monthly salary:"); /* 1 */
printf(" $_______\b\b\b\b\b\b\b");        /* 2 */
scanf("%f", &salary);
printf("\n\t$%.2f a month is $%.2f a year.", salary,salary * 12.0);      /* 3 */
printf("\rGee!\n");         /* Gee! $4000.00 a month is $48000.00 a year. */
return 0;
}

刷新输出

printf()何时把输出发送到屏幕上?

最初,printf()语句把输出发送到一个叫作缓冲区(buffer)的中间存储区域,然后缓冲区中的内容再不断被发送到屏幕上。

C 标准明确规定了何时把缓冲区中的内容发送到屏幕:
当缓冲区满、遇到换行字符或需要输入的时候(从缓冲区把数据发送到屏幕或文件被称为刷新缓冲区)。例如,前两个 printf()语句既没有填满缓冲区,也没有换行符,但是下一条 scanf()语句要求用户输入,这迫使printf()的输出被发送到屏幕上。

猜你喜欢

转载自blog.csdn.net/qq_35746344/article/details/80422520