欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語(yǔ)言預(yù)處理預(yù)編譯命令及宏定義詳解

 更新時(shí)間:2021年10月13日 17:15:10   作者:Bitdancing  
這篇文章主要為大家介紹了C語(yǔ)言預(yù)處理預(yù)編譯命令及宏定義的詳解,其中包含運(yùn)行環(huán)境命名約定條件及#under等基礎(chǔ)詳解,有需要的朋友可以借鑒參考下

.c 源程序 ----- 編譯 ----- 鏈接 ---- exe ----運(yùn)行 -------->

程序翻譯環(huán)境和執(zhí)行環(huán)境

翻譯環(huán)境:源代碼被轉(zhuǎn)換為可執(zhí)行機(jī)器指令(二進(jìn)制代碼)。

執(zhí)行環(huán)境:用于實(shí)際執(zhí)行代碼。

翻譯環(huán)境:詳解編譯+鏈接

在這里插入圖片描述

1.組成程序的每個(gè)源文件通過(guò)編譯過(guò)程分別轉(zhuǎn)換成目標(biāo)代碼。

2.每個(gè)目標(biāo)文件由鏈接器捆綁在一起,形成一個(gè)單一而完整的可執(zhí)行程序。

3.鏈接器同時(shí)也會(huì)引入標(biāo)準(zhǔn)C函數(shù)庫(kù)中任何被該程序所用到的函數(shù),而且他可以搜索程序員個(gè)人的程序庫(kù),將其需要的函數(shù)也鏈接到程序。

extern聲明外部文件中的函數(shù)

在這里插入圖片描述

1. 編譯 — 預(yù)處理/預(yù)編譯 test.c ---- test.i

文本操作

#include 頭文件的包含

注釋刪除:使用空格替換注釋

#define 替換,所以宏無(wú)法進(jìn)行調(diào)試。

……

2. 編譯 — 編譯 test.i ---- test.s

把c語(yǔ)言代碼翻譯成匯編代碼

語(yǔ)法分析

詞法分析

語(yǔ)義分析

符號(hào)匯總

3. 編譯 — 匯編 test.s ---- test.obj

把匯編代碼轉(zhuǎn)換成二進(jìn)制代碼(指令)。

形成符號(hào)表。(符號(hào)+地址)

4. 鏈接 test.obj ---- test.exe

合并段表

符號(hào)表的合并和重定位

在這里插入圖片描述

運(yùn)行環(huán)境

1.程序必須載入內(nèi)存中。在有操作系統(tǒng)的環(huán)境中:一般這個(gè)由操作系統(tǒng)完成。在獨(dú)立的環(huán)境中,程序的載入必須由手工安排,也可能是通過(guò)可執(zhí)行代碼置入只讀內(nèi)存來(lái)完成。

2.程序的執(zhí)行便開(kāi)始。接著便調(diào)用main函數(shù)。

3.開(kāi)始執(zhí)行程序代碼。這個(gè)時(shí)候程序?qū)⑹褂靡粋€(gè)運(yùn)行時(shí)堆棧(stack),存儲(chǔ)函數(shù)的局部變量和返回地址。程序同時(shí)也可以使用靜態(tài)(static)內(nèi)存,存儲(chǔ)于靜態(tài)內(nèi)存中的變量在程序的整個(gè)執(zhí)行過(guò)程一直保留他們的值。

4.終止程序。正常終止main函數(shù);也有可能是意外終止

預(yù)處理/預(yù)編譯詳解

預(yù)定義符號(hào)

本來(lái)就有的符號(hào)

__FILE__      //進(jìn)行編譯的源文件
__LINE__     //文件當(dāng)前的行號(hào)
__DATE__    //文件被編譯的日期
__TIME__    //文件被編譯的時(shí)間
__STDC__    //如果編譯器遵循ANSI C,其值為1,否則未定義

應(yīng)用

printf("data: %s\n time: %s" ,__DATE__,__TIME__);

輸出

data: Jul 13 2021
time: 15:13:54

#define 定義標(biāo)識(shí)符

宏和define區(qū)別,宏是有參數(shù)的。

下面是宏的聲明方式:

#define name( parament-list ) stuff

其中的 parament-list 是一個(gè)由逗號(hào)隔開(kāi)的符號(hào)表,它們可能出現(xiàn)在stuff中

參數(shù)列表的左括號(hào)必須與name緊鄰。如果兩者之間有任何空白存在,參數(shù)列表就會(huì)被解釋為stuf的一部分。

例如

#define SQUARE(X) (X)*(X)
int main()
{
    int ret = SQUARE(5);
	return 0;
}

宏的參數(shù)是替換的,不是傳參的。

在定義宏的時(shí)候不要吝嗇括號(hào)。

#和##

#的作用

使用#,把一個(gè)宏參數(shù)變成對(duì)應(yīng)的字符串。

把參數(shù)插入到字符串中

#define PRINT(X) printf("the value of "#X" is %d\n", X)
int main()
{
	int a = 10;
	int b = 20;
	PRINT(a);
	PRINT(b);
	return 0;
}

輸出

the value of a is 10
the value of b is 20

##的作用

## 可以把位于他兩邊的符號(hào)合成一個(gè)符號(hào),允許宏定義從分離的文本片段創(chuàng)建創(chuàng)建標(biāo)識(shí)符。

#define CAT(X,Y) X##Y
int main()
{
	int class84 = 2021;
	printf("%d\n", CAT(class, 84));
}

輸出

2021

帶副作用的宏參數(shù)

#define MAX(a, b)  ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);
//輸出的結(jié)果是什么?

這里我們得知道預(yù)處理器處理之后的結(jié)果是什么:

z = ( (x++) > (y++) ? (x++) : (y++));

