国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【C語言】預(yù)處理詳解

這篇具有很好參考價值的文章主要介紹了【C語言】預(yù)處理詳解。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

????????本文目錄

1 預(yù)定義符號

2 #define

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

2.2 #define 定義宏

2.3 #define 替換規(guī)則

2.4 #和##

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

2.6 宏和函數(shù)對比

2.7 命名約定

3 #undef

4 命令行定義

5 條件編譯

6 文件包含

6.1 頭文件被包含的方式

6.2 嵌套文件包含


1 預(yù)定義符號

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

這些預(yù)定義符號都是語言內(nèi)置的。

舉個栗子:

printf("file:%s line:%d\n", __FILE__, __LINE__);

2 #define

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

//語法:
#define name stuff

舉個栗子:

#define MAX 1000
#define reg register //為 register這個關(guān)鍵字,創(chuàng)建一個簡短的名字
#define do_forever for(;;) //用更形象的符號來替換一種實現(xiàn)
#define CASE break;case //在寫case語句的時候自動把 break寫上。
// 如果定義的 stuff過長,可以分成幾行寫,除了最后一行外,每行的后面都加一個反斜杠(續(xù)行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t    \
                            date:%s\ttime:%s\n",  \
                            __FILE__,__LINE__,    \
                            __DATE__,__TIME__)

提問:

在define定義標(biāo)識符的時候,要不要在最后加上 ; ?

比如:

#define MAX 1000;
#define MAX 1000

建議不要加上 ; ,這樣容易導(dǎo)致問題。

比如下面的場景:

if (condition)
	max = MAX;
else
	max = 0;

這里會出現(xiàn)語法錯誤。

2.2 #define 定義宏

#define 機(jī)制包括了一個規(guī)定,允許把參數(shù)替換到文本中,這種實現(xiàn)通常稱為宏(macro)或定義宏(define macro)。

下面是宏的申明方式:

#define name( parament-list ) stuff

其中的 parament-list 是一個由逗號隔開的符號表,它們可能出現(xiàn)在 stuff 中.

注意:

參數(shù)列表的左括號必須與 name 緊鄰。

如果兩者之間有任何空白存在,參數(shù)列表就會被解釋為 stuff 的一部分。

如:

#define SQUARE( x ) x * x

這個宏接收一個參數(shù) x ,

如果在上述聲明之后,你把

SQUARE( 5 );

置于程序中,預(yù)處理器就會用下面這個表達(dá)式替換上面的表達(dá)式:

5 * 5

警告:

這個宏存在一個問題:

觀察下面的代碼段:

int a = 5;
printf("%d\n", SQUARE(a + 1));

乍一看,你可能覺得這段代碼將打印36這個值。

事實上,它將打印11

為什么?

替換文本時,參數(shù) x 被替換成 a + 1,所以這條語句實際上變成了:

printf("%d\n", a + 1 * a + 1);

這樣就比較清晰了,由替換產(chǎn)生的表達(dá)式并沒有按照預(yù)想的次序進(jìn)行求值。

在宏定義上加上兩個括號,這個問題便輕松的解決了:

#define SQUARE(x) (x) * (x)

這樣預(yù)處理之后就產(chǎn)生了預(yù)期的效果:

printf("%d\n", (a + 1) * (a + 1));

這里還有一個宏定義:

#define DOUBLE(x) (x) + (x)

定義中我們使用了括號,想避免之前的問題,但是這個宏可能會出現(xiàn)新的錯誤。

int a = 5;
printf("%d\n", 10 * DOUBLE(a));

這將打印什么值呢?

warning:

看上去,好像打印100,但事實上打印的是55,

我們發(fā)現(xiàn)替換之后:

printf("%d\n", 10 * (5) + (5));

乘法運(yùn)算先于宏定義的加法,所以出現(xiàn)了

?55?

這個問題,的解決辦法是在宏定義表達(dá)式兩邊加上一對括號就可以了。

#define DOUBLE( x ) ( ( x ) + ( x ) )

提示:

所以用于對數(shù)值表達(dá)式進(jìn)行求值的宏定義都應(yīng)該用這種方式加上括號,避免在使用宏時由于參數(shù)中的操作符或鄰近操作符之間不可預(yù)料的相互作用。

