c语言入门-程序运行的过程
目錄
程序運行的過程
?1.編譯
預(yù)編譯
編譯:
匯編
2.鏈接
1.段表的合并
2.符號表的合并和重定位
3.運行
預(yù)處理
#define
?宏實現(xiàn)計算
?#define和typedef
?#define的替換規(guī)則
#和##---將參數(shù)插入代碼中
帶有副作用的宏
?宏和函數(shù)比較
#undef---移除被定義的宏
#if---條件編譯
?#if defined---判斷是否被定義
程序運行的過程
編譯可以分為三個部分:預(yù)編譯,編譯, 匯編。
?我們寫的代碼是文本文件(.c文件),.c文件通過編譯轉(zhuǎn)換為.obj文件,.obj文件通過鏈接轉(zhuǎn)換為.exe文件(可執(zhí)行文件)。可執(zhí)行文件在運行環(huán)境中運行。
.obj文件---二進制目標文件? ?.exe---二進制可執(zhí)行文件
?1.編譯
由于vs是集成開發(fā)環(huán)境,所以使用linux來觀察過程。
編譯可以分為三個部分:預(yù)編譯,編譯, 匯編。
預(yù)編譯
執(zhí)行操作:
處理預(yù)處理指令(#include,#if……)
去掉注釋
#define---宏展開
宏展開:?MAX改成了100
編譯:
將文本文件轉(zhuǎn)化為匯編代碼
詞法分析
語法分析
語義分析
符號匯總
詞法分析:
編譯器將代碼的字符序列自動轉(zhuǎn)化為一系列的記號。
例如:array=(i+2)*j;
經(jīng)過詞法分析,得到以下這幾個記號:
array ---標識符? ? ? ? ? ? ? = ---賦值
(---左圓括號? ? ? ? ? ? ? ??i---標識符
+---加號? ? ? ? ? ? ? ? ? ??2---數(shù)字
*---乘號? ? ? ? ? ? ? ?? ? ??j---標識符
除了識別記號的同時,掃描器也完成了其他工作,例如,將標識符存放到符號表中,將數(shù)字,字符串常量放到文字表中。
語法分析:
語法分析器對詞法分析得到的記號進行語法分析,將其化為各種表達式、或者語句等。同時進行判斷,如果出現(xiàn)結(jié)構(gòu)錯誤(例如:括號不匹配,逗號中英文格式……),編譯器就會報告語句分析階段錯誤。
語義分析:
編譯器進行語義分析的是靜態(tài)語義(編譯期間確定),靜態(tài)語義通常包括聲明和類型的匹配,類型的轉(zhuǎn)化。
例如:浮點型數(shù)據(jù)傳給整形,發(fā)生隱式類型轉(zhuǎn)化,而浮點型數(shù)據(jù)給指針,報錯。
符號匯總:
匯總?cè)址?/p>
匯編
形成符號表。在符號表中,程序中的每個標識符都和它的聲明或使用信息綁定在一起,比如其數(shù)據(jù)類型、作用范圍以及內(nèi)存地址。
將匯編代碼轉(zhuǎn)為二進制文件目標文件(.obj)
2.鏈接
.obj文件轉(zhuǎn)化為.exe文件
1.段表的合并
一個二進制文件按照段表來存儲各種信息,例如:?
?這樣的每一個段表對應(yīng)每一個.o文件(linux下的可執(zhí)行文件)
段表合并:例如text.o和add.o文件,將兩者的段表按照某種規(guī)則可以合并到一起,完成代碼實現(xiàn)。
2.符號表的合并和重定位
段表合并,其內(nèi)的符號表也會合并。合并過程中,無效的符號表信息會被有效信息代替,這就是重定位。
3.運行
1.程序載入內(nèi)存,有操作系統(tǒng)的情況下,由操作系統(tǒng)進行代碼運行。沒有時,程序根據(jù)別的方式進入內(nèi)存。
2.代碼從main函數(shù)開始運行
3.調(diào)用別的函數(shù),開辟函數(shù)棧幀。
4.代碼從main函數(shù)正常結(jié)束或者異常結(jié)束。
預(yù)處理
#include<stdio.h> int main() {printf("%s\n ", __FILE__);//代碼文件名printf("%d\n ", __LINE__);//行號printf("%s \n", __TIME__);//代碼運行時間return 0; }#define
定義宏? 定義符號
#include<stdio.h> #define MAX 100 int main() {int m = MAX;return 0; }預(yù)處理階段---處理預(yù)處理指令---展開#include<stdio.h>的內(nèi)容至800多行
預(yù)處理階段---#define直接替換---代碼里的MAX都被替換為100,MAX替換后消失
?宏實現(xiàn)計算
#include<stdio.h> #define ADD(x,y) ((x)+(y)) int main() {printf("%d ", ADD(3, 4));//x被替換為(3),y被替換為(4)return 0; }?括號的重要性---運算符的優(yōu)先順序,可能會影響值的結(jié)果
#include<stdio.h> #define MUL(x,y) x*y #define FUN(x,y)((x)*(y)) int main() {printf("%d ", MUL(3+4, 4));//結(jié)果19 x替換為3+4,y替換為4 計算式:3+4*4=19printf("%d ", FUN(3 + 4, 4));//結(jié)果28 x替換為(3+4),y替換為(4) 計算式:(3+4)*4=28return 0; }?#define和typedef
#include<stdio.h> #define INT1 int //INT1定義為int,INT1是標識符,預(yù)編譯被替換為int typedef int INT2;//int被重命名為INT2,INT2是類型 int main() {int m = 10;printf("%d ", m);INT1 a = 20;printf("%d ", a);INT2 b = 30;printf("%d ", b);return 0; } #include<stdio.h> typedef int* type1;//int*被重命名為type1,type1是類型 #define type2 int*//type2是標識符,內(nèi)容是int*,預(yù)編譯產(chǎn)生替換 int main() {type2 a, b;//type2被替換為int*,int*a,b ---a是int類型的指針,b是int類型的變量type1 c, d;//type1是一種類型---c,d都是int類型的指針return 0; }?#define的替換規(guī)則
#define在預(yù)編譯期間替換
宏不可以實現(xiàn)遞歸,但是可以嵌套使用,例如:ADD(ADD(3,4),5)
#和##---將參數(shù)插入代碼中
在C語言中宏定義中,#的功能是將其后面的宏參數(shù)進行字符串化操作。
#include<stdio.h> #define print( ch) printf(""#ch"的值%d\n", ch) #define fun(ch) printf(""#ch"") int main() {int a = 10;printf("%c的值%d\n", 'a', 10);int b = 20;printf("%c的值%d\n",'b',b);print(a);//相當于printf("%c的值%d\n", 'b', b);print(b);fun(ch);//打印ch//相當于 printf("%s",ch)return 0; }##是連接符,前加##或后加##,將標記作為一個合法的標識符的一部分,不是字符串.多用于多行的宏定義中。
#include<stdio.h> #define fun(x,y) x##y int main() {int arr = 10;printf("%d ", fun(ar, r));//ar r在fun里面連接成為arrreturn 0; }帶有副作用的宏
替換過程中,例如下列代碼的自增操作,使變量的值方式改變
#include<stdio.h> #define MAX(x,y) ((x)>(y)?(x):(y)) int main() {printf("%d ", MAX(3 + 2, 1));int a = 2;int b = 3;printf("%d ", MAX(a++, b++));//((a++) > (b++) ? (x) : (y));// 2>3判斷,a變?yōu)?,b變?yōu)?return 0; } #include<stdio.h> int MAX(x, y) {int m = x > y ? x : y;return m; } int main() {printf("%d ", MAX(3 + 2, 1));int a = 2;int b = 3;printf("%d ", MAX(a++, b++));// 函數(shù)傳入2,3 打印結(jié)束之后a為3,b為4printf("%d ", a);printf("%d ", b);return 0; }?宏和函數(shù)比較
宏優(yōu)點:
- 函數(shù)調(diào)用,每一次都要開辟函數(shù)棧幀,時間占用多,宏在預(yù)編譯時直接替換,時間性能上更好,運算速度快。
- 函數(shù)有參數(shù)的類型要求,宏沒有類型要求
宏缺點:
- 不進行類型檢查,容易出錯
- 宏代碼在每一次調(diào)用,都要插入調(diào)用函數(shù)中,如果宏內(nèi)容十分長,空間占用多。而函數(shù)調(diào)用的代碼就那一個,不會插入進調(diào)用函數(shù)。
- 宏不能調(diào)試,函數(shù)可以調(diào)試
- 宏在計算上有可能產(chǎn)生錯誤(運算符優(yōu)先級,副作用的參數(shù)……)
- 宏不能遞歸,函數(shù)可以遞歸
#undef---移除被定義的宏
#include<stdio.h> #define ADD(x,y) ( (x)+(y)) int main() {printf("%.2lf ", ADD(2.4 , 0.9)); #undef ADD//ADD被移除printf("%.2lf ", ADD(2.4 , 0.9));//error :無法引用的外部符號return 0; }#if---條件編譯
#if開始,#endif結(jié)束
#if的條件為真,#if和#endif之間的代碼參與編譯,為假時,不進行編譯
在預(yù)編譯期間進行,#if條件滿足,預(yù)編譯期間讓代碼參入了后續(xù)編譯,不滿足,則后續(xù)代碼不會出現(xiàn)#if和#endif的中間代碼內(nèi)容。
#include<stdio.h> int main() { #if 0//為假printf("hi"); #endifprintf("hello");//打印hello return 0; }?多分支的條件編譯
#include<stdio.h> #define a 1 int main() {//int a = 1; //#if 在預(yù)編譯期間運行,int a=1在編譯進行,預(yù)編譯時期沒有a//可以使用宏定義 #if a<1printf("hi"); #elif a==1printf("hello"); #else printf("he"); #endif }?#if defined---判斷是否被定義
#include<stdio.h> #define MAX 2 int main() { #if defined MAXprintf("yes"); #elseprintf("no");#endifreturn 0; }總結(jié)
以上是生活随笔為你收集整理的c语言入门-程序运行的过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【OpenCv3】 VS C++ (五)
- 下一篇: dp线长什么样子_一根DP线引发的显示器