摘要:傳送門樓下大爺看完直呼簡單語言預處理上一命令行編譯什么是命令行編譯在編譯的時候通過命令行的方式對其進行相關的定義,叫做命令行編譯。替換方式為,預處理器先刪除這條指令,并用包含文件的內容替換。
本文為C語言預處理的下篇,將繼續講解C語言預處理的基礎知識。
? 傳送門:樓下大爺看完直呼簡單!C語言預處理(上)
? 什么是命令行編譯?
? 在編譯的時候通過命令行的方式對其進行相關的定義,叫做命令行編譯。
? 介紹:許多C的編譯器提供的一種能力,允許在命令行中定義符號。用于啟動編譯過程。當我們根據同一個源文件要編譯出不同的一個程序的不同版本的時,可以用到這種特性,增加靈活性。
? 例子:假如某個程序中聲明了一個某個長度的數組,假如機器甲內存有限,我們需要一個很小的數據,但是機器丙的內存較大,我們需要一個大點的數組。
#include int main() { int arr[ARR_SIZE]; int i = 0; for (i = 0; i < ARR_SIZE; i++) { arr[i] = i; } for (i = 0; i < ARR_SIZE; i++) { printf("%d ", arr[i]); } printf("/n"); return 0;}
? gcc 環境下測試:(VS 里面不太好演示)
gcc test.c -D ARR_SIZE=5
ls
a.out? test.c
./a.out
0 1 2 3 4 5
gcc test.c -D ARR_SIZE=20
./a.out
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
? 在編譯一個程序時,通過條件編譯指令將一條語句(一組語句)編譯或者放棄是很方便的。
? 調試用的代碼刪除了可惜,保留了又礙事。我們就可以使用條件編譯來選擇性地編譯:
#include #define __DEBUG__ // 就像一個開關一樣int main(void){ int arr[10] = {0}; int i = 0; for (i = 0; i < 10; i++) { arr[i] = i; #ifdef __DEBUG__ // 因為__DEBUG__被定義了,所以為真 printf("%d ", arr[i]); // 就打印數組 #endif // 包尾 } return 0;}
? 運行結果:1 2 3 4 5 6 7 8 9 10
?? 如果不想用了,就把 #define __DEBUG__ 注釋掉:
#include // #define __DEBUG__ // 關int main(void){ int arr[10] = {0}; int i = 0; for (i = 0; i < 10; i++) { arr[i] = i; #ifdef __DEBUG__ // 此時ifdef為假 printf("%d ", arr[i]); #endif } return 0;}
? (代碼成功運行)
? 介紹:如果常量表達式為真,參加編譯。反之如果為假,則不參加編譯。
? 代碼演示:常量表達式為真
#include int main(void) {#if 1 printf("Hello,World!/n");#endif return 0;}
?? 運行結果:Hello,World!
? 代碼演示:常量表達式為假
#include int main(void) {#if 0 printf("Hello,World!/n");#endif return 0;}
? (代碼成功運行)
? 當然也可以用宏替換,可以表示地更清楚:
#include #define PRINT 1#define DONT_PINRT 0int main(void) {#if PRINT printf("Hello,World!/n");#endif return 0;}
? 介紹:多分支的條件編譯,直到常量表達式為真時才執行。
? 代碼演示:
#include int main(void) {#if 1 == 2 // 假 printf("rose/n");#elif 2 == 2 // 真 printf("you jump/n");#else printf("i jump/n")#endif return 0;}
? 代碼運行結果:you jump
? 定義:ifdef 和 if defined() ,ifndef 和 if !defined() 效果是一樣的,用來判斷是否被定義。
? 代碼演示:
#include #define TEST 0// #define TEST2 // 不定義int main(void) {/* 如果TEST定義了,下面參與編譯 */// 1#ifdef TEST printf("1/n");#endif// 2#if defined(TEST) printf("2/n");#endif/* 如果TEST2不定義,下面參與編譯 */// 1#ifndef TEST2 printf("3/n");#endif// 2#if !defined(TEST2) printf("4/n");#endif return 0;}
? 和 if 語句一樣,是可以嵌套的:
#if defined(OS_UNIX) #ifdef OPTION1 unix_version_option1(); #endif #ifdef OPTION2 unix_version_option2(); #endif#elif defined(OS_MSDOS) #ifdef OPTION2 msdos_version_option2(); #endif#endif
我們已經知道,#include 指令可以使另外一個文件被編譯。就像它實際出現于 #include 指令的地方一樣。替換方式為,預處理器先刪除這條指令,并用包含文件的內容替換。這樣一個源文件被包含10次,那就實際被編譯10次。
?? < > 和 " " 包含頭文件的本質區別:查找的策略的區別
① " " 的查找策略:先在源文件所在的目錄下查找。如果該頭文件未找到,則在庫函數的頭文件目錄下查找。(如果仍然找不到,就提示編譯錯誤)
Linux環境 標準頭文件的路徑:
/usr/include
VS環境 標準頭文件的路徑:
C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/include
② < > 的查找策略:直接去標準路徑下去查找。(如果仍然找不到,就提示編譯錯誤)
? 既然如此,那么對于庫文件是否也可以使用 " " 包含?
? 當然可以。但是這樣做查找的效率就低些,當然這樣也不容易區分是庫文件還是本地文件了。為了效率不建議這么做。
? 代碼演示:
①? add.h
int Add(int x, int y);
②? add.c
int Add(int x, int y) { return x + y;}
③? test.c
#include #include "add.h"int main(void) { int a = 10; int b = 20; int ret = Add(a, b); printf("%d/n", ret); return 0;}
? 運行結果:30
?? 頭文件重復引入的情況:
comm.h 和 comm.c 是公共模塊。
test1.h 和 test1.c 使用了公共模塊。
test2.h 和 test2.c 使用了公共模塊。
test.h 和 test.c 使用了 test1 模塊和 test2 模塊。
這樣最終程序中就會出現兩份 comm.h 的內容,這樣就造成了文件內容的重復。
? 那么如何避免頭文件的重復引入呢?
? 使用條件編譯指令,每個頭文件的開頭寫:
#ifndef __TEST_H__#define __TEST_H__// 頭文件的內容#endif
? 如果嫌麻煩,還有一種非常簡單的方法:
#pragma once // 讓頭文件即使被包含多次,也只包含一份
?
? 筆試題:選自《高質量C/C++編程指南》
① 頭文件中的 ifnde / define / endif 是干什么用的?
答:防止頭文件被重復多次包含。
② #include <filename.h> 和 #include "filename.h" 有什么區別?
答:尖括號是包含庫里面的頭文件的,雙引號是包含自定義頭文件的。它們在查找策略上不同,尖括號直接去庫目錄下查找。而警號雙引號是現去自定義的代碼路徑下查找,如果找不到頭文件,則在庫函數的頭文件目錄下查找。
?
陳正沖. 《C語言深度解剖》[M]. 第三版. 北京航空航天大學出版社, 2019.
比特科技. C語言進階[EB/OL]. 2021[2021.8.31]. .
林銳博士. 《高質量C/C++編程指南》[M]. 1.0. 電子工業, 2001.7.24.
本章完。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/118779.html
摘要:快杰云主機搭建。快杰云主機,搭載選用第二代處理器,主頻,領先的制程工藝帶來顯著的性能提升,使之打破總體性價比世界記錄。因此,糖豆與神策數據此次最終共同選擇了快杰服務器。在涼風習習的夜晚里,璀璨的燈火映照下,隨處都能碰到翩翩起舞的人群,這就是廣場舞,在這嘹亮、節奏鮮明的歌聲里有一款大媽們熱愛的APP—-?糖豆APP。 ?一、挑戰 在發展之初,多家企業看好廣場舞這個賽道,而糖豆創業初...
摘要:如果經過一系列輸入,最終如果能達到狀態,則輸入內容一定滿足正則表達式。正則表達式可以轉換為,已經有成熟的算法實現這一轉換。不過有時候轉換為可能導致狀態空間的指數增長,因此直接用識別正則表達式。 原文地址 先來看一個讓人震撼的小故事,故事來自知乎問題PC用戶的哪些行為讓你當時就震驚了? 同學在一個化妝品公司上班,旁邊一個大媽(四十多歲)發給他一個exl表,讓他在里面幫忙找一個經銷商的資料...
摘要:傳輸協議問題我們先解決第一個,傳輸協議的問題。信封里面的信分抬頭和正文板栗燜雞協議我們學過,這個請求使用方法,發送一個格式為的正文給,從而下一個單,這個訂單封裝在的信封里面,并且表明這是一筆交易,而且訂單的詳情都已經寫明了。 【前五篇】系列文章傳送門: 網絡協議 15 - P2P 協議:小種子大學問 網絡協議 16 - DNS 協議:網絡世界的地址簿 網絡協議 17 - HTTPDN...
閱讀 2375·2021-09-22 15:15
閱讀 640·2021-09-02 15:11
閱讀 1783·2021-08-30 09:48
閱讀 1883·2019-08-30 15:56
閱讀 1479·2019-08-30 15:52
閱讀 2041·2019-08-30 15:44
閱讀 430·2019-08-29 16:29
閱讀 1537·2019-08-29 11:06