主頁(yè) > 知識(shí)庫(kù) > Linux折騰記(六):感悟GNU C及把Vim打造成C/C++的半自動(dòng)化IDE

Linux折騰記(六):感悟GNU C及把Vim打造成C/C++的半自動(dòng)化IDE

熱門標(biāo)簽:優(yōu)邁系統(tǒng)外呼顯示亂層 布谷電銷機(jī)器人價(jià)格 岑溪電銷機(jī)器人 大連企業(yè)電銷機(jī)器人線路 義烏市400電話辦理 怎么查看地圖標(biāo)注的地點(diǎn) 上海電銷卡外呼系統(tǒng)供應(yīng)商 電銷機(jī)器人錄音用什么軟件 營(yíng)銷智能外呼系統(tǒng)口碑推薦

C語(yǔ)言在Linux系統(tǒng)中的重要性自然是無(wú)與倫比、不可替代,所以我寫Linux江湖系列不可能不提C語(yǔ)言。C語(yǔ)言是我的啟蒙語(yǔ)言,感謝C語(yǔ)言帶領(lǐng)我進(jìn)入了程序世界。雖然現(xiàn)在不靠它吃飯,但是仍免不了經(jīng)常和它打交道,特別是在Linux系統(tǒng)下。

  Linux系統(tǒng)中普遍使用的是GNU-C,這里有一份Gnu-C語(yǔ)言手冊(cè).pdf。The GNU C Reference Manual的主頁(yè)在這里:http://www.gnu.org/software/gnu-c-manual/。C語(yǔ)言的內(nèi)核極其緊湊,該手冊(cè)總共只有91頁(yè),去掉目錄、附錄和索引后就只有70頁(yè)。我一般一個(gè)多小時(shí)就可以將其從頭至尾復(fù)習(xí)一遍。我曾有過(guò)將其翻譯成中文的想法,后來(lái)還是放棄了。翻譯這種字斟句酌的事情還是讓別人來(lái)干吧。我只寫寫我自己的感悟。

感悟一:C語(yǔ)言標(biāo)準(zhǔn)干不過(guò)GNU擴(kuò)展

   最近為了研究X Window的底層協(xié)議,開(kāi)始嘗試使用XCB編程。當(dāng)我打開(kāi)XCB的頭文件的時(shí)候,我被大量的__restrict__關(guān)鍵字驚呆了,好在有GNU C語(yǔ)言手冊(cè)為我答疑解惑。__restrict__又是一個(gè)GNU擴(kuò)展的關(guān)鍵字,后面我會(huì)詳細(xì)講解該關(guān)鍵字的用途。其實(shí)C語(yǔ)言的C99標(biāo)準(zhǔn)中已經(jīng)引入了restrict關(guān)鍵字,沒(méi)有前后的下劃線,但是在大量的開(kāi)源代碼中,使用最普遍的還是GNU的擴(kuò)展,而不是C語(yǔ)言標(biāo)準(zhǔn)。

  和restrict關(guān)鍵字有相同命運(yùn)的還有inline、_Complex等,它們都是在C99標(biāo)準(zhǔn)中引入的關(guān)鍵字,但是其實(shí)在C99標(biāo)準(zhǔn)出來(lái)之前,GNU C中早就有了__inline__、__complex__等擴(kuò)展關(guān)鍵字。還記得多年前我學(xué)習(xí)Linux 0.11版的源代碼時(shí),看到大量的__inline__曾經(jīng)疑惑不已,不知道為什么Linus在91年就能用上了如此先進(jìn)的語(yǔ)言功能,后來(lái)才知道,這是GNU的擴(kuò)展關(guān)鍵字。

  C語(yǔ)言的標(biāo)準(zhǔn)有C89和C99,使用GCC的時(shí)候甚至要顯示指定-std=c99才能全面支持C99標(biāo)準(zhǔn),所以在開(kāi)源界,大家還是喜歡首選GNU的擴(kuò)展關(guān)鍵字。比如__inline__、__complex__和__restrict__。總而言之,C語(yǔ)言標(biāo)準(zhǔn)干不過(guò)GNU擴(kuò)展。

  下面來(lái)看看__restrict__的真正含義。還記得CSDN上曾經(jīng)載過(guò)一篇文章《為什么有些語(yǔ)言會(huì)比別的快》,其中提到“很長(zhǎng)一段時(shí)間,相同的兩個(gè)程序在Fortran和C(或者C++)中運(yùn)行,F(xiàn)ortran會(huì)快一些,因?yàn)镕ortran的優(yōu)化做的更好。這是真的,就算C語(yǔ)言和Fortran的編譯器用了相同的代碼生成器也是一樣。這個(gè)不同不是因?yàn)镕ortran的某種特性,事實(shí)上恰恰相反,是因?yàn)镕ortran不具備的特性。”這是因?yàn)镃語(yǔ)言中的指針給編譯器的優(yōu)化帶來(lái)了困難,文章中繼續(xù)說(shuō)道:“問(wèn)題就來(lái)了。這些指針可以代替任何內(nèi)存地址。更重要的是,他們可以重疊。輸出數(shù)組的內(nèi)存地址也可以同時(shí)是輸入數(shù)組的。甚至可以部分重疊,輸出數(shù)組可以覆蓋一個(gè)輸入數(shù)組的一半。這對(duì)編譯器優(yōu)化來(lái)說(shuō)是個(gè)大問(wèn)題,因?yàn)橹盎跀?shù)組的優(yōu)化不再適用。特別的,添加元素的順序也成問(wèn)題,如果輸出重疊的數(shù)組,計(jì)算的結(jié)果會(huì)變得不確定,取決于輸出元素的動(dòng)作是發(fā)生在元素被覆蓋之前還是之后。”

  有了__restrict__,C語(yǔ)言的該問(wèn)題將不復(fù)存在。用__restrict__修飾一個(gè)指針后,①該指針只能在定義的時(shí)候被初始化;②不會(huì)再有別的指針指向該指針指向的內(nèi)存,因此編譯器可以對(duì)算法進(jìn)行優(yōu)化。如下代碼:

復(fù)制代碼
代碼如下:

int * __restrict__ p = (int*)malloc(100*sizeof(int));

指針p有__restrict__關(guān)鍵字修飾,所以它只能在定義的時(shí)候被初始化,以后不能賦值,而沒(méi)有__restrict__修飾的指針,可以隨時(shí)賦值,如下:

復(fù)制代碼
代碼如下:

int arr[100];
int* pArr;
pArr = arr;

指針pArr沒(méi)有被__restrict__關(guān)鍵字修飾,所以可以將數(shù)組的首地址賦值給它。

比如我們定義一個(gè)函數(shù)對(duì)兩塊數(shù)據(jù)進(jìn)行操作,結(jié)果放入第3塊內(nèi)存,如下:

復(fù)制代碼
代碼如下:

void func1(void* p1, void* p2, void* p3, int size){
for(int i=0; isize; i++){
p3[i] = p1[i] + p2[i];
}
}

很顯然,由于編譯器沒(méi)辦法判斷指針p1、p2、p3指向的內(nèi)存是否重疊,所以無(wú)法進(jìn)行優(yōu)化,加上__restrict__關(guān)鍵字后,如下:

復(fù)制代碼
代碼如下:

void func1(void* __restrict__ p1, void* __restrict__ p2, void* __restrict__ p3, int size){
for(int i=0; isize; i++){
p3[i] = p1[i] + p2[i];
}
}

相當(dāng)于明確告訴編譯器這幾塊內(nèi)存不會(huì)重疊,所以編譯器就可以放心大膽對(duì)程序進(jìn)行優(yōu)化。

   另一個(gè)關(guān)鍵字是_Complex,C99才引入,而且需要包含complex.h>頭文件。其實(shí)在GNU C中,早就有__complex__、__real__、__imag__等擴(kuò)展關(guān)鍵字。如下代碼:
 

復(fù)制代碼
代碼如下:

#include stdlib.h>
#include stdio.h>/p> p>int main(){
__complex__ a = 3 + 4i;
__complex__ b = 5 + 6i;
__complex__ c = a + b;
__complex__ d = a * b;
__complex__ e = a / b;
printf("a + b = %f + %fi\n", __real__ c, __imag__ c);
printf("a * b = %f + %fi\n", __real__ d, __imag__ d);
printf("a / b = %f + %fi\n", __real__ e, __imag__ e);
return 0;
}

  可以看到,在C語(yǔ)言中也可以直接對(duì)復(fù)數(shù)進(jìn)行計(jì)算。數(shù)值計(jì)算再也不是Fortran的專利。

 感悟二:指針和數(shù)組還真是不一樣

  從學(xué)C語(yǔ)言開(kāi)始,老師就教導(dǎo)我們說(shuō)指針和數(shù)組是一樣的,它們可以用同樣的方式進(jìn)行操作。而事實(shí)上,指針和數(shù)組還是有差別的。直到多年后讀《C專家編程》,才直到所謂指針和數(shù)組一樣是一個(gè)美麗的錯(cuò)誤,只是因?yàn)樵凇禩he C Programming Language》這本書(shū)里,把“作為函數(shù)參數(shù)時(shí),指針和數(shù)組一樣”這樣一句話前后分開(kāi)分別印到了兩頁(yè)而已。

  比如,指針不保存數(shù)據(jù)的長(zhǎng)度信息,而數(shù)組有,如下代碼:

復(fù)制代碼
代碼如下:

#include stdlib.h>
#include stdio.h>/p> p>int main(){
int* p = (int*)malloc(100*sizeof(int));
int arr[100] = {0};
printf("The size of p: %d\n", sizeof(p));
printf("The size of arr: %d\n", sizeof(arr));
return 0;
}

這段代碼的運(yùn)行結(jié)果為:

復(fù)制代碼
代碼如下:

The size of p: 8
The size of arr: 400

  我們經(jīng)??梢允褂萌缦碌拇a片段來(lái)獲得一個(gè)數(shù)組中有多少個(gè)元素,如下:

復(fù)制代碼
代碼如下:

int arr[100];
size_t length = sizeof(arr)/sizeof(int);

  但是,當(dāng)使用數(shù)組作為函數(shù)的參數(shù)的時(shí)候,數(shù)組會(huì)退化成指針。如下代碼:

復(fù)制代碼
代碼如下:

#include stdlib.h>
#include stdio.h>/p> p>void test_array(int arr[]){
printf("The size of arr in function: %d\n", sizeof(arr));
return;
}/p> p>int main(){
int arr[100] = {0};
printf("The size of arr in main: %d\n", sizeof(arr));
test_array(arr);
return 0;
}

這段代碼的運(yùn)行結(jié)果為:

復(fù)制代碼
代碼如下:

The size of arr in main: 400
The size of arr in function: 8

感悟三:C語(yǔ)言中的不完全類型(Incomplete Types)

   在GNU C中可以定義不完全類型,不完全類型主要有兩種,一種是空的結(jié)構(gòu),一種是空的數(shù)組,比如:

復(fù)制代碼
代碼如下:

struct point;
char name[0];

空的結(jié)構(gòu)不能定義變量,只能使用空結(jié)構(gòu)的指針??战Y(jié)構(gòu)可以在后面再將它補(bǔ)充完整,如下:

復(fù)制代碼
代碼如下:

struct point{
int x,y;
};

空結(jié)構(gòu)在定義鏈表的時(shí)候經(jīng)常用到,如下:

復(fù)制代碼
代碼如下:

struct linked_list{
struct linked_list* next;
int x;
/*other elements here perhaps */
}
struct linked_list* head;

  還有一種不完全類型就是將一個(gè)結(jié)構(gòu)的最后一項(xiàng)定義為一個(gè)空的數(shù)組,這樣可以用來(lái)表示一個(gè)可變長(zhǎng)度的結(jié)構(gòu)或數(shù)組,演示該技術(shù)的代碼如下:


復(fù)制代碼
代碼如下:

#include stdlib.h>
#include stdio.h>/p> p>typedef struct {
int length;
int arr[0];
} incomplete_type;/p> p>int main(){
char hello[] = "Hello, world!";
int length = sizeof(hello) / sizeof(char);
incomplete_type* p = (incomplete_type*)malloc(sizeof(int) + length*sizeof(char));
p->length = length;
for(int i=0; ip->length; i++){
p->arr[i] = hello[i];
}
printf("p->length=%d\n", p->length);
printf("p->arr=%s\n", p->arr);
}

打造C/C++的IDE

  后面的內(nèi)容展示如何將Vim打造成一個(gè)半自動(dòng)的C/C++ IDE。讀過(guò)我的Java博客的朋友應(yīng)該知道,其實(shí)我更喜歡用Eclipse。只有在需要寫非常簡(jiǎn)單的程序(比如做習(xí)題)的情況下,我才會(huì)用Vim。這在我的《打造屬于自己的Vim》中有論述。在這篇文章中我展示了怎么使用Vundle管理插件以及怎么怎么閱讀幫助文檔,同時(shí)展示了taglist.vim的簡(jiǎn)單用法。如果要用Vim來(lái)寫C/C++程序,還需要做少許擴(kuò)展。

  第一,安裝以下幾個(gè)插件,由于使用Vundle管理插件,所以只需要把插件名寫入.vimrc配置文件,然后運(yùn)行:BundleInstall即可,如下圖:

