宣告介紹

宣告的示例是:

int a; /* declaring single identifier of type int */

上面的宣告宣告瞭名為 a 的單個識別符號,它引用了一些 int 型別的物件。

int a1, b1; /* declaring 2 identifiers of type int */

第二個宣告宣告瞭名為 a1b1 的 2 個識別符號,它們引用了一些其他物件,但具有相同的 int 型別。

基本上,它的工作方式是這樣的 - 首先你放一些型別 ,然後你寫一個或多個表示式通過逗號分隔(,)(此時不會被評估 - 否則應該被稱為宣告者)這個背景 )。在編寫這樣的表示式時,你只能將間接(*),函式呼叫(( ))或下標(或陣列索引 - [ ])運算子應用於某些識別符號(你根本不能使用任何運算子)。使用的識別符號不需要在當前範圍內可見。一些例子:

/* 1 */ int /* 2 */ (*z) /* 3 */ , /* 4 */ *x , /* 5 */ **c /* 6 */ ;
# 描述
1 整數型別的名稱。
2 未評估的表示式將間接應用於某些識別符號 z
3 我們有一個逗號,表示在同一個宣告中會有一個表示式。
4 未評估的表示式將間接應用於其他識別符號 x
5 未計算的表示式將間接應用於表示式 (*c) 的值。
6 宣告結束。

請注意,在此宣告之前,上述識別符號都不可見,因此使用的表示式在它之前無效。

在每個這樣的表示式之後,其中使用的識別符號被引入當前範圍。 (如果識別符號已為其分配了連結,則也可以使用相同型別的連結重新宣告它,以便兩個識別符號引用相同的物件或函式)

另外,等操作符號(=)可用於初始化。如果宣告中的 = 後面有一個未評估的表示式(宣告符) - 我們說正在初始化引入的識別符號。在 = 符號之後,我們可以再次新增一些表示式,但是這次它將被評估並且其值將被用作宣告的物件的初始值。

例子:

int l = 90; /* the same as: */

int l; l = 90; /* if it the declaration of l was in block scope */

int c = 2, b[c]; /* ok, equivalent to: */

int c = 2; int b[c];

稍後在你的程式碼中,你可以從新引入的識別符號的宣告部分編寫完全相同的表示式,為你提供在宣告開頭指定的型別的物件,假設你已為所有訪問的檔案分配了有效值物件的方式。例子:

void f()
{
    int b2; /* you should be able to write later in your code b2 
            which will directly refer to the integer object
            that b2 identifies */
    
    b2 = 2; /* assign a value to b2 */
    
    printf("%d", b2); /*ok - should print 2*/

    int *b3; /* you should be able to write later in your code *b3 */

    b3 = &b2; /* assign valid pointer value to b3 */

    printf("%d", *b3); /* ok - should print 2 */

    int **b4; /* you should be able to write later in your code **b4 */

    b4 = &b3;

    printf("%d", **b4); /* ok - should print 2 */

    void (*p)(); /* you should be able to write later in your code (*p)() */

    p = &f; /* assign a valid pointer value */

    (*p)(); /* ok - calls function f by retrieving the
            pointer value inside p -    p
            and dereferencing it -      *p
            resulting in a function
            which is then called -      (*p)() -

            it is not *p() because else first the () operator is 
            applied to p and then the resulting void object is
            dereferenced which is not what we want here */
}

b3 的宣告指定你可以使用 b3 值作為訪問某個整數物件的平均值。

當然,為了將間接(*)應用於 b3,你還應該在其中儲存適當的值( 有關詳細資訊,請參閱指標 )。在嘗試檢索物件之前,你還應該首先將一些值儲存到物件中(你可以在此處檢視有關此問題的更多資訊 )。我們在上面的例子中完成了所有這些。

int a3(); /* you should be able to call a3 */

這個告訴編譯器你將嘗試呼叫 a3。在這種情況下,a3 指的是函式而不是物件。物件和函式之間的一個區別是函式總是會有某種聯絡。例子:

void f1()
{
    {
        int f2(); /* 1 refers to some function f2 */
    }
    
    {
        int f2(); /* refers to the exact same function f2 as (1) */
    }
}

在上面的例子中,2 個宣告引用相同的函式 f2,而如果它們在這個上下文中宣告物件(具有 2 個不同的塊作用域),則它們將是 2 個不同的不同物件。

int (*a3)(); /* you should be able to apply indirection to `a3` and then call it */

現在看起來似乎變得複雜了,但是如果你知道運算子優先順序,那麼閱讀上述宣告就會遇到 0 問題。需要括號,因為*運算子的優先順序低於 ( ) 運算子。

在使用下標運算子的情況下,結果表示式在宣告後實際上不會有效,因為其中使用的索引([] 中的值)將始終高於此物件/函式的最大允許值 1。

int a4[5]; /* here a4 shouldn't be accessed using the index 5 later on */

但它應該可以被低於 5 的所有其他索引訪問。示例:

a4[0], a4[1]; a4[4];

a4[5] 將進入 UB。有關陣列的更多資訊,請訪問此處

int (*a5)[5](); /* here a4 could be applied indirection
                indexed up to (but not including) 5
                and called */

對我們來說不幸的是,雖然在語法上可行,但是當前標準禁止宣稱 a5