黃山網(wǎng)站建設(shè)jidela十大中文網(wǎng)站排名
C語言編譯過程
- 1、C語言編譯過程
- 2、單c文件編譯實(shí)踐
- 3、多c文件編譯實(shí)踐
- 4、define
- 4.1、不帶參宏
- 4.2、帶參宏
- 4.3、帶參宏和帶參函數(shù)的區(qū)別
- 5、選擇性編譯ifdef、ifndef、if
- 5.1、#ifdef
- 5.2、#ifndef
- 5.3、#if
- 6、靜態(tài)庫和動態(tài)鏈接庫
- 6.1、靜態(tài)庫實(shí)踐
- 6.1.1、將mylib.c制作成靜態(tài)庫
- 6.1.2、編譯源程序
- 6.2、動態(tài)庫實(shí)踐
- 6.2.1、將mylib.c制作成動態(tài)鏈接庫
- 6.2.2、動態(tài)鏈接庫的使用
1、C語言編譯過程
1、預(yù)編譯
將 .c 中的頭文件展開、宏展開。生成的文件是 .i 文件。預(yù)處理操作過程不會進(jìn)行語法檢查。
2、編譯
將預(yù)處理之后的 .i 文件生成 .s 匯編文件。
3、匯編
將 .s 匯編文件生成 .o 目標(biāo)文件。
4、鏈接
將 .o 文件鏈接成 可執(zhí)行目標(biāo)文件。
2、單c文件編譯實(shí)踐
hello.c文件
#include<stdio.h>int main() {printf("Hello World! \n");return 0;
}
Linux 下GCC 編譯器編譯過程
1、預(yù)處理
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

3、多c文件編譯實(shí)踐
#include<xxx.h>
// 用尖括號包含頭文件,在系統(tǒng)指定的路徑下找頭文件
#include "xxx.h"
// 用雙引號包含頭文件,先在當(dāng)前目錄下找頭文件,找不到,再到系統(tǒng)指定的路徑下找。
注意:include 經(jīng)常用來包含頭文件,可以包含.c 文件,但是大家不要包含.c。
因?yàn)?include 包含的文件會在預(yù)編譯被展開,如果一個.c 被包含多次,展開多次,會導(dǎo)致函數(shù)重復(fù)定義。所以不要包含.c 文件。
test.c文件
#include<stdio.h>
#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 max(int v1, int v2);
gcc -E test.c -o test.i
gcc -S test.i -o test.s
gcc -c test.s -o test.ogcc -E max.c -o max.i
gcc -S max.i -o max.s
gcc -c max.s -o max.ogcc -E min.c -o min.i
gcc -S min.i -o min.s
gcc -c min.s -o min.ogcc test.o max.o min.o -o test


4、define
定義宏用 define 關(guān)鍵字,宏是在預(yù)編譯的時候進(jìn)行替換。
4.1、不帶參宏
#define PI 3.1415926
在預(yù)編譯的時候如果代碼中出現(xiàn)了 PI
就用 3.1415926
去替換。
宏定義的好處:只要修改宏定義,其他地方在預(yù)編譯的時候就會重新替換。
注意:宏定義后邊不要加分號。
宏定義的作用范圍:從定義的地方到本文件末尾。
如果想在中間終止宏的定義范圍
//終止PI 的作用
#undef PI
4.2、帶參宏
#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
】
4.3、帶參宏和帶參函數(shù)的區(qū)別
帶參宏: 被調(diào)用多少次就會展開多少次,執(zhí)行代碼的時候沒有函數(shù)調(diào)用的過程不需要壓棧彈棧。所以帶參宏,是浪費(fèi)了空間,因?yàn)楸徽归_多次,節(jié)省時間。
帶參函數(shù): 代碼只有一份,存在代碼段,調(diào)用的時候去代碼段取指令,調(diào)用的時候要壓棧彈棧。有個調(diào)用的過程。
帶參函數(shù)是浪費(fèi)了時間,節(jié)省了空間。
帶參函數(shù)的形參是有類型的,帶參宏的形參沒有類型名。
5、選擇性編譯ifdef、ifndef、if
選擇性編譯都是在預(yù)編譯階段處理的事情。
5.1、#ifdef
#ifdef AAA代碼段一
#else代碼段二
#endif
#include<stdio.h>
#define AAAint main(int argc, char *argv[]){
#ifdef AAAprintf("Hello World!\n");
#elseprintf("Hello China!\n");
#endifreturn 0;
}
5.2、#ifndef
#ifndef AAA代碼段一
#else代碼段二
#endif
#ifndef
和 #ifdef
是一種互補(bǔ)。這種方法,經(jīng)常用在防止頭文件重復(fù)包含。
5.3、#if
如果表達(dá)式為真,編譯第一段代碼,否則編譯第二段代碼。
#if 表達(dá)式程序段一
#else程序段二
#endif
6、靜態(tài)庫和動態(tài)鏈接庫
一、動態(tài)編譯
動態(tài)編譯使用的是動態(tài)庫文件進(jìn)行編譯,默認(rèn)使用的就是動態(tài)編譯。
gcc hello.c -o hello1
二、靜態(tài)編譯
靜態(tài)編譯使用的靜態(tài)庫文件進(jìn)行編譯。
gcc -static hello.c -o hello2

