C語言編譯和編譯預處理

編譯預處理

? 編譯是指把高級語言編寫的源程序翻譯成計算機可識別的二進制程序(目標程序)的過程,它由編譯程序完成。
? 編譯預處理是指在編譯之前所作的處理工作,它由編譯預處理程序完成

在對一個源程序進行編譯時,系統將自動調用預處理程序對源程序中的預處理部分作處理,處理完畢后自動編譯源程序

宏定義

? 宏:C語言源程序中允許用一個標識符來表示一個字符串
? 宏名:被定義為宏的標識符
? 宏展開:在編譯預處理時,對程序中所有出現的宏名,都用宏定義中的字符串替換
? 宏定義由宏定義命令完成
? 宏展開由預處理程序自動完成
? 宏分為:不帶參數的宏和帶參數的宏

不帶參數的宏定義

? 不帶參數的宏,其宏名后不帶參數

? ? ? ? #define? 標識符? 字符串

? ? ? ? #define PI 3.1415926

? ? ? ?“標識符”是為所定義的宏名

? ? ? ?“字符串”為宏名將要被替換的字符串,可以是常量字符串、表達式字符串、格式字符串等?

宏定義

常量宏定義

//【例9. 1】常量的宏定義。
#include <stdio.h>
#define PI 3.1415926
void main()
{	float r,l,s,v;printf("Input radius:  ");scanf("%f",&r);l=2.0*PI*r;	//宏展開為  l=2.0*3.1415926*r;s=PI*r*r;	//宏展開為  s=3.1415926*r*r;v=4.0/3*PI*r*r*r; //宏展開為  v=4.0/3*3.1415926*r*r*r;printf("l=%.4f\ns=%.4f\nv=%.4f\n",l,s,v);
} 

表達式的宏定義

//【例9. 2】表達式的宏定義。
#include <stdio.h>
#define M (y*y+3*y)
void main()
{   int s,y;printf("Input a number:  ");scanf("%d",&y);s=3*M+4*M+5*M;  //宏展開為  s=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y);printf("s=%d\n",s);
}

函數名和格式字符串的宏定義

//【例9. 3】函數名和格式字符串的宏定義。
#include <stdio.h>
#define P printf
#define F "%4d\t%.2f\n"
void main()
{	int a=3, c=5, e=11;float b=4.6, d=7.9, f=22.08;P(F,a,b);  //宏展開為 printf("%4d\t%.2f\n",a,b);P(F,c,d);  //宏展開為 printf("%4d\t%.2f\n",c,d);P(F,e,f);  //宏展開為 printf("%4d\t%.2f\n",e,f);
}

? 說明:
? 1 )宏不是變量,不能存數據,也沒有數據類型
? 2 )宏定義與變量定義不同,它只作字符串替換,不分配內存空間
? 3 )宏名習慣上用大寫字母表示,以便與變量名相區別,但也允許用小寫字母
? 4 )宏定義是用宏名來表示一個字符串,在宏展開時又以該字符串替換宏名,這只是一種簡單的源程序代碼的替換。
? 字符串中可以包含任何字符,預處理程序不作任何正確性檢查。只有在編譯已經預處理后的源程序時才能發現

????????define PI 3.l4l5926

????????把數字“1”寫成了小寫字母“l”

? 5 宏定義不是語句或說明,在行末不必加分號 ,如加上分號則連分號也一起替換

????????#define PI 3.1415926;

????????……

????????area=PI*r*r;

????????……

????????宏展開后為: area=3.1415926;*r*r;

? 6 在源程序中用雙引號引起來的字符串內,與宏名相同的字符不進行替換
//【例9. 4】雙引號中與宏名相同的字符不作替換。
#include <stdio.h>
#define PI 3.1415926
void main()
{	printf("PI\n");	//不進行宏展開printf("%f\n",PI);  //進行宏展開
}
? 7 )宏定義允許嵌套,在宏定義的字符串中可以使用已經定義的宏名
#define PI 3.1415926
#define S PI*r*r????????? // PI 是已定義的宏名
語句: printf("%f",S);
宏展開后為: printf("%f",3.1415926*r*r);

????????8)宏定義必須寫在函數之外,其作用域為宏定義命令起到源程序結束。如要終止其作用域可使用# undef命令。

//【例9. 5】使用# undef結束宏的作用域。
#include <stdio.h>
#define PI 3.1415926
void main()
{    float r=2,area;area=PI*r*r;  
//宏展開為area=3.1415926*r*r; printf("area=%f",area);
}
#undef PI
f1()
{    float r=2,area;area=PI*r*r;  
//PI不能被宏展開,此處語法報錯,PI沒有定義printf("area=%f",area);
}

