二狗子這個名字,在大街小巷,在電視劇中幾乎都能聽到。我也不知道老一輩的父母為什么這么喜歡給自己的孩子取這樣的名字,唯一能讓我信服的理由是:順口!大叔大伯們之所以叫這個名字還有一個理由,之前孩子多,希望孩子像小狗兒一樣好喂養(yǎng)。
正好我們村有個孩子也叫二狗子,大名叫張力萬,無論是叫二狗子還是叫張力萬,都指的是同一個人。我們大多數(shù)情況下還是叫他二狗子,他也習(xí)慣了倒也覺得親切,叫張力萬一般都是在正式場合。
有些企業(yè)文化中規(guī)定:“不允許在公司直接喊同事的名字,每個人必須有個英文名”。比如二狗子入職到這樣的企業(yè),大家不允許喊他二狗子或者張力萬,于是二狗子又有了一個英文名:“Jack”,你看 “Jack” 這個名字既符合企業(yè)文化又聽起來高大尚。
無論是 “二狗子“ 還是 “Jack“ 都是張力萬的別名。
在 C 語言中,關(guān)鍵字?define?和?typedef?就可以用來取別名,但是二者又有不同點,今天主要分享一下?define?的用法。
下面是使用?define?來模擬別名,示例如下:
#defineZhangliwan1"二狗子"#defineZhangliwan2"Jack"#defineZhangliwan"張力萬"intmain(intargc,constchar*argv[]){? ? printf("我們村的%s可以叫他%s也可以叫他%s\n", Zhangliwan, Zhangliwan1, Zhangliwan2);return0;}
輸出結(jié)果:
我們村的張力萬可以叫他二狗子也可以叫他Jack
虛構(gòu)一下
丹尼斯·里奇?一個偉大而低調(diào)的牛人,是Unix之父、C語言之父。

