2016-02-26 180 views
18

我有認識一些C++的語法與函數指針和函數聲明相結合的問題,那就是:函數指針的說明

通常,當我們要聲明一個類型的功能,我們做一樣的東西:

typedef void(*functionPtr)(int); 

這對我來說很好。從現在開始functionPtr是一個類型,代表 指向函數的指針,它返回void並將int作爲參數取值。

我們可以按如下方式使用它:

typedef void(*functionPtr)(int); 

void function(int a){ 
    std::cout << a << std::endl; 
} 

int main() { 

    functionPtr fun = function; 
    fun(5); 

    return 0; 
} 

而我們得到5印在屏幕上。

我們得到了指向功能fun的指針,我們將一些現有的指針分配給函數 - function,我們通過指針執行這個函數。涼。

現在,當我在某些書中讀到的時候,函數和指向函數的指針在某種程度上是相同的,所以實際上在聲明function()函數後,我們每次說函數都是指真正的函數和指向相同類型函數的指針,編譯和每個指令給出了相同的結果(5打印屏幕上):

int main() { 

    functionPtr fun = function; 
    fun(5); 
    (*fun)(5); 
    (*function)(5); 
    function(5); 

    return 0; 
} 

所以現在只要我能想象,那個函數指針和功能幾乎相同,那麼它在某種程度上對我很好。

然後我雖然,如果函數指針和真正的功能是一樣的,那麼爲什麼我不能做如下:

typedef void(functionPtr)(int); //removed * 

void function(int a){ 
    std::cout << a << std::endl; 
} 

int main() { 

    functionPtr fun = function; 
    fun(5); 

    return 0; 
} 

這給了我以下錯誤:

prog.cpp:12:14: warning: declaration of 'void fun(int)' has 'extern' and is initialized functionPtr fun = function;

所以我的理解,由於某種原因,編譯器現在可以理解,那有趣的是已經存在的函數。然後,我嘗試以下方法:

int main() { 

    functionPtr fun; 
    fun(5); 

    return 0; 
} 

而且出現鏈接錯誤。我以某種方式理解,作爲編譯器現在將樂趣視爲已經存在的函數,然後由於事實,樂趣無處定義,我會得到鏈接錯誤。在主陰影所以現在

typedef void(functionPtr)(int); 

void function(int a){ 
    std::cout << a << std::endl; 
} 

int main() { 

    functionPtr function; 
    function(5); 

    return 0; 
} 

功能全局命名功能,因此function(5)從聲明functionPtr function;使用它在屏幕上正常工作和印刷5:所以我改變了變量的名稱。

所以現在我感到震驚。爲什麼發生這種情況?此外誤導的事情是,當函數指針聲明如下:

typedef void(*functionPtr)(int); 

我可以按以下方式創建類型functionPtr的功能:

functionPtr function(int a){ 
    std::cout << a << std::endl; 
} 

然而,聲明是這樣的時候:

typedef void(functionPtr)(int); 

使得這個:

functionPtr function(int a){ 
    std::cout << a << std::endl; 
} 

被編譯器解釋爲函數返回函數。如果是這樣,爲什麼之前的聲明(typedef void(functionPtr)(int);)知道,這是一個函數返回void而不是函數返回functionPtr?

有人能解釋一下真正發生在我身上的事嗎?

我在啓用C++ 14選項時使用了g ++ C++編譯器。

回答

6

好吧,它有點混亂。

功能類型和函數指針類型的確兩種不同類型的(不超過int更相似和指針int)。但是,有一條規則,功能類型衰減幾乎在所有上下文中指向函數類型。這裏衰減鬆散的意思是轉換(類型轉換和衰減有區別,但你現在可能不感興趣)。

重要的是,每次使用函數類型時,您最終都會得到指向函數類型的指針,即差不多。但幾乎注意到 - 幾乎每次都不是總是

而且當它沒有時,你正在碰到一些情況。

typedef void(functionPtr)(int); 
functionPtr fun = function; 

該代碼試圖將一個函數(不是指針!函數!)複製到另一個函數。但是,當然,這是不可能的 - 你不能在C++中複製函數。編譯器不會允許這樣做,我不能相信你得到它編譯(你說你得到了鏈接錯誤?)

現在,這個代碼:

typedef void(functionPtr)(int); 
functionPtr function; 
function(5); 

function不影東西。編譯器知道它不是一個可以調用的函數指針,只是調用你的原始文件function

+0

這段代碼沒有編譯。這段代碼是: 'functionPtr fun; (0);'(沒有任務) 這裏的樂趣被看作是一些函數,那個地方存在,但鏈接無法看到它,所以我得到鏈接錯誤 – DawidPi

+0