帶參數的宏定義

? C語言允許宏帶參數。
? 宏定義中的參數稱為形式參數,在程序中使用宏的語句中的參數稱為實際參數。
? 在預編譯時,帶參數的宏不但要進行宏展開,而且要用實參去替換形參。
?? #define? 宏名 ( 形參表 )? 字符串
? 在源程序中使用帶參數的宏的一般形式為 :
?? 宏名 ( 實參表 )

//【例9. 6】帶參數的宏定義。
#include <stdio.h>
#define MAX(a,b) (a>b)?a:b
void main()
{	int x,y,max;printf("Input two numbers:");scanf("%d,%d",&x,&y);max=MAX(x,y);  //宏展開為   max=(x>y)?x:y;printf("max=%d\n",max);
}
? 1 )宏名和形參表外的括號之間不能加空格。否則將空格以后的字符都作為替代字符串的一部分。
? 例如 :
? 宏定義“ #define? MAX(a,b)? (a>b)?a:b”
? 改寫為:
??????????? #define? MAX? (a,b)? (a>b)?a:b

語句:?????????? max=MAX(x,y);

宏展開后為:max=(a,b)? (a>b)?a:b(x,y); 是錯誤的。

2)宏定義中的形參是標識符,語句中的實參可以是表達式

【例9. 7】語句中的實參為表達式。
#include <stdio.h>
#define SQ(y) (y)*(y)
void main()
{    int a,sq;printf("Input a number:  ");scanf("%d",&a);sq=SQ(a+1);   //宏展開為 sq=(a+1)*(a+1);printf("sq=%d\n",sq);
}

3)在宏定義中,形參通常要用括號括起來以避免出錯。

宏定義:

?????? #define SQ(y) (y)*(y)

(y)*(y)表達式的y都用括號括起來,因此結果是正確的

如果去掉括號,定義形式如下:

?????? #define SQ(y) y*y

語句:??????????? sq=SQ(a+1);

宏展開后為:sq=a+1*a+1;

? 有時即使在參數兩邊加括號還是不夠的

按以下形式定義:

??????????? #define SQ(y) (y)*(y)

sq=1.0/SQ(a+1);

宏展開后為:

??????????? sq=1.0/(a+1)*(a+1);??? //先算除法,再算乘法

要想先算乘法后算除法,應該在宏定義中的整個字符串外加括號,按如下形式定義:

????????? #define SQ(y) ((y)*(y))

帶參數的宏和函數的區別

? 帶參數的宏和帶參數的函數的區別:
? 1 )函數調用時,將值傳給形參。帶參數的宏展開時只是完整的實參表達式字符替代形參。
? 2 )函數調用在程序運行時進行。宏展開在編譯前進行。
? 3 )函數中的形參和實參都要定義類型。宏的形參無需定義類型,它們都只是一串字符。
? 4 )調用函數只可得到一個返回值,而宏可以設法得到幾個結果。
//【例9. 8】通過宏展開得到若干結果。
#include <stdio.h>
#define SSSV(L,W,H,SA,SB,SC,VV) SA=L*W;SB=L*H;SC=W*H;VV=W*L*H;
void main()
{   int l=3,w=4,h=5,sa,sb,sc,vv;SSSV(l,w,h,sa,sb,sc,vv);   //宏展開后為sa=l*w;sb=l*h;sc=w*h;vv=w*l*h;printf("sa=%d\nsb=%d\nsc=%d\nvv=%d\n",sa,sb,sc,vv);
} 

調用函數只可得到一個返回值,而宏可以設法得到幾個結果。

文件包含

? 文件包含是指一個源文件中可以包含另一個源文件,即把另一個源文件插入到該文件中

文件包含命令的一般形式為:

#include "文件名"

#include <文件名>

? 文件包含命令的功能是把文件名所指定的文件插入到該命令行位置并取代該命令行,從而把指定的文件與當前的源程序文件連成一個源文件
?? ????????? #include"file2.c"
//【例9. 9】文件包含命令的使用。
(1)文件file1.c內容如下:
#include <stdio.h>
#include "file2.c"
void main()
{    int a,b,c;printf("Input two numbers: ");scanf("%d,%d",&a,&b);c=max(a,b);printf("max=%d\n",c);
}
//(2)文件file2.c內容如下:
int max(int x,int y)
{    int z;if(x>y)    z=x;else     z=y;return(z);
}
? 1 )文件頭部的被包含文件,常以“ .h” 為后綴。也可用“ .c” 或“ .cpp” 為后綴,也可無后綴。
? 2 )一個 #include 命令只能指定一個被包含文件
? 3 )文件包含允許嵌套,即在一個被包含的文件中又可以包含另一個文件

