一、知识点梳理
1.零碎知识点梳理
- gcc编译步骤
(1)预处理
作用:读取C语言源文件,对以“#”开头的语句进行处理
命令格式:gcc -E .c -o .i
(2)编译
作用:生成汇编代码
命令格式:gcc -S .i -o .s
(3)汇编
作用:生成二进制代码
命令格式:gcc -c .s -o .o
(4)链接
作用:生成可执行文件
命令格式:gcc .o -o a.out - typedef
作用:声明一种新的数据类型
命令格式:typedef 数据类型 新的数据类型 - define和const的区别
(1)就起作用的阶段而言: #define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用
(2)就起作用的方式而言: #define只是简单的字符替换,没有类型检查,存在边界的错误;const对应数据类型,进行类型检查; (3)就存储方式而言:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份,占用代码段空间;const定义的只读变量在程序运行过程中只有一份备份,占用数据段空间。
(4)从代码调试的方便程度而言: const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉了。 (5)从是否可以再定义的角度而言: const不足的地方,是与生俱来的,const不能重定义,而#define可以通过#undef取消某个符号的定义,再重新定义。
(6)从某些特殊功能而言: define可以用来防止头文件重复引用,而const不能;
(7)从用于类中来看:const用于类成员变量的定义,只要一定义,不可修改。define 不可用于类成员变量的定义,但是可以用于全局变量。
(8)const采用一个普通的常量名称,define可以采用表达式作为名称。 - define和typedef的区别
(1)#define之后不带分号,typedef之后带分号。
(2)#define可以使用其他类型说明符对宏类型名进行扩展,而 typedef 不能这样做。如:
#define INT1 int
unsigned INT1 n; //没问题
typedef int INT2;
unsigned INT2 n; //有问题
(3)在连续定义几个变量的时候,typedef 能够保证定义的所有变量均为同一类型,而 #define 则无法保证。如:
#defin PINT int *
PINT a,b; //int *a;int b;
typedef int* PINT;
PINT a,b; //int *a;int *b;
- 调试技巧——宏定义开关和printf
#include
#define __DEBUG
#ifdef __DEBUG
#define DEBUG(format,...) printf("File: "__FILE__", Line: %05d: "format"/n", __LINE__, ##__VA_ARGS__)
//输出文件路径,行号
#else
#define DEBUG(format,...)
#endif
int main() {
char str[]="Hello World";
DEBUG("A ha, check me: %s",str);
return 0;
}
-
编译器内置宏
ANSI C标准中有几个标准预定义宏(也是常用的):
__ LINE__:在源代码中插入当前源代码行号;
__ FILE__:在源文件中插入当前源文件名;
__ DATE__:在源文件中插入当前的编译日期
__ TIME__:在源文件中插入当前编译时间;
__ STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;
__cplusplus:当编写C++程序时该标识符被定义。 -
函数功能单一简单且频繁调用时使用带参的宏
-
头文件卫士
-
makefile 规则
目标:依赖文件
(tab)通过依赖文件生成目标的命令
依赖文件发生改变下一次make会自动运行相应命令,未改动部分则不用
TARGET = APP
SOURCES = $(wildcard *.c) //指定编译当前目录下所有.c文件
OBJS = $(patsubst %.c,%o,$(SOURCES)) //函数patsubst:匹配替换,三个参数。第一个是需要匹配的样式,第二个目标样式,第三个是需要处理的由空格分离的列表。
CC = gcc
CFLAGS = -g -c
LIBS = -lm -lpthread
INCLUDE = -I./include
$(TARGET):$(OBJS)
$(CC) $(LIBS) $^ -o $@
%.o:%.c %.h
$(CC) $(CFLAGS) $(INCLUDES) $< -o $@
.PHONY:clean
clean:
rm -f $(OBJS) $(TARGET)
-Wall:输出所有警告信息
-O:在编译时进行优化
-g:表示编译debug版本
$@–目标文件,
$^–所有的依赖文件
$<–第一个依赖文件
%–Make命令允许对文件名,进行类似正则运算的匹配,主要用到的匹配符是%
10. gcc编译选项
gcc -g
在可执行文件中加入调试信息,方便进行程序的调试
gcc -E
单独执行预处理:gcc -E test.c -o test.i
gcc -S
代码翻译成汇编语言:gcc -S test.i -o test.s
gcc -c
将编译阶段生成的.s汇编文件翻译成二进制机器代码:gcc -c test.s -o test.o
gcc -O
对生成的代码使用优化
gcc -D
定义一个宏
gcc -Idir
在编译源程序时增加一个搜索头文件的额外目录
gcc -Ldir
增加一个搜索库文件的额外目录
gcc -wall
表示产生所有警告
gcc -Werror
含义是将所有警告作为错误(error),即出现警告就停止编译
11. 条件编译
(1)如果标识符已被 #define命令定义过则对程序段1进行编译;否则对程序段2进行编译
#ifdef 标识符
程序段1
#else
程序段2
#endif
(2)如果标识符没有被 #define命令定义过则对程序段1进行编译;否则对程序段2进行编译
#ifndef 标识符
程序段1
#else
程序段2
#endif
(3)如果常量表达式为真则对程序段1进行编译;否则对程序段2进行编译
#if 常量表达式
程序段1
#else
程序段2
#endif
二、今日心得
今天学习了gcc编译相关的知识,难度没有上周那么高但是知识点也不少。今天上课有些分神了,所以今天课堂上的内容还得回忆一遍并加以记忆。