日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C语言再学习 -- 存储类型关键字

發布時間:2025/3/15 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言再学习 -- 存储类型关键字 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

定義: 是對聲明的實現或者實例化。連接器(linker)需要它(定義)來引用內存實體。與上面的聲明相應的定義如下:參看:C語言再學習 -- 存儲類、鏈接

C語言中有 5 個作為存儲類說明符的關鍵字,分別是 auto、register、static、extern 以及 typedef。關鍵字typedef 與內存存儲無關,由于語法原因被歸入此類。

現在簡單了解一下這五個存儲類說明符的關鍵字:

說明符 auto? 表明一個變量具有自動存儲時期。該說明符只能用于在具有代碼塊作用域的變量聲明中,而這樣的變量已經擁有自動存儲時期,因此它主要用來明確指出意圖,使程序更易讀。

說明符 register ?也只能用于具有代碼塊作用域的變量。它將一個變量歸入寄存器存儲類,這相當于請求將該變量存儲在一個寄存器內,以更快地存取。它的使用也使得不能獲得變量的地址

說明符 static ?在用于具有代碼塊的作用域的變量的聲明時,使該變量具有靜態存儲時期,從而得以在程序運行期間(即使在包含該變量的代碼塊沒有運行時)存在并保留其值。變量仍具有代碼塊作用域和空鏈接。static 用于具有文件作用域的變量的聲明時,表明該變量具有內部鏈接。

說明符 extern ?表明在聲明一個已經在別處定義了的變量。如果包含 extern 的聲明具有文件作用域,所指向的變量必須具有外部鏈接。如果包含 extern 的聲明具有代碼塊作用域,所指向的變量可能具有外部鏈接也可能具有內部鏈接,這取決于該變量的定義聲明。

關鍵字 typedef 參看:C語言再學習 -- 關鍵字typedef

注意,這?5 個作為存儲類說明符的關鍵字,不可以同時出現的。

例如: ?typedef static int int32 ?是錯誤的。

下面來一一詳細介紹:

1、auto關鍵字

auto 關鍵字在C語言中只有一個作用,那就是修飾局部變量。?
auto 修飾局部變量,表示這個局部變量是自動局部變量,自動局部變量分配在棧上。(既然在棧上,說明它如果不初始化那么值就是隨機的)?
平時定義局部變量時就是定義的auto的,只是省略了auto關鍵字而已。可見,auto的局部變量其實就是默認定義的普通的局部變量。 即 int a = 10; 等價于 auto int a = 10;

auto 修飾局部變量,若省去數據類型,變量默認為 int 類型

#include <stdio.h> //auto int d; 修飾全局變量 錯誤: 文件作用域聲明‘d’指定了‘auto’ int main (void) {auto int a = 10; //等價于 int a = 10;auto b = 9; //默認數據類型 為 intauto c; //不初始化,值為隨機的printf ("sizeof (b) = %d\n", sizeof (b));printf ("c = %d\n", c);return 0; } 輸出結果: sizeof (b) = 4 c = -1217310732

?

2、register關鍵字

在 C 語言中的 register 修飾的變量表示將此變量存儲在CPU的寄存器中,由于CPU訪問寄存器比訪問內存快很多,可以大大提高運算速度。但在使用register時有幾點需要注意。

1)用register修飾的變量只能是局部變量,不能是全局變量。CPU的寄存器資源有限,因此不可能讓一個變量一直占著CPU寄存器。

2)register變量一定要是CPU可以接受的值。

3)不可以用&運算符對register變量進行取址。比如 int i;(自動為auto)int *p=&i;是對的, 但register int j; int *p = &j; 是錯的,因為無法對寄存器的定址。

4)register只是請求寄存器變量,不一定能夠成功

5)隨著編譯程序設計技術的進步,在決定那些變量應該被存到寄存器中時,現在的C編譯環境能比程序員做出更好的決定。實際上,許多編譯程序都會忽略register修飾符,因為盡管它完全合法,但它僅僅是暗示而不是命令。

