gcc编译相关知识点及学习心得

本文深入讲解GCC编译过程,包括预处理、编译、汇编和链接四个阶段,以及调试技巧、宏定义开关和printf的使用。同时,文章还介绍了gcc的编译选项和条件编译,帮助读者掌握更高效的编译技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、知识点梳理

1.零碎知识点梳理

  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
  2. typedef
    作用:声明一种新的数据类型
    命令格式:typedef 数据类型 新的数据类型
  3. 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可以采用表达式作为名称。
  4. 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;
  1. 调试技巧——宏定义开关和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;  
}  
  1. 编译器内置宏
    ANSI C标准中有几个标准预定义宏(也是常用的):
    __ LINE__:在源代码中插入当前源代码行号;
    __ FILE__:在源文件中插入当前源文件名;
    __ DATE__:在源文件中插入当前的编译日期
    __ TIME__:在源文件中插入当前编译时间;
    __ STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;
    __cplusplus:当编写C++程序时该标识符被定义。

  2. 函数功能单一简单且频繁调用时使用带参的宏

  3. 头文件卫士

  4. 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编译相关的知识,难度没有上周那么高但是知识点也不少。今天上课有些分神了,所以今天课堂上的内容还得回忆一遍并加以记忆。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值