1、预编译
将 .c 中的头文件展开、宏展开。生成的文件是 .i 文件。预处理操作过程不会进行语法检查。
2、编译
将预处理之后的 .i 文件生成 .s 汇编文件。
3、汇编
将 .s 汇编文件生成 .o 目标文件。
4、链接
将 .o 文件链接成 可执行目标文件。
hello.c文件
#include
int main() {
printf("Hello World! \n");
return 0;
}
Linux 下GCC 编译器编译过程
1、预处理
gcc -E hello.c -o hello.i
2、编译
gcc -S hello.i -o hello.s
3、汇编
gcc -c hello.s -o hello.o
4、链接
gcc hello.o -o hello
#include
// 用尖括号包含头文件,在系统指定的路径下找头文件
#include "xxx.h"
// 用双引号包含头文件,先在当前目录下找头文件,找不到,再到系统指定的路径下找。
注意:include 经常用来包含头文件,可以包含.c 文件,但是大家不要包含.c。
因为 include 包含的文件会在预编译被展开,如果一个.c 被包含多次,展开多次,会导致函数重复定义。所以不要包含.c 文件。
test.c文件
#include
#include "max.h"
#include "min.h"
int main() {
int maxVal = max(1, 5);
int minVal = min(1, 5);
printf("最大值maxVal = %d;最小值minVal = %d \n", maxVal, minVal);
return 0;
}
max.c文件
int max(int v1, int v2) {
int z;
if (v1>v2) {
z = v1;
}else {
z = v2;
}
return z;
}
min.c文件
int min(int v1, int v2) {
int z;
if (v1 < v2) {
z = v1;
}else {
z = v2;
}
return z;
}
max.h文件
extern int max(int v1, int v2);
min.h文件
extern int min(int v1, int v2);
gcc -E test.c -o test.i
gcc -S test.i -o test.s
gcc -c test.s -o test.o
gcc -E max.c -o max.i
gcc -S max.i -o max.s
gcc -c max.s -o max.o
gcc -E min.c -o min.i
gcc -S min.i -o min.s
gcc -c min.s -o min.o
gcc test.o max.o min.o -o test
定义宏用 define 关键字,宏是在预编译的时候进行替换。
#define PI 3.1415926
在预编译的时候如果代码中出现了 PI
就用 3.1415926
去替换。
宏定义的好处:只要修改宏定义,其他地方在预编译的时候就会重新替换。
注意:宏定义后边不要加分号。
宏定义的作用范围:从定义的地方到本文件末尾。
如果想在中间终止宏的定义范围
//终止PI 的作用
#undef PI
#define S(a,b) a*b
注意带参宏的形参 a 和 b 没有类型名:(注意歧义)
S(1,5)
即【1*5
】
S(3+4,3)
即【3+4 * 3
】
S((3+4),3)
即【(3+4)*3
】
带参宏: 被调用多少次就会展开多少次,执行代码的时候没有函数调用的过程不需要压栈弹栈。所以带参宏,是浪费了空间,因为被展开多次,节省时间。
带参函数: 代码只有一份,存在代码段,调用的时候去代码段取指令,调用的时候要压栈弹栈。有个调用的过程。
带参函数是浪费了时间,节省了空间。
带参函数的形参是有类型的,带参宏的形参没有类型名。
选择性编译都是在预编译阶段处理的事情。
#ifdef AAA
代码段一
#else
代码段二
#endif
#include
#define AAA
int main(int argc, char *argv[]){
#ifdef AAA
printf("Hello World!\n");
#else
printf("Hello China!\n");
#endif
return 0;
}
#ifndef AAA
代码段一
#else
代码段二
#endif
#ifndef
和 #ifdef
是一种互补。这种方法,经常用在防止头文件重复包含。
如果表达式为真,编译第一段代码,否则编译第二段代码。
#if 表达式
程序段一
#else
程序段二
#endif
一、动态编译
动态编译使用的是动态库文件进行编译,默认使用的就是动态编译。
gcc hello.c -o hello1
二、静态编译
静态编译使用的静态库文件进行编译。
gcc -static hello.c -o hello2
三、静态编译和动态编译区别
1、使用的库文件的格式不一样:动态编译使用动态库,静态编译使用静态库。
2、静态编译要把静态库文件打包编译到可执行程序中。
3、动态编译不会把动态库文件打包编译到可执行程序中,它只是编译链接关系。
=== mytest.c ====================================
#include
#include "mylib.h"
int main(int argc, char* argv[]) {
int a = 10, b = 20, max_num, min_num;
max_num = max_fun(a, b);
min_num = min_fun(a, b);
printf("max_num = %d \n", max_num);
printf("min_num = %d \n", min_num);
return 0;
}
=== mylib.c ====================================
int max_fun(int x, int y) {
return (x > y) ? x : y;
}
int min_fun(int x, int y) {
return (x < y) ? x : y;
}
=== mylib.h ====================================
#ifndef __MYLIB_H__
#define __MYLIB_H__
extern int max_fun(int x, int y);
extern int min_fun(int x, int y);
#endif
gcc -c mylib.c -o mylib.o
ar rc libmylib.a mylib.o
注意:静态库文件 起名的时候必须以 lib
开头以 .a
结尾
方法1:在同一目录下编译
编译源程序命令:
gcc -static mytest.c libmylib.a -o mytest
方法2:可以指定头文件及库文件的路径
如将 libmylib.a
mylib.h
移动到 /root/staticlib
下
mv libmylib.a mylib.h /root/staticlib
编译源程序命令:
gcc mytest.c \
-static \
-o mytest \
-L /root/staticlib \
-l mylib \
-I /root/staticlib
注意:
-L
是指定库文件的路径
-l
指定找哪个库,指定的只要库文件名 lib
后面 .a
前面的部分
-I
指定头文件的路径
方法3:将库文件及头文件存放到系统默认指定路径
库文件 默认路径是 /lib
或者是 /usr/lib
头文件 默认路径是 /usr/include
mv libmylib.a /usr/lib
mv mylib.h /usr/include
编译源程序的命令
gcc mytest.c -o mytest -l mylib -static
使用gcc 编译、制作动态链接库
gcc -shared mylib.c -o libmylib.so
注意:动态链接库文件 起名的时候必须以 lib
开头以 .so
结尾
方法1:库函数、头文件均在当前目录下
gcc mytest.c libmylib.so -o mytest
export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
此时就可以在执行了哦: ./mytest
方法2:库函数、头文件假设在 /opt/
目录
mv libmylib.so mylib.h /opt
gcc mytest.c -o mytest -L/opt -lmylib -I/opt
编译通过,运行 mytest 时出错,编译时找到了库函数,但运行链接时找不到库,要把链接库文件所在目录加入默认搜索路径
export LD_LIBRARY_PATH=/opt:$LD_LIBRARY_PATH
此时就可以在执行了哦: ./mytest
方法3:库函数、头文件均在系统路径下
cp libmylib.so /usr/lib
cp mylib.h /usr/include
gcc mytest.c -o mytest -lmylib
如果出现如下错误,则添加查找路径,解决问题
[root@michael cc]# ./mytest
./mytest: error while loading shared libraries: libmylib.so: cannot open shared object file: No such file or directory
解决方案:
export LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH
此时就可以在执行了哦: ./mytest
注:
问:此时有个问题出现了?静态库 和 动态库 都在/usr/lib
下,那么默认链接的到底是动态库还是静态库呢?
答:当静态库与动态库重名时,系统会优先连接动态库,或者编译时加入-static
指定使用静态库。