#include <stdio.h> //register int n; 修飾全局變量 錯誤: ‘n’的寄存器名無效int main (void) {register int i;//int *p = &i; 對i取地址 錯誤: 要求寄存器變量‘i’的地址。int tmp = 0;for (i = 1; i < 100; i++)tmp += i;printf ("tmp = %d\n", tmp);return 0; } 輸出結果: tmp = 4950

?

寄存器變量(register): 寄存器變量會盡量把變量放到寄存器(而不是棧或堆), 從而加快存取速度。下面的例子可以很好的看出:

#include <stdio.h> #include <sys/timeb.h> long long getSystemTime() {struct timeb t;ftime(&t);return 1000 * t.time + t.millitm; }#define TIME 1000000000int m, n = TIME; /* 全局變量 */ int main(void) { register int a, b = TIME; /* 寄存器變量 */int x, y = TIME; /* 一般變量 */long long start = 0, end = 0;start=getSystemTime();for (a = 0; a < b; a++);end=getSystemTime();printf("寄存器變量用時: %lld ms\n", end - start);start=getSystemTime();for (x = 0; x < y; x++);end=getSystemTime();printf("一般變量用時: %lld ms\n", end - start);start=getSystemTime();for (m = 0; m < n; m++);end=getSystemTime();printf("全局變量用時: %lld ms\n", end - start);return 0; } 輸出結果: 寄存器變量用時: 533 ms 一般變量用時: 3513 ms 全局變量用時: 3587 ms

?

3、static關鍵字

?

參看:C語言再學習 -- 內存管理

參看:C語言再學習 -- 存儲類、鏈接

首先了解下,進程中的內存區域劃分
(1)代碼區?存放程序的功能代碼的區域,比如:函數名
(2)只讀常量區?主要存放字符串常量和const修飾的全局變量
(3)全局區?主要存放?已經初始化的全局變量?和?static修飾的全局變量
(4)BSS段?主要存放?沒有初始化的全局變量?和?static修飾的局部變量,BSS段會在main函數執行之前自動清零
(5)堆區?主要表示使用malloc/calloc/realloc等手動申請的動態內存空間,內存由程序員手動申請和手動釋放
(6)棧區?主要存放局部變量(包括函數的形參),const修飾的局部變量,以及塊變量,該內存區域由操作系統自動管理

下面詳細介紹 static 關鍵字,主要有三類用法:

1)static 修飾全局變量

static 修飾的全局變量也叫靜態全局變量,和已經初始化的全局變量同?在全局區

該類具有靜態存儲時期文件作用域內部鏈接僅在編譯時初始化一次如未明確初始化,它的字節都被設定為0。static全局變量只初使化一次,是為了防止在其他文件單元中被引用;利用這一特性可以在不同的文件中定義同名函數和同名變量,而不必擔心命名沖突。

示例說明:

file.h

//頭文件衛士 #ifndef __FILE_H__ #define __FILE_H__ void foo (); #endif

file1.c

#include <stdio.h> #include "file.h"int n = 5; //已初始化的全局變量 static int m = 10; //已初始化的靜態全局變量int x; //未初始化的全局變量,自動初始化為 0 static int y; //未初始化的靜態全局變量, 自動初始化為 0void foo () //靜態全局變量,具有文件作用于,靜態定義,內部鏈接 {printf ("x = %d, y = %d\n", x, y);printf ("n = %d, m = %d\n", n, m); }

file2.c

#include <stdio.h> #include "file.h" int main (void) {extern int n; extern int m; foo ();printf ("n = %d\n", n); //全局變量,可被其他文件使用//printf ("m = %d\n", m); //靜態全局變量, 不可被其他文件使用//出現錯誤 file2.c:(.text+0x27): undefined reference to `m'return 0; }

輸出結果:

編譯: gcc file1.c file2.c -o file 輸出結果: x = 0, y = 0 n = 5, m = 10 n = 5

