gcc编译器
GNU工具
编译工具:把一个源程序编译为一个可执行程序
调试工具:能对执行程序进行源码或者汇编级调试
软件工程工具:用于协助多人开发或者大型软件项目的管理,如make,CVS,Subvision
其他工具:用于把多个目标文件链接成可执行文件的连接器,或者用做格式转换的工具
GCC简介
全称为GNU CC,GNU项目中符合ANSI C标准的编译系统
编译如C,C++,Object C,Java,Fortran,Pascal,Modula-3和Ada等多种语言
GCC是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%
一个交叉平台编译器,适合在嵌入式领域的开发编译
gcc所支持后缀名解释
.c c原始程序
.C/.cc/.cxx C++原始程序
.h 预处理文件(头文件)
.i 已经过预处理过的C原始程序
.ii已经过与处理的C++原始程序
.s/.S汇编语言原始程序
.o目标文件
.a/.so编译后的库文件
编译器的主要组件
分析器
分析器将源语言程序代码转换为汇编语言。因为要从一种格式转换为另一种格式(C到汇编),所以分析器需要知道目标机器的汇编语言。
汇编器
汇编器将汇编语言代码转换为CPU可以执行字节码
链接器
链接器将汇编器生成的单独的目标文件组合成可执行的应用程序。链接器需要知道这种目标格式以便工作
标准C库
核心的C函数都有一个主要的C库来提供。如果在应用程序中用到了C库重的函数,这个库就会通过连接器和源代码链接来生成最终的可执行程序
d.GCC的基本用法和选项
e.GCC的错误类型
第一类:C语法错误
第二类:头文件错误
第三类:档案库错误
第四类:未定义符号
f.GCC编译过程
预处理(Pre-Processing)
gcc -E test.c -o test.i
用wc命令,查看这两个阶段代码大小
$ wc test.c test.i
编译(Compiling)
检查语法错误,并生成编译文件
-$ gcc -S test.i -o test.s
汇编(Assembling)
链接(Linking)
将目标程序链接库资源,生成可执行程序
gcc test.o -o test
./test
gdb调试工具
使用gcc对tes.c进行编译,注意一定加上选项'-g'
gcc -g test.c -o test
gdb test进入调试模式
输入字母小L查看代码内容
设置断点输入b命令 eg: b 12
输入run继续运行代码
p a 查看变量a的值
n是一步步往下执行
基本命令:
break设置断点
run继续运行
bt展示程序堆栈信息
c继续运行程序
next
edit
list
step
help [name]
quit
条件编译
编译器根据条件的真假决定是否编译相关代码
常见的条件编译有两种方法:
1.根据宏是否定义,其语法如下:
#ifdef <macro>
......
#else
.......
#endif
2.根据宏的值,其语法如下
#if <macro>
......
#else
.....
#endif
结构体
定义方式以及三种赋值方式以及内存对齐问题:
结构体数组
结构体指针
结构体嵌套结构体
含义:
结构体中的成员可以是另一个结构体
语法:
struct 结构体名{
struct 结构体名 成员名;
};
结构体大小
字节对齐主要是针对结构体而言的,通常编译器会自动对其变量进行对齐,以提高数据存取的效率
字节对齐作用:
平台原因(移植原因—):不是所有的硬件平台都能访问任意地址上的任意数据的:某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
性能原因:数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需要一次访问。
结构体大小的计算方法
自身对齐
默认对齐
有效对齐
位域
定义:
所谓的“位域”是把一个字节中的二进制位分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来
位域的一般形式:
struct位域结构体名
{
位域列表;
};
其中位域列表的形式为:
类型说明符 位域名:位域长度表示。
共用体以及typedef
共用体概念
在c语言中,不同数据类型的数据可以使用共同的存储区域,这种数据构造类型称为共用体,简称共用,又称联合体。共用体在定义、说明和使用形式上与结构体相似。两者本质上的不同仅在于使用内存的方式上。
定义一个共用体类型的一般形式为:
union 共用体名
{
成员列表;
};
共用体大小计算也是按照最大字节来内存对其
typedef
在c语言中,允许使用关键字typedef定义新的数据类型
其语法如下:
typedef <已有数据类型> <新数据类型>;
typedef int INTEGER;
这里定义了数据类型INTEGER,其等价于int
INTEGER i; <==> int i;
指针方式:
枚举
c语言中构造类型之一,在开发中,有些变量只有几种可能的取值,比如:一周有七天、一年有四季、在枚举的定义中,会将变量一一列出来。
枚举定义:
语法:
enum 枚举名
{
枚举成员列表
};
枚举的使用
enum 枚举名 变量名
enum 枚举名 变量名 = 枚举常量
定义枚举时顺便创建变量
枚举的特点
默认第一个成员从0开始,后面成员依次+1
有特定值的成员后面,从特定值开始依次+1
同一个枚举类型中,成员值可以相同
不同的枚举类型中,成员名不可以一样
内存管理
内存管理的意义:
不同区域存放的数据,赋予不同的生命周期,带来了更大的灵活编程
C/C++定义了4个内存区间:
代码区:
存放函数体的二进制代码,由操作系统进行管理(cpu执行的机器指令,共享只读)
全局区(全局变量和静态变量区)
存放全局变量和静态变量以及常量(包含const)
特点是生命周期比较长
栈区(局部变量区)
由编译器自动分配释放,存放函数的参数值,局部变量等
堆区(动态存储区):
由程序员分配和释放,若程序员不释放程序结束时由操作系统回收
静态存储分配
通常定义变量,编译器在编译时都可以根据该变量的类型知道所需内存空间的大小,从而系统在适当的时候为他们分配确定的存储空间。
在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
动态存储分配
有些操作对象只有在程序运行时才能确定,这样编译器在编译时就无法为他们预定存储空间,只能在程序运行时,系统根据运行时的要求进行内存分配,这种方法称为动态分配。
所有动态存储分配都在堆区中进行。
从堆上分配,亦称动态内存分配。程序在运行的时候用malloc申请任意多少的的内存,程序员自己负责在何时用free释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也多。
堆内存的分配与释放
当程序运行到需要一个动态分配的变量或对象时,必须向系统申请取得堆中的一块所需大小的存储空间,用于存储该变量或对象。当不再使用该变量或对象时,也就是他的生命结束时,要显式释放它所占用的存储空间,这样系统就能对该堆空间进行再次分配,做到重复使用有限的资源。
堆区是是不会自动在分配时做初始化的(包括清零),所以必须用初始化(initializer)来现实初始化
动态分配内存
内存泄漏(memory leak)
是指申请的内存空间使用完毕之后未回收。一次内存泄露危害可以忽略,但若一直泄漏,无论有多少内存,迟早都会被占用光,最终导致程序crash。(因此,开发中我们要尽量避免内存泄漏的出现)
内存溢出(out of memory)
是指程序在申请内存时,没有足够的内存空间供其使用。通俗理解就是内存不够用了,通常在运行大型应用或游戏时,应用或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。最终导致机器重启或者程序crash
malloc/free
void * malloc(size_t num)
void free(void *p)
malloc函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。
malloc申请到的是一块连续的内存,有时可能会比所申请的空间大。有时会申请不到内存,返回NULL.
malloc返回值类型是void *,所以在调用malloc时要显式地进行类型转换,将void*转换成所需要的指针类型。
如果free的参数是NULL的话,没有任何效果。
释放一块内存中的一部分是不被允许的。
malloc/free注意事项:
删除一个指针p(free(p);),实际意思是删除了p所指向的目标(变量或对象等),释放了他所占的堆空间,而不是删除p本身,释放堆空间后,p成了空悬指针
动态分配失败。返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。
malloc和free是配对使用的,free只能释放堆空间。如果malloc返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另分配,所以必须妥善保存malloc返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。
野指针:不是NULL指针,是指向“垃圾”内存的指针。
野指针是很危险的。
野指针的成因主要有两种
指针变量没有被初始化。
指针p被free之后,没有置为NULL,让人误以为p是个合法指针。指针操作超越了变量的作用范围。这种情况让人防不胜防。