簡體   English   中英

外部塊范圍變量的鏈接,C

[英]Linkage of extern block scope variable, C

C標准說:

對於在范圍內使用存儲類說明符extern聲明的標識符,其中該標識符的先前聲明是可見的,31)如果先前聲明指定內部或外部鏈接,則后面聲明中標識符的鏈接與在先前聲明中指定的聯系。 如果沒有先前聲明可見,或者先前聲明未指定鏈接,則標識符具有外部鏈接。

不清楚的是,先前要考慮的標識符是否必須具有相同的類型(注意:C ++標准明確說明“具有相同名稱和類型的實體”)。 例如:

static int a;   // internal linkage

void f()
{
    float a;      // no linkage, instead of 'int a' we have 'float a'

    {
        extern int a;     // still external linkage? or internal in this case?
        a = 0;            // still unresolved external?
    }
}

我嘗試用不同的編譯器測試它,但似乎聯系主題並不是具有很大團結性的主題。

C對其所有全局變量使用平面名稱空間。 與C ++不同,C ++需要鏈接器注意全局變量的類型(查找名稱修改以獲取更多信息),C將此要求提供給程序員。

在更改同一翻譯單元內的鏈接時,重新聲明具有不同類型的變量是錯誤的。

我將使用你的例子添加一小部分

static int a;   // internal linkage
static int b;   // internal linkage

void f()
{
    float a = 123.25; // this variable shadows static int a
    int b = 321;      // this variable shadows static int b

    { // Open a new scope, so the line below is not an illegal re-declaration
        // The declarations below "un-shadow" static a and b
        extern int a; // redeclares "a" from the top, "a" remains internal
        extern int b; // redeclares "b" from the top, "b" remains internal
        a = 42;       // not an unresolved external, it's the top "a"
        b = 52;       // not an unresolved external, it's the top "b"
        printf("%d %d\n", a, b); // static int a, static int b
    }
    printf("%f %d\n", a, b);     // local float a, int b
}

這個例子打印

42 52
123.250000 321

當您在多個翻譯單元中更改類型時,C ++將在鏈接時捕獲它,而C將鏈接正常,但會產生未定義的行為。

我想我有一個答案。 我將一般性地寫下關聯主題。

C標准說:

在該組翻譯單元中,具有外部鏈接的特定標識符的每個聲明表示相同的實體(對象或功能)。 在一個翻譯單元內,具有內部鏈接的標識符的每個聲明表示相同的實體。

C ++標准說:

當名稱具有外部鏈接時,其表示的實體可以通過其他翻譯單元的范圍或同一翻譯單元的其他范圍中的名稱來引用。 當名稱具有內部鏈接時,其表示的實體可以通過同一翻譯單元中其他范圍的名稱來引用。

這有兩個含義:

  • 在翻譯單元集中,我們不能有多個具有相同名稱的不同外部實體(除了C ++中的重載函數),因此表示該單個外部實體的每個聲明的類型應該一致。 我們可以在一個翻譯單元中檢查類型是否一致,這是在編譯時完成的。 我們無法在編譯時或鏈接時檢查不同翻譯單元之間的類型是否一致。

從技術上講,在C ++中我們可以違反in the set of translation units we cannot have multiple distinct external entities with the same name規則,而不會出現函數重載。 由於C ++具有對類型信息進行編碼的名稱修改,因此可以使多個外部實體具有相同的名稱和不同的類型。 例如:

file-one.cpp:

int a;                // C decorated name: _a
                      // C++ decorated name (VC++): ?a@@3HA

//------------------------------------------------

file-two.cpp:

float a;              // C decorated name: _a
                      // C++ decorated name (VC++): ?a@@3MA

在C中,這實際上是一個外部實體,第一個單元中的代碼將其視為int ,而第二個單元中的代碼將其視為float

  • 在一個翻譯單元中,我們不能有多個具有相同名稱的不同內部實體(除了C ++中的重載函數),因此表示該單個內部實體的每個聲明的類型應該一致。 我們檢查翻譯單元中的類型是否一致,這是在編譯時完成的。

現在我們將更接近這個問題。

C ++標准說:

在塊作用域中聲明的函數的名稱和由塊作用域extern聲明聲明的變量的名稱具有鏈接。 如果存在具有相同名稱和類型的鏈接的實體的可見聲明,忽略在最內部封閉命名空間范圍之外聲明的實體,則塊范圍聲明聲明該實體並接收先前聲明的鏈接。 如果存在多個這樣的匹配實體,則該程序是不正確的。 否則,如果未找到匹配的實體,則塊范圍實體接收外部鏈接。

// C++

int a;                // external linkage

void f()
{
    extern float a;         // external linkage
}

這里我們沒有先前聲明具有相同名稱( a )和類型( float )的實體,因此extern float a的鏈接是外部的。 由於我們在此翻譯單元中已經有int a帶外部鏈接的int a且名稱相同,因此類型應該一致。 在這種情況下它們沒有,因此我們有編譯時錯誤。

// C++

static int a;             // internal linkage

void f()
{
    extern float a;       // external linkage
}

這里我們沒有先前聲明具有相同名稱( a )和類型( float )的實體,因此extern float a的鏈接是外部的。 這意味着我們必須在另一個翻譯單元中定義float a 請注意,我們在一個轉換單元中具有相同的外部和內部鏈接標識符(我不知道為什么C會考慮這種未定義的行為,因為我們可以在不同的轉換單元中具有相同名稱的內部和外部實體)。

// C++ (example from standard)

static int a;        // internal linkage

void f()
{
    int a;            // no linkage

    {
        extern int a;    // external linkage
    }
}

這里前面的聲明int a沒有鏈接,所以extern int a有外部鏈接。 這意味着我們必須在另一個翻譯單元中定義int a

C標准說:

對於在范圍內使用存儲類說明符extern聲明的標識符,其中該標識符的先前聲明是可見的,31)如果先前聲明指定內部或外部鏈接,則后面聲明中標識符的鏈接與在先前聲明中指定的聯系。 如果沒有先前聲明可見,或者先前聲明未指定鏈接,則標識符具有外部鏈接。

所以我們可以看到在C中只考慮名稱(沒有類型)。

// C

int a;                // external linkage

void f()
{
    extern float a;         // external linkage
}

這里前面標識符a聲明有外部鏈接,所以extern float a的鏈接是相同的(外部)。 由於我們在此翻譯單元中已經有int a帶外部鏈接的int a且名稱相同,因此類型應該一致。 在這種情況下它們沒有,因此我們有編譯時錯誤。

// C

static int a;             // internal linkage

void f()
{
    extern float a;       // internal linkage
}

這里先前聲明的標識符a具有內部鏈接,因此extern float a的鏈接是相同的(內部)。 由於我們已經在此翻譯單元中static int a帶內部鏈接的static int a且名稱相同,因此類型應該一致。 在這種情況下它們沒有,因此我們有編譯時錯誤。 而在C ++中,這段代碼很好(我認為類型匹配要求在添加時考慮了函數重載)。

// C

static int a;        // internal linkage

void f()
{
    int a;            // no linkage

    {
        extern int a;    // external linkage
    }
}

這里前面的標識符聲明a沒有鏈接,所以extern int a有外部鏈接。 這意味着我們必須在另一個翻譯單元中定義int a 但是GCC決定拒絕使用variable previously declared 'static' redeclared 'extern'錯誤的variable previously declared 'static' redeclared 'extern' ,可能是因為我們根據C標准有未定義的行為。

暫無
暫無

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

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