欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報(bào)激活碼778899分享:開發(fā)語言 C語言詳解(預(yù)編譯)

柚子快報(bào)激活碼778899分享:開發(fā)語言 C語言詳解(預(yù)編譯)

http://yzkb.51969.com/

Hi~!這里是奮斗的小羊,很榮幸您能閱讀我的文章,誠請?jiān)u論指點(diǎn),歡迎歡迎 ~~ ??個(gè)人主頁:奮斗的小羊 ??所屬專欄:C語言

?本系列文章為個(gè)人學(xué)習(xí)筆記,在這里撰寫成文一為鞏固知識,二為展示我的學(xué)習(xí)過程及理解。文筆、排版拙劣,望見諒。

目錄

前言1、預(yù)定義符號2、#define定義常量和標(biāo)識符3、#define定義宏4、帶有副作用的宏參數(shù)5、宏替換的規(guī)則6、宏和函數(shù)的對比7、#和##7.1 #運(yùn)算符7.2 ##運(yùn)算符

8、命名的約定9、#undef10、命令行定義11、條件編譯12、頭文件的包含12.1 頭文件被包含的方式12.1.1 本地文件包含12.1.2 庫文件包含

12.2 嵌套文件的包含

總結(jié)

前言

本篇文章將詳細(xì)介紹編譯過程中預(yù)編譯的具體細(xì)節(jié) 在C語言的學(xué)習(xí)中部分人可能會忽視這一部分的學(xué)習(xí),因?yàn)橄馰S這樣相對強(qiáng)大的集成開發(fā)環(huán)境,我們在寫好代碼后只需要開始執(zhí)行即可,所以部分人認(rèn)為這一部分不值得我們花費(fèi)時(shí)間去學(xué)習(xí) 其實(shí)不然,學(xué)習(xí)C語言預(yù)編譯過程可以幫助我們更深入地了解C語言的編譯過程和語法特性,提高代碼編寫的效率和質(zhì)量,以及拓展編程技能

1、預(yù)定義符號

C語言設(shè)置了一些預(yù)定義符號,可以直接使用,預(yù)定義符號也是在預(yù)編譯階段處理的

__FILE__:正在編譯的源文件的文件名__LINE__:文件當(dāng)前的行號__DATE__:文件被編譯的日期__TIME__:文件被編譯的時(shí)間__STDC__:如果編譯器遵循 ANSI C,其值為1,否則未定義

例如:

2、#define定義常量和標(biāo)識符

#define定義的常量和標(biāo)識符在預(yù)編譯階段完成替換

基本語法:

#define name stuff

特別的,為了區(qū)分普通常量這個(gè)name我們一般用大寫形式 比如:

#define MAX 10000

#define REG register

#define后面的代碼理論上講只能寫一行,但是如果后面的代碼過長,我們可以使用'\'來實(shí)現(xiàn)換行,相當(dāng)于轉(zhuǎn)義轉(zhuǎn)義字符'\'轉(zhuǎn)義了轉(zhuǎn)義字符'\n'

#define DEBUG_PRINT printf("file:%s\tline:%d\t\

date:%s\ttime:%s\n,\

__FILE__,__LINE__,\

__DATE__,__TIME__)

值得注意的是,行末最好不要加;,在某些場景下是沒什么問題,但是在大多數(shù)情況下是有語法錯(cuò)誤的,所以我們要養(yǎng)成良好的編程習(xí)慣,行末不加;

3、#define定義宏

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

#define name(parament_list) stuff

其中parament-list(參數(shù)列表)是一個(gè)由逗號隔開的符號表,它們可能出現(xiàn)在stuff中

注意: 參數(shù)列表的左括號必須與name緊鄰,如果兩者之間有任何空白存在,參數(shù)列表就會被解釋為stuff的一部分

舉例:輸入一個(gè)數(shù),輸出它的平方數(shù)

#include

#define SQUARE(x) x*x

int main()

{

int n = 0;

scanf("%d", &n);

int ret = SQUARE(n);

printf("%d\n", ret);

return 0;

}

上面的代碼看似沒有什么問題,但當(dāng)我們想計(jì)算n+1的平方數(shù)時(shí),就會出現(xiàn)問題:

#include

#define SQUARE(x) x*x

int main()

{

int n = 0;

scanf("%d", &n);

int ret = SQUARE(n + 1);

printf("%d\n", ret);

return 0;

}

這是為什么呢?

原因就是帶參數(shù)的宏在替換的時(shí)候括號內(nèi)的表達(dá)式是不做任何計(jì)算的