?

2)static 修飾局部變量

?

static 修飾的局部變量也叫靜態局部變量,和沒有初始化的全局變量同?BBS段。而非靜態局部變量是被分配在上面的。非靜態局部變量,函數調用結束后存儲空間釋放靜態局部變量,具有靜態存儲時期。只在程序開始時執行一次,函數調用結束后存儲區空間并不釋放,保留其當前值。

該類具有靜態存儲時期代碼作用域空鏈接僅在編譯時初始化一次如未明確初始化,它的字節都被設定為0

file.h

//頭文件衛士 #ifndef __FILE_H__ #define __FILE_H__ void foo (); #endif

file1.c

?

#include <stdio.h> #include "file.h" void foo () {int n = 5; //已初始化,局部變量static m = 10; //已初始化,靜態局部變量printf ("n = %d, m = %d\n", n, m); n++;m++; }

file2.c

#include <stdio.h> #include "file.h" int main (void) {foo ();foo ();foo (); //自動局部變量,函數調用結束后存儲空間釋放foo (); //靜態局部變量,具有靜態存儲時期。只在程序開始時執行一次,函數調用結束后存儲區空間并不釋放,保留其當前值。extern int n;extern int m; // printf ("n = %d\n", n); // printf ("n = %d\n", m); //靜態局部變量,為空鏈接,不可以被其他文件使用,出現錯誤 // file2.c:(.text+0x1f): undefined reference to `n' // file2.c:(.text+0x36): undefined reference to `m'int x; //未初始化,局部變量,初始化為隨機數static int y; //未初始化,靜態局部變量,自動初始化為 0printf ("x = %d, y = %d\n", x, y);return 0; }

輸出結果:

編譯: gcc file1.c file2.c -o file 輸出結果: n = 5, m = 10 n = 5, m = 11 n = 5, m = 12 n = 5, m = 13 x = -1216741388, y = 0

?

3)static 修飾函數

外部函數可被其他文件中的函數調用,而靜態函數只可以在定義它的文件中使用。例如,考慮一個包含如下函數聲明的文件:

double?gamma?();?//默認為外部的?? static?double?beta?();?//靜態函數?? extern?double?delta?();??

函數gamma ()和delta ()可被程序的其他文件中的函數使用,而beta ()則不可以,因為beta ()被限定在一個文件內,故可在其他文件中使用相同名稱的不同函數。使用 static 存儲類的原因之一就是創建為一個特定模塊所私有的函數,從而避免可能的名字沖突。

通常使用關鍵字 extern 來聲明在其他文件中定義的函數。這一習慣做法主要是為了程序更清晰,因為除非函數聲明使用了關鍵字 static ,否則認為就是extern 的。

示例:

file.h

//頭文件衛士 #ifndef __FILE_H__ #define __FILE_H__ void call (void); static void foo (void); #endif

?

file1.c

#include <stdio.h> #include "file.h"//靜態函數,不能被其他文件使用 static void foo (void) {printf ("foo\n"); }void call (void) {foo (); }

?

file2.c

#include <stdio.h> #include "file.h"//使用相同名字的不同函數 void foo (void) {printf ("hello world\n"); } int main (void) {call (); // foo (); 錯誤: file2.c:(.text+0xc): undefined reference to `foo'foo ();return 0; }

輸出結果:

編譯: gcc file1.c file2.c -o file 輸出結果: foo hello world

?

4、extern 關鍵字

?

整理了好久, extern 算是最讓我糾結的了。看了好多篇文章,都沒有講出個所以然來,搞得我好郁悶。這也體現出很有必要詳細講解下的它的用法了。

首先,再講解之前先要了解下,聲明和定義的區別。

參看:C語言再學習 -- 聲明與定義


舉個例子: A)int i; B)extern int i;?
哪個是定義?哪個是聲明?或者都是定義或者都是聲明?

