【Deep C (and C++)】深入理解C/C++(1)__C++

來源:互聯網
上載者:User

譯自Deep C (and C++) by Olve Maudal and Jon Jagger,本身半桶水不到,如果哪位網友發現有錯,留言指出吧:)

 

編程是困難的,正確的使用C/C++編程尤其困難。確實,不管是C還是C++,很難看到那種良好定義並且編寫規範的代碼。為什麼專業的程式員寫出這樣的代碼。因為絕大部分程式員都沒有深刻的理解他們所使用的語言。他們對語言的把握,有時他們知道某些東西未定義或未指定,但經常不知道為何如此。這個投影片,我們將研究一些小的C/C++程式碼片段,使用這些程式碼片段,我們將討論這些偉大而充滿危險的語言的基本原則,局限性,以及設計哲學。

 

         假設你將要為你的公司招聘一名C程式言,你們公司是做嵌入式開發的,為此你要面試一些候選人。作為面試的一部分,你希望通過面試知道候選人對於C語言是否有足夠深入的認識,你可以這樣開始你們的談話:

int main (){         int a= 42;         printf(“%d\n”,a);}

當你嘗試去編譯連結運行這段代碼時候,會發生什麼。

 

一個候選者可能會這樣回答:

         你必須通過#include<stdio.h>包含標頭檔,在程式的後面加上 return 0; 然後編譯連結,運行以後將在螢幕上列印42.

         沒錯,這個答案非常正確。

 

         但是另一個候選者也許會抓住機會,藉此展示他對C語言有更深入的認識,他會這樣回答:

         你可能需要#include<stdio.h>,這個標頭檔顯示地定義了函數printf(),這個程式經過編譯連結運行,會在標準輸出上輸出42,並且緊接著新的一行。

         然後他進一步說明:

         C++編譯器將會拒絕這段代碼,因為C++要求必須顯示定義所有的函數。然而,有一些特別的C編譯器會為printf()函數建立隱式定義,把這個檔案編譯成目標檔案。再跟標準庫連結的時候,它將尋找printf()函數的定義,以此來匹配隱式的定義。

         因此,上面這段代碼也會正常編譯、連結然後運行,當然你可能會得到一些警告資訊。

 

         這位候選者乘勝追擊,可能還會往下說,如果是C99,傳回值被定義為給運行環境指示是否運行成功,正如C++98一樣。但是對於老版本的C語言,比如說ANSI C以及K&R C,程式中的傳回值將會是一些未定義的垃圾值。但是傳回值通常會使用寄存器來傳遞,如果傳回值的3,我一點都不感到驚訝,因為printf()函數的傳回值是3,也就是輸出到標準輸出的字元個數。

         說到C標準,如果你要表明你關心C語言,你應該使用 intmain (void)作為你的程式入口,因為標準就這麼說的。

         C語言中,使用void來指示函式宣告中不需要參數。如果這樣聲明函數int f(),那表明f()函數可以有任意多的參數,雖然你可能打算說明函數不需要參數,但這裡並非你意。如果你的意思是函數不需要參數,顯式的使用void,並沒有什麼壞處。

int main (void){         inta = 42;         printf(“%d\n”,a);}

然後,有點炫耀的意思,這位候選人接著往下說:

         如果你允許我有點點書生氣,那麼,這個程式也並不完全的符合C標準,因為C標準指出原始碼必須要以新的一行結束。像這樣:

int main (){         inta = 42;         printf(“%d\n”,a);} 

同時別忘了顯式的聲明函數printf():

#include <stdio.h>int main (void){         inta = 42;         printf(“%d\n”,a);} 

現在看起來有點像C程式了,對嗎。

 

然後,在我的機器上編譯、連結並運行此程式:

$  cc–std=c89 –c foo.c$  ccfoo.o$ ./a.out42$ echo $?3  $  cc–std=c99 –c foo.c$  ccfoo.o$ ./a.out42$ echo $?0

