- gcc编译器有什么用?
将代码文本编译为机器能识别的二进制指令。
- 从代码最终得到程序,经过了哪4个步骤?
预处理、编译、汇编、链接
- 经过哪个步骤之后,代码不能直接阅读了?
汇编。
- 对于代码的语法检查(提示错误行号)属于在哪个步骤进行?
编译。
- 为什么代码漏写函数,gcc编译时不提示出现错误的行号?
因为函数链接定位在链接的阶段,而不是在编译阶段。
1.gcc编译流程
- 预处理:(代码文本 ---> 预处理文本)
指令:gcc hello.c -o hello.i -E
作用:处理所有的预处理指令(以#开头,比如头文件包含、宏定义、条件编译)
- 编译:(预处理文本 ---> 汇编文件)
指令:gcc hello.i -o hello.s -S
作用:词法、语法分析,根据平台生成最接近机器的语言(汇编)。
- 汇编:(汇编文件 ---> ELF可重定位文件)
指令:gcc hello.s -o hello.o –c
作用:将汇编指令文件翻译为可直接运行的二进制指令流。
- 链接:(ELF可重定位文件 + 库链接 ---> 最终程序)
指令:gcc hello.o -o hello –lc -lgcc
作用:重定位(函数和全局变量等)、链接库、合并段
备注:
- 汇编阶段,是处理汇编文件,生成二进制机器指令。
- -lXXX表示链接XXX库(-l: library)
- 预处理阶段,不会对代码进行语法词法分析。
- 链接阶段会对所有函数、全局变量进行重定位、链接,如果此时出现问题,不会提示出错行号。
2.预处理指令
在源码中,以#开头的语句,称为预处理指令,他们不属于C语言语法。
- 宏定义
#define PI 3.1415926 // 预处理阶段中,将代码中所有宏进行替换。
- 条件编译(有点类似于分支结构)
#if #ifdef #ifndef #elif #else #endif
- 头文件包含
#include <stdio.h>
备注:
- 宏名一般使用全大写字母,用以区别普通变量、函数。
- 带参宏,也称为宏函数,但是不存在函数调用机制。
- 宏定义的作用:
- 可使代码更具可读性:字符单词一般比纯数字更容易理解含义。
- 使得代码更方便修改:只需修改宏定义,即修改了所有该宏定义表达式。
- 提高程序运行效率:函数的调用是需要切换时间的,而宏定义是直接展开,不占用运行时间。
gcc编译指令中,可添加 -D选项,往工程中添加指定的宏。
可添加一些调试语句,通过编译指令去控制调试语句的输出。
gcc demo4_条件编译ifdef-ifndef.c -o demo –DTEST
条件编译属于预处理指令,在预处理阶段就已经完成了判断,程序运行过程中不会再次判断。
下列语句可避免头文件中的内容重复加入编译:
demo1_编译过程
#include <stdio.h> #include <string.h> void hahaha(char *str); int main() { printf("hello world.\n"); hahaha("hello world.\n"); return 0; }
demo2_define宏定义
#include <stdio.h> #include <string.h> // 不带参宏 // #define PI #define PI 3.1415926 // 预处理阶段中,将代码中所有宏进行替换。 // 带参宏(宏函数) #define SUM(a,b) (a+b) // 子函数 float func_sum(float a, float b); int main() { printf("hello world.\n"); float r = 5.0; printf("面积: %f\n", PI*r*r); printf("周长: %f\n", 2*PI*r); float var1 = 1.2; float sum = func_sum(r, var1)*2; printf("sum: %f\n", sum); sum = SUM(r, var1)*2; printf("sum: %f\n", sum); return 0; } float func_sum(float a, float b) { return a+b; }
demo3_条件编译_if-else
#include <stdio.h> #include <string.h> int main() { #if 0 printf("hello world.\n"); printf("hello world.\n"); printf("hello world.\n"); printf("hello world.\n"); printf("hello world.\n"); printf("hello world.\n"); #else printf("nihao 123.\n"); printf("nihao 123.\n"); printf("nihao 123.\n"); printf("nihao 123.\n"); printf("nihao 123.\n"); printf("nihao 123.\n"); #endif return 0; }
demo4_条件编译ifdef-ifndef
#include <stdio.h> #include <string.h> int main() { #ifdef TEST printf("[%d]\n", __LINE__); #endif printf("nihao 123.\n"); printf("nihao 123.\n"); printf("nihao 123.\n"); printf("nihao 123.\n"); printf("nihao 123.\n"); printf("nihao 123.\n"); #ifdef TEST printf("[%d]\n", __LINE__); #endif return 0; }
demo5_使用自己的头文件
#include <stdio.h> #include <string.h> // 包含系统头文件 #include "myhead.h" #include "myhead.h" #include "myhead.h" #include "myhead.h" #include "myhead.h" #include "myhead.h" #include "myhead.h" #include "myhead.h" #include "myhead.h" #include "myhead.h" // 包含自定义头文件 int main() { printf("%d\n", ABC); return 0; }
myhead.h
#ifndef _MYHEAD_H #define _MYHEAD_H #define ABC 1234 #endif