輸出結(jié)果

x=6 y=10 z=9

宏和函數(shù)的對(duì)比

對(duì)于上述的宏,也可以用函數(shù)實(shí)現(xiàn)其功能。

使用宏的優(yōu)點(diǎn):

1.用于調(diào)用函數(shù)和從函數(shù)返回的代碼可能比實(shí)際執(zhí)行這個(gè)小型計(jì)算工作需要的時(shí)間更多,所以宏比函數(shù)在程序的規(guī)模和速度方面更勝一籌。

2.函數(shù)的參數(shù)必須聲明為特定的類型,所以函數(shù)只能在類型合適的表達(dá)式上使用。反之,這個(gè)宏可以用于整型、長(zhǎng)整型、浮點(diǎn)數(shù)等等,宏是類型無(wú)關(guān)的。

使用宏的缺點(diǎn):

1.每次調(diào)用宏,一份宏定義的代碼插入程序中,除非宏比較短,否則可能會(huì)大幅度增加代碼的長(zhǎng)度。

2.宏無(wú)法調(diào)試。在預(yù)編譯(預(yù)處理)階段,已經(jīng)把 # define 給替換了,已經(jīng)不再是宏了。

3.宏由于類型無(wú)關(guān),也就不夠嚴(yán)謹(jǐn)。

3.宏可能會(huì)帶來(lái)運(yùn)算符優(yōu)先級(jí)的問(wèn)題,更容易導(dǎo)致程序出錯(cuò)。

inline 內(nèi)聯(lián)函數(shù)

命名約定

函數(shù)和宏語(yǔ)法相似,語(yǔ)言本身沒(méi)法幫我們區(qū)分二者。把宏名全部大寫(xiě),函數(shù)名不要全部大寫(xiě)。

#undef 移除宏定義

這條指令用于移除宏定義。

如果現(xiàn)存的一個(gè)名字需要被重新定義,那么他的舊名字首先要被移除。

#undef NAME

命令行定義

許多C的編譯器提供了一種能力,允許在命令行中定義符號(hào),用于在啟動(dòng)編譯過(guò)程。例如:當(dāng)我們根據(jù)一個(gè)源文件要編譯出不同的一個(gè)程序的不同版本的時(shí)候,這個(gè)特性有點(diǎn)用處。假設(shè)某個(gè)程序中聲明了一個(gè)某個(gè)長(zhǎng)度的數(shù)組,如果機(jī)器內(nèi)存有限,我們需要一個(gè)很小的數(shù)組,但是另外一個(gè)機(jī)器內(nèi)存大寫(xiě),我們需要一個(gè)數(shù)組能夠大寫(xiě)。

條件編譯

在這里插入圖片描述

#define DEBUG
#ifdef DEBUG
#endif

常見(jiàn)的條件編譯指令

#if 常量表達(dá)式
//...
#endif

舉例子:為真參與編譯,為假 (0)不參與編譯。

#if 1
	printf("balabala....");
#endif

二、多個(gè)分支的條件編譯

#if 常量表達(dá)式
//...
#elif 常量表達(dá)式
//...
#else
//....
#endif

舉例子

#if 1==1
#elif 2==1
#else
#endif

三、判斷是否被定義

#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol

四、嵌套指令

#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

文件包含

我們已經(jīng)知道,#include指令可以使另外一個(gè)文件被編譯。就像它實(shí)際出現(xiàn)于#include指令的地方一樣。這種替換的方式很簡(jiǎn)單:預(yù)處理器先刪除這條指令,并用包含文件的內(nèi)容替換。這樣一個(gè)源文件被包含10次,那就實(shí)際被編譯10次。

頭文件包含的方式:

1.本地文件包含:#include "Filename"

查找策略:先在源文件所在目錄下查找,如果該頭文件未找到,編譯器就像查找?guī)旌瘮?shù)頭文件一樣在標(biāo)準(zhǔn)位置查找頭文件。如果找不到就提示編譯錯(cuò)誤。

2.庫(kù)文件包含:#include <Filename.h>

查找策略:查找頭文件直接去標(biāo)準(zhǔn)路徑下去查找,如果找不到就返回錯(cuò)誤信息。

這樣是不是可以說(shuō),對(duì)于庫(kù)文件也可以使用“”的形式包含?

答案是肯定的,可以。但是這樣做查找的效率就低些,當(dāng)然這樣也不容易區(qū)分是庫(kù)文件還是本地文件了。

wwww想到自己經(jīng)常重復(fù)包含,留下了悔恨的淚水~~

出現(xiàn)嵌套文件包含解決方法 :條件編譯

每個(gè)頭文件開(kāi)頭這樣寫(xiě):

#ifndef __TEST__H__
#define __TEST__H__
 //頭文件的內(nèi)容
#endif         //__TEST__H__

或者

#pragma once

就可以避免頭文件的重復(fù)引入。

總結(jié)一下:預(yù)處理階段的預(yù)處理指令:條件編譯指令 / #include / #define / #error /#pragma / ……

offsetof(宏類型,成員名字)偏移量模擬實(shí)現(xiàn)

在這里插入圖片描述

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
struct S
{
	char c1;
	int a;
	char c2;
};
#define OFFSETOF(struct_name, member_name) (int)&(((struct_name*)0)->member_name)
int main()
{
	printf("%d\n", OFFSETOF(struct S, c1));
	printf("%d\n", OFFSETOF(struct S, a));
	printf("%d\n", OFFSETOF(struct S, c2));
	return 0;
}

以上就是C語(yǔ)言預(yù)處理預(yù)編譯命令及宏定義詳解的詳細(xì)內(nèi)容,更多關(guān)于C語(yǔ)言預(yù)處理預(yù)編譯命令及宏的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論