条件编译
编译器根据条件真假决定是否编译相关的代码
常见的条件编译有两种方法
- 根据宏是否定义
#ifdef <macro>
......
#else
......
#endif
- 根据宏的值
#if <macro>
......
#else
......
#endif
结构体
在实际的处理对象中,有许多信息是由多个不同类型的数据组合在一起进行描述,而且这些不同类型的数据是互相联系组成了一个有机的整体。此时,就要用到一种新的构造类型数据——结构体(structure),简称结构。
结构体的使用为处理复杂数据结构(如动态数据结构)提供了有效手段,而且,它们为函数间传递不同类型的数据提供了方便。
struct 结构体名
{
数据类型 成员名1;
数据类型 成员名2;
:
数据类型 成员名n;
};
在大括号中的内容也称为“成员列表”或“域表”。
其中,每个成员的命名规则与变量名相同;
数据类型可以是基本变量类型和数组类型,或者是一个结构体类型;
用分号“;”作为结束符。整个结构的定义也用分号作为结束符
结构体类型中的成员名可以与程序变量名相同,二者并不代表同一对象,编译程序可以自动化对它们进行分区。
-
结构体类型是用户自行构造的。
-
它由若干不同的基本数据类型的数据构成。
-
它属于C语言的一种数据类型,与整型、实型相当。因此,定义它时不分配空间,只有它定义变量时才分配空间。
结构体类型变量的定义方法
先定义结构体类型再定义变量名,这是C语言中定义结构体类型变量最常见的方式
struct 结构体名
{
成员列表;
};
struct 结构体名 变量名;
在定义类型的同时定义变量
struct 结构体名
{
成员列表;
}变量名;
直接定义结构体类型变量
struct
{
成员列表;
}变量名;
大小
一个结构体变量占用内存空间的实际大小,也可以利用sizeof求出。它的运算表达式为:sizeof(运算量)
//求出给定的运算量占用内存空间的字节数
其中运算量可以是变量、数组或结构体变量,可以是数据类型的名称。
结构体变量的适用形式
结构体变量名.成员名
-
不能将一个结构体类型变量作为一个整体加以引用,而只能对结构体类型变量中各个成员分别引用。
-
如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级成员。只能对最低级的成员进行赋值或存取以及运算。
-
对成员变量可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
-
在数组中,数组是不能彼此赋值的,而结构体类型变量可以相互赋值。
结构体变量的初始化
与其他类型变量一样,也可以给结构体的每个成员赋初值,这成为结构体的初始化。一中的hi在定义结构体变量时进行初始化。
struct 结构体名 变量名={初始数据表};
struct 结构体名
{
成员列表;
}变量名={初始化数据表};
结构体数组
具有相同结构体类型的结构体变量也可以组成数组,称它们为结构体数组。结构体数组的每一个数组元素都是结构体类型的数据,它们都分别是包括各个成员项。
结构体数组初始化
struct 结构体名
{
成员列表;
};
struct 结构体名
在定义结构体类型同时定义结构体数组
struct 结构体名
{
成员列表;
}数组名[元素个数];
直接定义结构体数组
struct
{
成员列表;
}数组名[元素个数];
结构体指针
可以设定一个指针变量用来指向一个结构体变量。此时该指针变量的值是结构体变量的起始地址,该指针称为结构体指针。
struct 结构体名 *结构指针名;
其中的结构体名必须是已经定义过的结构体类型。
当表示指针变量p所指向的结构体变量中的成员时,“(*结构体指针名).成员名”这种表示形式总是需要使用圆括号,显得不简练。因此,对于结构体指针指向的结构体成员项,给出了另外一种简洁的表示方法。
结构体指针名->成员名
与(*结构体指针名).成员名
在意义上完全等价的。
共用体
在C语言中,不同数据类型的数据可以使用共同的存储区域,这种数据构造类型称为共用体,简称共用,又称联合体。共用体在定义、说明和使用形式上与结构体相似。两者本质上的不同仅在于使用内存的方式上。
union 公用体名
{
成员列表;
};
由于共用体中各成员的数据长度不同,所以公用体变量在存储时总是按其成员中数据长度最大的成员占用内存空间。结构体类型变量在存储时总是按各成员的数据长度之和占用内存空间。
在使用共用体类型变量的数据是要注意:在共用体类型变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用。
在程序中经常使用结构体与共用体相互嵌套的形式。即共用体类型的成员可以是结构体类型,或者结构体类型的成员是共用体类型。
typedef
在C语言中,允许使用关键字typedef定义新的数据类型
typedef <已有数据类型> <新数据类型>
动态内存
C/C++定义了4个内存区间
-
代码区
-
全局变量与静态变量区
-
局部变量区即栈区
-
动态存储区即堆区
静态存储分配
通常定义变量,编译器在编译时都可以根据该变量的类型知道所需内存空间的大小,从而系统在适当的时候为他们分配确定的存储空间。
在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
动态存储分配
有些操作对象只有在程序运行时才能确定,这样编译器在编译时就无法为他们预定存储空间,只能在程序运行时,系统根据运行时的要求进行内存分配。
所有动态存储分配都在栈区中进行。从堆上分配,亦成为动态内存分配。程序在运行的时候用malloc申请任意多少的内存,程序员自己负责在任何时用free释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
malloc/free
void * malloc(size_t num)
void free(void *p)
malloc函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。申请到的是一块连续的内存,有时可能会比所申请的空间大。其有时会申请不到内存,返回NUll。malloc返回值的类型是void *,所以在调用malloc时要显示地进行类型转换,将void *转换成所需要的指针类型。如果free的参数是NULL的话,没有任何效果。释放一块内存中的一部分是不被允许的。
注意:
删除一个指针p(free§?,实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身,释放堆空间后,p成了空悬指针,动态分配失败。返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。
malloc 与free是配对使用的,free只能释放堆空间。如果malloc返回的指针值丢失,则分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能以另分配,所以必须妥善保存malloc返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。
动态分配的变量或对象的生命期。无名对象的生命期并不依赖于建立它的作用域,比如在函数中建立的动态对象在函数返回后任可使用。我们也称堆空间作为自由空间(free store)就是这个原因。但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放是一件很容易失控的事,往往会出错。
野指针
不是NULL指针,是指向“垃圾”内存的指针。“野指针”是很危险的。
“野指针”的成因主要有两种:
-
指针变量没有被初始化
-
指针p被free之后,没有置为NULL,让人误以为p是个合法的指针。
-
指针操作超越了变量的作用范围。这种情况让人防不胜防。
make简介
工程管理器,是指管理较多的文件,也是个“自动编译管理器”,这里的“自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入Makefiile文件的内容来执行大量的编译工作。Make将只编译改动的代码文件,而不用完全编译。
Makefile基本结构
Makefile是Make读入的唯一配置文件
由make工具创建的目标体(target),通常是目标文件或可执行文件,要创建的目标体所依赖的文件(dependency_file),创建每个目标文件体时需要运行的命令(command)。
注意:命令行前面必须是一个“TAB键”,否则编译错误为:*** missing separator. Stop.
Makefile格式:
target:dependency_files
<TAB> command
-Wall:表示允许发出gcc所有有用的报警信息
-c:只是编译不链接,生出目标文件“.o”
-o file:表示把输出文件输出到file里