2011-10-18 161 views
3

我實現在C.隊列的實現我的接口包括5個簡單的函數來訪問隊列在線聲明:混淆使用C

#ifndef QUEUE_H 
#define QUEUE_H 

#include <stdbool.h> 
#include <stddef.h> 

struct queue { 
    struct cell* first; 
    struct cell* last; 
}; 

typedef struct queue queue; 

extern queue newQueue(void); 
extern bool isEmpty(queue); 
extern queue enqueue(queue,void*); 
extern queue dequeue(queue); 
extern void* front(queue); 
extern void freeQueue(queue); 

因爲他們兩個(newQueueisEmpty)是如此平凡我相信編譯器可以對它們做很多優化,我決定爲它們編寫內聯聲明:

/* replacing the two lines 

extern queue newQueue(void); 
extern bool isEmpty(queue); 

in the original header */ 

extern inline queue newQueue(void) { 
    queue q = { NULL, NULL }; 
    return q; 
} 

extern inline bool isEmpty(queue q) { 
    return q.first == NULL; 
} 

這個編譯和gcc很好。但是當我用clang編譯它時,它給了我一個錯誤。一個快速的研究表明,從GNU風格做這些內聯聲明is different的官方方式。我可以通過-std=gnu89或根據上面的鏈接更改功能簽名。我chosed第二個選項:

inline queue newQueue(void) { 
    queue q = { NULL, NULL }; 
    return q; 
} 

inline bool isEmpty(queue q) { 
    return q.first == NULL; 
} 

但現在,無論鐺和gcc說一些關於重複函數聲明,在C99編譯模式。這是queue.c中的相關定義:

#include "queue.h" 

/* ... */ 

queue newQueue() { 
    queue q = { NULL, NULL }; 
    return q; 
} 

bool isEmpty(queue q) { 
    return q.first == NULL; 
} 

我在做什麼錯了?我怎樣才能得到我想要的,而無需切換到gnu89模式?

這些都是錯誤的消息我與第二風格得到:

$ gcc -std=c99 queue.c 
queue.c:12:7: error: redefinition of ‘newQueue’ 
queue.h:14:21: note: previous definition of ‘newQueue’ was here 
queue.c:17:6: error: redefinition of ‘isEmpty’ 
queue.h:19:20: note: previous definition of ‘isEmpty’ was here 
$ clang -std=c99 queue.c 
queue.c:12:7: error: redefinition of 'newQueue' 
queue newQueue() { 
    ^
In file included from queue.c:5: 
./queue.h:14:21: note: previous definition is here 
extern inline queue newQueue(void) { 
        ^
queue.c:17:6: error: redefinition of 'isEmpty' 
bool isEmpty(queue q) { 
    ^
In file included from queue.c:5: 
./queue.h:19:20: note: previous definition is here 
extern inline bool isEmpty(queue q) { 
       ^
2 errors generated. 
+0

你能證明你得到確切* *錯誤信息? 「關於重複函數定義的一些事情」還不夠具體。 –

+0

@Greg對不起,忘記了錯誤信息。我已經添加了它。 – fuz

回答

5

被宣佈他們如果要定義在頭文件功能使他們static。這應該足以讓編譯器內聯它們(inline只是一個額外的提示)。

static如果您在整個程序中多次包含該標題,則標題中的函數將導致多個定義。

我做了一些研究,並有更多的信息:

您可以使用inline這種方式。至少在C99中。在queue.c中,您不能同時使用內聯和非內聯定義。您需要在#ifdef中包裝內聯定義或將它們移至queue.c中未包含的標題。

您需要兩次編寫函數並使用預處理器進行操作,但它應該按照您的要求工作。當函數不內聯時,它只會被髮射一次。

+0

在將函數定義爲'static'的情況下,函數是否可能最終會在可執行文件中被多次編譯?有沒有辦法確保如果一個函數真的生成,那麼只有一次? – fuz

+0

@FUZxxl你爲什麼在意?生成足夠可執行文件是工具集的工作。編譯器和鏈接器可以在該任務中合作。使用clang/llvm,您還可以進行鏈接時優化,以優化整個程序,而不僅僅是單個模塊。無論從中獲得什麼收益,都會導致由於重複功能而導致的任何「損失」都無關緊要。或者換句話說,除非你可以衡量它有什麼影響,否則不要冒這樣的事情。這不是過早的悲觀化。 –

+0

@KubaOber並非所有的工具集都足夠聰明,可以發現不同翻譯單元中的兩個功能是相同的。在大多數工具鏈(包括GNU工具鏈)中,這樣的事情可能是不可能的,因爲重要信息不可用(例如函數結束)。我不想編寫一個工具鏈,我想編寫隨處可用的可移植代碼,並且儘可能高效。這排除了關於一個特定工具鏈的能力的任何假設。 – fuz

1

你不應該在queue.c

+0

這實際上並非如此。他不應該在queue.c中定義*它們,但是在標準c內聯規則下(與GCC的pre-c99內聯行爲相反),您需要有一個'extern'聲明(最方便的是在相應的.c中文件),或者創建內聯函數'static'。我不相信許多編譯器可以刪除由「靜態內聯」引起的重複代碼,所以將'extern'聲明放在queue.c中可能是他想要的最佳選擇。 –

1

在c99及之後的版本中,正確的做法是僅僅在.c文件中有一個內聯函數的外部聲明,而不是它的定義。這將強制爲該函數創建獨立代碼,以便在出於某種原因內聯不可行的情況下正確鏈接。請參閱:http://www.greenend.org.uk/rjk/tech/inline.html

由於函數默認extern這就足夠了:

queue.c

#include "queue.h" 

/* ... */ 

queue newQueue(); 

bool isEmpty(queue q);