分別介紹一下這幾個(gè)插件。The-NERD-tree是一個(gè)瀏覽目錄和文件的插件,可以使用:help NERD_tree.txt查看它的幫助文檔。taglist.vim是瀏覽符號(hào)以及在符號(hào)之間跳轉(zhuǎn)的插件,使用:help taglist.txt查看它的幫助文檔。a.vim是在源代碼文件和頭文件之間跳轉(zhuǎn)的插件,不需要幫助文檔,它的命令就是:A。c.vim是提供IDE功能的主要插件,它提供的功能有自動(dòng)注釋、反注釋、自動(dòng)插入代碼塊及自動(dòng)運(yùn)行,如果安裝了splint,還可以對(duì)代碼進(jìn)行靜態(tài)檢查,使用:help csupport.txt查看它的文檔。OmniCppComplete是一個(gè)提供自動(dòng)補(bǔ)全功能的插件,使用:help omnicppcomplete.txt查看它的文檔。

  這些插件中,taglist.vim和OmniCppComplete需要ctags軟件的支持,所以需要安裝exuberant-ctags軟件包,在Fedora 20中,只需要使用yum install ctags即可自動(dòng)安裝。

  第二,生成tags數(shù)據(jù)庫(kù),并將其加入到Vim中。

  我們寫C程序的時(shí)候,使用到的文件主要存在于兩個(gè)地方,一個(gè)是我們工作的當(dāng)前目錄,另外一個(gè)是/usr/include。所以要到/usr/include目錄下使用ctags命令生成tags數(shù)據(jù)庫(kù)文件。為了使tags數(shù)據(jù)庫(kù)中包含盡可能多的信息(結(jié)構(gòu)、枚舉、類、函數(shù)、宏定義等等),需要指定ctags的參數(shù),如下:

 然后將該tags文件的路徑加入到.vimrc配置文件中,同時(shí)設(shè)置一個(gè)鍵盤映射,使得按Ctrl+F12時(shí),在工作目錄中調(diào)用ctags命令。如下配置文件的最后兩行:

  然后,在使用Vim寫C程序的時(shí)候,如果輸入了.、->這樣的元素,則其成員會(huì)自動(dòng)補(bǔ)全。如果輸入的是一個(gè)字符串(比如函數(shù)名),可以按Ctrl-X Ctrl-O調(diào)用自動(dòng)補(bǔ)全,如下圖:

不僅會(huì)彈出候選窗口,而且在最上面的窗口中會(huì)顯示函數(shù)的完整的簽名,及其所在的文件。這對(duì)于我們經(jīng)常記不全函數(shù)名、記不清函數(shù)簽名的人來(lái)說(shuō),已經(jīng)是莫大的福音了。

  taglist.vim和OmniCppComplete插件提供的功能用起來(lái)都只需要一個(gè)命令,而c.vim提供的命令就比較多了。而且在c.vim的幫助文檔中并沒(méi)有列出所有功能的命令,有一個(gè)辦法可以學(xué)習(xí)這些命令,那就是打開(kāi)GVim,通過(guò)GVim菜單中的C/C++菜單來(lái)學(xué)習(xí)c.vim提供的功能和命令。

  相比網(wǎng)上其它的將Vim打造成IDE的文章,我的配置比較簡(jiǎn)單,基本上只安裝了幾個(gè)插件,而沒(méi)有做過(guò)多的設(shè)置。當(dāng)我需要某個(gè)功能的時(shí)候,我會(huì)使用命令顯式地調(diào)用它,所以,稱它為半自動(dòng)化IDE吧。

 

標(biāo)簽:楚雄 荊州 迪慶 遼陽(yáng) 淄博 來(lái)賓 忻州 阜陽(yáng)

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Linux折騰記(六):感悟GNU C及把Vim打造成C/C++的半自動(dòng)化IDE》,本文關(guān)鍵詞  Linux,折騰,記,六,感悟,GNU,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Linux折騰記(六):感悟GNU C及把Vim打造成C/C++的半自動(dòng)化IDE》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于Linux折騰記(六):感悟GNU C及把Vim打造成C/C++的半自動(dòng)化IDE的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章