三、靜態(tài)編譯和動態(tài)編譯區(qū)別
1、使用的庫文件的格式不一樣:動態(tài)編譯使用動態(tài)庫,靜態(tài)編譯使用靜態(tài)庫。
2、靜態(tài)編譯要把靜態(tài)庫文件打包編譯到可執(zhí)行程序中。
3、動態(tài)編譯不會把動態(tài)庫文件打包編譯到可執(zhí)行程序中,它只是編譯鏈接關(guān)系。
=== mytest.c ====================================
#include <stdio.h>
#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
6.1、靜態(tài)庫實(shí)踐
6.1.1、將mylib.c制作成靜態(tài)庫
gcc -c mylib.c -o mylib.o
ar rc libmylib.a mylib.o
注意:靜態(tài)庫文件 起名的時候必須以 lib
開頭以 .a
結(jié)尾
6.1.2、編譯源程序
方法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:將庫文件及頭文件存放到系統(tǒng)默認(rèn)指定路徑
庫文件 默認(rèn)路徑是 /lib
或者是 /usr/lib
頭文件 默認(rèn)路徑是 /usr/include
mv libmylib.a /usr/lib
mv mylib.h /usr/include
編譯源程序的命令
gcc mytest.c -o mytest -l mylib -static
6.2、動態(tài)庫實(shí)踐
6.2.1、將mylib.c制作成動態(tài)鏈接庫
使用gcc 編譯、制作動態(tài)鏈接庫
gcc -shared mylib.c -o libmylib.so
注意:動態(tài)鏈接庫文件 起名的時候必須以 lib
開頭以 .so
結(jié)尾
6.2.2、動態(tài)鏈接庫的使用
方法1:庫函數(shù)、頭文件均在當(dāng)前目錄下
gcc mytest.c libmylib.so -o mytest
export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
此時就可以在執(zhí)行了哦: ./mytest
方法2:庫函數(shù)、頭文件假設(shè)在 /opt/
目錄
mv libmylib.so mylib.h /opt
gcc mytest.c -o mytest -L/opt -lmylib -I/opt編譯通過,運(yùn)行 mytest 時出錯,編譯時找到了庫函數(shù),但運(yùn)行鏈接時找不到庫,要把鏈接庫文件所在目錄加入默認(rèn)搜索路徑
export LD_LIBRARY_PATH=/opt:$LD_LIBRARY_PATH
此時就可以在執(zhí)行了哦: ./mytest
方法3:庫函數(shù)、頭文件均在系統(tǒng)路徑下
cp libmylib.so /usr/lib
cp mylib.h /usr/include
gcc mytest.c -o mytest -lmylib
如果出現(xiàn)如下錯誤,則添加查找路徑,解決問題
[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
此時就可以在執(zhí)行了哦: ./mytest
注:
問:此時有個問題出現(xiàn)了?靜態(tài)庫 和 動態(tài)庫 都在/usr/lib
下,那么默認(rèn)鏈接的到底是動態(tài)庫還是靜態(tài)庫呢?
答:當(dāng)靜態(tài)庫與動態(tài)庫重名時,系統(tǒng)會優(yōu)先連接動態(tài)庫,或者編譯時加入-static
指定使用靜態(tài)庫。