預處理(或稱預編譯)是指在進行編譯的第一遍掃描(詞法掃描和語法分析)之前所作的作業,預處理指令指示在程式正式編譯前就由編譯器進行的操作,可放在程式中任何位置,
當我們寫了一個代碼,從一個文本檔案的代碼到最后成為一個可執行程式的程序中,要經過
編譯+鏈接->.exe檔案(二進制檔案)
一個工程中每個源檔案都會經過編譯器生成目標檔案(.obj,linux下是.o檔案),經過聯結器(鏈接鏈接庫和目標檔案)輸出.exe檔案
其中編譯分為三個部分:預編譯 -> 編譯 -> 匯編
預編譯的作用
1)#include頭檔案的包含
2)注釋洗掉,使用空格替換注釋
3)#define進行替換
在linux下,采用gcc -E的指令將源檔案進行預編譯

在目錄下會生成test.i檔案

可以看到預編譯階段完成了上述的描述的功能,
預編譯完成之后,下一步是將test.i進行編譯,
編譯的作用是將C代碼翻譯成匯編代碼,并對代碼進行語法分析、詞法分析、語意分析、符號匯總
在linux下采用gcc -S將test.i檔案編譯生成test.s檔案


可以看到test.s里面寫的都是匯編代碼,
同樣的編譯的最后一步匯編是將test.s檔案里面的匯編轉換為二進制代碼,并且形成符號表(將之前的符號匯總與相應的地址對應起來形成映射表)
linux下采用gcc -c的指令

默認生成test.o檔案

因為采用的是文本編輯器vim打開的,所以看到的二進制顯示出來的都是亂碼,
要生成可執行程式還差一步鏈接作業
聯結器的兩個主要任務是符號決議和重定位,符號決議將目標檔案中的每個全域符號都系結到一個唯一的定義,而重定位確定每個符號的最終記憶體地址,并修改對那些目標的參考,
鏈接可以在編譯時由靜態編譯器來完成,也可以在加載時和運行時由動態聯結器來完成,
在linux下采用gcc -o的命令

針對預處理(預編譯),C語言提供多種預處理功能
1、預定義符號
__FILE__:表示代碼所在的路徑
__LINE__:表示代碼所在行數
__DATE__:獲取日期
__TIME__:獲取時間
printf("%s\n", __FILE__);//列印代碼所在檔案
printf("%d\n", __LINE__);//列印該句代碼所在的行數
printf("%s\n", __DATE__);//列印當前日期
printf("%s\n", __TIME__);//列印當前時間
2、預處理指令
包括:
#define,#include,#program,#if,#endif,#ifdef,#ifdef等等
#define可以宣告常量、運算式,#define定義的宏是直接替換,而不是賦值
比如說下面這段代碼
#define Add(a,b) a+b
printf("%d", 5 * Add(3, 4));
這題的結果是19而不是35,在預編譯之后,printf陳述句會變成printf("%d", 5 * 3 + 4);
采用宏的方式,使用#也可以將一個宏引數變成字串,例如
#define PRINT(X) printf("I love "#X"\n")
PRINT(CSDN);
運行結果:I love CSDN
##在宏中的作用
把位于他兩邊的符號合成一個符號
#define sum##num等效于#define sumnum
宏的好處沒有型別的概念,直接替換在某些場合使用宏可以避免型別不匹配的問題,且效率比函式高,沒有函式呼叫和回傳的開銷,
宏的缺點就是無法除錯,因為沒有型別,所以不夠嚴謹,無法做型別檢查,
條件編譯
1、#ifdef、#ifndef和#endif
#ifdef DEBUG//如果定義了DEBUG就執行下面的代碼
printf("DEBUG\n");
#endif
#ifndef RELEASE//如果沒有定義了RELEASE就執行下面的代碼
printf("RELEASE\n");
#endif
#ifndef、#define、#endif或者#program once可以解決頭檔案重復多次包含
#ifndef _TEST_H_
define _TEST_H_
//…………
#endif
//或者采用program once
#program once
2、#if、#elif和#else
#if 運算式
//……
#elif 運算式
//……
#else 運算式
//……
#endif 運算式
//如果運算式為真則參與編譯
#include
#include<>與#include" "的區別
<>是直接去標準庫函式路徑下去找 ,如果找不到就報錯,
" "是首先在源檔案所在目錄查找,如果找不到,就去標準庫函式路徑底下去找,如果再找不到,就會報錯,""的效率要低一些,
offsetof
計算結構體成員偏移量的宏
如果不清楚結構體大小如何計算,可以參考這篇文章
https://blog.csdn.net/weixin_43164548/article/details/118405163?spm=1001.2014.3001.5501
https://blog.csdn.net/weixin_43164548/article/details/118405163?spm=1001.2014.3001.5501采用C語言官方的例子
struct foo {
char a;
char b[10];
char c;
};
int main ()
{
printf ("offsetof(struct foo,a) is %d\n",(int)offsetof(struct foo,a));
printf ("offsetof(struct foo,b) is %d\n",(int)offsetof(struct foo,b));
printf ("offsetof(struct foo,c) is %d\n",(int)offsetof(struct foo,c));
return 0;
}
運行結果:
offsetof(struct foo, a) is 0
offsetof(struct foo, b) is 1
offsetof(struct foo, c) is 11
那么offsetof是如何實作的呢?
#define OFFECTOF(struct_name, member_name) (int)&((struct_name* )0->member_name)
//直接假設從起始地址0開始,取出每個成員的地址就是偏移量
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/300785.html
標籤:其他
