3.1 C语言的数据类型
算法处理的对象是数据,而数据是以某种特定的形式存在的(如:整数、实数、字符等形式)。
不同数据之间还存在某些联系(如:由若干个整数组成一个整数数组)。所以,所谓的数据结构就是数据的组织形式。
注意:①不同的计算机语言所允许定义和使用的数据结构是不同的(如:C语言提供了“结 构体”这样一种数据结果,而FORTRAN语言汇中没有)。
②处理同一类问题,如果数据结构不同,算法也会不同(如:对10个整数排序和对 由10个整数构成的数组排序的算法是不同的)。
因此,我们考虑算法时,必须注意数据结构,应当综合考虑算法和数据结构,选择最佳的数据结构和算法。
C语言提供的基本数据类型体系图(由此还可以构成更复杂的数据结构,如:利用指针和结构体类型可以构成表、树、栈(zhàn)等复杂的数据结构):
数据有常量与变量之分,分别属于上述这些类型(如:整型数据包括整型变量和整型常量)
总结:数据是区分类型的,类型不一样,在内存中存放形式也不一样,而且有常变量之分,所以在程序对用到的所以数据都要指定其数据类型!
3.2 常量与变量
3.2.1 常量和符号常量
⒈常量是在程序运行过程中,其值不能被改变的量【也称直接常量或字面常量】。区分为不同
类型。
⒉符号常量:用一个符号代表一个常量【以标识符形式表示】
定义方法:#define 符号常量名 常量
优点:①含义清楚。命名时应尽量考虑“见名知意”
②一改全改。因为符号常量不能再被赋值以及在程序中多处使用
注意:习惯上,符号常量用大写表示。
3.2.2 变量
⒈要搞清楚变量的概念,必须要清楚变量名、变量值和变量的含义。
变量是代表内存中具有特定属性的一个存储单元,用来存放数据。【仓库】
变量值是存放进去的数据。【物品】
变量名是以一个名字代表一个地址。【仓库地址】
存放的过程:在对程序连接时由编译系统给每一个变量分配对应的内存地址,从变量中取值【实际上,是通过变量名找到相应的内存地址,从该存储单元中读取数据。】
⒉标识符:用来对变量、符号常量、函数、数组、类型等数据对象命名的有效字符序列的统称
规则:
①只能由字母、数字和下划线3种字符组成。
②第一个字符必须为字母或下划线
③不能是关键字
注意:
①区分大小写的
②命名时做到“见名知意”
③变量名的长度最好不要超过允许的范围,尽可能的简洁
④先定义,后使用
总结:
常量包括字面常量和符号常量,字面常量包含整型常量、浮点型常量和字符型常量;符号常量就是以符号代表一个常量,用#define先定义。
变量就是一个存储单元,通过变量名找到变量值。变量是一个仓库,变量名是仓库地址,变量值是物品;通过仓库地址寻找到仓库,才能从中拿到这个物品。
变量名要符合标识符的命名规则。
3.3整型数据
3.3.1整型常量的表示方法
整型常量既整常数。
在C语言中用一下3种形式表示:
⒈十进制整数,(如:123、-526、4等)
⒉八进制整数,以0开头的数(如:0123表示八进制的123)
⒊十六进制整数,以0x开头的数(如:0x123表示十六进制的123)
3.3.2整型变量
⒈整型数据在内存中的存放形式
数据在内存中是以二进制形式(原码)存放的。【适用于正整数】
不同的编译系统为整数数据分配的字节数是不同的。
数值是以补码表示的,一个正整数的补码和概述的原码【二进制】相同;如果数值是负的,则进行求负数的补码。其方法:
S1:取该数的绝对值
S2:用二进制形式表示
S3:按位取反
S4:加1
可知:在存放整数的存储单元中,最左边的一位是表示符号的,该位为0,值为正;1,则负【如果定义为unsigned int型的话,则最左边不再表示符号,也存放数据】
⒉整型变量的分类
基本类型是int【int型的范围:-215~215-1,即-32768~32767】,根据数值范围将变量定义为基本整型、短整型或长整型。【可以根据需要加上修饰符<modifier>:short<短型>或long<长型>】
实际应用中,变量的值常常是正的,因此为了充分利用变量的值的范围,姿势可以将变量定义为“无符号”类型。加上修饰符signed【可省略】,则指定是“有符号数”;加上修饰符unsigned,则指定为“无符号数”。对以上3类都可以加上修饰符unsigned指定“无符号数”。
归纳,用一下4类整型变量:
①有符号基本整型:[signed] int
②有符号短整型: [signed] short [int]
③有符号长整型: [signed] long [int]
④无符号基本整型:unsigned int
⑤无符号短整型: unsigned short [int]
⑥无符号长整型: unsigned long [int]
注:“[]”里的内容可以省略
不指定unsigned或指定signed型,则存储单元的最高为代表符号【0正,1负】;指定unsigned型,存储单元中去啊不二进位用作存放数本身,不包括符号。因为unsigned型只存放不带符号的数,所以它可以存放的正数范围比一般整型变量中的整数范围扩大一倍【unsigned型范围0~216-1】
⒊整型变量的定义
前面提到,C语言程序中所有用到的变量都必须在程序中定义,即“强制类型定义”
定义的格式:
类型符 变量名;
说明:①对变量的定义一般放在一个函数的开头部分的声明部分【也可以放在函数中的某一 段分程序内,但作用域只限所在的分程序】
②不同类型的整数刑拘可以进行算术运算。
⒋整数数据的溢出
例如:在Turbo C和Turbo C++中,一个int型的变量最大允许值为32767,如果在加回出现什么情况呢?动手做实验试试
32767的二进制 01111111 11111111
加1后的二进制 1000000 0000000
上面说过数值以以补码形式表示的,我们反推一下:
10000000 00000000-1 得01111111 11111111
取反 得 1000000 00000000
十进制表示 得 32768
去掉绝对值 得 -32768或32768
int型最大只有32767故得 -32768
看了上面的分析,知道了一个整型变量只能容纳-32768~32767范围内的数,无法表示大于32767或小于-32767的数,超出就会发生“溢出”,得不到预期的结果【小心】
3.3.3 整型常量的类型
整型变量分为6种:int、short int、long int、unsigned int、unsigned short、unsigned long。
那么整型常量是否也有这些类别呢?在将一个整型常量赋值给上述几种类别的整型变量时如何做到类型匹配呢?
答:整型常量也区分类型【看其范围】,但要注意几点(假定整型数据在内存中占2个字节):
①一个整数,如果其值在-32768~32767范围内,认为它是int型,可赋值给int、long int、short int
②一个整数,如果其值超过上述范围,而在-2147483648~2147483647范围内,认为是long int
可赋值给一个long int型变量
③如果所用的C语言版本分配给short int与int数据在内存中占的长度相同,则表述范围与int
相同,因而一个int常量同时也是一个short int常量,可赋值给int、short int变量。
④一个整常量后面加一个字母u或U,认为是unsigned int型。
⑤一个整常量后面加一个字符l或L,认为是long int型。
总结:
整型数据区分为整变量和整常量。
整型变量分为6种类型:int、short int、long int、unsigned int、unsigned short、unsigned long;在内存中按补码形式存放,unsigned整型最大的区别就是最高位不表示符号位。
整常量按其范围区分上述类型,赋值给变量。注意:不要溢出,不然得不到预期结果的;做到“先定义,再使用”
3.4 浮点型数据
3.4.1 浮点型常量的表示方法
C语言中的浮点数【floating point number】就是平常所说的实数【real number】
有两种表示形式:
⒈十进制小数形式。由数字和小数点组成(如:0.123、123.、0.0)
注意:必须有小数点
⒉指数形式(如135e6或135E6,都代表135x106)
注意:字母e或E之前必须有数字,且e后面的指数必须为整数
一个浮点数可以用多种指数表示形式(如:123.456,可以表示为1.23456e2、12.3456e1、123.456e0、1234.56e-1等)其中1.23456e2称为“规范化的指数形式”:即在字母e或E之前的小数部分中,小数点左边应有一位【且只能一位】非零的数字。
一个浮点数在用指数形式输出时,是按规范化的指数形式输出的。
3.4.2 浮点型变量
⒈浮点型数据在内存中的存放形式
一个浮点型数据一般在内存中占4个字节【32位】,按照指数形式存储。
系统把一个浮点型数据分成小数部分和指数部分,分别存放。指数部分用规范化的指数形式。实际上,在计算机中是用二进制数来表示小数部分以及用2的幂【mì】次来表示指数部分。
至于用多少位表示小数部分,多少位表示指数部分,标准并无具体规定,由各编译系统自定。小数部分占的位越多,数的有效数字越多,精度也就越高;指数部分占的位数越多,则能表示的数值越大。
⒉浮点型变量的分类
浮点型变量分为单精度【float】、双精度【double】和长双精度【long double】3类
注意:ANSI C并未具体规定每种类型数据的长度、精度和数值范围,不同的系统会有差异。
和整型一样,先定义,后使用。定义方法:浮点数类型 变量名
⒊浮点型数据的舍入误差
因为浮点型变量是由有限的存储单元组成,因此能提供的有效数字也是有限的,在有效位一位的数字将被舍去,由此可能会产生一些误差。
看图吧,输出一样的
前8位是准确的,后面几位不准确,无意义了。
应当避免将一个很大的数和一个很小的数直接相加或相减,否则就会“丢弃”小的数。
3.4.3 浮点型常量的类型
C语言编译系统将浮点型常量作为双精度来处理。如果在数的后面加字母f或F,这样编译系统就会把他们按单精度处理。
一个浮点型常量可以赋给一个float型、double型或long double型变量,根据变量类型截取浮点型常量中相应的有效位数据。
总结:
浮点型数据分浮点型常量和浮点型变量。浮点型常量,就是数学中的实数,用小数表现形式和指数表现形式两种,编译系统默认作为双精度处理;浮点型变量,分为单精度(float)、双精度(double)和长双精度(long double)三种,在内存中小数部分和指数部分分开存放,指数部分按规范化指数形式存放。注意:浮点型数据的舍入误差!!!
3.5字符型数据
3.5.1字符常量
概念:C语言的字符常量是用撇号括起来的一个字符(如:'a'、'A'、'3')
注意:大小写敏感!!!
转义字符:一种特殊形式的字符常量【因为无法用一般形式的字符表示】,也是一种控制字 符【不能显示在屏幕上】,以一个“/"反斜杠开头(如:/n表示换行),将反斜杠后 面的字符转换为另外的意义。
常用转义字符及其作用表:
字符形式 | 含义 | ASCII代码 |
/n | 换行,将当前位置移到下一行开头 | 10 |
/t | 水平制表【也就是跳到下一个Tab位置】 | 9 |
/b | 退格,将当前位置移到前一列 | 8 |
/r | 回车,将当前位置移到本行开头 | 13 |
/f | 换页,将当前位置移到下页开头 | 12 |
// | 代表一个反斜杠“/” | 92 |
/' | 代表一个撇号“'” | 39 |
/" | 代表一个引号“"” | 34 |
/ddd | 1到3为八进制数所代表的字符 |
|
/xhh | 1到2位十六进制数所代表的字符 |
|
用这张表的倒数两行的方法可以表示任何可输出的字母字符、专用字符、图形字符和控制字符。
注意:'/0'或'/000'是代表ASCII码为0的控制字符,即“空操作”字符,常用在字符串中。
空操作字符:不引起任何控制动作,也不是一个可显示的字符。
3.5.2 字符变量
概念:用来存放字符常量【只能存放一个字符】
定义形式:char 变量名;
一般编译系统(如:Turbo C、Visual C++)中都以一个字节来存放一个字符【或者说一个字符变量在内存中占一个字节】
3.5.3 字符数据在内存中的存储形式及其使用方法
一个字符常量放到一个字符变量中,并不是将其本身放到内存单元中去,而是将其ASCII码放到内存单元中去。
看得出,字符数据以ASCII存储,其存储形式与整型数据相似。这样使得字符型数据与整型数据之间可通用,可以进行算术运算【也就是将它们的ASCII码进行算术运算】,可以互相赋值。
注意:unsigned char的范围是0~255;char的范围是-128~127
3.5.4字符串常量
概念:由一对引号括起来的字符序列(如:'"how do you do")
不要将字符常量与字符串常量混淆,二者不同的,为什么不同呢?
答:C规定:在每一个字符串常量的结尾加一个“字符床结束标志”,以字符'/0'作为字符串结束标志,以便系统据此判断字符串是否结束。也就是说:在原本的基础上再加一个字节才是一个字符串的真正的宽度。(如:"china",是5个字节,还要加一个空操作的字节,实际他占了6个字节)
注意:不能把一个字符穿常量赋给一个字符变量;如果将一个字符串存放在变量中,必须使用字符数组。
总结:字符型数据分字符型常量和字符型变量。字符型常量中有一特殊的控制字符叫转义字符;字符型变量用来存放常量,一个变量只能存放一个常量,在内存中以ASCII码存储,因此字符型数据和整型数据可以相同的。
3.6 变量赋初值
C语言允许在定义变量的同时使变量初始化
定义形式:类型符 变量名=初值
例如:
定义一部分初值 int a,b=19,c
对几个变量赋予同一个初值 int a=3,b=3,b=3 不能是int a=b=c=3
初始化不是在编译阶段完成的【只有在静态存储变量和外部变量的初始化实在编译截断完成的】,而是在程序运行时执行本函数时赋初值的,相当一个赋值语句。
3.7 各类数值型数据间的混合运算
整型和浮点型可以混合运算,且字符型数据和整型可以通用,因此,浮点型、整型、字符型之间可以进行运算。
进行运行时,不同类型的数据要转换成同一类型,然后运算,转换规则:
double float
高
long
unsigned
低 int char
横向:表示必定转换(如:float型数据一律先转换成double型数据运算【即使两个数据都是float】)
纵向:表示当运算对象为不同类型时转换(如:int型和double型进行运算,先把int型转换成double型,再两个double型数据运算,结果是double型的)
注意:箭头方向只表示数据类型级别的高低,由低向高转换。上述的类型转换是由系统自动进行的。
3.8 算术运算符和算术表达式
3.8.1 C语言运算符简介
C语言把除了控制语句和输入输出函数以外的几乎所有的基本操作都作为运算符处理,可见其范围的宽(如:将赋值符“=”作为赋值运算符、方括号作为下标运算符)
运算符有以下几类:
运算符类型 | 符号 |
算术运算符 | + - * / % |
关系运算符 | < > == <= >= != |
逻辑运算符 | ! && || |
位运算符 | << >> ~ | ^ & |
赋值运算符 | =及其扩展赋值运算符 |
条件运算符 | ?: |
逗号运算符 | , |
指针运算符 | *和& |
求字节数运算符 | Sizeof |
强制类型转换运算符 | (类型) |
分量运算符 | . -> |
下标运算符 | [] |
其他 | 如函数调用运算符() |
注意:条件运算符是C中唯一一个三目【三元】运算符
3.8.2算术运算符和算术表达式
1.基本的算术运算符
+ - * /
%:模运算符,或称取余运算符;%两侧均应该为整型数据
注意:两个整数相除的结果为整数,如:5/3的结果为1,舍去小数部分。如果除数或被除数中有一个负值,则舍入方向是不固定的。
多数C编译器(如turbo C)采用的是“向零取整”的方法
向零取整:一个数向着零的方向(数轴上)取整
⒉算术表达式和运算符的优先级与结合性
C算术表达式:用算术运算符和括号将运算对象【也称操作数】链接起来的、复合C语法规 则的式子。
运算对象包括了常量、变量、函数等。
C语言规定了优先级和结合性:表达式求值时,先按运算符的优先级高低次序执行;优先级相同时,按规定的结合性进行运算。
结合性:C语言规定各种运算符的结合方向(结合性),算术运算符是自左向右,又称左结合性【性的概念是C的特点之一,其他一些高级语言没有的】
优先级【高到低】:
第一级:
圆括号【()】、下标运算符【[]】、分量运算符的指向结构体成员运算符【->】、结构体成员运算符【.】
第二级:
逻辑非运算符【!】、按位取反运算符【~】、自增自减运算符【++ --】、负号运算符【-】、类型转换运算符【(类型)】、指针运算符和取地址运算符【*和&】、长度运算符【sizeof】
第三级:乘法运算符【*】、除法运算符【/】、取余运算符【%】
第四级:加法运算符【+】、减法运算符【-】
第五级:左移动运算符【<<】、右移动运算符【>>】
第六级:关系运算符【< > <= >= 】
第七级:等于运算符【==】、不等于运算符【!=】
第八级:按位与运算符【&】
第九级:按位异或运算符【^】
第十级:按位或运算符【|】
第十一级:逻辑与运算符【&&】
第十二级:逻辑或运算符【||】
第十三级:条件运算符【?:】
第十四级:赋值运算符【= += -= *= /= %= >>= <<.= &= |= ^=】
第十五级:逗号运算符【,】
说明:①G1不要求运算对象的个数,G2是单目运算符,G13条件运算符是三目运算符,其他都是双目运算符。
②G2、G13条件运算符、G14赋值运算符是自右向左的【也就是右结合性】,其他都是自左向右【左结合性】
归纳各类运算符:
初等运算符【()、[]、->、.】 G1
单目运算符 G2
算术运算符(先乘除【取余】,后加减) G3,4
位运算符【<< >>】 G5
关系运算符 G6,7
位运算符【递减& ^ |】 G7,8,9
逻辑运算符(不包括!) G11,12
条件运算符 G13
赋值运算符 G14
逗号运算符 G15
⒊强制类型转换运算符
概念:可以利用强制类型转换运算符将一个表达式转换成所需类型
一般形式:(类型)(表达式)
注意:①表达式应该用括号括起来。
②使用强制类型转换时,得到的是一个所需类型的中间数据,原来的变量不改变
可知:有两种类型转换:①系统自动进行的类型转换【自动类型转换】②强制类型转换【和上面讲的强制类型定义不要混淆】
当自动类型转换不能实现目的时,可以用强制类型转换(如:取余运算符要求两侧均为整型量,不然不合法,若x为float型,则“x%3”不合法,这样进行强制类型转换,(int)x%3,就是合法的了)
⒋ 自增、自减运算符
作用:使变量的值增1或减1
+i和i++的作用都相当与i=i+1,不同之处在于:
++i是先执行i=i+1,再使用i的值
i++是先使用i的值,再执行i=i+1
注意:只能用于变量,而不能用于常量或表达式【逻辑思考下就明白了】;是右结合性的。
自增、自减运算符常用于循环语句,趋向结束;也用于指针变量,使指针指向下一个地址
⒌有关表达式使用中的问题说明
①c运算符和表达式使用灵活,利用这一点可以巧妙地处理许多在其他语言中难以处理的问题。但是注意:ANSI C并没有具体地规定表达式的子表达式的求值顺序。
②c语言中有的运算符为一个字符,有的运算符由两个字符组成,在表达式中如何组合呢?
(如:i+++j,是理解为(i++)+j还是理解为i+(++j)呢)编译系统在处理时尽可能地按左结合性将若干个字符组成一个运算符【在处理标识符、关键字时也按同一原则处理】,所以i+++j理解为(i++)+j,而不是i+(++j)。
这类问题还用一些……
总之,不要写出别人看不懂的,也不知道系统会怎么执行的程序就可以了!
使用 ++和--时,常会出现一些人们“想不到”的副作用。
总结:C语言的运算符很丰富,除了控制语句和输入输出函数外都是!要知道运算符的类型及其优先级和结合性。注意强制类型定义和强制类型转换的区别。前置自增(减)和后置自增(减)的不同之处!
3.9 赋值运算符和赋值表达式
⒈赋值运算符
概念:赋值符号“=”称为赋值运算符
作用:将一个数据【可以是一个表达式的值】赋给一个变量。
赋值运算:执行一次赋值操作
⒉类型转换
如果赋值运算符两侧的类型不一致,但都是数值型或字符型时,在赋值时进行类型转换。
转换方法在不同系统中有些差别,大体上的方法:
①将浮点型数据赋给整型变量时,舍弃浮点数的小数部分
②将整型数据赋给单、双精度变量时,数值不变,以浮点数形式存储到变量中。
③将double型数据赋给float型变量时,截取前面7位有效数字,存放到float变量的存储单
元【4字节】中,但不要超出float型数据的范围。
将float数据赋给double变量时,数字不变,有效位数扩展到16位,内存中以8字节存储
④字符型数据赋给整型变量时,由于字符只占一个字节,而整型变量占2个字节,因此将字符数据放到整型变量存储单元的低8位中,有两种情况:
Ⅰ如果所用系统将字符处理为无符号的字符类型或已经定义为unsigned char型,则将字 符的8位放到整型变量低8位,高8位补零。
Ⅱ如果所用系统将字符处理为signed char,进行符号扩展。这样做目的使数值保持不变。
符号扩展:若字符最高位为0,则高8位全补0;若字符最高位为1,则高8位全补1.
⑤将int、short、long型数据赋给char型变量时,截断低8位,就是将其低8位原
封不动地送到char型变量
⑥将带符号的整型数据赋给long变量时,进行符号扩展。
将long型数据赋给一个int型变量,截断低16位
⑦将unsigned int型数据赋给long int型变量时,不存在符号扩展问题,只需将高位补0.
将unsigned数据赋给一个占字节数相同的signed型数据时,原样送到signed变量中。
【数据范围超过相应的范围,会出现数据错误】
signed型数据赋给unsigned型数据,也是原样赋值【连原有的符号位也做为数值传送】
3.复合的赋值运算符
概念:在赋值符“=”之前加上其他运算符
如:a+=3 等价于a=a+3
x*=y-3 等价于x=x*(y-3)
x%=4 等价于x=x%4
凡是二元[二目]运算符,都可以与赋值符一起组合成复合赋值符
C语言规定可以使用的10种复合赋值运算符:
-=、+=、*=、/=、%=、<<=、>>=、&=、^=、|=【后5种有关位运算】
好处:①为了简化程序,使程序精炼
②为了提高编译效
4.赋值表达式
概念:将一个变量和一个表达式连接起来的式子
一般形式:变量 赋值运算符 表达式
表达式可以也是一个赋值表达式
左值[left value,简写lvalue]:赋值运算符左侧的标识符
右值[right value,简写rvalue]:赋值运算符右侧的标识符
左值只能是变量,但左值都可以作为右值
总结:赋值符号“=”就是赋值运算符,作用是将一个数据赋给一个变量,所以赋值表达式的一般形式:变量 赋值运算符 表达式 。如果赋值运算符两侧类型不一致,都是数值型或字符型时【而不是结构体、指针类型的时候】,进行类型转换。类型转换中注意:unsigned型数据转换不存在符号扩展,高位补0;只有signed型数据转换中存在符号扩展。
3.10 逗号运算符和逗号表达式
逗号运算符:特殊的运算符,又称“顺序求值运算符用一个逗号将两个表达式连接起来。
一般形式:表达式1,表达式2
求解过程:先求解表达式1,再求解表达式2.整个表达式的值是表达式2的值。
一个逗号表达式又可以与另一个表达式组成新的逗号表达式,所以
一般形式可以扩展为:
表达式1,表达式2,表达式3,…,表达式n
整个表达式的值为表达式n的值
逗号表达式无非是把若干个表达式串联起来,在很多情况下,使用逗号表达式的目的只想分别得到各个表达式的值,而非一定需要整个表达式的值。
注意:并不是任何地方出现的逗号都是作为逗号运算符的。
总结:逗号表达式是所以运算符中优先级最低的。使用逗号表达式不一定就要整个表达式的值,而只是想得到各个表达式的值,常用于循环语句!!