2.3 #define 替換規(guī)則

在程序中擴(kuò)展#define定義符號和宏時,需要涉及幾個步驟。

  1. 在調(diào)用宏時,首先對參數(shù)進(jìn)行檢查,看看是否包含任何由#define定義的符號。如果是,它們首先被替換。
  2. 替換文本隨后被插入到程序中原來文本的位置。對于宏,參數(shù)名被他們的值所替換。
  3. 最后,再次對結(jié)果文件進(jìn)行掃描,看看它是否包含任何由#define定義的符號。如果是,就重復(fù)上述處理過程。

注意:

  1. 宏參數(shù)和 #define 定義中可以出現(xiàn)其他 #define 定義的符號。但是對于宏,不能出現(xiàn)遞歸。
  2. 當(dāng)預(yù)處理器搜索 #define 定義的符號的時候,字符串常量的內(nèi)容并不被搜索。

2.4 #和##

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

首先我們看看這樣的代碼:

char* p = "hello ""bit\n";
printf("hello"" bit\n");
printf("%s", p);

這里輸出的是不是

?hello bit ?

答案是確定的:是。

我們發(fā)現(xiàn)字符串是有自動連接的特點的。

? ? ? ? 1. 那我們是不是可以寫這樣的代碼?

#define PRINT(FORMAT, VALUE)\
    printf("the value is "FORMAT"\n", VALUE);
...
PRINT("%d", 10);

這里只有當(dāng)字符串作為宏參數(shù)的時候才可以把字符串放在字符串中。

? ? ? ? 2. 另外一個技巧是:

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

? ? ? ? ??? 比如:

int i = 10;
#define PRINT(FORMAT, VALUE)\
    printf("the value of "#VALUE" is "FORMAT"\n", VALUE);
...
PRINT("%d", i+3);//產(chǎn)生了什么效果?

代碼中的 #VALUE 會預(yù)處理器處理為:

"VALUE"

最終的輸出的結(jié)果應(yīng)該是:

the value of i+3 is 13

## 的作用

##可以把位于它兩邊的符號合成一個符號。

它允許宏定義從分離的文本片段創(chuàng)建標(biāo)識符。

#define ADD_TO_SUM(num, value)\
    sum##num += value;
...
ADD_TO_SUM(5, 10);//作用是:給sum5增加10

注:

這樣的連接必須產(chǎn)生一個合法的標(biāo)識符。否則其結(jié)果就是未定義的。

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

當(dāng)宏參數(shù)在宏的定義中出現(xiàn)超過一次的時候,如果參數(shù)帶有副作用,那么你在使用這個宏的時候就可能出現(xiàn)危險,導(dǎo)致不可預(yù)測的后果。副作用就是表達(dá)式求值的時候出現(xiàn)的永久性效果。

例如:

x+1;//不帶副作用
x++;//帶有副作用

MAX宏可以證明具有副作用的參數(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

2.6 宏和函數(shù)對比

宏通常被應(yīng)用于執(zhí)行簡單的運(yùn)算。

比如在兩個數(shù)中找出較大的一個。

#define MAX(a, b) ((a)>(b)?(a):(b))

那為什么不用函數(shù)來完成這個任務(wù)?

原因有二:

  1. 用于調(diào)用函數(shù)和從函數(shù)返回的代碼可能比實際執(zhí)行這個小型計算工作所需要的時間更多。
    所以宏比函數(shù)在程序的規(guī)模和速度方面更勝一籌。
  2. 更為重要的是函數(shù)的參數(shù)必須聲明為特定的類型。
    所以函數(shù)只能在類型合適的表達(dá)式上使用。反之這個宏怎可以適用于整形、長整型、浮點型等可以用于>來比較的類型。
    宏是類型無關(guān)的。

宏的缺點:當(dāng)然和函數(shù)相比宏也有劣勢的地方:

  1. 每次使用宏的時候,一份宏定義的代碼將插入到程序中。除非宏比較短,否則可能大幅度增加程序的長度。
  2. 宏是沒法調(diào)試的。
  3. 宏由于類型無關(guān),也就不夠嚴(yán)謹(jǐn)。
  4. 宏可能會帶來運(yùn)算符優(yōu)先級的問題,導(dǎo)致程容易出現(xiàn)錯。

宏有時候可以做函數(shù)做不到的事情。比如:宏的參數(shù)可以出現(xiàn)類型,但是函數(shù)做不到。

#define MALLOC(num, type)\
    (type*)malloc(num * sizeof(type))
...
//使用
MALLOC(10, int);//類型作為參數(shù)

//預(yù)處理器替換之后:
(int*)malloc(10 * sizeof(int));

宏和函數(shù)的一個對比

#define定義宏 函數(shù)

每次使用時,宏代碼都會被插入到程序中。除了非常小的宏之外,程序的長度會大幅度增長 函數(shù)代碼只出現(xiàn)于一個地方;每次使用這個函數(shù)時,都調(diào)用那個地方的同一份代碼

執(zhí)

更快 存在函數(shù)的調(diào)用和返回的額外開銷,所以相對慢一些

優(yōu)

宏參數(shù)的求值是在所有周圍表達(dá)式的上下文環(huán)境里,除非加上括號,否則鄰近操作符的優(yōu)先級可能會產(chǎn)生不可預(yù)料的后果,所以建議宏在書寫的時候多些括號 函數(shù)參數(shù)只在函數(shù)調(diào)用的時候求值一次,它的結(jié)果值傳遞給函數(shù)。表達(dá)式的求值結(jié)果更容易預(yù)測

用的參數(shù)

參數(shù)可能被替換到宏體中的多個位置,所以帶有副作用的參數(shù)求值可能會產(chǎn)生不可預(yù)料的結(jié)果 函數(shù)參數(shù)只在傳參的時候求值一次,結(jié)果更容易控制
參數(shù)類型 宏的參數(shù)與類型無關(guān),只要對參數(shù)的操作是合法的,它就可以使用于任何參數(shù)類型 函數(shù)的參數(shù)是與類型有關(guān)的,如果參數(shù)的類型不同,就需要不同的函數(shù),即使他們執(zhí)行的任務(wù)是相同的
調(diào)試 宏是不方便調(diào)試的 函數(shù)是可以逐語句調(diào)試的
遞歸 宏是不能遞歸的 函數(shù)是可以遞歸的

2.7 命名約定

一般來講函數(shù)的宏的使用語法很相似。所以語言本身沒法幫我們區(qū)分二者。

那我們平時的一個習(xí)慣是:

把宏名全部大寫

函數(shù)名不要全部大寫

3 #undef

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

#undef NAME
//如果現(xiàn)存的一個名字需要被重新定義,那么它的舊名字首先要被移除。

4 命令行定義

許多 C 的編譯器提供了一種能力,允許在命令行中定義符號。用于啟動編譯過程。

例如:當(dāng)我們根據(jù)同一個源文件要編譯出一個程序的不同版本的時候,這個特性有點用處。(假定某個程序中聲明了一個某個長度的數(shù)組,如果機(jī)器內(nèi)存有限,我們需要一個很小的數(shù)組,但是另外一個機(jī)器內(nèi)存大些,我們需要一個數(shù)組能夠大些。)

#include <stdio.h>
int main()
{
	int array[ARRAY_SIZE];
	int i = 0;
	for (i = 0; i < ARRAY_SIZE; i++)
	{
		array[i] = i;
	}
	for (i = 0; i < ARRAY_SIZE; i++)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
	return 0;
}

編譯指令:

//linux 環(huán)境演示
gcc -D ARRAY_SIZE=10 programe.c

5 條件編譯

在編譯一個程序的時候我們?nèi)绻獙⒁粭l語句(一組語句)編譯或者放棄是很方便的。因為我們有條件編譯指令。

比如說:

調(diào)試性的代碼,刪除可惜,保留又礙事,所以我們可以選擇性的編譯。

#include <stdio.h>
#define __DEBUG__

int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
#ifdef __DEBUG__
		printf("%d\n", arr[i]);//為了觀察數(shù)組是否賦值成功。
#endif //__DEBUG__
	}
	return 0;
}

常見的條件編譯指令:

1.
#if 常量表達(dá)式
    //...
#endif
//常量表達(dá)式由預(yù)處理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
    //..
#endif

2.多個分支的條件編譯
#if 常量表達(dá)式
    //...
#elif 常量表達(dá)式
    //...
#else
    //...
#endif

