0,引入
程序运行时需要把程序文件/可执行文件加载到内存中,期间产生的一些数据和结果都会保存在内存中(内存分配的基本单位是 字节 byte)
c语言提出了数据类型的概念,不同的数据归于不同的类型,如:整数,小数,字符 .....
不同的数据类型有不同的属性,比如:需要的内存大小不一样,存储方式不一样,数据计算的方式也不一样....
1,c语言的数据类型
有4大类数据类型
(1)基本类型
就是c语言中已经定义好了,我们可以直接使用
1.1 整型数据(整数)
short (int) //短整型
int //整型
long (int) //长整型
long long (int) //长长整型
区别在于能够表示的数据大小的范围不一样 -》 占用的内存大小不一样
short 占2字节(16bit)
int 占4字节
long 4或者8字节 在32位机器上占4字节,在64位机器上占8字节
long long 8字节
如果记不住怎么办?
sizeof(int) -> 求int占几个字节
unsigned / signed -》用来修饰整型数据的
表示无符号和有符号数据
如:
int <-> signed int //有符号整型 int其实就是 signed int,但是一般省略这个 signed
unsigned int //无符号整型
无符号整型:这个数据的所有 bit都是数据位
有符号整型:最高bit位为符号位,剩下的才是数据位
符号位为0 表示这个数是正数
符号位为1 表示这个数为负数
1.2 字符型数据:用来保存字符
char 占1字节
1.3 浮点型 :用来保存小数
float 占4字节
double 占8字节
long double 占16字节
(2) 构造类型:需要我们程序员自己去定义
数组
结构体
共用体
枚举
(3) 指针类型
(4) 空类型 void ,一般用于函数返回值位置,表示该函数没有返回值
c语言程序中的数据按照是否可以被修改(是否可写)分为两种:
常量(不可修改/ 不可写)
变量(可以修改/可写)
2,常量
在程序的运行过程中不能被修改的数据
常量也有多种数据类型
(1)整型常量
以0开头的:是八进制,在代码中由一段数字组成 [0-7]
如:
0231
0654
0768 //error
以0x/0X 开头的是十六进制,是由 数字 [0-9]+[a/A-f/F]这16个组成
如:
0xab
0x12f
0x78d
....
以0b/0B开头的是二进制 (不是所有平台都支持)
如:
0b1010100
其它的就是十进制
123 默认是int类型
123 l 后缀为l的是 long 类型
123 u unsigned int 类型
....
换算规则:
十进制换算成二进制
除二取余法:一直除2,直到商为0,然后再反过来取每一次的余数
25 -》 0b11001
十进制换算成八进制
除八取余法
25 -》 031
十进制换算成十六进制
除16取余法
25 -》0x19
二进制换算成十进制
假设从右到左依次为 n0 n1 n2 n3 ....
n0*2^0 + n1*2^1 + n2*2^2 + .......
如:
0b011100
1*2^2 + 1*2^3 + 1*2^4 = 28
在这里用^表示次方,但是c语言并没有次方这个符号,^也不次方
0b0011011 = 27
八进制换算十进制
0123
3*8^0 + 2*8^1 + 1*8^2 = 3+2*8+1*64 = 3+16+64 = 83
十六进制换算成十进制
0x123
3*16^0 + 2*16^1 + 1*16^2 = 3+32+256 = 291
二进制换算成八进制
常规方法:先换算成10进制,再换算成八进制
0b100110 = 38 = 046
二进制的每3位对应八进制的一位,注意要从低位开始
0b 001 001 100 = 0114
1 1 4
二进制换算成十六进制
0b0 0100 1100 = 0x4c
八进制换算成二进制
023 = 0b010011
十六进制换算成二进制
0x23 = 0b00100011
(2)字符常量
字符常量是用单引号 '' 引起来的一个字符
如: '1' 'x' '%' ....
在计算机中怎么保存一个字符呢?
不是保存这个字符的形状,而是保存这个字符的 编码 :ascii码
编码:一个字符对应一个数字
除了 ascii码之外还有很多其它编码,课后可以去了解一下
'0' 48
'a' 97
'A' 65
字符有两种:
普通字符:有形状,可以显示出来
特殊字符:没有形状,但是有特殊含义
'\n' 换行
'\t' tab
'\0' 字符串结束标志 ascii 为 0
...
字符可以有多种表示方式
如:
'A'
65
'\***' :由\开头,后面接一个八进制数字
这个八进制数字就是该字符的 ascii码的八进制值
'\101' <-> 'A'
'\x**' :由 \x 开头,后面接一个十六进制数字
这个16进制数字就是该字符的 ascii码的16进制值
'\x41' <-> 'A'
(3) 浮点型常量 :小数
可以有后缀 f/F l/L
l/L long double
f/F float
没有后缀默认是double
1.5 -> double
1.5f -> float
1.5l -> long double
科学计数法:
123.456 -> 1.23456 * 10^2
由整数部分,小数点,小数部分,一个 e/E ,一个可选的带符号的整型指数,一个可选的
后缀
123.456 -> 1.23456e2
0.0001234 -> 1.234e-4
(4)符号常量(宏定义)
#define X 123
#define PI 3.1415926
3,变量
变量是指在程序的运行期间,其值可以被修改
int a = 10;
a = 20;
a = 19;
....
变量是用来保存数据的,那么肯定需要合适大小的内存空间,需要多大的空间呢?
由这个变量/数据的类型决定
所以我们在定义变量的时候,必须指定它的数据类型
定义变量的语法格式如下:
数据类型 变量名;
或者
数据类型 变量名 = 初始值;
如:我想定义一个变量用来保存整数,那么就得定义一个整型变量
int a;
int b = 60;
注意:变量名不能随便取,要遵循一定的规则:只能由字母、数字、下划线组成,并且不能以数字开头
并且不能是c语言的关键字
int a;
当程序运行到这行代码的时候,会分配内存空间(4字节),并且记录了这个变量名 a 和这4个字节的对应关系
a = 10;
当程序运行到这行代码的时候,会把数据10写入变量a对应的4字节内存中去
printf("a=%d\n",a);
从a对应的4字节中读取数据并打印输出
-----------------------------------------
int b = 20;
当程序运行到这行代码的时候,会分配内存空间(4字节),并且记录了这个变量名 b 和这4个字节的对应关系,
并把20这个数据写入到这4字节内存中
最终效果和
int b ;
b=20 ;//赋值语句
这两行代码一样,但是有不同说法:
int b = 20; //初始化
int b;//定义变量
b = 20;//赋值
-------------------------------------------
变量名到底怎么理解?
int a ,b;
a = 20; (1)
printf("a=%d\n",a); (2)
b = a; (3)
a = b; (4)
请问前面4行代码中的a怎么理解?
在c语言中,对变量的访问,本质上都是两种形式
读 就是从变量对应的内存中读取数据 (2) (3)
写 就是把数据存储到变量对应的内存中 (1) (4)
在c语言中变量名,也有两种含义
1,代表变量的值 -》 右值
对应 (2) (3)
2,代表变量的地址(内存单元编号) -》左值
对应 (1) (4)
4,整型数据在内存中存储形式 (包括 char)
因为 char 的本质是存储 字符的 ascii码, ascii码本身就是一个整数
所以 -》 char 也可以归纳到整型中
整型数据在计算机是以二进制补码的形式存储的
一个整数
原码:把它换算成二进制
如: 100
0 00..0000 0110 0100
-100
1 00..000 0110 0100
反码:把数据位全部取反
补码:正数的补码是原码本身
负数的补码是绝对值的原码取反加1
如: 100
绝对值原码 000..0000 0110 0100 《- 100的补码
-100
绝对值原码 000..0000 0110 0100
取反 111..1111 1001 1011
加一 111..1111 1001 1100 《- -100的补码
练习:
int a = 50; 00..000 110010
int b = -6;
请计算a,b的补码
绝对值原码 00..00 0110
取反 11..11 1001
加一 11..11 1010
10 + (-5) = 5
如果存储的是原码:
00..000 1010
10..000 0101
-------------------
10..000 1111 -> -15 结果错误
现在存储的补码
00..000 1010
11..111 1011
------------------
1 00..000 0101
最高位的1保存不了,丢弃。
所以最终的结果
00..000 0101 -> 5 结果正确
练习:
求 unsigned int a = 4294967290 的补码
1111111111111111111111111111 1010
int b = -6 的补码也是
1111111111111111111111111111 1010
int b = -6;//分配了4字节的内存(32bit) 依次存储 11111111111111111111111111111010
//我们又知道, unsigend int 类型的 4294967290 也是这么存储的
//那么这4字节内存里面的值到底是多少? 取决于你怎么解析它
//如果你把它当作 unsigned int 来解析,那就意味着32bit全部都是数据位,没有符号
//通过这个补码直接可以算出值(二进制换算成10进制): 4294967290
//如果你把它当作 int 来解析,那就意味着最高位位符号位,其余才是数据
//先看符号位,发现是1,说明是负数,然后按照负数求补码的过程反过来计算
//先减一 11111111111111111111111111111001
//再取反 00000000000000000000000000000110
// 所以它的绝对值为 6 -》这个值本身是 -6
内存中有4个字节存储的是整数,二进制补码为
1111111111111111111111111101 1010
请问这个值可能是多少??
如果是无符号数据,直接换算
4294967258
如果是有符号数据,先看符号位,符号位是1,说明是负数
.....
-38
内存中有4个字节存储的是整数,二进制补码为
0111111111111111111111111101 1010
请问这个值可能是多少??
如果是无符号数据,直接换算
2147483610
如果是有符号数据,先看符号位,符号位是0,说明是正数,直接换算
2147483610
printf();函数
%d 打印 int
%u 打印 unsigned int
%hd 打印 short
%hu 打印 unsigned short
%ld 打印 long
%lu 打印 unsigned long
%c 打印 char
%s 打印字符串
%f 打印 float
%lf 打印 double
一个 unsigned int 和 int相加减,结果默认是 unsigned int
short + int -> int
int + double -> double
...
unsigned int a = 6;
int b = -20;
if(a+b > 6)
{
printf("a+b>6\n");
}
else
{
printf("a+b<=6\n");
}
字符的存储:
char c = 'A';
'A' 的 ascii是65,二进制补码是 01000001
printf("%c\n",c);//输出 A
printf("%d\n",c);//输出 65
5,浮点型存储形式
先换算成二进制,再用科学计数法表示
如:
8.25
换算成二进制的方法是: 整数部分除二取余,小数部分乘二取整(直到小数部分为0,取整的时候
顺着取)
8 -> 1000
0.25 -> 01
-》 1000.01 -> 1.00001*2^3
13.75 1101.11 -> 1.10111*2^3
任何小数都可以用 1.xxx * 2^n表示
1和小数点和2都是一样的,不一样的 符号位 和 xxx(尾数部分) 和 n(指数部分)
只需要存不同的东西: 符号位 指数部分 尾数部分
符号位 指数部分 尾数部分
float 1bit 8bit 23bit
double 1bit 11bit 52bit
符号位 : 为0表示正数,为1表示负数
指数部分存储: 指数部分+127 再换算成2进制
尾数部分: 把尾数部分换算成二进制,然后低位补0(补满23bit或者52bit)
8.25 1.00001*2^3
符号位 指数部分 尾数部分
0 3+127 (10000010) 00001 补18个0
->
8.25占4字节在内存中存储形式是:
0 10000010 00001000000000000000000
13.75
0 10000010 10111000000000000000000
6,整数之间的赋值问题
int a = -6; // 11111111111111111111111111111010
short b = 100;
(1) 长的赋值给短的(溢出)
b = a; //b 变量16bit 分别是 1111 1111 1111 1010
高位丢弃,保留低位
printf("%hd\n",b);//输出 -6
printf("%hu\n",b);//输出 65530
练习:
short a = 0x4142;
char c = a;
printf("%c\n",c);//请问输出什么? B
(2) 短的赋值给长的
a = b;
短的直接赋值给长的的低位
高位补什么?
如果短的是无符号数据,长的高位全部补0
如果短的是有符号数据,长的高位全部补短的的符号位
int a = -6;
short b = 100;// 0000 0000 0110 0100
a = b; // 高位补b的符号位0 00..0000 0000 0000 0110 0100
int a = -6;
short b = -100;// 1111 1111 1001 1100
a = b;// 高位补b的符号位 1 11..1111 1111 1111 1001 1100
int a = -6;
unsigned short b = 100;
a = b; // 高位直接补0 00..0000 0000 0000 0110 0100
int a = -6;
unsigned short b = -100;// 1111 1111 1001 1100
a = b;// 高位直接补0 00..0000 1111 1111 1001 1100
练习:
1,
short int a = -6; //1111 1111 1111 1010
int b = a;//11..111 1111 1111 1111 1010
printf("%d\n",b);// -6
printf("%u\n",b);// 429496....
2,
unsigned short int a = -6;//1111 1111 1111 1010
int b = a;//00..000 1111 1111 1111 1010
printf("%d\n",b);//65530
printf("%u\n",b);//65530
3,
short int a = -6; //1111 1111 1111 1010
printf("%d\n",a);// %d是当作 int打印,会先把 a赋值给/转换为 int类型,再进行打印
//效果和第一种情况一样
printf("%u\n",a);// 也和第一种情况一样
4, char c = 358;
358是一个int类型的常量,00..0000 0001 0110 0110
c是char类型,
-》长的赋值给短的 ,高位丢弃,保留低8位
c : 0110 0110 102
printf("%c\n",c);// f
printf("%d\n",c);//c是 char类型,当作 int 来打印,意味着短的赋值给长的
//高位补符号位 0 00..000 0110 0110 -》 102