2017-01-03 68 views
2

我有一個通用的一組元素作爲庫提供。C:傳遞函數指針時發生通用ADT錯誤

/** Type for defining the set */ 
typedef struct Set_t *Set; 

/** Element data type for set container */ 
typedef void* SetElement; 

/** Type of function for copying an element of the set */ 
typedef SetElement(*copySetElements)(SetElement); 

要創建集合,我必須提供一個函數指針來處理我打算使用該集合的元素的複製。

Set setCreate(copySetElements copyElement); 

我寫了下面的類型和複製功能:

typedef struct location_t { 
    char *name; 
} *Location; 

Location locationCopy(Location location){ 

    Location new_location = locationCreate(location->name); 

    return new_location; 
} 

*很顯然,我的一切簡化集中討論。

當我打電話:

Set locations = setCreate(locationCopy); 

我得到一個編譯器錯誤:

警告:傳遞 'setCreate' 的參數1從兼容的指針 型

預期 'copySetElements',但參數的類型爲'struct location_t *(*)(struct location_t *)'

+2

不要將指針自然隱藏在'typedef'後面,除了函數指針可能除外。它迷惑了每個人,包括你。 –

回答

3

你的例子可以歸結到

typedef void * (*VF)(void *); 
typedef int * (*IF)(int *); 

IF a = 0; 
VF b = a; //Warning 

C standard函數指針比指針的對象少的通用性。
您可以將指針轉換爲對象的指針void(和背面)沒有任何投(和警告),因爲在標準明確允許它

作廢的指針可能是條款轉換成指針或指向任何對象類型。
指向 的指針可能會將任何對象類型轉換爲指向void和返回的指針;
結果應該等於 等於原始指針。

請注意,函數不是一個對象。

關於指向函數的唯一事情是,標準保證是:

一個指針,指向一個類型的函數可被轉換成一個指針到另一個類型,然後再返回的函數;結果應與原始指針相等。
如果轉換後的指針可以用來調用其類型與引用類型兼容的功能,該行爲是未定義

後來的標準明確這是什麼意思了兩個功能是兼容的:

要使兩種函數類型兼容,兩者應指定兼容的返回類型。
此外,參數類型列表(如果兩者都存在)應符合參數的數量和使用省略號終結符;相應的參數應具有兼容的類型。

現在你可能認爲void*struct location_t*是兼容的類型,畢竟你可以指定對方。
但是標準很清晰:

如果兩個類型的類型相同,則兩種類型具有兼容類型。

後來繼續與更復雜的類型,並限定類型擴展這種關係。

這可能聽起來令人驚訝,但int*void*不兼容。
它們可以分配但不兼容,畢竟void*可能指向float或具有不同對齊要求的對象。

當涉及到不同類型的指針時,標準主要關心來回分配它們。
它不禁止在不兼容的指針類型之間轉換,但使用它們是未定義的行爲,因此警告。


更好的方法是執行函數內部投,as suggested in this answer

您的所有函數都應該有簽名void* (void*),以便不需要在指向函數的指針之間進行強制轉換。

SetElement locationCopy(SetElement element) 
{ 
    Location location = (Location)element; 

    Location new_location = locationCreate(location->name); 

    return new_location; 
} 

職能內的強制轉換有再次受到不確定的行爲,如果SetElement指針轉換爲不兼容的指針,但是如果你會打電話給locationCopy只能用指針Location S(如你確實希望應該不會發生去做)。


作爲邊注,一些程序員可以在使用typedef來隱藏指針類型皺眉頭。

+0

非常好的引用標準。唯一值得注意的是要包含對您所依賴的特定標準的引用。 (例如C89/90,C99,C11)。我認爲這種情況並沒有什麼不同,但它們之間存在着微妙而重要的變化。 (我看到你提供了一個C11草案的鏈接 - 這就足夠了) –

0

您定義copySetElements

POINTER (SetElement => SetElement) 

也可以定義setCreate

copySetElements => Set 

你打電話setCreate與參數類型的locationCopy

POINTER(Location => Location) 

從該生成的警告IMCO聲明參數類型的可兼容性和實際參數的類型。