3.判斷是否被定義
#if defined(symbol)
#ifdef symbol

#if !defined(symbol)
#ifndef symbol

4.嵌套指令
#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

6 文件包含

我們已經(jīng)知道, #include 指令可以使另外一個文件被編譯。就像它實際出現(xiàn)于 #include 指令的地方一樣。

這種替換的方式很簡單:

預(yù)處理器先刪除這條指令,并用包含文件的內(nèi)容替換。

這樣一個源文件被包含10次,那就實際被編譯10次。

6.1 頭文件被包含的方式

  • 本地文件包含
#include "filename"

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

如果找不到就提示編譯錯誤。

Linux環(huán)境的標(biāo)準(zhǔn)頭文件的路徑:

/usr/include

VS環(huán)境的標(biāo)準(zhǔn)頭文件的路徑:

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
//這是VS2013的默認(rèn)路徑

注意按照自己的安裝路徑去找。

  • 庫文件包含
#include <filename.h>

查找頭文件直接去標(biāo)準(zhǔn)路徑下去查找,如果找不到就提示編譯錯誤。

這樣是不是可以說,對于庫文件也可以使用 “” 的形式包含?

答案是肯定的,可以。

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

6.2 嵌套文件包含

如果出現(xiàn)這樣的場景:

【C語言】預(yù)處理詳解,C語言,c語言,開發(fā)語言,學(xué)習(xí)方法,程序人生

comm.h 和 comm.c 是公共模塊。

test1.h 和 test1.c 使用了公共模塊。

test2.h 和 test2.c 使用了公共模塊。

test.h 和 test.c 使用了 test1 模塊和 test2 模塊。

這樣最終程序中就會出現(xiàn)兩份 comm.h 的內(nèi)容。這樣就造成了文件內(nèi)容的重復(fù)。

如何解決這個問題?

答案:條件編譯。

每個頭文件的開頭寫:

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

或者:

#pragma once

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


本文完文章來源地址http://www.zghlxwxcb.cn/news/detail-632105.html

