# 数据类型
## 问题的引入
电脑 外国人: computer
计算机是我们人类用来解决某些问题的一种工具而已。那么计算机是通过什么方式来解决我们的问题的呢??
计算
计算机是通过把问题域里面数据保存起来,然后对数据进行一系列的操作(计算),从而达到我们的目的。
程序:数据 + 逻辑
程序:数据结构 + 算法
也就是说,计算机实现要解决的是数据的保存问题,在保存数据之前呢???我们要了解这个数据大小,类型,范围.....
如果我们连这些东西都不知道的话,我们根本没办法在计算机中保存这些数据。
## 最基本的数据类型!
在C语言中,仅仅就4种数据类型 ---- 整型,浮点型,指针,聚合类型(数组和结构),其他的类型都是从者四种类型种派生出来的
### 基本类型
C语言中已经为我们定义好的类型,我们可以直接拿过来使用!
主要针对的是:“数”:整数,浮点数
char/unsigned char :1 byte = 8 bits 0000 0000
unsigned char 0 ~ 255
char : -128 ~ 127
在不同的编译器 , short int long 他们占的字节数不一样
short/unsigned short
int/unsigned int
long/unsigned long
sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long)
eg:
在gcc ubuntu18.04中
sizeof(short) = 2
sizeof(int) = 4
sizeof(long) = 8
float 单精度浮点型 一般占4字节
double 双精度浮点型 一般是占8字节
long double 长双精度浮点数 >= 10个字节
在C语言中,整数默认的类型就是int , 小数默认就是double
typeof(1) = int
typeof(1.0) = double
练习:
测试一下你Ubuntu中 int short long 的字节数到底是不是这个!
### 构造类型 : 自定义的类型
C语言中,允许我们程序员自定义类型!!
//数组
int a[4]; //what is a type ??? a 是一个含有4 个 int 类型元素的数组 int[4]
int b[10]; //int[10]
那么请问a和b是同一种类型吗??? B
A 一样
B 不一样
C 不知道
typeof(a) : int[4]
typeof(b) : int[10]
typeof(a) : int nononon
typeof(1) : int
//结构体
//联合体
//枚举
### 指针类型
先 wait wait 后面会有专题来讲这个知识
### void
空类型。 在C语言中,void总共会出现在三个地方:
(1)、void当函数的返回值类型,表示该函数没有返回值
void xxx(int a)
{
}
调用:xxx(1);
(2)、void 当函数的参数,表示该函数没有参数
void xxx(void)
{
}
调用:xxx();
(3)、void * 表示通用指针
void * xxx(void)
{
}
## 数据对象之变量
数据对象分为两类:
变量:在程序运行期间,其值是可以被改变的数据对象
常量:在程序运行期间,其值不能被改变的数据对象
### 变量的定义
变量在使用之前,需要定义。 ”先定义,后使用“
定义变量的语法:
变量的类型 变量名 = {初始化};
{}是可选的!
变量的类型:
任何C语言合法的类型都可以
基本类型/构造类型/指针类型/void *
变量名:(见名知其意)
”标识符“ : 在C语言的标准里,标识符必须由字母,下划线,数字组成,并且
第一个字符必须是下划线和字母
标识符 : 标识某个对象的符号,某个对象的名字,变量名,数组名,函数名.....
eg:
zxf520
520zxf NO
_520zxf
zxf
eg:
int sum = 0;
int int = 0; //变量名不能是C语言的关键字
int s ;
讨论一个问题?
int s;
printf("s = %d\n",s);
请问上面这两行代码,有没有毛病??或者说这两行代码能不能正常的输出一个结果?
不同的编译器,结果都会不一样!
一个变量,你没有初始化,不代表他没有值!
相反他有一个值,只不过这个值你不知道而已!(gcc 是 0)
### 变量的属性
int a = 5;
变量名
变量的存储单元:
程序的运行期间,会为每一个变量,分配一个大小合适的存储单元。
存储单元按照字节的大小,编号。这个编号,称之为存储单元的地址
变量的存储单元会有一个唯一的编号,变量的地址
变量的值:
变量存储单元中的内容
1 byte = 8 bits
保存其实是一个”电压值“
高电平就是 1
低电平就是 0
### 变量的访问
你定义的变量,肯定是想要去使用这个变量的
read / write
a = 5 ; //把数值5 存储到 变量a 的地址中去
b = a ; //把变量a的值 赋值给 变量b
在C语言中,任何一个变量 都有两层意思:
(1)、左值:代表是变量的地址
lvalue : left value
location value :可写的地址
(2)、右值:代表是变量的值
rvalue : right value
readable value : 可读的值
## 整型变量
### 整型在存储器中是如何存放的??
int a = 5;
整数在计算机中是以二进制的补码形式存放的
补码:
正数的补码就是其原码的本身
eg:
13 : 8bits
13 = 8 + 4 + 1
13 = 2^3 + 2^2 + 2^0
0000 0000
0000 1101 -----> 13 在计算机中存放的形式 (8bit)
负数的补码:
其 绝对值的原码 取反 +1 得到的
-13 : 8 bits
0000 1101
1111 0010 取反
1111 0011 -----> 13 在计算机中存放的形式(8bits)
#### 练习
假设用8bits存储 一个整数
1、-1和255在计算机中存放的形式是什么??
-1 :
1:
0000 0001
1111 1110
1111 1111 ----》 就是-1在我们计算机中存放的形式
255:
1111 1111 ----》255 在我们计算机中存放的形式
2、-2和254在计算机中存放的形式是什么??
-2 :
1111 1110
254:
1111 1110
3、-3和253在计算机中存放的形式是什么??
......
结论:
1、一个负整数会和一个比较大的正整数的补码形式是一样的!
-4 和 252 的补码形式是一样
-x 和 2^n-x 的补码形式是一样
32bit
11111111 11111111 11111111 11111111
请问这个整数是多少?
2^32-1
-1
2、在CPU底层是没有符号位的概念,都是数值位,都参与了运算,至于这个数是正数还是负数,都要看编译器的意思
也就是说你这个小卡拉米想要这个数是一个有符号的数,还是一个无符号的数
编译器符号数(逻辑层面)
有符号数
符号位(最高位) + 数值位(其他位)
1 负数
0 正数
无符号数
数值位(全部)
#### 练习
假设 int 是32 bits
(1)、分析如下的程序的输出结果
int a = -3;
3:
00000000 00000000 00000000 00000011
11111111 11111111 11111111 11111100
11111111 11111111 11111111 11111101 ---- -3在计算机中存放的形
式,也就是变量a存储单元里面就是保存的这个小玩意
printf("%d\n",a); //-3
printf("%u\n",a); //2^32-3
%d : 把后面那个玩意(a)当成一个signed int 有符号的数来看,输出(十进制)
%u :把后面那个玩意(a)当成一个unsigned int 无符号的数来看
(2)、分析如下的程序的结果
unsigned int a = -4u;
printf("%d\n",a); //-4
printf("%u\n",a); //2^32-4
typeof(-4) : int
typeof(-4u) : unsigned int
-4/-4u
00000000 00000000 00000000 00000100
11111111 11111111 11111111 11111011
11111111 11111111 11111111 11111100 ===>-4在计算机中存放的形式,也就是变量a存储单元里面就是保存的这个小玩意
## 格式说明符
%d : 用于打印或者扫描有符号的十进制数(int)
%u : 用于打印或者扫描无符号的十进制数(unsigned int)
%f : 用于打印或者扫描浮点数(float)
%lf : 用于打印或者扫描双精度浮点数(double)
%c : 用于打印或者扫描字符 (char)
%s : 用于打印或者扫描字符串(char *)
%p : 用于打印指针的值 (void *)
%x : 用于打印或者扫描无符号的十六进制的整数(unsigned int)
%o : 用于打印或者扫描无符号的八进制的整数(unsigned int)
%e : 用于打印或者扫描以指数表示的浮点数(科学计数法)
%ld : 用于打印或者扫描有符号长整型
%lu : 用于打印或者扫描无符号长整型
%g : 用于打印或者扫描浮点数,自动选择以指数或者十进制表示(自动选择%e %f)
%10d: 指定你打印一个宽度为10的整数字段
%.2f: 指定打印一个浮点数,并且保留两位小数
## 常量
在程序运行期间,其值是不可以被改变的
### 常量的形式
#### 整型常量
在代码文本中,代表整数的常量值
八进制整型常量
0[0-7]*
以字符0开头后面接0个或者多个0-7的字符
如:
0123
0777
0123456789 ERROR
088 ERROR
01234567
int a = 0123;
printf("%o\n",a);
printf("%d\n",a);
八进制和二进制的关系
一位八进制数字对应三位二进制的数字
八进制 二进制
0 000
1 001
2 010
3 011
4 100
5 101
6 110
7 111
十六进制整型常量
0[X/x][0-9a-fA-F]
以字符0x/0X开头接一个或者多个十六进制的字符
如:
0xff
0x123
0x89a
0x23G error
int a = 0x23;
printf("0x%x\n",a);
printf("%d\n",a);
十六进制与二进制的关系
一位十六进制对应4位二进制
十六进制 二进制
0 0000
1 0001
2 0010
3 0011
4 ..
5
6
7 ..
8
9 ..
a 1010
b 1011
c 1100
d 1101
e 1110
f 1111
十进制
略
#### 字符常量
字符常量是 用 单引号 引起来的一个或者多个字符序列
eg:
'a'
'\n'
'\x12'
在计算机中,保存一个字符,保存的是字符的ASCII , 而不是它的形状。
ASCII: American standard Code information interchange
英国把每个字符分配一个唯一的整数值来标识他们,这个整数就是我们说的ASCII,
由于美国使用的字符个数不超过256,所以这个整数值只需要8bit
ASCII ---》 char unsigned char 8bits
man ascii
把我们的字符分为了两类:
(1)、普通字符:可以打印的字符,有形状
如:
'a'---'z'
'A'---'Z'
'0'---'9'
....
char a = 'a';
char c = 'z';
(2)、特殊字符(转义字符):不可以被打印的字符,没有形状
'\n' : 换行符
'\r' : 回车符
'\t' : 制表符
.......
'\oooo' : 有\后面跟1个,2个,3个八进制数
这些八进制数字用来所期望的字符的ASCII码
eg:
'A' : 101 65 41
'A'
'\101'
'\x41'
65
0101
0x41
'\xhh' :由\x后面跟一个或者两个十六进制的数字组成
这些十六进制的数字用来指定所期望的字符的ASCII
如:
char ch = 'A'
char ch = '\101'
char ch = '\x41'
char ch = 65;
char ch = 0101;
char ch = 0x41;
null字符:ASCII码0的字符 ,一般用来表示字符串的结束
'\0' ---- > 0
0 -----> 48
char ch = '\0'; ===> 0
char ch = 0; ===>0
char ch = '0' ====> 48
NOTE:
(1)、字符常量 和 字符串常量 是不一样的
字符常量 表示的是单个字符 , 用单引号引起来
如:
'0'
字符串常量 表示 的是一串字符 用双引号引起来的
如:
"0"
(2)、‘A’ - ‘Z’ ‘0’ - ‘9’ 是连续的
#### 浮点常量(带小数)
由整数部分,小数点,小数部分,一个e/E,一个可选的带符号的整型指数和可选的表示类型的后缀(F/f/l/L)
f/F : float
l/L : long double
没有后坠的 : double
float a = 34.567;
double a = 1.234;
double a = .123;
整数部分:可以省略
小数部分:也可以省略
但是整数部分和小数部分不可以同时省略
例子:
float f = 2.3E3; YES
float f = .3E3; YES
float f = 5E3; YES
float f = 3e-3; YES
float f = E5; error
#### 枚举常量
后面讲
##### 练习:
分析如下结果,机器32bits
(1)、
char ch = 65;
printf("%c\n",ch); //A
(2)、
char ch = 253;
ch = ch + 7; ch = ch + 15 //4
printf("%c\n",ch); // ascii为4的那个字符 //ascii为12的那个字符
printf("%d\n",ch); //4 12
printf("%u\n",ch); //4 12
printf("%x\n",ch); //4 c
## 总结
(1)、变量的属性即变量的左值和右值
(2)、整数在计算机中存放的形式的两个结论
结论一:
一个负数会和一个较大的正整数的补码形式相同
结论二:
在cpu底层中没有符号位的概念,都是数值位,都参与了运算,至于这个数是 正数还是负数,得看你的脸色来!
ps.不同类型的整型之间的赋值问题:
在C语言中允许不同类型的整数之间可以相互赋值:
char a; //8bits
int b; //32bits
short c; //16bits
a = b; //YY
c = a; //YY
为什么长度,不一样,你如何赋值的!
很标准的一个C语言的建议:
(1)、长 ------ 短
高字节直接就pass(丢弃),低字节直接拷贝
(2)、短 ----- 长
低字节直接拷贝,高字节我们补什么呢?
if 短的是有符号位,高位就全部补符号位
if 短的是无符号位,高位就全部补0;
eg:
假设运行如下的代码的机器是32bits
unsigned char c = 250;
char d;
d = c+8;
printf("%d\n",d); //2
printf("%u\n",d); //2
c : 8bits
1111 1010
c+8 :
c - > 32 bits : 短的----》长的 c:unsigned , 高位全部补 0
1111 1010
00000000 00000000 00000000 00001000 8
00000000 00000000 00000000 11111010 c
00000000 00000000 00000001 00000010 c+8
c+8 ---》d 长 ----- 》 短 低字节直接拷贝,高字节字节pass
d:
0000 0010