什么是定義:所謂的定義就是(編譯器)創建一個對象,為這個對象分配一塊內存并給它取上一個名字,這個名字就是我們經常所說的變量名或對象名。但注意,這個名字一旦和這塊內存匹配起來,它們就同生共死,終生不離不棄。并且這塊內存的位置也不能被改變。一個變量或對象在一定的區域內(比如函數內,全局等)只能被定義一次如果定義多次,編譯器會提示你重復定義同一個變量或對象。

什么是聲明:有兩重含義,如下:
第一重含義:告訴編譯器,這個名字已經匹配到一塊內存上了,下面的代碼用到變量或對象是在別的地方定義的。聲明可以出現多次。
第二重含義:告訴編譯器,我這個名字我先預定了,別的地方再也不能用它來作為變量名或對象名。比如你在圖書館自習室的某個座位上放了一本書,表明這個座位已經有人預訂,別人再也不允許使用這個座位。其實這個時候你本人并沒有坐在這個座位上。這種聲明最典型的例子就是函數參數的聲明,例如:
void fun(int i, char c);
好,這樣一解釋,我們可以很清楚的判斷: A)是定義; B)是聲明。
記住, 定義聲明最重要的區別:定義創建了對象并為這個對象分配了內存,聲明沒有分配內存。

聲明: 指定了一個變量的標識符,用來描述變量的類型,是類型還是對象,或者函數等。聲明,用于編譯器(compiler)識別變量名所引用的實體。以下這些就是聲明:

extern int bar; extern int g(int, int); double f(int, double); // 對于函數聲明,extern關鍵字是可以省略的。 class foo; // 類的聲明,前面是不能加class的。

定義: 是對聲明的實現或者實例化。連接器(linker)需要它(定義)來引用內存實體。與上面的聲明相應的定義如下:

int bar; int g(int lhs, int rhs) {return lhs*rhs;}? double f(int i, double d) {return i+d;}? class foo {};// foo 這里已經擁有自己的內存了,對照上面兩個函數,你就應該明白{}的用處了吧? 無論如何,定義 操作是只能做一次的。如果你忘了定義一些你已經聲明過的變量,或者在某些地方被引用到的變量,那么,連接器linker是不知道這些引用該連接到那塊內存上的。然后就會報missing symbols 這樣的錯誤。如果你定義變量超過一次,連接器是不知道把引用和哪塊內存連接,然后就會報 duplicated symbols這樣的錯誤了。以上的symbols其實就是指定義后的變量名,也就是其標識的內存塊。總結: 如果只是為了給編譯器提供引用標識,讓編譯器能夠知道有這個引用,能用這個引用來引用某個實體(但沒有為實體分配具體內存塊的過程)是為聲明。如果該操作能夠為引用指定一塊特定的內存,使得該引用能夠在link階段唯一正確地對應一塊內存,這樣的操作是為定義。 聲明是為了讓編譯器正確處理對聲明變量和函數的引用。定義是一個給變量分配內存的過程,或者是說明一個函數具體干什么用。 通過上述對聲明和定義的解釋可以看出,在C語言中,修飾符 extern 用在變量或者函數的聲明前,用來說明“此變量/函數是在別處定義的,要在此處引用”。extern 是 C/C++ 語言中表明函數和全局變量作用范圍(可見性)的關鍵字,該關鍵字告訴編譯器,其聲明的函數和變量可以在本模塊或其它模塊中使用。 1)extern 修飾變量的聲明 具有外部鏈接的靜態變量具有文件作用域,外部鏈接和靜態存儲時期。這一類型有時被稱為外部存儲類,這一類型的變量被稱為外部變量。把變量的定義聲明放在所有函數之外,即創建了一個外部變量。為了使程序更加清晰,可以在使用外部變量的函數中通過使用 extern 關鍵字來再次聲明它。如果變量是在別的文件中定義,使用 extern 來聲明該變量就是必須的。 ? int n; /*外部定義的變量*/ double Up[100]; /*外部定義的數組*/ extern char Coal; /*必須的聲明,因為Coal在其他文件中定義*/ void next (void); int main (void) { extern double Up[]; /*可選的聲明,此處不必指明數組大小*/ extern int n; /*可選的聲明,如果將extern漏掉,就會建立一個獨立的自動變量*/ } void next (void) { ... } ? 下列 3 個例子展示了外部變量和自動變量的 4 種可能組合: ? /*例1*/ int H; int magic (); int main (void) { extern int H; /*聲明H為外部變量*/ ... } int magic () { extern int H; /*與上面的H是同一變量*/ } /*例2*/ int H; int magic (); int main (void) { extern int H; /*聲明H為外部變量*/ ... } int magic () { ... /*未聲明H,但知道該變量*/ } /*例3*/ int H; /*對main()和magic()不可見,但是對文件中其他不單獨擁有局部H的函數可見*/ int magic (); int main (void) { int H; /*聲明H, 默認為自動變量,main()的局部變量*/ ... } int P;/*對magic()可見,對main()不可見,因為P聲明子啊main()之后*/ int magic () { auto int H; /*把局部變量H顯式地聲明為自動變量*/ } 這些例子說明了外部變量的作用域:從聲明的位置開始到文件結尾為止。它們也說明了外部變量的生存期。

