2009-04-23 81 views

回答

53

函數是在頭文件中定義的嗎?因此,實際的代碼是在函數直接給出,就像這樣:

static int addTwo(int x) 
{ 
    return x + 2; 
} 

那麼這只是一個到許多不同的C文件提供了一個有用的功能的方式。每個包含頭文件的C文件都會得到它自己可以調用的定義。這當然會浪費內存,並且(在我看來)是一件相當醜陋的事情,因爲在頭文件中使用可執行代碼通常不是一個好主意。

請記住,#include:如果編譯器看到,頭部基本上只是將頭部的內容(和它包含的任何其他頭部)的內容粘貼到C文件中。編譯器從不知道一個特定的函數定義來自頭文件。

UPDATE:在許多情況下,它實際上是一個好主意,做一些像上面,然後我意識到我的回答聽起來很黑與白的這個這是一種過於簡單化的東西一點。例如,代碼模型(或只是使用)intrinsic functions可以表示類似上面,並用顯式inline關鍵字連:

static inline int addTwo(int *x) 
{ 
    __add_two_superquickly(x); 
} 

這裏,__add_two_superquickly()功能是一個虛構的本質,因爲我們希望整個函數基本上編譯成單個指令,我們真的希望它被內聯。儘管如此,上面比使用宏更清晰。

僅僅直接使用內在的優點當然是將它包裝在另一個抽象層中,這使得可以在缺乏該特定內在因素的編譯器上構建代碼,方法是提供一個替代實現並根據哪一個選擇合適的代碼編譯器正在被使用。

+12

那麼,編譯器可能會內聯短的函數。所以如果功能足夠短,它實際上可以使用更少的內存。但是我會預先設置一個「內聯」,所以你不會收到關於未使用的靜態函數的編譯警告。 – quinmars 2009-04-23 09:26:32

+0

@quinmars好點,我編輯過。我希望晚點好點。 :) 謝謝。 – unwind 2014-02-02 17:58:15

+0

我不知道鏈接器是否會優化。它看到b.obj上的addTwo沒有被引用,那麼它將刪除obj中的定義?如果是這樣,開銷只是(函數的大小)*(引用它的不同obj文件的數量)。仍然大於(函數的大小),但不是不好? – doorfly 2014-06-30 22:10:44

10

它將有效地創建一個單獨的靜態函數,它在每個包含在其中的cpp文件中具有相同的名稱。這同樣適用於全局變量。

7

正如其他人所說的,它的含義與.c文件本身的static函數完全相同。這是因爲.c.h文件之間沒有語義差異;只有編譯單元由實際傳遞給編譯器的文件(通常名爲.c)組成,其中包含#include行(通常名爲.h)中指定的任何和所有文件的內容,因爲它們被預處理器看到。

約定C源文件名爲.c,公共聲明位於名爲.h的文件中只是一個約定。但它通常是一個很好的。根據該公約,.h文件中應該出現的唯一內容是聲明,因此通常避免在單個程序中多次使用相同的符號定義

在這種特殊情況下,static關鍵字使符號對模塊是私有的,所以不存在多重定義衝突等待造成麻煩。所以從某種意義上說,這是安全的。但是如果沒有保證函數會被內聯的話,那麼你就會冒這個函數在每個發生在#include這個頭文件中的模塊中被實例化的風險,這最多是代碼段中的內存浪費。

我不確定在通常可用的公共頭文件中用什麼用例可以證明這一點。

如果.h文件中生成的代碼和僅包含在一個單一的.c文件,然後我會親自命名比.h其他文件的東西以強調它實際上不是一個公共頭都沒有。例如,將二進制文件轉換爲初始化變量定義的實用程序可能會寫入一個文件,該文件旨在通過#include使用,並且很可能包含該變量的一個static聲明,甚至可能包含訪問器或其他相關實用程序的static定義功能。

1

定義在源文件或頭文件中沒有語義上的差異,基本上這兩個意思在使用static關鍵字時在純C中是一樣的,你限制了範圍。

但是,在頭文件中編寫這個問題時存在一個問題,這是因爲每次將頭文件包含在源文件中時,都會有一個具有相同實現的函數的副本,這與具有正常函數在頭文件中定義。通過在頭文件中添加定義,您不能達到靜態函數的目的。

因此,我建議你應該只在你的源文件中實現你的實現,而不是在頭文件中。

1

它在一些帶有小內聯函數的「僅標題」庫中是有用的。在這種情況下,你總是希望製作一個函數的副本,所以這不是一個壞模式。然而,這給你一個簡單的方法在單頭文件中插入不同的接口和實現部分:

// header.h 

// interface part (for user?!) 
static inline float av(float a, float b); 

// implementation part (for developer) 
static inline float av(float a, float b) 
{ 
    return (a+b)/2.f; 
} 

蘋果在GLK框架矢量數學庫使用構建適應和支持等(例如GLKMatrix4.h)。

2

如果您在頭文件中定義函數(而不是簡單地聲明它),函數的副本將在每個翻譯單元(基本上在每個包含該頭文件的cpp文件中)中生成。

這可能會增加可執行文件的大小,但是如果函數很小,這可能會忽略不計。優點是大多數編譯器可以內聯函數,這可能會提高代碼的性能。

但是可能有一個這樣做的區別在任何答案中都沒有提及。如果你的函數使用靜態局部變量,例如:

static int counter() 
{ 
    static int ctr = 0; 
    return ctr++; 
} 

不是:

//header 
int counter(); 

//source 
int counter() 
{ 
    static int ctr = 0; 
    return ctr++; 
} 

然後包括這個標題中的每個源文件都有其自己的計數器。如果函數在頭文件中聲明並在源文件中定義,則計數器將在整個程序中共享。

所以說唯一的區別是性能和代碼大小是錯誤的。

相關問題