? 4 )包含命令中的文件名可以用雙引號括起來,也可以用尖括號括起來
?? #include <stdio.h>
? ? #include "file2.h "
? 使用尖括號時,系統直接在存放 C 庫函數頭文件的系統目錄中尋找,這稱為標準方式。
? ②使用雙引號時,系統先在用戶當前目錄(即源文件所在目錄)中尋找,若找不到,再按標準方式查找(即按尖括號的方式查找)

條件編譯

? 條件編譯:有時希望程序中一部分語句只在滿足一定條件時才進行編譯,不滿足條件時不進行編譯,或編譯另一部分語句
? 利用條件編譯,可以減少程序的輸入,方便程序的調試,增強程序的可移植性

條件編譯-形式一

#ifdef? 標識符

? 程序段1

#else

? 程序段2

#endif

#ifdef? 標識符

? 程序段

#endif

功能是:如果所指定標識符在之前已被 #define命令定義過,則在編譯時編譯程序段1;否則編譯程序段2

9. 10】給定半徑r,求圓的面積s。要求設置條件編譯:若π值已定義,則直接計算面積;若π值未定義,則定義π值后再計算面積。

#include <stdio.h>

void main()

{??? float r,s;

??? printf("Input radius:? ");

??? scanf("%f",&r);

??? #ifdef PI??? //條件編譯

??????? s=PI*r*r;????????????? //程序段1

??? #else

??????? #define PI 3.1415926?? //程序段2

??????? s=PI*r*r;????????????????

??? #endif

??? printf("s=%f\n",s);

}

1)程序中在條件編譯前未定義π值(即PI),所以編譯程序段2

2)若程序開頭加入宏定義“#define PI 3.1415926”,則編譯程序段1?

條件編譯-形式二

#ifndef 標識符

? 程序段1

#else

? 程序段2

#endif

功能是:如果標識符之前未被#define命令定義過,則對程序段1進行編譯,否則對程序段2進行編譯

9. 11】按形式二修改9. 10】的程序。

#include <stdio.h>

void main()

{??? float r,s;

??? printf("Input radius:? ");

??? scanf("%f",&r);

??? #ifndef PI?? //條件編譯

??????? #define PI 3.1415926?? //程序段1

??????? s=PI*r*r;????????????????

??? #else

??????? s=PI*r*r;????????????? //程序段2

??? #endif

??? printf("s=%f\n",s);

}

1)程序中在條件編譯前未定義π(即PI),所以編譯程序段1

2)若在程序的開頭加入宏定義“#define PI 3.1415926”,則編譯程序段2

條件編譯-形式三

#if 表達式

? 程序段1

#else?

? 程序段2

#endif

功能是:若表達式的值為真(非0),則編譯程序段1,否則編譯程序段2

9. 12】設置條件編譯。求圓的面積或正方形的面積。

#include <stdio.h>

#define PI 3.1415926

#define R 1

void main()

{??? float c,s;

??? printf ("Input a number:? ");

??? scanf("%f",&c);

??? #if R??????? //條件編譯

??????? s=PI*c*c;?????????? //程序段1

??????? printf("Area of circle is : %f\n",s);

??? #else

??????? s=c*c;???????????????? //程序段2

??????? printf("Area of square is : %f\n",s);

??? #endif

}

程序宏定義中,定義R1,因此在條件編譯時,表達式R的值為真,故編譯程序段1,求圓的面積

說明

? 1 )三種形式的條件編譯必須嚴格按照形式說明中的格式書寫,每條條件編譯命令必須單獨成行
?? #if R s=PI*c*c;??? // 出錯
? ? 將程序段“ s=PI*c*c;” 與條件編譯命令“ #if R” 寫在同一行,是不正確的
? 2 )形式一和形式二中的標識符,若在條件編譯之前被 #define 命令定義過,不管被定義為何值,甚至不定義任何值,只要被定義過,都會編譯相應的程序段

#ifdef COMPUTER_A

? #define INTEGER_SIZE 16

#else

? #define INTEGER_SIZE 32

#endif