也就是說,上面替換后的形式是:5 + 1 * 5 + 1,為了解決這個(gè)問題,我們可以在定義宏的時(shí)候給x加上括號:

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

這樣替換后的結(jié)果就變成了:(5 + 1)*(5 + 1),但是這樣給單獨(dú)的參數(shù)加括號的形式在某些場景下還是存在問題,比如:

#include

#define SQUARE(x) (x)+(x)

int main()

{

int n = 0;

scanf("%d", &n);

int ret = 5 * SQUARE(n + 1);

printf("%d\n", ret);

return 0;

}

那為了解決這個(gè)問題,我們可以(x)+(x)整體加上括號:((x) + (x))

#include

#define SQUARE(x) ((x)+(x))

int main()

{

int n = 0;

scanf("%d", &n);

int ret = 5 * SQUARE(n + 1);

printf("%d\n", ret);

return 0;

}

所以,在寫宏的時(shí)候一定不要吝嗇括號

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

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

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

上面兩個(gè)表達(dá)式的值是相同的,但是第一個(gè)表達(dá)式x的本身沒有發(fā)生改變,而第二個(gè)表達(dá)式x本身發(fā)現(xiàn)了改變,這就是副作用

例如:使用宏實(shí)現(xiàn)求兩個(gè)數(shù)的較大值

#include

#define MAX(x, y) ((x)>(y)?(x):(y))

int main()

{

int a = 10;

int b = 20;

int ret = MAX(a, b);

printf("%d\n", ret);

return 0;

}

上面代碼中宏參數(shù)在宏定義中出現(xiàn)了兩次,我們使用MAX(a, b);時(shí)沒什么問題,但當(dāng)我們使用MAX(a++, b++);時(shí)問題就會出現(xiàn):

#include

#define MAX(x, y) ((x)>(y)?(x):(y))

int main()

{

int a = 10;

int b = 20;

int ret = MAX(a++, b++);

printf("%d\n", ret);

printf("a = %d, b = %d\n", a, b);

return 0;

}

可以發(fā)現(xiàn)a和b的值會發(fā)生改變,就是表達(dá)式求值的時(shí)候出現(xiàn)了永久性效果。

與函數(shù)對比:

#include

int MAX(int x, int y)

{

printf("a = %d, b = %d\n", x, y);

return (x > y ? x : y);

}

int main()

{

int a = 10;

int b = 20;

int ret = MAX(a++, b++);

printf("%d\n", ret);

return 0;

}

從上面的代碼中可以看出來,帶參數(shù)的宏替換和函數(shù)傳參是非常相似的,但是它們的傳參是有本質(zhì)區(qū)別的。 帶參數(shù)的宏替換是直接將參數(shù)做整體替換,替換過后的表達(dá)式是:((a++)>(b++)?(a++):(b++));而函數(shù)參過后的表達(dá)式是:(a > b ? a : b)。

5、宏替換的規(guī)則

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

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

例如:

#include

#define M 10

#define N M + 2

#define MAX(x, y) ((x)>(y)?(x):(y))

int main()

{

int ret = MAX(M, N);

return 0;

}

MAX(M, N)首先被替換成:((10)>(M + 2)?(10):(M + 2)) 然后((10)>(M + 2)?(10):(M + 2))再被替換成:((10)>(10 + 2)?(10):(10 + 2))

注意:

宏參數(shù)和#define定義中可以出現(xiàn)其他#define定義的符號,但宏不能實(shí)現(xiàn)遞歸

比如:#define N M + 2這個(gè)是可以的,但#define N N + 2是不行的。

當(dāng)預(yù)處理器搜索#define定義的符號時(shí),字符串常量的內(nèi)容并不被搜索

比如:

#include

#define M 10

#define N M + 2

#define MAX(x, y) ((x)>(y)?(x):(y))

int main()

{

printf("MAX(M, N)");

return 0;

}

可以看到宏MAX(M, N)并沒有展開。

6、宏和函數(shù)的對比

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

比如在兩個(gè)數(shù)中找較大數(shù),用宏實(shí)現(xiàn)更有優(yōu)勢:

#define MAX(x, y) ((x)>(y)?(x):(y))

那為什么不用函數(shù)呢?原因有二:

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

但是和函數(shù)相比宏還是有劣勢的:

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

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

#include

#define MALLOC(n, type) (type*)malloc(n * sizeof(type))

int main()

{

//int* p = (int*)malloc(10 * sizeof(int));

int* p = MALLOC(10, int);

//int *p = (int*)malloc(10 * sizeof(int));

return 0;

}