丹尼斯·里奇還將Unix的設(shè)計原則定為?KISS 原則?即 Keep it simple stupid,保持簡單和直接,所以Unix一直都是經(jīng)典中的經(jīng)典。這也說明丹尼斯·里奇不僅是一個優(yōu)秀的工程師,還是一個優(yōu)秀的產(chǎn)品經(jīng)理。
我在想,當(dāng)初丹尼斯·里奇和肯·湯普遜在實驗室里沒事也會討論C語言的事情。
丹尼斯·里奇:“老兄,你看我們現(xiàn)在的語言是不是過于復(fù)雜了?”
肯·湯普遜:“的確有點復(fù)雜,我有個大膽的想法,不知當(dāng)講不當(dāng)講?”
丹尼斯·里奇:“你還跟我墨跡啥,有話直接說唄,呵呵!”
肯·湯普遜:“嗯,我們可以開發(fā)一門新的語言,他要足夠的簡單、高效?!?/p>
丹尼斯·里奇:“想法是挺好的,那就開干吧!”
于是C語言誕生了。
肯·湯普遜:“你有沒有覺得我們在定義常量的時候不太方便?”
丹尼斯·里奇:“是呀,這樣你看行不行,弄個預(yù)處理器可以讓我們?nèi)我舛x常量,暫時稱他為 ‘宏’ 吧!”
肯·湯普遜:“我覺得完全沒有問題,來,徒手寫一個?!?/p>
丹尼斯·里奇:“哈哈,給力!”
于是?define?就有了。
以上純屬個人猜想,并不是冒犯兩位大師,本故事純屬虛構(gòu),如有雷同,純屬巧合。Unix和C語言的大道至簡,對后代科學(xué)的發(fā)展奠定了不可磨滅的貢獻和影響。
基本用法
關(guān)鍵字?define?是 C 語言中的預(yù)處理命令,它用于宏定義,在大多數(shù)定義下可以提高代碼的可讀性,為編程提供方便。
在 C 語言中預(yù)處理命令以 “#” 號開頭,如?#include?、?#ifdef?、?#endif?和宏定義命令?#define?等。
關(guān)鍵字?define?的用法如下:
#define新類型名 原類型名
#defineINTEGER intINTEGERa =100;#definePI 3.1415927#defineUserName"user_name"#defineMAX(x, y) (x)>(y)?(x):(y);
在 C 語言中,關(guān)鍵字?define?的定義的常量都會在預(yù)處理階段將用到的別名直接被原樣替換掉。例如在編寫源程序時,所有用到 3.1415927 的地方都可用 PI 代替,而編譯時,將先由預(yù)處理程序進行宏代換即用 3.1415927 去置換所有的宏名 PI,然后再進行編譯。
關(guān)鍵字?define?還可以結(jié)合 “#”、“##”、“#@” 使用。
符號 “#”,表示將其字符串化。
符號 “##”,表示連接變量。
符號 “#@”,表示將其字符化。
#defineM(x)x##x#defineL(x)#xintmain(int argc, const char *argv[]){// M(1): 11, L(1): "1"printf("M(1): %i, L(1): %s\n", M(1), L(1));}
我使用 “#@” 定義,無論是GCC編譯器還是Clang編譯器都無法通過編譯,錯誤信息:“ ‘#’ is not followed by a macro parameter ”。示例如下如下:
#defineK(x) #@x
關(guān)鍵字?define?給我們寫代碼帶來了一定的便利,但是如果過多的亂用它也會代碼不小的麻煩,比如下面的例子:
#definesquare(x) x*xintmain(){inti;? ? i =64/square(4);printf("i = %d\n", i);return0;}
定義宏?square(x)?本來是求某個數(shù)的平方,按理說 64/16 結(jié)果應(yīng)該是 4,但是運行程序你會發(fā)現(xiàn)結(jié)果是 64.
我們把上面的例子展開,因為?define?是直接原樣替換,如下:
i=64/4*4;i=16*4;i=64;
修改一下程序中?define?的定義,結(jié)果就對了。
#definesquare(x) (x*x)intmain(){inti;? ? i =64/square(4);// 4printf("i = %d\n", i);return0;}
其實最安全的做法是這樣定義,如下:
#definesquare(x) ({ \typeof(x) y = (x);? \y*y;? ? ? ? ? ? ? ? \})
作用域
可以在C文件的開頭,也可以在方法體里面,還可以在方法的聲明前都可以使用?define?關(guān)鍵字。
定義在文件開頭:
#defineNAME"name"intmain(){printf("Define NAME: %s\n", NAME);return0;}
定義在方法中:
voidplay(){? ? #defineNAME"name"printf("Define NAME: %s\n", NAME);}
這里要注意,定義在方法中,并不是指該宏定義?NAME?只能用在該方法里面,其他地方照樣可以使用。
voidplay(){? ? #defineNAME"name"printf("Define NAME: %s\n", NAME);}/* 注意:該方法一定是在define定義之后才能使用NAME */voideat(){printf("Define NAME: %s\n", NAME);}
條件編譯
在C語言中或者在類C語言中如Objective-C和C++中,我們會經(jīng)常用到條件編譯語句,如下:
#ifdefNAME#else#endif
大家在做一些跨平臺開發(fā)工作的時候,也會用到條件編譯語句。
#ifdefANDROID#definePLAYFORM 1#else#definePLAYFORM 2#endif
還有就是類似防止重復(fù)包含(重復(fù)定義)頭文件,也會用到條件編譯,如下:
#ifndef__Header_Person_H__#define__Header_Person_H__#endif
下面是來自Linux Kernel里面的代碼片段:
#ifdefined(CONFIG_ALPHA_GENERIC)#defineGAMMA_BIASalpha_mv.sys.t2.gamma_bias#elifdefined(CONFIG_ALPHA_GAMMA)#defineGAMMA_BIAS_GAMMA_BIAS#else#defineGAMMA_BIAS0#endif
既然我們可以定義宏,那么是否可以取消宏定義呢?答案是當(dāng)然可以。
voidplay(){? ? #defineNAME"name"printf("Define NAME: %s\n", NAME);}#ifdefNAME// 取消宏定義#undefNAME#endifvoideat(){// Compile errror: Use of undeclared identifier 'NAME'printf("Define NAME: %s\n", NAME);}
看我主頁簡介免費C++學(xué)習(xí)資源,視頻教程、職業(yè)規(guī)劃、面試詳解、學(xué)習(xí)路線、開發(fā)工具
每晚8點直播講解C++編程技術(shù)。