外部變量H和P存在的時間與程序運行時間一樣,并且它們不局限于任一函數,在一個特定函數返回時并不消失。

多文件的程序中聲明外部變量,使用 extern 來聲明該變量就是必須的。注意能夠被其他模塊以extern修飾符引用到的變量通常是全局變量,可以放在file2.c文件的任何位置

//file1.c int n = 10, m = 5; //n, m 為全局變量,只能定義在一處 //file2.c #include <stdio.h> //extern int n, m; //聲明 全局變量 //int n= 2, m = 3; /* 若果再次定義n,m。會出現錯誤 /tmp/cc4R2MbY.o:(.data+0x0): multiple definition of `n' /tmp/ccwV9hWd.o:(.data+0x0): first defined here /tmp/cc4R2MbY.o:(.data+0x4): multiple definition of `m' /tmp/ccwV9hWd.o:(.data+0x4): first defined here collect2: ld 返回 1 */void max (void);int main (void) {//printf ("n = %d, m = %d\n", n, m);max ();return 0; }void max (void) {extern int n, m; //n, m為全局變量printf ("n = %d, m = %d\n", n, m); } 編譯: gcc file1.c file2.c -o file 輸出結果: n = 10, m = 5

?

2)extern 修飾函數的聲明

外部函數可被其他文件中的函數調用,而靜態函數只可以在定義它的文件中使用。例如,考慮一個包含如下函數聲明的文件:

double?gamma?();?//默認為外部的?? static?double?beta?();?//靜態函數?? extern?double?delta?();??

函數gamma ()和delta ()可被程序的其他文件中的函數使用,而beta ()則不可以,因為beta ()被限定在一個文件內,故可在其他文件中使用相同名稱的不同函數。使用 static 存儲類的原因之一就是創建為一個特定模塊所私有的函數,從而避免可能的名字沖突。

通常使用關鍵字 extern 來聲明在其他文件中定義的函數。這一習慣做法主要是為了程序更清晰,因為除非函數聲明使用了關鍵字 static ,否則認為就是extern 的。換句話說,在定義(函數)的時候,這個extern居然可以被省略。

如果函數的聲明中帶有關鍵字 extern,僅僅是暗示這個函數可能再別的源文件里定義,沒有其它作用。即下述這兩個函數聲明沒有明顯的區別:extern int foo (); 和 int foo (); 函數定義和聲明時 extern 可有可無

