这次将所有数据类型做一个整理,作为C语言的结束篇。下一期就是数据结构与常见算法。
数据类型分类
基本类型 | 整型 | 短整型short | 2字节 |
整型int | 4 | ||
长整型long | 4 | ||
长长整型longlong | 8 | ||
浮点型 | 单精度float | 4 | |
双精度double | 8 | ||
长双精度long double | 8 | ||
字符类型 | char | 1,汉字2 | |
枚举类型 | enum | ||
构造类型 | 数组 | ||
结构体 | struct | ||
共用体 | union | ||
指针类型 | |||
空类型 |
在C语言里面,变量必须先定义再使用。变量定义包括:数据类型,存储类型,作用域。
定义变量为:[存储类型] 数据类型 变量名;作用域是看定义在那个部分上了。
作用域
作用域就是定义变量所在的范围,不在这个范围不能用。所以不同的函数可以用相同名字的变量名。
局部变量:在函数或者语句块里面定义的变量。它的作用域就是这个函数或者语句块。
全局变量:就是在源文件里不在函数或者语句块定义的变量。作用域是整个源文件。也可以拓展到别的文件。
存储类型
存储类型是指变量在内存中的存储方式。
定义存储类型的目的是管理内存和资源,同时又定义了变量的生命周期,还有指定链接属性。
用户区
动态存储区 | 堆 | |
内存用户区 | 静态存储区 | bss数据段 |
代码区 | 普通数据段 |
图中的红色表示所属关系。
动态存储区存放一些动态存储的变量,在调用时分配内存并赋初值,调用结束释放内存。
auto:自动存储类,局部变量默认的存储类,声明的变量将在栈上分配内存,在栈上先进后出,栈里面存储函数调用的上下文信息,包括局部变量,返回地址和函数参数。生命周期起于被调用时,这时在栈上分配内存,终结于调用结束,释放内存。在被调用时赋初值,可以多次赋值。
寄存器
register:用于定义存储在寄存器中而不是 RAM (以后在单片机里面再讲这个)中的局部变量。不能对它应用一元的 '&' 运算符(因为它没有内存位置)。变量的访问速度更快,在需要频繁访问的变量上使用 register 存储类可以提高程序的运行速度。生命周期和auto一样,都是对局部变量的声明。但是寄存器有限,不足时会自动转化成auto型变量。
静态存储区这里只讲普通数据段。普通数据段存放静态变量,初始化的全局变量或者常量配内存,函数调用结束,释放内存。
在函数或者语句块外面的声明的变量就是全局变量,存储在静态存储区,但是还有两个关键字也可以进行说明。
static:静态存储类,声明的变量只分配一次内存,在程序的整个运行期间都存在,直到程序结 束。可以在函数外面用来声明全局变量,这时,只能在本源文件使用。但是也可以在函数里面声明局部变量(在编译时赋初值,如果没有赋值自动赋值0或者空字符),而且只能在函数内部使用,同时只在第一次初始化,之后都是在前一次调用的基础上进行调用,和AUTO型变量是不同的。
extern:外部存储类,声明的变量在其他文件中定义,在本文件中只声明,不分配内存。这样就可以将全局变量作用域扩展到别的文件里。
同时如果已经在函数里面定义了一个局部变量,则也可以通过这个关键字的在函数外声明,从而使这个变量的作用域变成整个源文件。成为全局变量。
volatile:易失性存储类,声明的变量在程序运行过程中可能被程序控制流之外修改,编译器不会对其进行优化。多用于以下情况:
1.变量被多个线程共享,防止编译器对其进行优化。
2.变量在硬件中断服务历程(ISR)中被修改。如果在ISR中被修改,而主程序也可能访问,这种情况下应该声明。
const:常量存储类,声明的变量的值不能被修改。
restrict:限制指针存储类,声明的指针只能指向特定内存区域,而且是唯一的方式,别的指针无法修改这个指针所对应的内存里的内容。
数据类型
本文只列举一些特色的常用的数据类型。
1>数组
定义方式:数据类型 数组名[常量表达式];
需要注意的是数组元素下标从0开始。引用时逐个引用,A[0],A[10]。另一点值得注意的是数组元素都是相同数据类型。
二维数组
数据类型 数组名[常量表达式1][常量表达式2];
2>指针
首先来看直接访问和间接访问的概念。
直接访问,可以思考,当我们定义一个变量后,要存到用户区,每个变量是和一个地址对应的,所以也就是说有一个类似表的东西实现这种映射关系。在实际情况下,确实有这么的一个表,是在编译阶段时完成的。所以当我们直接使用变量时不用管到底存到了哪里。这就是直接访问。
但是当我们把变量的地址给了另一个变量,意味着使用这个变量时要通过这个存了这个变量地址的变量,这就是间接访问。
指针就是这种间接访问。
指针定义:类型说明符 *指针变量名;
双重指针定义:类型说明符 **指针变量名;
注意事项:1.当定义多个指针时可以在类型说明符后面写多个变量,但是必须带上*,否则只有第一个是指针,意义不对。
2.未经赋值的指针不能用,通过&加变量名将其地址赋值给指针变量。
3.根据*a_i=*&i=i,所以*和&是一个逆运算,相互抵消当遇见时。
4.可以改变指针的值,也可以用指针给指针赋值。但是注意的是这时候是指针名,别弄错。
指针与数组
C语言为一个数组分配一块连续的地址,数组名表示这块连续地址的首地址,即int*ptr=&array[0];
ptr,array,&array[0]是等价的。所以数组元素array[i]的地址就是ptr+i或者array+i。
数组指针和指针数组
数组指针是指一个数组的指针,也就是把一个数组的地址给指针变量。
类型说明符(*指针变量名)[长度];
类型说明符一维数组的数据类型,长度是一维数组的长度,一维数组的长度是数组指针的步长,括弧不可省略。
指针数组是数组的元素是指针,类型说明符 *数组名[长度];
类型说明符:指针数组元素指向的元素数据类型。
3>结构体
当很多元素的数据类型不一样时,使用构造类型。
struct 结构体名{
成员列表;
};
成员列表可以是各种不同类型的变量,一般形式是类型说明符 成员名;
结构体名和结构体变量
结构体名和结构体变量是不一样的,结构体变量有三种定义方法。
第一种 已经定义好结构体
struct 结构体名{
成员列表;
};
结构体名 结构体变量名;
第二种在定义结构时同时定义结构体变量
struct 结构体名{
成员列表;
} 结构体变量名1 ,结构体变量名2;
第三种在定义结构体时直接定义多个结构体变量
struct {
成员列表;
} 结构体变量名1 ,结构体变量名2;
同时结构体也可以嵌套。后面使用时就是一个数据变量类型自然也有数组指针。
typedef
为数据类型取别名。
typedef 原类型名 新类型名;原类型名可以是各种数据类型。
4>枚举类型
枚举类型是一种基本数据结构但是定义变量又和结构体一样。同样是第一种已经定义好结构体在定义变量,第二种定义同时定义变量……
当一个变量的值被限定在一个有范围的情况下用枚举。
enum 枚举类型名 {枚举值列表};
注意
1.不能赋值,是常量
2.枚举元素有默认数值,依次为0,1……例如:enum weekday{ sunday,monday}workday;
workday=monday;相当于workday=1;
3.可以进行关系运算,按照他们表示的数值比较。
5>共用体
union 共用体名{
成员列表;
};
和结构体不同的是每个成员共用一个内存空间,也就是同住一个屋子但是屋子不够大,一次只能住一个,所以内存空间大小是最大的成员的大小。变量定义和结构体一样的三种方式。
对共用体赋值时,起作用的是最后一次被赋值的成员,以前的存储单元中的内容被取代。