這兩名候選者有什麼區別嗎。是的,沒有什麼特別大的區別,但是你明顯對第二個候選者的答案更滿意。

 

        也許這並不是真的候選者,或許就是你的員工,呵呵。

         讓你的員工深入理解他們所使用的語言,對你的公司會有很大協助嗎。

         讓我們看看他們對於C/C++理解的有多深……

        

#include <stdio.h> void foo(void){   int a = 3;   ++a;   printf("%d\n", a);} int main(void){   foo();   foo();   foo();}

 

這兩位候選者都會是,輸出三個4.然後看這段程式:

#include <stdio.h> void foo(void){   static int a = 3;   ++a;   printf("%d\n", a);} int main(void){   foo();   foo();   foo();} 

他們會說出,輸出4,5,6.再看:

#include <stdio.h> void foo(void){   static int a;   ++a;   printf("%d\n", a);} int main(void){   foo();   foo();   foo();} 


第一個候選者發出疑問,a未定義,你會得到一些垃圾值。

你說:不,會輸出1,2,3.

候選者:為什麼。

你:因為靜態變數會被初始化未0.

 

第二個候選者會這樣來回答:

         C標準說明,靜態變數會被初始化為0,所以會輸出1,2,3.

 

再看下面的程式碼片段:

#include <stdio.h> void foo(void){   int a;   ++a;   printf("%d\n", a);} int main(void){   foo();   foo();   foo();}
 

第一個候選者:你會得到1,1,1.

你:為什麼你會這樣想。

候選者:因為你說他會初始化為0.

你:但這不是靜態變數。

候選者:哦,那你會得到垃圾值。

 

第二個候選者登場了,他會這樣回答:

a的值沒有定義,理論上你會得到三個垃圾值。但是實踐中,因為自動變數一般都會在運行棧中分配,三次調用foo函數的時候,a有可能存在同一記憶體空間,因此你會得到三個連續的值,如果你沒有進行任何編譯最佳化的話。

你:在我的機器上,我確實得到了1,2,3.

候選者:這一點都不奇怪。如果你運行於debug模式,運行時機制會把你的棧空間全部初始化為0.

 

 

接下來的問題,為什麼靜態變數會被初始化為0,而自動變數卻不會被初始化。

第一個候選者顯然沒有考慮過這個問題。

第二個候選者這樣回答:

把自動變數初始化為0的代價,將會增加函數調用的代價。C語言非常注重運行速度。

然而,把全域變數區初始化為0,僅僅在程式啟動時候產產生本。這也許是這個問題的主要原因。

更精確的說,C++並不把靜態變數初始化為0,他們有自己的預設值,對於原生類型(native types)來說,這意味著0。

 

再來看一段代碼:

#include<stdio.h> static int a; void foo(void){    ++a;    printf("%d\n", a);} int main(void){    foo();    foo();    foo();} 


第一個候選者:輸出1,2,3.

你:好,為什麼。

候選者:因為a是靜態變數,會被初始化為0.

你:我同意……

候選者:cool…

 

 

這段代碼呢:

#include<stdio.h> int a; void foo(void){    ++a;    printf("%d\n", a);} int main(void){    foo();    foo();    foo();} 


第一個候選者:垃圾,垃圾,垃圾。

你:你為什麼這麼想。

候選者:難道它還會被初始化為0。

你:是的。

候選者:那他可能輸出1,2,3。

你:是的。你知道這段代碼跟前面那段代碼的區別嗎。 有static那一段。

候選者:不太確定。等等,他們的區別在於私人變數(private variables)和公有變數(public variables).

你:恩,差不多。

 

第二個候選者:它將列印1,2,3.變數還是靜態分配,並且被初始化為0.和前面的區別:嗯。這和連結器(linker)有關。這裡的變數可以被其他的編譯單元訪問,也就是說,連結器可以讓其他的目標檔案訪問這個變數。但是如果加了static,那麼這個變數就變成該編譯單元的局部變數了,其他編譯單元不可以通過連結器訪問到該變數。

         你:不錯。接下來,將展示一些很不錯的玩意。靜候:)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.