第三章基本数据类型、运算符与表达式
3.1 C语言的数据类型
在C程序中使用的每一个数据都属于唯一的一种数据类型,没有无类型的数据,一个数据也不能同属于多种数据类型
3.1.1 C语言的数据类型可分为四大类:
-
基本类型:不可再分解为其他类型
- 整型
- 实型
- 字符型
- 枚举型
-
构造类型:一个构造类型可以分解为若干个成员或元素
- 数组类型
- 结构体类型
- 共用体类型
-
指针类型:指针类型的值表示某个量在内存储器中的地址
-
空 类 型:有些函数没有返回值,则该函数类型定义为空类型
3.2 常量和变量
对基本数据类型量,分为常量和变量两种;其值不发生改变的量称为常量,其值可以发生改变的量称为变量 |
3.2.1 常量
常量必须存到变量中 |
-
常量可分为四类:
-
整形常量
-
实型常量
-
字符常量
-
字符串常量
-
-
书写形式上分为两类:
- 字面常量
- 符号常量
3.2.1.1字面常量
字面常量又称直接常量,从字面上可直接看出它们是什么
例如:12、0、-3;4.6、-1.23;‘a’、‘b’
3.2.1.2 符号常量
-
标识符:程序中使用的各种名称,如变量名,函数名,数组名,符号常量等一些由专门含义的名称
①关键字:具有固定含义的标识符,均为小写字母;int、 while等
②预定义标识符:在C语言中有特定的含义;printf、include等
③用户自定义标识符:由用户根据需要定义的标识符,来给变量、函数、数组等命名;
标识符由字母、数字和下划线组成的一串符号,必须以英文字母或下划线开头,后面可以随机组合字母、下划线、数字 |
- 标识符区分大小写,sum SUM Sum是三个不同的标识符
- 不允许使用关键字作为用户标识符名称
- 用户标识符与预定义标识符相同时,预定义标识符将失去原有定义,代之以用户确定的含义,建议用户标识符不要与预定义标识符相同
-
符号常量:也称宏常量,指用一个标识符代表一个常量
①定义格式:#define 符号常量 常量(#define PI 3.14159)
②命名规则:遵循标识符命名规则,习惯上用大写字母作为符号常量标识符,符号常量定义过后,凡在源程序中使用该符号常量时,都用其后面指定的常量代替
例1 符号常量的使用
#include <stdio.h> #define PI 3.14159 /*定义符号常量PI为3.14159*/ #define R 5.3 /*定义符号常量R为5.3*/ main () { printf("area=%f\n",PI*R*R); /*输出圆面积*/ printf("circumference=%f\n",2*PI*R);/*输出圆周长*/ }
③使用符号常量的好处:修改程序方便,仅需修改符号常量后面常量的值,一改全改;阅读程序方便
3.2.2 变量
变量用来保存程序运行过程中的输入数据、计算获得的中间结果和最终结果 |
-
变量必须遵循先定义后使用
-
变量定义语句格式:
-
类型标识符 变量名1,变量名2,…;
-
类型标识符说明变量的类型(整型、实型、字符型)
-
变量名属于用户标识符
-
-
编译时,系统根据指定的类型分配给变量若干个连续字节存储空间:
-
int x,y; /*VC编译系统中,为变量x,y各分配4个字节,并按整数方式存储数据*/
-
在定义变量的同时也可对变量赋初值,又称变量的初始化,若定义变量时没有赋初值,则该变量的值为随机值,不是0
-
变量定义语句可放在函数体内的前部,也可放在函数的外部或复合语句的开头
-
3.3 整型数据
3.3.1 整型常量的表示
整型常量就是整数,用来表示一个正的、负的或零的整数值;C语言中,整型常量有十进制、八进制、十六进制三种数制表示方法 |
- 二进制整数:0、1,两种编码符号;逢二进一
- 十进制整数:0——9,十种编码符号;逢十进一
- 八进制整数:0——7,八种编码符号;逢八进一;书写时以0开头;如076、00、014、-036
- 十六进制整数:0——9、A——F或(a——f),十六种编码符号;逢十六进一;书写时以0x或0X开头;如0xA3、0Xad、-0x8fb
3.3.1.1四种进制转换:
- 十进制转二进制:
-
除2取余倒排(每次除一定有一个余数,要除到商为0)
- 例:十进制25=二进制?
25/2=12…1
12/2=6…0
6/2=3…0
3/2=1…1
1/2=0…1 此时商为0,则余数倒排为11001,故十进制25=二进制11001
- 二进制转十进制:
-
按位乘权相加(二进制最右侧开始第一位数值×2的0次方,往后依次第n位上数值称2的n-1次方)
- 例:二进制101111=十进制?
1 ∗ 2 0 + 1 ∗ 2 1 + 1 ∗ 2 2 + 1 ∗ 2 3 + 0 ∗ 1 4 + 1 ∗ 2 5 = 1 + 2 + 4 + 8 + 0 + 32 = 47 1*2^0+1*2^1+1*2^2+1*2^3+0*1^4+1*2^5=1+2+4+8+0+32=47 1∗20+1∗21+1∗22+1∗23+0∗14+1∗25=1+2+4+8+0+32=47
- 八进制转二进制:
-
八进制一位展成三位二进制
八进制 二进制 0 000 1 001 2 010 3 011 4 100 5 101 6 110 7 111 - 例:八进制363=二进制?
八进制3——>二进制011;八进制6——>二进制110;故八进制363=二进制011110011;0舍去得11110011
- 二进制转八进制:
-
二进制从右到左三位压成一位八进制(不够3位前面加0)
- 例:二进制1011110=八进制?
二进制110——>八进制6;二进制011——>八进制3;二进制001——>八进制1,故二进制1011110=八进制136
- 十六进制转二进制:
-
十六进制一位展成四位二进制
十六进制 二进制 0 0000 1 0001 2 0010 3 0011 4 0100 5 0101 6 0110 7 0111 8 1000 9 1001 A 1010 B 1011 C 1100 D 1101 E 1110 F 1111 - 例:十六进制7aB1=二进制?
十六进制7——>二进制0111;十六进制a——>二进制1010;十六进制B——>二进制1011;十六进制1——>二进制0001,则十六进制7aB1=二进制0111101010110001
- 二进制转十六进制:
-
二进制从右到左四位压成一位十六进制(不够四位前面加0)
- 例:二进制101111=十六进制?
二进制1111——>十六进制F;二进制0010——>十六进制2,则二进制101111=十六进制2F
3.3.1.2 整型常量的类型
整型常量也有基本整型、长整型、有符号无符号之分,通常情况下编译程序会根据数值大小判断常量是哪种类型 |
- 有需要指出整数位long int型时,须在常量整数末尾加上L或l;如:149L(十进制长整型数)、013L(八进制长整型数)、0Xa6BL(十六进制长整型数)
- 一般基本整型或长整型数,默认识别为有符号数,八进制和十六进制数通常是无符号数;若是长整型无符号整型常量,可以在末尾加LU或lu;如3582LU、0x4A3lu
3.3.2 整型变量
3.3.2.1 地址与内存
-
内存的性质:
-
内存由若干个存储单元构成,每个单元内都可存放一个内容值
-
内存是一维的、线性的
-
内存是有唯一一个地址来标识该内存空间,通过其地址访问其内容值
-
每个存储单元内存放内容是一个固定大小,可以为一个字节,也可以两个字节表示一个字
-
对内存操作,每个存储单元内容值的操作不是读就是写,读不会影响内容值,但写需要刷新内容值
-
内存关机,信息全部丢失
-
-
地址与内存关系:
-
地址唯一标注存储单元
-
内存大小由地址位数决定
-
地址n位,最多2^n个存储单元
-
内存大小 地址位数 4 2 8 3 16 4 2^n n
-
-
机器码:
-
真 值<——>机器码(原、补、反码)
十进制<——>二进制
-
-
地址映射:
字节(Byte) 位(bit) 1B(字节) 8b(位) 1KB 1024B(字节))=2^10*8b(位) 1MB 1024KB=1024×1024B(字节)=2^20×8b(位) 1GB 1024MB=1024×1024KB=1024×1024×1024B=2^30×8b(位) 1TB 1024GB=1024×1024MB=1024×1024×1024KB=1024×1024×1024×1024B=2^40×8b
3.3.2.2 整型变量的分类
整型变量的基本类型为int型,若加上修饰符,可定义更多的整数数据类型 |
根据表达范围整型变量可分为:基本整型、短整型、长整型,这些变量在内存中所占字节数规定short int<=int<=long int;根据是否有符号可分为有符号型和无符号型,若未指定是否有符号,则默认为有符号
整型类型符 | 占用字节数 | 数值范围 |
---|---|---|
【signed】int | 4B=32(b)位 | -231~(231-1) |
【signed】short【int】 | 2B=16(b)位 | -215~(215-1) |
【signed】long【int】 | 4B=32(b)位 | -231~(231-1) |
unsigned【int】 | 4B=32(b)位 | 0~(2^32-1) |
unsigned short【int】 | 2B=16(b)位 | 0~(2^16-1) |
unsigned long【int】 | 4B=32(b)位 | 0~(2^32-1) |
【对有符号型的数值范围:占用字节数n位对应内存大小为2^n,分正负即正负各占2^n/2=2^(n-1)】 |
【对无符号型的数值范围:只有正数这一侧】 |
【整型变量有其允许存储的范围,当数值超过这个范围时就发生了"溢出",但系统并不报错(C语言的灵活性),此时将变量的数据类型改为占用字节更多的数据类型即可】 |
3.3.2.3 整型变量的定义
定义整型变量时,某些类型修饰符可省略
int a,b,c; /*a,b,c为有符号的整型变量*/
short x,y,z; /*x,y,z为有符号的短整型变量*/
unsigned p,q,w; /*p,q,w为无符号的整型变量*/
3.3.2.4 整型数据在内存中存放形式
在内存中数据是以二进制形式存放的,对有符号整型来说,从左至右,第一位为符号位,其余十五位为数值位;无符号整型数,其十六位全为数值位,用来存放二进制数,没有符号位
-
原码:
-
一个数的原码是原始的二进制码,以有符号短整型为例,有符号短整型占2个字节16位,从左至右,第一位为符号位,其余十五位为数值位
-
最高位是符号位,0表示正数,1表示负数
-
数值位就是数值本身的绝对值的二进制数
-
负数的原码是在其数值绝对值的二进制数基础上,最高位为1
-
例:十进制数5和-5的原码是?
十进制5的二进制数为0000000000000101,则其原码为0000000000000101
故十进制-5的原码为1000000000000101
-
-
使用原码存储数据会产生两个问题:①数值0有两种表示方法;②正数和负数相加结果不正确(计算机只会加法,减法实质为加法) |
- 数值0原码有两种表示方法:
- +0原码:0000000000000000
- -0原码:1000000000000000
- 正数和负数相加,结果不正确:
- 1原码:0000000000000001
- -1原码:1000000000000001
- 1-1=1+(-1)=0000000000000001+1000000000000001=1000000000000010=十进制-2不等于0
-
反码:
-
对于正数,反码和原码相同
-
对于负数,反码为原码符号位不变,数值位按位取反
-
例:十进制数5和-5;0和-0的原码和反码是?
十进制数5 原码:0000000000000101 十进制数0 原码:0000000000000000
反码:0000000000000101 反码:0000000000000000
十进制数-5 原码:1000000000000101 十进制数-0 原码:1000000000000000
反码:1111111111111010 反码:1111111111111111
-
-
使用反码存储数据会产生一个问题:①数值0有两种表示方法;但反码解决了正数负数相加问题 |
-
数值0反码有两种表示方法:
-
+0反码:0000000000000000
-
-0反码:1111111111111111
-
反码解决了正负数相加问题:
-
+5反码:0000000000000101
-
-5 反码:1111111111111010
-
5-5=5+(-5)=0000000000000101+1111111111111010=1111111111111111=-0的反码,则结果为0
-
补码:
-
计算机中存储数据都是以补码的形式,为了解决负数存储的问题
-
对于正数,原码、反码、补码均相同
-
对于负数,其补码为其反码加1
-
补码符号位不变,其他位取反,末位加1,得到原码
-
例:十进制数5和-5;0和-0的原码反码补码是?
十进制数5 原码:0000000000000101 十进制数0 原码:0000000000000000
反码:0000000000000101 反码:0000000000000000
补码:0000000000000101 补码:0000000000000000
十进制数-5 原码:1000000000000101 十进制数-0 原码:1000000000000000
反码:1111111111111010 反码:1111111111111111
补码:1111111111111011 补码:
10000000000000000**(最高位舍弃)**
-
-
使用补码存储数据的意义: |
- 统一了0的编码
- 将减法运算变为加法运算
- 两个用补码表示的数相加,若最高位有进位,则进位被舍弃
3.4 实型数据
3.4.1 实型常量的表示
实型常量又称浮点型常量、实数或浮点数;实型常量的表示采用十进制书写方式有两种 |
-
**小数形式:**由数字和小数点组成;例如:25.0、2.34、-.12、0.0、-.15(123与123.不同,一个是整型一个是实型)
-
**指数形式:**由尾数(可带符号)、阶码标志"e"或"E"以及阶码(只能为整数,可带符号)组成;例如:0.5E7=0.5×10^7、
-2.8e-2=-2.8×10-2、3.E5=3.0×105
- 字母e或E前后必须要有数字,且后面必须为整数
- 字母e或E前后及各数字之间不能有空格
- 一个实数可以用多种指数形式表示
3.4.2 实型常量的类型
C编译系统通常把实型常量当作双精度处理,若要指定一个实型常量为单精度型,可在其后加F或f作为单精度数处理 |
3.4.3 实型变量
3.4.3.1 实型变量的分类
实型变量分为单精度(float)、双精度(double)和长双精度(long double) |
类型标识符 | 比特(位)数 | 有效数字位数 | 数值范围 |
---|---|---|---|
float | 32 | 6~7(一般情况下只保证6位数字正确) | -3.4×1038~3.4×1038 |
double | 64 | 15~16(一般情况下只保证15位数字正确) | -1.7×10308~1.7×10308 |
long double | 64 | 15~16 | -1.7×10308~1.7×10308 |
3.4.3.2 实型变量的定义
float a=1.5,b=2.3,c; /*定义单精度实型变量a,b,c并对a,b初始化*/
double x,y; /*定义双精度实型变量x,y*/
3.4.3.3 实型数据在内存中的存放形式
实数的存储由符号位、指数(阶码)以及尾数(小数)组成 |
对float型数据存储,这三部分共占32位,1位符号位、8位指数和32位尾数;double型占64位,1位符号位、11位指数和52位尾数;指数部分占的位数多,表示的数值范围大;尾数部分占的位数多,代表实型数据的有效数字多,精度就高 |
3.5字符型数据
3.5.1 字符型常量
3.5.1.1 字符常量
C语言中,用单引号括起来的一个字符成为字符常量;如'a'、'='、'_'、'?' |
-
在内存中,每个字符常量都占用一个字节,具体存放的是该字符对应的ASCII码值
-
在C语言中,一个字符常量可以看作整型常量,其值就是对应的ASCII代码值;字符常量还可进行运算,如:‘a’+5=整数值97+5=整数值102
-
字符常量只能用单引号括起来,只能是单个字符,故‘abc’是不合法的
-
C语言对大小写敏感,在任何时候都区分大小写,‘a’与’A’是不同的
-
空格字符表示‘ '表示,不能写成‘’(两个连续的单引号)
-
数字和数字字符是不同的,如1和‘1’,1在内存中存的是1的二进制,‘1’存的是49,即‘1’的ASCII代码值
-
常见的ASCII码值:
字符 数值 A~Z 65~90 a~z 97~122 0~9 48~57
3.5.1.2 转义字符常量
在字符集中,有一类字符无法直接使用键盘输入,或在键盘上没有对应的键,或按下键后不能显示键面上的字符,如回车、退格等,这些称为控制键,因此引入转义字符 |
-
转义字符以反斜杠”\“开头,后跟一个字符、一个八进制或十六进制数值,此形式将反斜杠后的字符或数值转换成了别的含义,称转义字符
-
每个转义字符只当一个字符
-
\ddd:最多有3位八进制数,ddd代表八进制的ASCII码值;如\074,074为八进制数,转为十进制为60,对应ASCII码为"<"
-
\xhh:最多有2位十六进制数,\x表示十六进制字符常量,hh待变十六进制的ASCII码值;如\x74,74为十六进制数,转为十进制为116,对应ASCII码为‘’t”
-
常用的转义字符及其含义:
转义字符 转义字符的含义 \n 换行 \t 横向跳到下一制表位置(相当于Tab键) \b 退格,将当前位置移到前一列 \r 回车,将当前位置移到本行开头 \ \ 反斜杠符“\” \ ’ 单引号字符 \ ‘’ 双引号字符 \a 鸣铃 \ddd 1~3位八进制数所代表的字符 \xhh 1~2位十六进制所代表的字符
3.5.1.3 字符串常量
字符串常量是由一对双引号括起的字符序列;如"china"、"C program"、"1223456" |
-
转义字符也可出现在字符串中;如:
"\\ABCD\\"代表“\ABCD\”、"\101\102\x43\x44"代表“ABCD”
-
双引号是字符串的定界符,在字符串中再使用双引号时必须使用转义字符\ ‘’,如:
"\"ABCD\""代表“"ABCD"”
-
3.5.1.4 字符常量与字符串常量的区别
-
字符常量用单引号括起来,字符串常量由双引号括起来
-
字符常量只能是单个字符,字符串常量可以为空字符串,也可以含一个或多个字符
-
C语言中没有字符串变量,故不能把一个字符串常量赋予一个字符串变量,但可以用一个字符数组来存放一个字符串常量
-
字符常量占一个字节的内存空间,字符串常量占的内存字节数等于字符串中字符数加1,增加的1个字节是‘\0’(ASCII码值为0),作为字符串结束的标志
- ‘a’在内存中占一个字节;
- “a”在内存中占两个字节;
-
空字符串""在内存中占一个字节,存储’\0’
3.5.2 字符变量
字符变量用于存放单个字符常量,使用关键字char定义 |
3.5.2.1 字符变量的定义
char cl='x',c2='y',c3;
char eng;
-
一个字符变量在内存中占一个字节,把一个字符常量放在一个字符变量中时,实际是将该字符常量对应的ASCII码值存到存储单元
- ‘x’的十进制ASCII码值为120,’y’的十进制ASCII码值为121,所以c1,c2两个单元内存放的是120、121的二进制代码
-
这样一来,字符型数据和整型数据之间可以通用,可以对整型变量赋予字符值,也可对字符型变量赋予整型值,输出时,允许按整型或字符型输出
-
整型变量为多字节变量,字符变量为单字节变量,当整型数据按字符型处理时,只有低八位参与处理
例:字符变量的值
#include <stdio.h>
main ()
{
char a=0x1261,b;
b=a-32;
printf("%c,%c\n%d,%d\n",a,b,a,b);
}
- 上面程序中,a,b为字符型变量,但赋予的值为整型,对变量a赋值,赋予的为0x1261中的低八位0x61,转为二进制为01100001,化为十进制数97,对应ASCII码为a,字符型变量b对应十进制数为65,对应ASCII码为A,故输出时,按%c输出分别为a,A;按%d输出为97,65
3.6 算数运算符与算数表达式
标识运算的符号称为运算符,按运算时运算对象个数分为单目运算符,双目运算符,三目运算符几类,运算符和括号把运算量连接起来的符合C语言语法规则的式子称为表达式,凡表达式都有一个值,即运算结果
3.6.1所有运算符优先级和结合方向
优先级 运算符 含义 要求运算对象个数 结合方向 举例
-
() 圆括号 自左向右 (5+3)
[] 下标运算符 a[2]
-> 指向结构体成员运算符 p->data
. 结构体成员运算符 student.xm
-
! 逻辑非运算符 1 (单目运算符) 自右向左 !(a>b)
~ 按位取反运算符 ~a
++ 自增运算符 i++ ++i
– 自减运算符 i-- --i
+ 正号运算符 +3
- 负号运算符 -3
* 取内容运算符 x=*p
& 取地址运算符 p=&x
(类型名) 类型强制转换运算符 (int)(1/2.5)
sizeof 求字节运算符 sizeof(int)
-
* 乘法运算符 2(双目运算符) 自左向右 x=a*b
/ 除法运算符 x=a/b
% 求余运算符 x=a%b
-
+ 加法运算符 2(双目运算符) 自左向右 x=a+b
- 减法运算符 x=a-b
-
<< 左移运算符 2(双目运算符) 自左向右 a<<2
>> 右移运算符 a>>2
-
< 小于运算符 2(双目运算符) 自左向右 a<b
> 大于运算符 a>b
<= 小于或等于运算符 a<=b
>= 大于或等于运算符 a>=b
-
== 等于运算符 2(双目运算符) 自左向右 a==b
!= 不等于运算符 a!=b
- & 按位与运算符 2(双目运算符) 自左向右 a&b
- ^ 按位异或运算符 2(双目运算符) 自左向右 a^b
- | 按位或运算符 2(双目运算符) 自左向右 a|b
- && 逻辑与运算符 2(双目运算符) 自左向右 a>b&&b>c
- || 逻辑或运算符 2(双目运算符) 自左向右 a>b||b>c
- ?: 条件运算符 3(三目运算符) 自右向左 x=a>b?a:b
-
= += *= /= %= 赋值运算符 2(双目运算符) 自右向左 x=2
&= ^= |= <<= >>= x&=a+b
- , 逗号运算符 自左向右 x=(a=1,b=2,a+b)
-
在优先级不同时,运算由运算符优先级决定,优先级相同考虑结合性
-
算数运算符中同级单目运算符结合性从右到左,同级双目运算符结合性从左到右
-
取余运算符两边必须为整型数值,VC中,取余运算结果符号与被除数相同
-
++ --只能用于变量,不能用于常量或表达式;++ --在前,先加减再标;++ --在后,先标后加减;标的是表达式的值
-
赋值运算符的左侧只能是变量不能是常量和表达式
-
C语言规定,在若干个+或-组成的表达式求解时,把从左到右尽可能多的符号组成运算符;例:i+++j为(i++)+j
-
逗号表达式的值就是最后一个表达式的值,求值顺序从左到右依次求解
-
优先级从高到低:
- 初等运算符()、[]、->、.
- 单目运算符
- 算术运算符(先正负后乘除取余后加减)
- 关系运算符
- 逻辑运算符(不含逻辑非运算符!,逻辑非运算符属于第二优先级单目运算符)
- 条件运算符?:
- 赋值运算符=
- 逗号运算符,
3.6.2 数据类型转换
3.6.2.1 自动类型转换
-
算术转换
自动将精度低、表示范围小的运算对象类型转换为精度高、表示范围大的运算对象类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P9V4NVM4-1628919821093)(C:\Users\23175\AppData\Roaming\Typora\typora-user-images\image-20210728141503610.png)]
算术转换规则
-
赋值转换
只有在赋值号右侧表达式的类型与左侧变量类型完全一样时,赋值操作才能进行;若赋值运算符两侧数据类型不同,系统自动将右侧表达式求得数值转换为赋值号左侧变量得类型;低级别数据类型向高级别转换时没有问题,但高级别向低级别数据类型转换会出现问题
-
长整型数据赋给短整型或字符型变量时,高位字节得数据会丢失,赋值号右边的值不能超出左侧变量的数值范围
#include <stdio.h> main() { short a=289; /*变量a为短整型在内存中占2个字节16位*/ char c; /*变量c为字符型在内存中占1个字节8位*/ long b=98304; /*变量b为长整型在内存中占4个字节32位*/ c=a; /*把a的值赋给c,c只占一个字节故只把a的低八位赋给c*/ a=b; /*把b的值赋给a,b占4个字节,a占2个字节故只把b的低16位赋给a*/ printf("a=%d\tc=%d",a,c); /**/ } 输出: a=-32768 c=33
-
将浮点型数据赋给整型变量时,舍去浮点数的小数部分
-
将double型数据赋给float型变量时,截取前面7位有效数字存到float型变量,故右边的数值不能超过左边的数值范围
-
3.6.2.2 强制类型转换
强制类型转换通过类型标识符可以将一种类型的变量转换为另一种类型;其形式为:(类型标识符) (表达式) |
-
类型标识符和表达式都应加括号(表达式为单个变量时可不加括号);如(int)(x+y)
-
无论是强制类型转换还是自动类型转换,都仅为了本次运算的需要对变量的数据长度进行临时转换,不改变该变量原始定义类型
3.6.2.3 计算类型长度运算符
sizeof(运算对象) 运算对象可以是任何数据类型、变量及表达式