? //file.c #include <stdio.h> void foo (void) { printf ("hello world!\n"); } //file2.c #include <stdio.h>extern void foo ( ); //該函數聲明可以放在 file2.c的任何位置 //等同于 void foo (); int main (void) {foo ();return 0; } 編譯: gcc file1.c file2.c -o file 輸出結果: hello world! ? 一般把所有的全局變量和全局函數都放在一個 *.c 文件中,然后用一個同名的 *.h 文件包含所有的函數和變量的聲明. ? //main.c #include <stdio.h> #include "read.h" int main (void) { read (); printf ("num = %d\n", num); return 0; } //read.c #include <stdio.h> #include "read.h" int num; //全局變量 定義void read (void) {printf ("請輸入一個數字:");scanf ("%d", &num); } //read.h //頭文件衛士,防止頭文件被重復定義 #ifndef __READ_H__ #define __READ_H__ extern int num; void read (void); //等價于 extern void read (void); #endif 編譯: gcc main.c read.c -o read 輸出結果: 請輸入一個數字:12 num = 12 注意: (1) extern int num = 10; 沒有這種形式,不是定義。如果在 read.h中如此寫的話會出現: ? read.h:3:12: 警告: ‘num’已初始化,卻又被聲明為‘extern’ [默認啟用] In file included from read.c:2:0: read.h:3:12: 警告: ‘num’已初始化,卻又被聲明為‘extern’ [默認啟用] /tmp/ccQ3Jzzm.o:(.data+0x0): multiple definition of `num' /tmp/cceLUBvB.o:(.data+0x0): first defined here collect2: ld 返回 1 (2)再有,在使用 extern 時候要嚴格對應聲明時的格式,例如: 聲明的函數為: extern void read (void); 定義的時候 返回值、形參類型、函數名 需要一致,為 void read (void) {...} C 程序中,不允許出現類型不同的同名變量。 (3)定義數組,修飾指針 在一個源文件里定義了一個數組:char a[100]; 在另外一個文件里用下列語句進行了聲明:extern char *a; 這樣是不可以的,程序運行時會告訴你非法訪問。原因在于,指向類型T的指針并不等價于類型T的數組。extern char *a聲明的是一個指針變量而不是字符數組,因此與實際的定義不同,從而造成運行時非法訪問。應該將聲明改為extern char a[ ]。 但是,extern char a[]與 extern char a[100]等價。 因為這只是聲明,不分配空間,所以編譯器無需知道這個數組有多少個元素。 ? ?

3)extern "C"

上面講到了,C 程序中,不允許出現類型不同的同名變量。例如: ? #include <stdio.h> void foo(void); int foo (int ,int); int main (void) { return 0; } 輸出結果: test.c:5:5: 錯誤: 與‘foo’類型沖突 test.c:3:6: 附注: ‘foo’的上一個聲明在此

C++程序中 卻允許出現重載重載的定義同一個作用域,函數名相同,參數表不同的函數構成重載關系,例如:

?

?

?

//renam.cpp #include <iostream> using namespace std; void foo (int i) { cout << i << endl; } void foo (int i, double d) { cout << i << ' ' << d << endl; } int main (void) { foo (1); //_Z3fooi (1); foo (1,2);//_Z3fooid (1, 2.); return 0; } gcc -c rename.cpp //生成 rename.o nm rename.o //查看 ============================= 000000f3 t _GLOBAL__I__Z3fooi 00000000 T _Z3fooi 0000002b T _Z3fooid 000000b3 t _Z41__static_initialization_and_destruction_0iiU _ZNSolsEPFRSoS_EU _ZNSolsEdU _ZNSolsEiU _ZNSt8ios_base4InitC1EvU _ZNSt8ios_base4InitD1EvU _ZSt4coutU _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ 00000000 b _ZStL8__ioinitU _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_cU __cxa_atexitU __dso_handleU __gxx_personality_v0 00000081 T main

可以看到:函數被 C++編譯后在庫中的名字與 C 語言的不同。
函數void foo (int i); 的庫名為 _Z3fooi
函數void foo (int i, double d);? 的庫名為 _Z3fooid

