C語言再學習 -- 儲存型別關鍵字
C語言中有 5 個作為儲存類說明符的關鍵字,分別是 auto、register、static、extern 以及 typedef。關鍵字typedef 與記憶體儲存無關,由於語法原因被歸入此類。
現在簡單瞭解一下這五個儲存類說明符的關鍵字:
說明符 auto 表明一個變數具有自動儲存時期。該說明符只能用於在具有程式碼塊作用域的變數宣告中,而這樣的變數已經擁有自動儲存時期,因此它主要用來明確指出意圖,使程式更易讀。
說明符 register也只能用於具有程式碼塊作用域的變數。它將一個變數歸入暫存器儲存類,這相當於請求將該變數儲存在一個暫存器內,以更快地存取。它的使用也使得不能獲得變數的地址。
說明符 static
說明符 extern表明在宣告一個已經在別處定義了的變數。如果包含 extern 的宣告具有檔案作用域,所指向的變數必須具有外部連結。如果包含 extern 的宣告具有程式碼塊作用域,所指向的變數可能具有外部連結也可能具有內部連結,這取決於該變數的定義宣告。
注意,這 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; //預設資料型別 為 int auto 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 1000000000
int 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關鍵字
首先了解下,程序中的記憶體區域劃分
(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; //未初始化的靜態全域性變數, 自動初始化為 0
void 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; //未初始化,靜態區域性變數,自動初始化為 0
printf ("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 (); //預設為外部的
- staticdouble beta (); //靜態函式
- externdouble delta ();
通常使用關鍵字 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 算是最讓我糾結的了。看了好多篇文章,都沒有講出個所以然來,搞得我好鬱悶。這也體現出很有必要詳細講解下的它的用法了。
首先,再講解之前先要了解下,宣告和定義的區別。
舉個例子:
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 (); //預設為外部的
- staticdouble beta (); //靜態函式
- externdouble delta ();
通常使用關鍵字 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_0ii
U _ZNSolsEPFRSoS_E
U _ZNSolsEd
U _ZNSolsEi
U _ZNSt8ios_base4InitC1Ev
U _ZNSt8ios_base4InitD1Ev
U _ZSt4cout
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
00000000 b _ZStL8__ioinit
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c
U __cxa_atexit
U __dso_handle
U __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++中 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語言再學習 -- 儲存型別關鍵字
C語言中有 5 個作為儲存類說明符的關鍵字,分別是 auto、register、static、extern 以及 typedef。關鍵字typedef 與記憶體儲存無關,由於語法原因被歸入此類。現在簡單瞭解一下這五個儲存類說明符的關鍵字:說明符 auto 表明一個變數具有自
C語言再學習 -- 關鍵字struct(轉)
結構體的一般定義形式為: 標籤(tag)欄位允許為成員列表提供一個名字,這樣它就可以在後續的宣告中使用。標籤允許多個宣告使用同一個成員列表,並且建立同一種類型的結構。 struct 標籤{ 型別名1 成員名1; 型別名2 成員名2; …… 型別名n 成員名n; }結構體變數;
C語言再學習--關鍵字
C語言一共有32個關鍵字,如下表所示: 關鍵字 說明 auto 宣告自動變數 short 宣告短整型變數或函式 int
C語言再學習 -- 關鍵字typedef
一、typedef 介紹 typedef為C語言的關鍵字,作用是為一種資料型別定義一個新名字。比如人們常常使用 typedef 來編寫更美觀和可讀的程式碼。所謂美觀,意指 tepe
C語言再學習 -- 關鍵字volatile
上週確實事情挺多的,年會、公司聚餐,一到過年就有忙不完的事分心。還好C語言再學習總結的已經差不多了,年前也不展開別的了,接下來這十幾天、總結幾篇典型的面試題吧。 言歸正傳,接下來看看關鍵字 volatile。 一、volatile 介紹 Indicates that
C語言再學習-定義變數
當我們在c語言裡建立一個變數的時候 int x = 5; int y = 6; 00C517B8 mov dword ptr [x],5 00C517BF mov dword ptr [y],6 實際上在彙編層面,
C語言再學習5-陣列與優化
什麼是陣列?為什麼要用陣列? 通俗來講,在記憶體中一塊連續儲存資料的叫陣列,陣列的每個子元素的寬度都一樣,並且只能為通用的資料型別做單位(char,short,int等等) 讓我們先定義一個數組,然後賦值: char arr1[2] = { 0 }; arr1
C語言再學習7-結構體
為什麼使用結構體? struct My { char name[20] = "如風斬嶽"; int age; char addr[50] ; int money; double Coordinates; //..... }; 當我們有這樣一種需求,
C語言再學習 -- 負數
有符號數的表示方法是由硬體決定,而不是由C決定的。有三種表示方法: 1、二進位制原碼 0000 0001 表示 1 1000 0001 表示 -1 這個方法有個缺點是有兩個零: +0 和 -0。這會引起混淆,而且用兩個位
C語言再學習 -- 檔案
檔案是什麼 一個檔案(file)通常就是磁碟上的一段命名的儲存區。C 將檔案看成是連續的位元組序列,其中每一個位元組都可以單獨地讀取。 二進位制和文字模式 1、在windows系統中,文字模式
C語言再學習 -- ASCII碼錶(轉)
ASCII碼錶第一部分:ASCII非列印控制字元表ASCII表上的數字0–31分配給了控制字元,用於控制像印表機等一些外圍裝置。例如,12代表換頁/新頁功能。此命令指示印表機跳到下一頁的開頭。(參詳ASCII碼錶中0-31)第二部分:ASCII列印字元數字 32–126 分配給了能在鍵盤上找到的字元,當您檢視
【 分類 】- C語言再學習
專欄達人 授予成功建立個人部落格專欄
C語言再學習 -- 詳解C++/C 面試題 1
對這篇文章記憶猶新,因為之前找工作面試的時候,遇到過一家公司就是用的這套面試題。現在就結合考查的知識點和我總結完 C 語言再學習後的深入理解,來詳細的講講我對這篇文章的總結。 一、請填寫BOOL ,
C語言再學習 -- 再論記憶體管理
但現在看來,缺少示例。從新再寫一篇文章,著重介紹常見記憶體錯誤、跨函式使用儲存區。開始吧,再論記憶體管理!!發生記憶體錯誤是件非常麻煩的事情。編譯器不能自動發現這些錯誤,通常是在程式執行時才能捕捉到。而這些錯誤大多沒有明顯的症狀時隱時現增加了改錯的難度。一、常見的記憶體錯誤及
C語言再學習 -- 記憶體管理
malloc ( )函式: malloc ( ) 向系統申請分配指定size個位元組的記憶體空間。返回型別是 void* 型別。void* 表示未確定型別的指標。C,C++規定,void* 型別可
C語言再學習 -- 時間函式
gmtime函式:可以把time函式得到的結果按照格林尼治時間轉換成一個結構體localtime函式:可以把time函式得到的結果按照當前時區轉換成一個結構體asctime函式:可以把一個記錄時間的結構體轉換成字串,一般與上兩個函式合用的格林時間,與北京時間換算,/* 時間函式演示 */ #
C語言再學習 -- 再論陣列和指標
之前有總結指標陣列,但是現在看來總結的太簡單了。好多重要的知識點都是一帶而過的。本想在後面新增後來想想算了,還是再寫一篇文章來詳細介紹陣列和指標這對冤家吧。一開始覺得C語言再學習專欄都寫了五十篇了,現在
C語言再學習 -- NUL和NULL的區別
NUL 是ASCII 字符集中 '\0' 字元的名字,它的位元組模式為全 0。NULL 指一個其值為 0 的指標。它們都是整型值,其值也相同,所以它們可以互換使用。然而,你還是應該使用適當的常量,因為
C語言再學習 -- 段錯誤(核心已轉儲)
一、什麼是段錯誤?一旦一個程式發生了越界訪問,cpu 就會產生相應的保護,於是 segmentation fault 就出現了,通過上面的解釋,段錯誤應該就是訪問了不可訪問的記憶體,這個記憶體區要麼是不存在的,要麼是受到系統保護的,還有可能是缺少檔案或者檔案損壞。二、段錯誤產
C語言再學習 -- 詳解C++/C 面試題 2
(經典)C語言測試:想成為嵌入式程式設計師應知道的0x10個基本問題。1、用預處理指令#define 宣告一個常數,用以表明1年中有多少秒(忽略閏年問題) #define SENCONDS_PER_YE