概述
数据类型
0.问题的引入
我们知道计算机是人类用来解决某些问题的一种工具
那么计算机是通过什么来解决问题的呢?
计算机是解决哪一类的问题?“计算” computer
计算机通过把问题域的数据保存起来,然后通过某些运算从而得到结果。
程序=算法+数据结构
计算机首先要解决数据保存的问题,在数据保存之前,我们首先要知道
这个数据的大小、取值范围、...等等数据的属性,不然我们怎么知道开辟
多大的空间保存这些数据呢?
数据的大小、取值范围...这些数据的属性,都是我们“数据类型”要讨论的问题
1.数据类型
就是数据的类型。 typeof(x) ==>求对象x的类型
typeof(3) ===> int
typeof(3.0) ===> double
...
在C语言,有如下类型:
1)基本类型:c语言已经为我们定义好的类型。
系统分配给基本类型的变量的内存大小是固定的。
主要用来保存数(整数/浮点数)
整数类型
char/unsigned char
short/unsigned short
int /unsigned int
long/unsigned long
...
上面这些基本都是用来保存整数的,有什么区别呢?
a.signed/unsigned
有符号(signed):符号位(最高位)+数值位
1 -> 负数
0 -> 0或者整数
无符号(unsigned):数值位
b.char /short /int ...
所占的空间大小不一样
char->8bits
unsigned char->8bits
unsigned char 能保存的数值的范围是[0,255]
unsigned char a = 260;//256 1111 1111 0000 0100
a ?4
short:一般16bits
int:32bits的机器下,一般为32bits
long:32bits的机器下,一般也为32bits
...
而且在不同的编译器,同一个类型的范围也不一样
如:
keil
int 16bits
ubuntu 18.04 32bits
int 32bits
sizeof(x)求类型x的所占的字节数
sizeof(char)=1 8bits
sizeof(long)>=sizeof(int)>=sizeof(short)>=sizeof(char)
在GCC编译器,整数默认类型是int
typeof(3) ==> int
int a;
typeof(a)==>int
typeof(3+4)==>int
typeof(3+1.0)==>double
"自动向高精度转”==>“自动向所占空间大的转”
浮点数
float :单精度浮点数
double:双精度浮点数
long double:长双精度浮点数
区别?
所占的空间大小不一样,保存精度也不一样
一般来说:
sizeof(float) == 4
sizeof(double) == 8
C语言中,浮点数默认类型就是double
typeof(2.0)==>double
sizeof(2.0f)==>float
typeof(2.0+4)==>double
2)构造类型:C语言允许程序自定义类型
系统分配给构造类型的变量的内存大小,决定改类型具体是怎么定义的。
数组:一组相同元素的数据集合
int a[10];//定义了一个数组,数组名为a,里面有10个int元素
int b[100];//定义了一个数组,数组名为b,里面有100个int元素
typeof(a) 数组
a是一个里面包含10个int元素的数组
==>int[10]
typeof(a)==>int[10]
typeof(b)
b是一个里面包含100个int元素的数组
==>int[100]
typeof(b) => int[100]
int c;
c是一个int类型的
typeof(c) => int
结构体
联合体
枚举
3)指针类型
先等等 后面会专门讲
4)void类型
void在C语言中,有三个作用
a.void*
通用指针
b.void当作是函数的形参,表示该函数不需要带参数
如:
int func(void)
{
}
调用func();
c.void当作是函数的返回值类型,表示该函数无返回值。
如:
void abs(int a)
{
}
调用abc:
int x=abc(3);//有问题的!!!!
在研究了数据类型后,下面要保存数据啦。
在C语言中数据有两类:
1)变量
2)常量
2.变量
变量是什么?在程序运行期间,可以改变其值的数据对象。
存储变量会对应一个储存单元。
并且这个储存单元一定是可写(我们要改变这个变量的值)
2.1变量的定义
变量在使用前必须要定义的。
语法:
变量的类型 变量名 {=变量的初始值};
{}可选
变量的类型:
所有的合法的类型都可以。
变量名:
一个对象的名字,标识符。
标识符:用来表示一个东东的符号。
标识符其实是一个对象(“如:数组 变量 函数 标号 类型 ...”)
的名字。
在C语言中,取名字不能乱取。要复合一定的规则,这个规则标识符的规则
标识符:
只能以字母,下划线,数字组成
第一个字符不能以数字开头
why?
标识符 “名如其意”
int sum;
int he;
int sum_array;
int he_shuzu;
变量的初始值:
定义变量时,赋值给变量的值,初始值。
例子:
int a;
printf("%dn",a);//程序的输出结果是多少?undefine
2.2 变量的属性
变量的类型
变量的名
变量的存储单元(变量的地址)
在程序运行时,系统会为每一个变量分配一个储存空间,用来
保存这个变量的值。并且这个储存空间会有一个唯一的地址,
这个地址,称之为变量的地址。
变量的值:
变量的存储单元中的内容(无非就是高电平/低电平)
每个变量一定会有一个确定的值(不管你有没有赋值)
why?因为储存单元中 bit要是1/0
bit 计算中存储数据最小的单位,它只有两种状态
1-->高电平
0-->低电平
2.3变量的访问
读/写
读:
从变量的存储单元中读取存储单元中的内容。
读变量的值。
写:
把一个数值写到变量所对应的储存单元中去。
“赋值”
例子:
int a = 5;
a=1024;//把1024这个值写到变量a所对应的地址中去。 【1】
b=a*5;//把a的值乘以5,赋值给b 【2】
上面1和2同样是一个a,但是它们的含义却是不一样的!!!!
结论:
在C语言中,任何变量,都有且仅有两层含义:
1)代表变量的地址
lvalue
location value 可寻址(可写)的值
left value 左值
2)代表变量的值
rvlaue
readable value 可读的值
right value 右值
变量的左值:
变量的地址,“=”的左边
变量的右值:
变量的值,“=”的右边
3.整数的存储问题
整数在计算机是如何存放的呢?
整数以二进制的补码的形式存放的。
课后:你们知道计算机中为什么要用补码?
1111 1111 2^8-1
2^7 +.... 2^1 2^0
正数
正数的补码就是其原码本身
原码:就是把相应的数值转换为二进制。
13:8bits
13 = 8+4+1
0000 1101 <----13的原码 也是13的补码
9:8bits
9=8+1
0000 1001 <---9的原码 也是9的补码
正数的补码其原码本身
int a=9;
00000000 00000000 00000000 00001001
负数:
负数的补码是其绝对值的原码 取反 +1 得到
-13 bits
|-13|=13 原码
0000 1101 <----绝对值的原码
1111 0010 <----取反
1111 0011 <--- -13(8bits)的补码 -13在计算中的存储形式 8bits
练习:
假设 8bits来存放一个整数
-2 的存放形式什么?
254的存放形式是什么?
1111 1110
-3的存放形式是什么?
253的存放形式是什么?
1111 1101
-4的存放形式是什么?
252的存放形式是什么?
...
直到你们找到规律或者下课为止
结论1:
一个负整数会和一个比较大的正整数的补码形式一样的;
一个负整数会和一个比较大的正整数在计算机中存放的形式一样的。
-x 和 2^n-x 一样的
2^n 表示2的n次幂
n表示什么意思?用多少Bits来存储一个整数!!!
n=8
-37 和256-37
结论2:
CPU内部是没有符号位的概念,对于CPU来说,所有的Bit位
都是数值位,都参与运算。至于是由符号的,还是无符号数
就得看编译器的词义啦(意思就是说,你把它当做是一个有
符号数,还是无符号数)。
练习:假设int 32bits
1)假设计算机中用8bits来存放一个整数,已知某个整数
在计算机中的存放形式如下:
1111 1110
请问这个整数使多少?
-2 或者 254
到底是-2还是254
就得看你把它当做是一个有符号(signed)还是一个无符号数(unsigned)
unsigned :
1111 1110
所有的Bits位都是数值位
254
signed:
符号位 数值位
1->负数
0->正数
1111 1110
最高位是1,表示它是一个负数
1111 1110是负数的补码
已知一个负数的补码,如何求这个负数呢?
绝对值的原码->取反->+1 ==>负数的补码
逆运算
负数的补码-->-1-->取反-->绝对值原码
1111 1110
-1
1111 1101
取反
0000 0010
该负数的绝对值是2
==>该负数是-2
2)分析如下程序的输出结果
int a=-2;
printf("%dn",a);//-2
printf("%un",a);//2^32-2
%d把后面的玩意,当做一个有符号的int,按十进制输出
%u把后面的玩意,当做一个无符号的int,按十进制输出
3)分析如下程序的输出结果
int a=-56;
printf("%dn",a);//-56
printf("%un",a);//2^32-56
4)分析如下程序的输出结果
unsigned int a=-1u;//u->unsigned 无符号 昨天有个3.0f 一样的道理
//unsigned int a=-1;
-1
00000000 00000000 00000000 00000001
11111111 11111111 11111111 11111110
11111111 11111111 11111111 11111111
printf("%dn",a);//-1
11111111 11111111 11111111 ==> 有符号数
最高位1 负数
11111111 11111111 11111111 11111110
00000000 00000000 00000000 00000001
-1
逆运算 -1
printf("%un",a);
11111111 11111111 11111111 11111111==》无符号数 全部都是数值位
2^32-1
5)分析如下程序的输出结果
unsigned int a = 1<<31;
// 1101 1100 << 4 ==>1100 0000
a:
00000000 00000000 00000000 00000001 << 31 ==>
10000000 00000000 00000000 00000000
printf("%dn",a);//-(2^31)
10000000 00000000 00000000 00000000 ==> %d
最高位1
负数
-1:01111111 11111111 11111111 11111111
取反:10000000 00000000 00000000 00000000
2^31
-(2^31)
printf("%un",a);//2^31
10000000 00000000 00000000 00000000 ==>数值位
=========================================================================
一个特定整数类型的取值范围的问题
GUN有一个标准的头文件 stdint.h (/usr/include)
int8_t //有符号的8bits整数类型
uint8_t //无符号的8bits整数类型
uint8_t
[0,255]
int8_t
max
0111 1111 127
min
1000 0000 -128
/*宏定义*/ 表示有符号数整数的最小值和最大值
# define INT8_MIN (-128)
# define INT16_MIN (-32767-1)
...
# define INT8_MAX (127)
# define INT16_MAX (32767)
...
===========================我是华丽的分割线 ================================
char c = 250;
char d;
d=c+8;
printf("d=%dn",d);//2
printf("d=%un",d);//2
---------------------------我是华丽的分割线--------------------------------
4.整数之间的赋值问题
在C语言中允许不同类型的整数之前相互赋值的
char -> int
int -> char
unsigned int -> int
...
有一个问题,不同类型的整数,存储空间大小不一样
char 8bits
int 32bits
这个问题怎么解决呢?
C标准建议:
(1)长-->短
长的赋值给短的,低字节直接拷贝
高字节全部discards(丢弃),没办法
int -> char
int -> short
short -> char
...
(2)短-->长
短的赋值给长的,低字节直接拷贝
高字节补什么?
如果短的是无符号的,高位就全部补0
如果短的是有符号的,高位就全部补符号位。
char c = 250;
char d;
d=c+8;
c:1111 1010
c+8 ==>typeof(c+8) int
int + int
c(char)-->32bits: 短的-->长的
11111111 11111111 11111111 11111010
00000000 00000000 00000000 00001000 8
100000000 00000000 00000000 00000010
d->char 长的-->短的
00000010
printf("d=%dn",d);//2
短-->长
printf("d=%un",d);//2
练习:
1.分析以下程序的输出结果
unsigned char c = 250;
c->1111 1010
char d;
d =c +8;//c-->自动往高精度转 char->int
c->32bits:短的->长的 低字节直接拷贝 高字节(短的是无符号补0)
c->0000 0000 0000 0000 0000 0000 1111 1010
8->0000 0000 0000 0000 0000 0000 0000 1000
0000 0000 0000 0000 0000 0001 0000 0010
长的->短的 低字节直接拷贝 高字节丢弃
0000 0010
printf("d=%dn",d);//2 短->长0000 0000 0000 0000 0000 0000 0000 0010
printf("d=%un",d);//2 0000 0000 0000 0000 0000 0000 0000 0010
---------------------
char c = 250;
int d;
d=c+8;
//00000000 00000000 00000000 00000010
printf("d=%dn",d);//2
printf("d=%un",d);//2
--------------------
unsigned char c = 250;
int d;
d=c+8;
//0000 0000 0000 0000 0000 0001 0000 0010
printf("d=%dn",d);//258
printf("d=%un",d);//258
2.分析以下程序的输出结果
char c = -3;
printf("c=%dn",c);//-3
printf("c=%un",c);//2^32-3
c:
0000 0011
1111 1100
1111 1101<-- -3在计算机中存储方式(8bits)
%d:把后面那个对象,转换成signed int来输出
c->signed int 短的赋值给长的
1111 1111 1111 1111 1111 1111 1111 1101
逆运算
-1 1111 1111 1111 1111 1111 1111 1111 1100
取反 0000 0000 0000 0000 0000 0000 0000 0011 3
负数 -3
%u:把后面的那个对象,转换成unsigned int来输出
c->unsigned int 短的赋值给长的
1111 1111 1111 1111 1111 1111 1111 1101
位值*权值之和 2^31*1+2^30*1+....
+2
32个1 2^32-1-2
2^32-3
---------------------
unsigned char c = -3;
1111 1101<-- -3在计算机中存储方式(8bits)
printf("c=%dn",c);//253
短->长 c是无符号 高位补0
0000 0000 0000 0000 0000 0000 1111 1101
253
printf("c=%un",c);//253
短->长 c是无符号 高位补0
0000 0000 0000 0000 0000 0000 1111 1101
253
"溢出“问题
“溢出”不是爆炸,相应的“溢出”会有一个确定的值!!!
5.常量
常量是指在程序运行期间,其值不能改变的数据对象。
常量在代码中有多种情况:
(1)整型常量:在代码文本中,代表整数的常量值。
八进制常量
0[0-7]*
以字符0开头后面接0个或者多个0-7的字符
如:
0123
0777
0134473
088 ERROR
int a = 0123;
printf("%dn",a);//83
//3*8^0+2*8^1+1*8^2=3+16+64=83
八进制与二进制对应关系
一个八进制对应三个二进制位
八进制 二进制
0 000
1 001
2 010
3 011
4 100
5 101
6 110
7 111
十六进制常量
0[xX][0-9A-Fa-f]+
以0x或者0X开头后面接一个或者多个0-9A-Fa-f
int a = 0xf3;
十六进制与二进制位的对应关系
一个十六进制对应四个二进制位
十六进制 二进制
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
a/A 1010
b/B 1011
c/C 1100
d/D 1101
e/E 1110
f/F 1111
255 ->0xff
十进制常量
[0-9]+
(2)字符常量
字符常量是用单引号引起来的一个或者多个字符的序列。
如:
'a'
'b'
'n'
'r'
...
在计算机中,保存一个字符,保存是字符的ASCII码,而不是它的形状。
ASCII:America standard code for Information Interchange
美国把每一个字符给一个唯一的整数值来表示它们,这个整数值
就是我们称之为的ASCII码。由于美国使用的字符不超过256个,
所以我们这个整数值只需要8bits就可以保存啦。
man ASCII
ASCII码 -> char /unsigned char
OCT 八进制
DEC 十进制
HEX 十六进制
Oct Dec Hex Char Oct Dec Hex Char
────────────────────────────────────────────────────────────────────────
000 0 00 NUL '