通過庫名,可以看出來包含了函數名、函數參數數量及類型信息,C++就是靠這種機制來實現函數重載的。而 C 語言則不會,因此會造成鏈接時找不到對應函數的情況,此時C函數就需要用extern “C”進行鏈接指定,來解決名字匹配問題,這告訴編譯器,請保持我的名稱,不要給我生成用于鏈接的中間函數名。 未加 extern "C" 聲明的,在C++中因為重載,庫名是 _Z3fooid,加上 extern "C" 會采用 C語言的方式 編譯生成 foo。extern “C”這個聲明的真實目的是為了實現C++與C及其它語言的混合編程。 ? ? ? 參看:c/c++ 混合編程的 extern “C” 參看:extern "c"用法之一 參看:extern "c"用法解析 參看:extern ”C"的使用 C++中 extern "C" 的兩種用法: ? 1)用C++語言寫的一個函數,如果想讓這個函數可以被其他C語言程序所用,則用extern "C" 來告訴C++編譯器,請用C語言習慣來編譯此函數。如: ? //add.h #ifndef _ADD_H #define _ADD_H #ifdef __cplusplus extern "C" { #endif int add (int ,int ); #ifdef __cplusplus } #endif #endif //add.cpp #include "add.h" int add (int x, int y) {return x + y; } //main.c #include <stdio.h> #include "add.h" int main (void) {int x=13,y=6;printf("%d+%d=%d\n",x,y,add(x,y));return 0; } 編譯: gcc add.cpp main.c -o add -lstdc++ 輸出結果: 13+6=19 __cplusplus是cpp中自定義的一個宏,告訴編譯器,這部分代碼按C語言的格式進行編譯,而不是C++的。 源文件為*.c,__cplusplus沒有被定義,extern "C" {}這時沒有生效對于C他看到只是 extern int add(int, int);? add 函數編譯符號成 add gcc -c main.c nm main.o U add 00000000 T main U printf

源文件為*.cpp(或*.cc,*.C,*.cpp,*.cxx,*.c++), __cplusplus被定義 ,對于C++他看到的是 extern "C" ?{ extern ?int add( int ,int);}編譯器就會知道 add(13, 6);調用的C風格的函數,就會知道去找add符號而不是_Z3addii ;因此編譯正常通過。

注:-lstdc++ 申明用c++庫
如果將,add.h 如下改寫,不使用 extern "C":

#ifndef _ADD_H #define _ADD_H /* #ifdef __cplusplus extern "C" { #endif int add (int ,int ); #ifdef __cplusplus } #endif */ extern int add (int, int); #endif 編譯:gcc add.cpp main.c -o add -lstdc++ 出現錯誤 /tmp/ccBSzdDa.o: In function `main': main.c:(.text+0x29): undefined reference to `add' collect2: ld 返回 1

但是,編譯:g++ add.cpp main.c -o add 是OK的

因為g++會自動將c的模塊中的符號表轉換為 _Z3addii 這也是GNU compiler的強大之處,可是別的編譯器也許就不這么智能了。所以在c/c++混合編程時還是最好加上extern “C”。
2)如果要在C++程序中調用C語言寫的函數, 在C++程序里邊用 extern "C" 修飾要被調用的這個C程序,告訴C++編譯器此函數是C語言寫的,是C語言編譯器生成的,調用他的時候請按照C語言習慣傳遞參數等。

//sub.h #ifndef _SUB_H #define _SUB_H int sub(int ,int); #endif //sub.c #include "sub.h" int sub(int x,int y) {return x + y; } //main.cpp #include <iostream> using namespace std; extern "C" { #include "sub.h" } int main (void) {int x=5,y=6;cout << x << "+" << y << "="<< sub(x, y) << endl;return 0; } 編譯: gcc sub.c main.cpp -o sub -lstdc++ 5+6=11

?

總結

以上是生活随笔為你收集整理的C语言再学习 -- 存储类型关键字的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。