? 3 )形式三與形式一和形式二不同,“ #if” 后為表達式,不是標識符,所以不存在定義過還是未定義過的問題。只要該表達式的值為真(非 0 ),就編譯程序段 1 ,否則編譯程序段 2
? 4 )條件編譯命令允許嵌套使用
? 在條件編譯中也可以使用語句: #elif 它代表 else if

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/40568.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/40568.shtml
英文地址,請注明出處:http://en.pswp.cn/web/40568.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

全國青少年軟件編程等級考試-四級-奇偶之和(真題)

題目&#xff1a;奇偶之和 1.準備工作 (1)保留舞臺中的小貓角色&#xff1b; 2.功能實現 (1)分別計算1&#xff5e;100中&#xff0c;奇數之和&#xff0c;偶數之和&#xff1b; (2)說出奇數之和&#xff0c;偶數之和。 講解&#xff1a; 1、如何判斷奇偶數 奇數是指除以2有…

C++deque容器

文章目錄 deque容器概念deque操作deque對象的帶參數構造deque頭部和末尾的添加移除操作deque的數據存取deque與迭代器deque賦值deque插入deque刪除 deque容器概念 deque是雙端數組&#xff0c;而vector是單端的。 ?deque頭部和尾部添加或移除元素都非常快速, 但是在中部安插元…

在x86/amd64的機器上使用Docker運行arm64v8/ubuntu并安裝ROS1

一、準備工作 主要是因為國內網絡的問題&#xff0c;可能導致鏡像拉取失敗&#xff0c;解決辦法參考&#xff1a;鏡像加速 二、安裝運行過程 2.1拉取鏡像&#xff1a; sudo docker pull arm64v8/ubuntu:20.04這個是ubuntu的拉取指令&#xff0c;其他的也是類似。 2.2 運行…

【DevOps】運維過程中經常遇到的Http錯誤碼問題分析(一)

一、解決HTTP 408錯誤&#xff1a;上傳3M文件時請求超時的問題 在開發Web應用程序時&#xff0c;遇到HTTP 408狀態碼&#xff08;請求超時&#xff09;是常見的問題。特別是在上傳大文件時&#xff0c;這種情況更容易發生。本文將探討在上傳一個3M文件時&#xff0c;Web服務器…

LeetCode題練習與總結:排序鏈表--148

一、題目描述 給你鏈表的頭結點 head &#xff0c;請將其按 升序 排列并返回 排序后的鏈表 。 示例 1&#xff1a; 輸入&#xff1a;head [4,2,1,3] 輸出&#xff1a;[1,2,3,4]示例 2&#xff1a; 輸入&#xff1a;head [-1,5,3,4,0] 輸出&#xff1a;[-1,0,3,4,5]示例 3&am…

封鎖-封鎖模式(共享鎖、排他鎖)、封鎖協議(兩階段封鎖協議)

一、引言 1、封鎖技術是目前大多數商用DBMS采用的并發控制技術&#xff0c;封鎖技術通過在數據庫對象上維護鎖來實現并發事務非串行調度的沖突可串行化 2、基于鎖的并發控制的基本思想是&#xff1a; 當一個事務對需要訪問的數據庫對象&#xff0c;例如關系、元組等進行操作…

【嵌入式開發 Linux 常用命令系列 1.6 -- grep 過濾指定的目錄】

請閱讀【嵌入式開發學習必備專欄 】 文章目錄 grep 過濾指定目錄 grep 過濾指定目錄 在Linux中使用grep搜索字符串并希望排除特定目錄時&#xff0c;可以使用--exclude-dir參數。這個參數允許你指定一個或多個目錄名稱來排除它們的內容不被grep搜索。這對于忽略一些常見的臨時…

LLM - 詞向量 Word2vec

1. 詞向量是一個詞的低維表示&#xff0c;詞向量可以反應語言的一些規律&#xff0c;詞意相近的詞向量之間近乎于平行。 2. 詞向量的實現&#xff1a; &#xff08;1&#xff09;首先使用滑動窗口來構造數據&#xff0c;一個滑動窗口是指在一段文本中連續出現的幾個單詞&#x…

llamaindex實戰-使用本地大模型和數據庫對話

概述 本文使用NLSQLTableQueryEngine 查詢引擎來構建SQL的自然語言處理查詢。 請注意&#xff0c;我們需要指定要與該查詢引擎一起使用的表。如果我們不這樣做&#xff0c;查詢引擎將提取所有架構上下文&#xff0c;這可能會溢出 LLM 的上下文窗口。 在以下情況都可以使用NL…

如何用Java寫一個整理Java方法調用關系網絡的程序