宏和函數(shù)的對比:

屬性#define定義宏函數(shù)代碼長度每次使用時(shí),宏代碼都會被插入到程序中,除了非常小的宏之外,程序的長度會大幅度增長函數(shù)代碼只出現(xiàn)于一個(gè)地方,每次使用這個(gè)函數(shù)時(shí),都調(diào)用那個(gè)地方的同一份代碼執(zhí)行速度更快存在函數(shù)的調(diào)用和返回的額外開銷,所以相對慢一些操作符優(yōu)先級宏參數(shù)的求值是在所有周圍表達(dá)式的上下文環(huán)境里,除非加上括號,否則鄰近操作符的優(yōu)先級可能會產(chǎn)生不可預(yù)料的結(jié)果,所以建議宏在書寫的時(shí)候多寫括號函數(shù)參數(shù)只在函數(shù)調(diào)用的時(shí)候求值一次,它的結(jié)果值傳遞給函數(shù),表達(dá)式的求值結(jié)果更容易預(yù)測帶有副作用的參數(shù)參數(shù)可能被替換到宏體中的多個(gè)位置,如果宏的參數(shù)被多次計(jì)算,帶有副作用的參數(shù)求值可能會產(chǎn)生不可預(yù)測的結(jié)果函數(shù)參數(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ù)是可以遞歸的

7、#和##

7.1 #運(yùn)算符

#運(yùn)算符將宏的一個(gè)參數(shù)轉(zhuǎn)換為字符串字面量,它僅允許出現(xiàn)在帶參數(shù)的宏的替換列表中 #運(yùn)算符所執(zhí)行的操作可以理解為“字符串化” 比如:當(dāng)我們有一個(gè)變量int a = 10;的時(shí)候,我們想打印出:the value of a is 10. 下面是常規(guī)寫法:

#include

int main()

{

int a = 10;

printf("the value of a is %d\n", a);

return 0;

}

如果我們想把打印的這條代碼通過宏替換來實(shí)現(xiàn),該怎么做呢?

#include

#define PRINT(format, n) printf("the value of n is "format"\n", n)

int main()

{

int a = 10;

PRINT("%d", a);

//printf("the value of n is ""%d""\n", a);

return 0;

}

如果寫成上面這種代碼很明顯并沒有解決問題,因?yàn)槿绻覀儗寫成%d時(shí)并不能打印出a,而只能打印出a的值,那為了能打印出a本身的字面量,我們就可以使用#操作符 如下:

#include

#define PRINT(format, n) printf("the value of "#n" is "format"\n", n)

int main()

{

int a = 10;

PRINT("%d", a);

//printf("the value of "a" is ""%d""\n", a);

double b = 3.14;

PRINT("%lf", b);

//printf("the value of "b" is ""%lf""\n", b);

return 0;

}

所以我們說:#運(yùn)算符所執(zhí)行的操作可以理解為“字符串化”,上面的代碼中是將a和b字符串化了。

當(dāng)n = a的時(shí)候,#n 就相當(dāng)于“a”

7.2 ##運(yùn)算符

##可以把位于它兩邊的符號合成一個(gè)符號,它允許宏定義從分離的文本片段創(chuàng)建標(biāo)識符。##被稱為記號粘合 這樣的連接必須產(chǎn)生一個(gè)合法的標(biāo)識符,否則其結(jié)果就是未定義的。

比如現(xiàn)在有這么一個(gè)問題:當(dāng)我們寫一個(gè)函數(shù)來求兩個(gè)數(shù)的較大值的時(shí)候,不同的類型我們就需要寫不同的函數(shù),這樣寫太繁瑣了,我們可以使用宏來簡化這件事:

#include

#define GENERIC(type) \

type type##_max(type x, type y)\

{\

return ((x) > (y) ? (x) : (y));\

}

GENERIC(int)

//int int_max(int x, int y)

//{

// return ((x) > (y) ? (x) : (y));

//}

GENERIC(double)

//double double_max(double x, double y)

//{

// return ((x) > (y) ? (x) : (y));

//}

int main()

{

printf("%d\n", int_max(10, 20));

printf("%lf\n", double_max(3.14, 6.28));

return 0;

}

上面的代碼中我們利用宏替換來實(shí)現(xiàn)創(chuàng)建不同類型的函數(shù),type##_max中的##操作符將type和_max連接成了一個(gè)新的標(biāo)識符

8、命名的約定