到了這里,關(guān)于【C語言】預(yù)處理詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【C語言】預(yù)處理詳解

    【C語言】預(yù)處理詳解

    ???????? 本文目錄 1 預(yù)定義符號 2 #define 2.1 #define 定義標(biāo)識符 2.2 #define 定義宏 2.3 #define 替換規(guī)則 2.4 #和## 2.5 帶副作用的宏參數(shù) 2.6 宏和函數(shù)對比 2.7 命名約定 3 #undef 4 命令行定義 5 條件編譯 6 文件包含 6.1 頭文件被包含的方式 6.2 嵌套文件包含 這些預(yù)定義符號都是語言內(nèi)置

    2024年02月14日
    瀏覽(35)
  • 詳解C語言—預(yù)處理

    詳解C語言—預(yù)處理

    目錄 一、預(yù)處理 1、預(yù)定義符號介紹 2、預(yù)處理指令 #define #define 定義標(biāo)識符:? #define 定義宏: #define 替換規(guī)則 3、預(yù)處理操作符# 4、預(yù)處理操作符## 5、帶副作用的宏參數(shù) 6、宏和函數(shù)對比 二、命名約定 三、預(yù)處理指令 #undef 四、命令行定義 五、條件編譯? 1、單分支#if:

    2024年02月08日
    瀏覽(28)
  • C語言預(yù)處理詳解

    C語言預(yù)處理詳解

    上一篇博客中我們講了C語言的編譯與鏈接,在編譯過程中有三個小階段:預(yù)處理、編譯、匯編。 本篇博客將詳細(xì)講述預(yù)處理部分的有關(guān)知識點 。 在C語言中,C語言本身設(shè)置了?些預(yù)定義符號,可以直接使? ,預(yù)定義符號的處理也是在預(yù)處理期間進(jìn)行的。 在這里介紹幾個常

    2024年04月15日
    瀏覽(39)
  • C語言·預(yù)處理詳解

    C語言·預(yù)處理詳解

    ????????C語言設(shè)置了一些預(yù)定義符號,可以直接使用,預(yù)定義符號也是在預(yù)處理期間處理的 ????????????????__FILE__ ?進(jìn)行編譯的源文件 ????????????????__LINE__ ?文件當(dāng)前的行號 ????????????????__DATE__ ?文件被編譯的日期 ????????????????_

    2024年01月21日
    瀏覽(34)
  • GPT-4科研實踐:數(shù)據(jù)可視化、統(tǒng)計分析、編程、機(jī)器學(xué)習(xí)數(shù)據(jù)挖掘、數(shù)據(jù)預(yù)處理、代碼優(yōu)化、科研方法論

    GPT-4科研實踐:數(shù)據(jù)可視化、統(tǒng)計分析、編程、機(jī)器學(xué)習(xí)數(shù)據(jù)挖掘、數(shù)據(jù)預(yù)處理、代碼優(yōu)化、科研方法論

    查看原文GPT4科研實踐技術(shù)與AI繪圖 GPT對于每個科研人員已經(jīng)成為不可或缺的輔助工具,不同的研究領(lǐng)域和項目具有不同的需求。 例如在科研編程、繪圖領(lǐng)域 : 1、編程建議和示例代碼: ?無論你使用的編程語言是Python、R、MATLAB還是其他語言,都可以為你提供相關(guān)的代碼示例。

    2024年02月07日
    瀏覽(106)
  • 【C語言進(jìn)階】預(yù)處理詳解

    【C語言進(jìn)階】預(yù)處理詳解

    對預(yù)處理的相關(guān)知識進(jìn)行詳細(xì)的介紹 ? ? ? ? ? ? ? ? ??? 豬巴戒 :個人主頁? ??????????????? 所屬專欄 :《C語言進(jìn)階》 ? ? ? ? ?? 跟著豬巴戒 ,一起學(xué)習(xí)C語言?? 目錄 引言 預(yù)定義符號 #define定義常量 #define定義宏 帶有副作用的宏參數(shù) 宏替換的規(guī)則 宏函數(shù)的

    2024年01月23日
    瀏覽(26)
  • 【C語言:編譯、預(yù)處理詳解】

    【C語言:編譯、預(yù)處理詳解】

    我們都知道,一個程序如果想運(yùn)行起來要經(jīng)過編譯、鏈接然后才能生成.exe的文件。 編譯?可以分解為三個過程: 預(yù)處理(有些書也叫預(yù)編譯)、 編譯 匯編 預(yù)處理階段 主要處理那些源文件中以#開始的預(yù)編譯指令。比如:#include,#define,處理的規(guī)則如下: 刪除所有的注釋

    2024年02月03日
    瀏覽(24)
  • 【C語言基礎(chǔ)】:預(yù)處理詳解(一)

    【C語言基礎(chǔ)】:預(yù)處理詳解(一)

    一、預(yù)定義符號 在C語言中設(shè)置了許多的預(yù)定義符號,這些預(yù)定義符號是可以直接使用的,預(yù)定義符號也是在預(yù)處理階段進(jìn)行處理的。 常見的預(yù)定義符號 : 【示例】 : 我們在VS上使用 _ _ STDC _ _ 會發(fā)現(xiàn)顯示未定義,這也就說明VS的編譯器是不完全遵循 ANSI C 的,為了展示效果

    2024年04月22日
    瀏覽(31)
  • 【c語言】詳解c語言#預(yù)處理期過程 | 宏定義前言

    【c語言】詳解c語言#預(yù)處理期過程 | 宏定義前言

    c語言系列專欄:?c語言之路重點知識整合?? 創(chuàng)作不易,本篇文章如果幫助到了你,還請點贊支持一下???)!!? 主頁專欄有更多知識,如有疑問歡迎大家指正討論,共同進(jìn)步! 給大家跳段街舞感謝支持!? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 代碼編譯到執(zhí)

    2024年02月01日
    瀏覽(28)
  • C語言預(yù)處理命令 #error 學(xué)習(xí)

    C語言預(yù)處理命令 #error 學(xué)習(xí)

    #error命令是C/C++語言的預(yù)處理命令之一,當(dāng)預(yù)處理器預(yù)處理到#error命令時將停止編譯并輸出用戶自定義的錯誤消息。 如下代碼輸出數(shù)字1000,如果加了 #error,構(gòu)建時不會通過,提示出錯如下; 這可能在大型項目中比較有用;? 如下?lián)f是freertos源碼中的例子,? #ifndef configEX

    2024年02月14日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包