宣告介紹
宣告的示例是:
int a; /* declaring single identifier of type int */
上面的宣告宣告瞭名為 a
的單個識別符號,它引用了一些 int
型別的物件。
int a1, b1; /* declaring 2 identifiers of type int */
第二個宣告宣告瞭名為 a1
和 b1
的 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
。