簡體   English   中英

在另一個函數中定義函數的范圍

[英]Scope of a function when defined within another function

在另一個函數中定義函數時會發生什么? 代碼1和代碼2有什么區別?

代碼1:

#include<stdio.h>

void m();

void main()
{
    m();
    void m()
    {
        printf("hi");
    }
}

代碼2:

#include <stdio.h>

void m();

void main()
{
    void m()
    {
        printf("hi");
    }
     m();
}

當我在gcc編譯器中編譯代碼1時,我遇到了鏈接錯誤。 但是當我編譯代碼2時 ,我沒有收到任何錯誤。 我輸出“hi” 當我們在另一個函數中編寫函數定義時,我想知道編譯是如何完成的。 我知道當我們編寫函數定義不在另一個函數中時,無論函數定義在哪里,我們調用該函數時都不會出現任何錯誤。 例如,請參閱以下代碼:


代碼3:

#include <stdio.h>
void m();
void main()
{
    m();
}

void m()
{
    printf("hi");
} 

代碼3中 ,即使我在定義之前調用了函數,它也沒有顯示任何錯誤,我得到了輸出。 為什么代碼1沒有發生。

您無法在函數內定義函數。 它不允許在C ,因此不會編譯。 代碼段1和2無效,只有代碼段3是有效的ANSI C代碼。

為什么代碼1不能編譯,代碼2在gcc中編譯得很好
由於GNU編譯器擴展,代碼2在gcc下編譯 您必須在首次使用之前聲明該功能,以使此擴展能夠正常工作。

那為什么代碼3會編譯
在Snippet 3中,您在第一次使用之前聲明該函數,這足以讓編譯器放置所需的代碼。 在鏈接時,需要與定義代碼進行必要的鏈接。 鏈接器沒有必要放置定義(在ISO C規范中),即使在單獨的編譯單元中也允許函數定義。

C和C ++都不允許在另一個函數中定義函數。 因此,您似乎正在使用編譯器GCC的特殊語言擴展,它允許在另一個函數內定義本地函數。

在第一個程序中,函數m在文件范圍內聲明。 因此,編譯器將在文件范圍內搜索其定義。 在main內部調用該函數時,此文件范圍聲明是可見的。 但是,編譯器未找到其文件范圍定義,因此它發出錯誤。

在第二個程序中,文件范圍中還有一個函數m的聲明。 但是在函數main內部還有另一個函數m的聲明,它具有塊作用域並用文件作用域隱藏聲明。 考慮到定義也是一個聲明。 所以m的調用指的是在main中聲明的函數m並且具有塊作用域。 編譯器具有函數定義並可以調用它。

在滿足C標准的第三個程序中,m的調用是指在文件范圍內聲明的函數m,並且在文件范圍內有該函數的定義。 所以沒有任何問題。

這是一個范圍問題。 在您的CODE 1示例中,m函數僅在main函數中可見。 所以你放在main之前的函數頭(void m();)期望一個全局的m函數並且沒有找到它,這就是你有鏈接錯誤的原因。 在你的代碼2樣本中,找到了m函數,因為它是在調用之前寫的,你的全局m函數仍然不存在但沒有使用=>你沒有鏈接錯誤。 在您的代碼3示例中,您的m函數是全局的,然后通過header => no error找到所需的函數

在代碼3中,即使我在定義之前調用了函數,它也沒有顯示任何錯誤,我得到了輸出。 為什么代碼1沒有發生?

代碼3符合標准C代碼。 只要聲明可見,就可以調用具有外部鏈接的函數(如果程序中某處存在定義,則不必在同一文件中)。

當我在gcc編譯器中編譯代碼1時,我遇到了鏈接錯誤。

嵌套函數是GCC擴展

嵌套函數始終沒有鏈接。 用extern或static聲明一個是錯誤的。 如果需要在定義之前聲明嵌套函數,請使用auto(這對函數聲明來說毫無意義)。

在調用m ,可以看到外部聲明(文件范圍聲明void m(); )。 由於此源文件中沒有外部定義,因此gcc假定它位於另一個轉換單元中,並且最終鏈接器無法找到它。 根據引用的文檔,有效的版本可能是

#include <stdio.h>

int main(void)
{
    auto void m(); /* Hides the outer declaration. */
    m(); /* Refers to the nested function, not to an external one now. */
    void m()
    {
        printf("hi");
    }
}

但是當我編譯代碼2時,我沒有收到任何錯誤。 我輸出“hi”。

此時,在調用m的位置,嵌套函數可見,隱藏文件范圍聲明。 從不使用文件范圍聲明(聲明具有外部鏈接的函數),因此鏈接器不會嘗試解析對它的調用。


一致的實現可以接受main的聲明作為返回void的函數,但不是必須的。 舊式(或K&R風格)聲明和定義在C89,C99和C11中已經過時,因此最好使用

int main(void);
void m(void);

檢查以下鏈接: C中的嵌套函數

C不支持嵌套函數,因此您的代碼片段1和2不是標准,盡管您可能會看到代碼片段2正常工作,因為您在函數定義后有函數調用。

唯一允許的代碼段是Code 3 ,其余部分不應該在C中完成。

GCC特殊擴展: https//gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html

您沒有將代碼編譯為標准C. GCC很棘手,因為默認情況下它會帶有糟糕的非標准設置。 要將GCC用作正確的C編譯器,必須執行gcc -std=c11 -pedantic-errors ,其中c11是首選標准(如果您有舊版本的GCC,請切換到c99)。 在此期間,請確保同時啟用所有警告:

gcc -std=c99 -pedantic-errors -Wall -Wextra

既然編譯器配置為C編譯器,我們會收到以下錯誤:

CODE1和CODE2:

  • 錯誤:'main'的返回類型不是'int'[ - Wmain]。

    出現此錯誤的原因是,當GCC設置為C編譯器時,它不提供實現定義的方式來指定main。 你必須將main更改為int main (void)

  • 錯誤:ISO C禁止嵌套函數[-Wpedantic]

    出現此錯誤的原因是C標准不允許嵌套函數。 你以前僥幸成功,因為你將GCC設置為非標准編譯器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM