目录
前言
程序开发的六个阶段:
编辑-->预处理-->编译-->链接-->运行-->调试
一、编辑阶段
1、代码编辑器的好处:
智能提醒,自动补全,语法高亮,具有丰富的快捷键,可适配性强
2、常用的编辑器:
vim,vscode,nodpad++
3、优秀代码特点:
严格按照编程规范编写代码
可读性强
可扩展性强
可移植性强
二、预处理
预处理阶段会处理三个事情:
1、头文件
1、头文件中出现ifndef--define--endif是起什么作用:
答:防止重复包含
2、#include<xxx.h>和#include "xxx.h"的区别
答:<>是在系统的默认的标准头文件搜索路径下找该头文件;" "是先在当前路径下查找,找不到再去默认的标准路径下查找,所以" "的范围比<>大
3、头文件的作用
答:实现函数的自动化声明
4、头文件中可不可以进行全局变量的定义
答: 不可以,编译不会出粗,链接的时候会出现重复定义的错误,全局变量要定义在.c文件中,在别的文件中使用extern关键字扩展全局变量的使用范围
2、宏定义
不带参数的宏
#define 宏名 宏体
带参数的宏
#define 宏名(参数) 宏体
作用:代替频繁调用的短小函数
1、宏定义的作用
答:1、定义常量,提高代码的可读性
2、定义简单的函数宏(宏函数),提高运行效率,空间换时间
3、代码复用,频繁使用的代码段,可以宏定义,以提高代码的复用性
4、条件编译
2、编写两个宏
某一位设置1 #define setBits(data,n) data = data | 0x01<<n
某一位清楚0 #define clearBits(data,n) data = data & ~(0x01<<n)
3、定义带参数的宏,宏体加上括号
#define DIV(x,y) (x)/(y)
3、条件编译
1、概念
根据一个宏是不是被定义过作为条件,选择删除或者保留部分代码
2、形式:
形式1:
#ifdef 标记
代码
#endif
形式2:
#ifndef 标记
代码
#endif
形式3:
#ifdef 标记
代码段1
#else
代码段2
#endif
3、作用
(1)用在头文件中防止重复包含
xxx.h
#ifndef XXX_H
#define XXX_H
头文件内容
#endif
(2) 对工程起配置作用
比如单片机中的stm32f10x.h
(3)嵌套注释程序
三、编译
gcc常见参数
-E :只进行预处理,不进行编译、链接等后续步骤
例如:gcc -E test.c
-o :用于指定输出文件的名称例如:gcc test.c -o myprogram
-I (大写i):用于指定头文件的搜索路径例如:gcc -I/home/user/include test.c
-c :只进行编译,不进行链接,生成目标文件(以.o为扩展名)例如:gcc -c file1.c file2.c
-L :用于指定库文件的搜索路径。在链接阶段,链接器需要找到程序所依赖的库文件(如.a静态库或.so动态库)例如:gcc -L/home/user/lib test.o
-l (小写L):不是标准库,需要指定要链接的库名,库名一般是去掉前缀lib和后缀(如.a或.so)后的名字。例如,要链接libm.so(数学库),就使用-lm例如:gcc test.o -L/usr/lib -lm
-static :用于强制使用静态库进行链接,默认是动态库链接例如:gcc -static test.o -lfoo
-Wall :显示警告信息例如:gcc -Wall test.c会在编译test.c时显示各种警告信息。
-g :生成调试信息例如:gcc -g test.c
-On :用于优化编译后的代码(不与-g同时使用)例如:gcc -O2 test.c会以-O2的优化级别编译test.c
四、链接
1、如何理解链接,为什么要进行链接?
单独编译,统一链接,所有的.o文件进行链接,和库文件进行链接,一个文件中使用到了另一个文件中的函数,不链接会出现链接错误
2、linkerror可能错误和原因分析
1、未定义的引用
原因:.c中的实现和.h中声明的名称不一致,或者.h中声明了,.c中没有实现
2、找不到库文件
原因:库文件未安装或者库文件版本号不一致,比如64位机安装了32位的库
3、重复定义:
原因:在头文件中不小心定义了函数或变量(而不是只进行声明),并且这个头文件被多个源文件包含,就会导致符号多重定义。
3、动态库和静态库
静态库 :将库中的代码和数据直接复制到最终的可执行文件中,所以静态链接后产生的可执行程序不需要库的支持,程序较大
静态库优点
独立性强 :可执行文件不依赖于外部库文件的存在,方便程序的分发和部署。即使在没有安装相应静态库的系统上,只要可执行文件本身完整,就可以正常运行。
稳定性高 :由于代码已经复制到可执行文件中,不用担心库文件版本变化或者被误删除、修改等情况影响程序运行。
静态库缺点
文件体积大 :因为库的代码会被复制到每个使用它的可执行文件中,所以会导致可执行文件体积增大。这在存储和网络传输时会占用更多资源。
更新维护不便:如果静态库的代码需要更新,那么所有使用这个静态库的可执行文件都需要重新编译链接,否则无法使用新的库功能。
动态库 :在程序编译时不会将库的代码和数据复制到可执行文件中。而是在程序运行时,由操作系统加载动态库到内存,并且多个程序可以共享同一份动态库的代码,动态链接产生的可执行程序需要库的支持,程序较小动态库优点
节省存储空间和内存 :多个程序可以共享同一份动态库,减少了磁盘上存储的冗余代码,并且在内存中也只需加载一份,节省了内存空间。
更新方便 :如果动态库的代码需要更新,只要更新动态库文件本身即可,不需要重新编译所有使用它的可执行文件。程序在下次运行时会自动使用更新后的动态库。
动态库缺点
依赖管理复杂 :程序运行时依赖于动态库的存在,如果动态库文件丢失、损坏或者版本不匹配,程序将无法正常运行。这需要在系统中正确地管理动态库的安装、版本和路径等信息。
加载速度可能较慢:相对于静态库,动态库在程序启动时需要额外的加载过程,可能会导致程序启动速度稍慢,尤其是在动态库较大或者系统资源紧张的情况下。
4、使用第三方库的步骤
源码库 : 下载.c和.h文件,移动到项目路径下,包含头文件使用即可
二进制库 :1、获取二进制库文件和相关头文件
2、了解库的使用文档和 API
3、配置项目以包含库和头文件,如在 Linux 系统下可以放在/usr/local/lib或项目指定的库目录中。对于头文件,也需要添加到项目的头文件搜索路径中
4、在项目中链接库
六、调试
1、怎么定位错误
1、使用printf打印三个宏:LINE ,FUNCTION ,FILE
2、查看错误日志
3、使用调试器
2、调试器的主要功能:
1、设置断点
break 命令设置断点break main会在main函数的第一行设置断点
break function_name会在function_name函数的入口处设置断点
break myfile.c:10会在myfile.c文件的第 10 行设置断点
2、单步执行
step (单步进入函数)
next (单步跳过函数)3、查看变量的值
可以使用print命令查看变量的值,例如print variable_name可以查看variable_name变量的值