大家好&#xff0c;我是猿碼叔叔&#xff0c;一位 Java 語言工作者&#xff0c;也是一位算法學習剛入門的小學生。很久沒有為大家帶來干貨了。 最近遇到了一個問題&#xff0c;大致是這樣的&#xff1a;如果給你一個 java 方法&#xff0c;如何找到有哪些菜單在使用。我的第一想…

線程中如何有效避免死鎖問題

1. 理解死鎖形成的原因 互斥條件&#xff1a;一個資源每次只能被一個線程使用。 請求與保持條件&#xff1a;線程因請求資源而阻塞時&#xff0c;對已獲得的資源保持不放。 不剝奪條件&#xff1a;線程已獲得的資源&#xff0c;在末使用完之前&#xff0c;不能強行剝奪。 循環…

c++ primer plus 第15章友,異常和其他:15.1.3 其他友元關系

c primer plus 第15章友&#xff0c;異常和其他&#xff1a;15.1.3 其他友元關系 提示&#xff1a;這里可以添加系列文章的所有文章的目錄&#xff0c;目錄需要自己手動添加 15.1.3 其他友元關系 提示&#xff1a;寫完文章后&#xff0c;目錄可以自動生成&#xff0c;如何生成可…

整潔架構SOLID-單一職責原則(SRP)

文章目錄 定義案例分析重復的假象代碼合并解決方案 小結 定義 SRP是SOLID五大設計原則中最容易被誤解的一個。也許是名字的原因&#xff0c;很多程序員根據SRP這個名字想當然地認為這個原則就是指&#xff1a;每個模塊都應該只做一件事。 在歷史上&#xff0c;我們曾經這樣描…

科研繪圖系列:R語言雙側條形圖(bar Plot)

介紹 雙側條形圖上的每個條形代表一個特定的細菌屬,條形的高度表示該屬的LDA得分的對數值,顏色用來區分不同的分類群或組別,它具有以下優點: 可視化差異:條形圖可以直觀地展示不同細菌屬在得分上的差異。強調重要性:較高的條形表示某些特征在區分不同組別中具有重要作用…

# Sharding-JDBC從入門到精通(6)-- Sharding-JDBC 水平分庫 和 垂直分庫。

Sharding-JDBC從入門到精通&#xff08;6&#xff09;-- Sharding-JDBC 水平分庫 和 垂直分庫。 一、Sharding-JDBC 水平分庫-分片策略配置 1、分庫策略定義方式如下 # 分庫策略&#xff0c;如何將一個邏輯表映射到多個數據源 spring.shardingsphere.sharding.tables.<邏…

第33集《大乘起信論》

《大乘起信論》和尚尼慈悲&#xff0c;諸位法師、諸位居士&#xff0c;阿彌陀佛&#xff01;&#xff08;阿彌陀佛&#xff01;&#xff09;請大家打開《講義》第七十四頁&#xff0c;子二、釋觀。 本論的特色&#xff0c;一言以蔽之就是文簡意賅、辭約理富&#xff0c;就是說…

VUE2拖拽組件:vue-draggable-resizable-gorkys

vue-draggable-resizable-gorkys組件基于vue-draggable-resizable進行二次開發, 用于可調整大小和可拖動元素的組件并支持沖突檢測、元素吸附、元素對齊、輔助線 安裝: npm install --save vue-draggable-resizable-gorkys 全局引用: import Vue from vue import vdr fro…

嵌入式linux面試1

1. linux 1.1. Window系統和Linux系統的區別 linux區分大小寫windows在dos&#xff08;磁盤操作系統&#xff09;界面命令下不區分大小寫&#xff1b; 1.2. 文件格式區分 windows用擴展名區分文件&#xff1b;如.exe代表執行文件&#xff0c;.txt代表文本文件&#xff0c;.…

運用Python與Keras框架打造深度學習圖像分類應用:詳盡步驟與代碼實例解析

引言 隨著深度學習技術的飛速發展&#xff0c;其在圖像識別和分類領域的應用日益廣泛。在這一背景下&#xff0c;Python因其豐富的數據科學庫和強大的生態系統而成為首選編程語言之一。在本文中&#xff0c;我們將深入探討如何使用Python和其中的Keras深度學習框架來完成一個實…

手動將dingtalk-sdk-java jar包打入maven本地倉庫

有時候,中央鏡像庫不一定有自己需要的jar包,這時候我們就需要用到該方法,將jar打入maven本地倉庫,然后項目中,正常使用maven的引入規則。 mvn install:install-file -Dmaven.repo.local=D:\software\maven\apache-maven-3.6.3-bin\apache-maven-3.6.3\repo -DgroupId=ding…