一般來講函數(shù)和宏的使用語法很相似,所以語言本身沒法幫我們區(qū)分二者,我們平時(shí)的習(xí)慣是:

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

9、#undef

#undef這條指令用于移除一個(gè)宏定義

#include

#define M 10

int main()

{

printf("%d\n", M);

#undef M

printf("%d\n", M);

return 0;

}

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

10、命令行定義

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

編譯指令:

//linux 環(huán)境演示

gcc -D ARRAY_SIZE=10 programe.c

11、條件編譯

滿足條件,就參與編譯;不滿足條件,就不參與編譯 在編譯一個(gè)程序的時(shí)候我們?nèi)绻獙⒁粭l語句(一組語句)編譯或者放棄是很方便的,因?yàn)槲覀冇袟l件編譯指令:

1.

#if 常量表達(dá)式 //常量表達(dá)式由預(yù)處理器求值

//...

#endif

如:

#define _DEBUG_ 1

int main()

{

#if _DEBUG_

printf("a");

#endif

return 0;

}

2.多個(gè)分支的條件編譯

#if 常量表達(dá)式

//...

#elif 常量表達(dá)式

//...

#else

//...

#endif

如:

#define M 1

int main()

{

#if M == 1

printf("a");

#elif M == 2

printf("b"):

#else

printf("C");

#endif

return 0;

}

3.判斷是否被定義

//如果定義了

#if defined(symbol)或

#ifdef symbol

如:

#define M 2

int main()

{

#ifdef M

printf("a");

#endif

return 0;

}

//如果沒定義

#if !defined(symbol)或

#ifndef symbol

4.嵌套指令

#ifdef 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

條件編譯通常用于跨平臺性代碼的編譯

12、頭文件的包含

12.1 頭文件被包含的方式

12.1.1 本地文件包含

一般指自己創(chuàng)建的頭文件

#include "filename.h"

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

12.1.2 庫文件包含

一般指標(biāo)準(zhǔn)庫中頭文件的包含

#include

查找策略: 直接去標(biāo)準(zhǔn)路徑下去查找,如果找不到就提示編譯錯(cuò)誤。 那這樣是不是就說明,對庫文件也可以使用" "的形式包含呢? 答案是可以的。但是這樣查找的效率比較低,也不容易區(qū)分是庫文件還是本地文件

12.2 嵌套文件的包含

我們已經(jīng)知道,#include指令可以使另外一個(gè)文件被編譯,就像它實(shí)際出現(xiàn)于#include指令的地方一樣。 這種替換的方式很簡單:預(yù)編譯器先刪除這條指令,并用被包含文件的內(nèi)容替換 一個(gè)頭文件被包含幾次,就會被實(shí)際編譯幾次,如果重復(fù)包含,編譯的壓力就比較大

#include "test.h"

#include "test.h"

#include "test.h"

#include "test.h"

#include "test.h"

int main()

{

return 0;

}

如果像上面這樣寫,test.h文件的內(nèi)容就會被拷貝5份,如果test.h文件比較大,這樣預(yù)處理后代碼量會劇增。 如果工程比較大,有公共使用的文件,被大家都能用,又不做任何的處理,那么后果會不堪設(shè)想。 為了解決頭文件被重復(fù)引入的問題,就要用到條件編譯 我們在每個(gè)頭文件的開頭這樣寫:

#ifndef __FILENAME_H__

#define __FILENAME_H__

//...

#endif

或者

#pragma once

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

總結(jié)

預(yù)編譯是C語言編譯過程的第一階段,在預(yù)編譯階段可以對源代碼進(jìn)行預(yù)處理,如宏定義、頭文件包含等。通過學(xué)習(xí)預(yù)編譯過程,可以更全面地理解C語言代碼的編譯過程。預(yù)編譯指令能夠簡化代碼結(jié)構(gòu)、提高代碼的重用性和可維護(hù)性。學(xué)習(xí)預(yù)編譯過程可以幫助程序員更好地利用預(yù)編譯指令優(yōu)化代碼結(jié)構(gòu),提高代碼的質(zhì)量。條件編譯是預(yù)編譯指令中的重要功能,可以根據(jù)不同條件編譯不同的代碼。通過學(xué)習(xí)預(yù)編譯過程,可以了解如何使用條件編譯來實(shí)現(xiàn)跨平臺編譯,提高代碼的可移植性。

柚子快報(bào)激活碼778899分享:開發(fā)語言 C語言詳解(預(yù)編譯)

http://yzkb.51969.com/

推薦鏈接

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19039651.html

發(fā)布評論

您暫未設(shè)置收款碼

請?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