<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
要支援c語言的實現,會有不同的編譯器出現,而這些編譯器都要遵循ANSI C,都存在兩種環境
第1種是翻譯環境,在這個環境中原始碼被轉換為可執行的機器指令。 第2種是執行環境,它用於實際執行程式碼。
.obj為字尾的就是目標檔案
而一個專案中可能會有很多.c字尾的原始檔,分別處理後每經過編譯器單獨處理,然後會生成對應的目標檔案(.obj),然後總體經過聯結器處理,最終變成可執行程式。
目標檔案最後還要加上連結庫整體一起通過連結器連結,變成可執行程式.
連結庫:在編寫程式碼的時候,會有一些不屬於我們自己寫的函數(如printf),這些函數是自帶的庫裡面包含的,這些庫就叫連結庫
(補函數的宣告與定義裡面的靜態庫)
從原始檔生成可執行程式的這一個過程就叫做翻譯環境
預編譯→編譯→組合
預編譯(預處理):
文字操作:
1.標頭檔案的包含,#include——預編譯指令,將包含的標頭檔案給展開
2.刪除註釋(註釋被空格替換)
3.#define定義符號的替換
生成.s的檔案
把c語言程式碼轉換成組合程式碼
1.語法分析
2.詞法分析
3.語意分析
4.符號彙總——彙總的是全域性符號
《程式設計師的自我修養》——通俗地講解程式碼編譯過程的細節
組合:
生成了test.o
把組合程式碼轉換成二進位制指令
形成符號表:
框內是十六進位制是地址
連結:
最終將.o檔案連結成.exe可執行程式
1.合併段表
2.符號表的合併和重定位(像Add一開始地址為預設0和另一個.c檔案內的Add地址的為0x200,會重新定位)
符號表的意義:多個目標檔案進行連結的時候會通過符號表檢視來自外部的符號是否真實存在
1.程式必須載入記憶體中。在有作業系統的環境中:一般這個由作業系統完成。在獨立的環境中,程式的載入必須由手工安排(電焊好伐),也可能是通過可執行程式碼置入唯讀記憶體來完成。
2.程式的執行便開始。接著便呼叫main函數。
3.開始執行程式程式碼。這個時候程式將使用一個執行時堆疊(stack)(也就是之前部落格中寫到的函數棧幀的建立與銷燬),儲存函數的區域性變數和返回地址。程式同時也可以使用靜態(static)記憶體,儲存於靜態記憶體中的變數在程式的整個執行過程一直保留他們的值。
4.終止程式。正常終止main函數;也有可能是意外終止。
作用:記錄紀錄檔:可記錄在哪個檔案在哪個日期在什麼時候在哪個檔案在哪一行
#define _CRT_sECURE_NO_WARNINGS 1 #include <stdio.h> int main() { printf("%sn", __FILE__); printf("%sn", __TIME__); printf("%dn", __LINE__); printf("%sn", __DATE__); return 0; }
預處理符號的應用:
//預處理符號的應用——寫紀錄檔 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> #include <errno.h> int main() { int i = 0; FILE* pf = fopen("test.txt", "w"); if (NULL == pf) { printf("error is%sn", strerror(errno)); return 0; } for (i = 0; i < 5; i++) { fprintf(pf, "%st%st%st%dti=%dn", __FILE__, __DATE__, __TIME__, __LINE__, i); } fclose(pf); pf = NULL; return 0; }
兩種用法:
#define MM 100 #define reg register——關鍵字替換
#define末尾有時候可以加分號有時又不可以加上分號,
不可以加上分號的情況:
//不可以加上分號的情況 #define _CRT_SECURE_NO_WARNINGS 1 #define MAX(x,y) ((x)>(y)?x:y); #include <stdio.h> int main() { int a = 5; int b = 3; printf("%dn", MAX(a, b)); return 0; }
因為加上分號會使得宏在替換的時候也帶上分號,所以在呼叫在一些函數內部的時候會出現錯誤。
綜上,當我們定義宏的時候,最好不要加分號在末尾。
這裡也是將全部引數給替換掉,在預處理的時候就替換掉了,不信的話可以在解決方案處右擊,點選屬性後選擇預處理,然後就可以在debug裡面發現又應該.i檔案,點開後就可以發現這裡已經被替換掉了。
#define Max(x,y) ((x)>(y)?(x):(y)) // Max->宏的名字 // x和y->宏的引數 // ((x)>(y)?(x):(y))->宏的內容
ps:在定義宏的內容的時候,最好每個引數都要加上小括號,然後最後整體加上小括號,否則如果傳入引數不是單獨一個值而是表示式的時候,會產生一些沒有意料到的優先順序計算改變
Tips:宏後面的引數的小括號一定要緊挨著宏的名
1.先看宏的引數內是不是有define的符號,優先替換掉define符號
2.對於宏,引數名被他們的值替換
注意!!
1.宏的引數裡可以出現其他#define定義的符號,但不可以遞迴
2.當define掃描預處理時,字串常數的內容並不被搜尋(也就是說字串裡面的東西是不會被宏預處理的)
#
相當於把宏的引數放進字串中變成所對應的字串
// #的用法 #define _CRT_SECURE_NO_WARNINGS 1 #define print(x) printf("the value of " #x " is %dn",x) #include <stdio.h> int main() { int a = 5; int b = 4; print(a); print(b); return 0; }
##
可以把兩邊分離片段合成一個符號
#define CAT(C,num) C##num int main() { int Class104=10000; printf("%dn",CAT(Class,104)); return 0; }
#define MAX(x,y) ((x)>(y)?(x):(y)) int main() { int a=3; int b=5; int m=MAX(a++,b++);//宏的引數是直接替換進去 所以替換完之後為: int m=((a++)>(b++)?(a++):(b++));//會出現錯誤 printf("%dn",m); printf("%d %dn",a,b); return 0; }
宏的優點:
1.用於呼叫函數和從函數返回的程式碼可能比實際執行這個小型計算工作所需要的時間更多。 所以宏比函數在程式的規模和速度方面更勝一籌
2.更為重要的是函數的引數必須宣告為特定的型別。 所以函數只能在型別合適的表示式上使用。反之這個宏怎可以適用於整形、長整型、浮點型等可以 用於>來比較的型別。
宏的缺點:
1.每次使用宏的時候,一份宏定義的程式碼將插入到程式中。除非宏比較短,否則可能大幅度增加程式的長度。
2.宏是沒法偵錯的。
3.宏由於型別無關,也就不夠嚴謹。
4.宏可能會帶來運運算元優先順序的問題,導致程容易出現錯
可從以下方面比較宏與函數的區別:
一般來講宏與函數的使用語法很類似,所以以後使用這種方法區分宏與函數:
去除一個宏定義
#undef 宏名
許多C 的編譯器提供了一種能力,允許在命令列中定義符號。用於啟動編譯過程。 例如:當我們根據同一個原始檔要編譯出不同的一個程式的不同版本的時候,這個特性有點用處。(假定某個程式中宣告了一個某個長度的陣列,如果機器記憶體有限,我們需要一個很小的陣列,但是另外一個機器記憶體大些,我們需要一個陣列能夠大些。)
#include <stdio.h> int main() { int array [SZ]; int i = 0; for(i = 0; i< SZ; i ++) { array[i] = i; } for(i = 0; i< SZ; i ++) { printf("%d " ,array[i]); } printf("n" ); return 0; }
在這裡我們可以知道SZ這個符號始終沒有被定義,到這裡為止我們的程式還是無法執行的,會報錯,但是:
編譯指令:
//linux 環境演示 gcc -D SZ=10 programe.c
在編譯完這一行以後程式就能夠執行了,這是因為我們在命令列中已經將SZ這個符號定義好了。
有一段程式碼,編譯了麻煩,刪去了可惜,這時可以選擇是否編譯,這時候就要用到條件編譯。
應用場景:當我們使用在不同系統時,比如在用到windows系統時我們需要用到這一段程式碼,而在Linus系統上又要用到另一段程式碼而不能用windows那段程式碼的時候,不可以刪除,因為要實現一個程式的跨平臺使用,這時候就需要用到條件編譯來選擇什麼時候使用哪段程式碼。
Tips:我們要明確條件編譯指令也是預處理指令
//條件編譯 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { int i = 0; for (i = 0; i < 10; i++) { #if 1 //這裡的常數非0,於是執行,如果為0,則不執行 printf("%dn", i); #endif return 0; } }
不能放變數,因為是預處理階段執行的,而變數在預處理中還沒有出現,所以我們只能放常數進去,否則放變數進去的話只能判定為0。
1.#if 常數/常數表示式 #endif
2.#if 常數表示式 #elif 常數表示式 #else 常數表示式 #endif
3.判斷是否被定義:只要你定義了宏就為真
#if !defined(宏名) ...#endif #ifdef !宏名 ... #endif
4.巢狀指令
//全部型別的條件編譯 #define _CRT_SECURE_NO_WARNINGS 1 #define VALUE 200 #define TEST 20 #include <stdio.h> int main() { int i = 0; for (i = 0; i < 10; i++) { #if 1 printf("%dn", i); #endif } #if VALUE<100 printf("value<100n"); #elif VALUE>=100&&VALUE<=150 printf("value>=100且value<=150n"); #else printf("value>150n"); #endif #ifdef VALUE printf("VALUE已定義n"); #else printf("VALUE未定義n"); #endif #if defined(VALUES) printf("VALUES已定義n"); #else printf("VALUES未定義n"); #endif //巢狀指令 #if VALUE<=150 #if defined(VALUE) printf("VALUE小於或等於150,VALUE已定義n"); #else printf("VALUE小於或等於150,VALUE未定義n"); #endif #elif VALUE>150&&VALUE<=200 #ifdef TEST printf("VALUE大於150且小於等於200,TEST已定義n"); #else printf("VALUE大於150且小於等於200,TEST未定義n"); #endif #endif return 0; }
我們知道在預編譯時會包含標頭檔案,而標頭檔案例如<stdio.h>從預編譯.i檔案上看我們可以知道有2000多行程式碼,若真的重複包含了五六次,則程式碼量直接上升到了一萬多行,這時候就會使得程式碼過於冗長,同時也佔用很多記憶體,這個時候我們就需要檔案包含來確認是否重複包含了同一個標頭檔案。
方法一:
//在標頭檔案中 #ifndef __TEST_H__ #define __TEST_H__ int Add(int x,int y); #endif
方法二:
//在標頭檔案中 #pragma once int Add(int x,int y);
ps:這個#pragma once是比較高階的寫法,在遠古編譯器裡面是無法使用的(如vc)
在小綠本人之前的三子棋以及掃雷的部落格中都有自己創造標頭檔案而我們在參照自己創造的標頭檔案時是以#include "fun.h" 這樣的形式參照的,但是在參照庫函數時確實以#include <stdio.h>的方式參照,那麼用不同的符號參照標頭檔案有什麼不一樣呢?
""的查詢策略
""的查詢策略是:先在原始檔所在的目錄下查詢,如果沒有找到,編譯器就像查詢庫函數標頭檔案一樣在標準位置查詢標頭檔案。再如果找不到就提示編譯錯誤。
<>的查詢策略
查詢標頭檔案直接去標準位置下查詢,如果找不到就直接提示編譯錯誤。
本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45