因此,爲什麼當'typedef void(functionPtr)(int); '是這樣的,我有: functionPtr樂趣;有趣();我得到鏈接錯誤?看到這裏:https://ideone.com/e20FmU – DawidPi

+0

@DawidPi因爲,正如SergeyA很好地解釋,代碼是不正確的。只能在這個答案中發佈的情況下實現函數功能。在你的情況下,樂趣不是。 – LPs

3

讓我們逐個看看您的例子,以及它們的真正含義。

typedef void(functionPtr)(int); 

void function(int a){ 
    std::cout << a << std::endl; 
} 

int main() { 

    functionPtr fun = function; 
    fun(5); 

    return 0; 
} 

這裏你正在創造它採取和int,並沒有返回值的函數的類型定義functionPtrfunctionPtr實際上不是函數指針的typedef,而是實際函數的類型定義。

然後你試圖聲明一個新函數fun,並賦值給它function。不幸的是,你不能分配給函數,所以這是行不通的。

int main() { 

    functionPtr fun; 
    fun(5); 

    return 0; 
} 

同樣,您聲明您指定的簽名功能fun。但是你沒有定義它,所以正確地說你失敗了鏈接階段。

typedef void(functionPtr)(int); 

void function(int a){ 
    std::cout << a << std::endl; 
} 

int main() { 

    functionPtr function; 
    function(5); 

    return 0; 
} 

這裏會發生什麼?您定義了typedef,並在主要中寫入functionPtr function;。這基本上只是您已經編寫的功能的原型,function。它重申這個函數存在,但否則它什麼也不做。事實上,你可以寫:

typedef void(functionPtr)(int); 

void function(int a){ 
    std::cout << a << std::endl; 
} 

int main() { 

    functionPtr function; 
    functionPtr function; 
    functionPtr function; 
    void function(int); 

    function(5); 

    return 0; 
} 

你想要多少次,它不會改變一件事情。你打電話給的function(5)總是一樣的。

你可以在C++中做的一件事是使用這樣的typedef聲明一個函數的原型,但是你不能以這種方式定義它。

typedef void(*functionPtr)(int); 

functionPtr function(int a){ 
    std::cout << a << std::endl; 
} 

這裏你定義了一個函數,它返回一個函數指針,但是你不返回它。編譯器,根據您的設置,可能會或可能不會抱怨。但是function又與functionPtr完全分開。您實際上已將書面

void (*)(int) function(int a) { 
    ... 
} 

最後一個例子,一個在那裏你有一個函數返回的功能,簡直是不允許的,因爲這將是毫無意義的。

4

最有趣的你的例子是這樣的一個,在這裏重現,而使用的typedef:

void function(int a) { // declaration and definition of 'function' 
    std::cout << a << std::endl; 
} 

int main() { 
    void function(int); // declaration of 'function' 
    function(5); 
} 

在大多數情況下在C++中,在局部範圍內的function再次聲明將影子全球::function。所以期待一個鏈接器錯誤是有道理的 - main()::function沒有定義權利?

除了功能在這方面是特殊的。從[basic.scope.pdel]記:

Function declarations at block scope and variable declarations with the extern specifier at block scope refer to declarations that are members of an enclosing namespace, but they do not introduce new names into that scope.

所以上面的示例代碼是完全等同於:

void function(int a) { /* ... */ } 
void function(int); // just redeclaring it again, which is ok 

int main() { 
    function(5); 
} 

您也可以通過將全球function到一些命名空間驗證這一點,N 。此時,本地範圍聲明將爲::添加一個名稱,但它沒有定義 - 因此您確實收到鏈接器錯誤。


你碰到的另一個有趣的事情是函數指針轉換的概念,[轉換。FUNC]:

An lvalue of function type T can be converted to a prvalue of type 「pointer to T」. The result is a pointer to the function.

當你有一個函數調用表達式 - 如果你調用的是一個函數類型,它首先被轉換爲一個指針到函數。這就是爲什麼這些是相同的:

fun(5);   // OK, call function pointed to by 'fun' 
(*fun)(5);  // OK, first convert *fun back to 'fun' 
function(5); // OK, first convert to pointer to 'function' 
(*function)(5); // OK, unary* makes function get converted to a pointer 
       // which then gets dereferenced back to function-type 
       // which then gets converted back to a pointer 
-1
typedef void functionPtr (int); 

void function (int a){ 
    std::cout << a << std::endl; 
} 

int main() { 

    functionPtr *func; 
    func = function; 
    func(5); 
    return 0; 
} 

可以以這種方式使用它,我已經測試。 這個問題真的很棘手。

+0

這不回答問題*爲什麼會出現此問題。請只添加與以前不同的答案或添加一些相關的點。 – izlin