2009-09-13 37 views
7

我已經被告知如果我在ANSI-C中編寫聲明變量將被使用的順序,聲明指針不爲空,並且索引在邊界,並在變量使用之前進行初始化。C:`const`關鍵字的行爲

如果我聲明一個const,我可以在斷言和代碼塊之後初始化它嗎? 在Java中,最終的初始化必須發生在聲明處,但它是否通過ANSI-C實現是一致的,我可以初始化一個const,但不一定在聲明時初始化?

回答

9

Java編譯器有少量的流程邏輯允許您在聲明後初始化變量final。這是合法的Java:

final int something; 

if (today == Friday) 
    something = 7; 
else 
    something = 42; 

Java將檢測是否有任何分支離開最終值undefined。它不會分析的條件,所以這是不合法的Java,即使它的邏​​輯類似:

final int something; 

if (today == Friday) 
    something = 7; 

if (today != Friday) 
    something = 42; 

在ANSI C89,const變量(比extern除外)必須在它們的聲明語句被初始化。

const int something = (today == Friday) ? 7 : 42; 

extern改性劑上的聲明告訴該變量在不同的complation單元(在該編譯單元或其他地方)初始化的編譯器。

在ANSI C99中,您可以混合使用聲明和代碼,因此您可以在斷言和代碼塊之後聲明並初始化一個const變量。 1999 ANSI C的可移植性仍然是一個問題。

用於C89的解決辦法是要注意,對於代碼的工作之前在塊範圍而不是函數範圍,所以你可以做這個聲明的規則:

#include<stdio.h> 

int main (void) 
{ 
    printf ("wibble\n"); 

    { 
     const int x = 10; 

     printf ("x = %d\n", x); 
    } 

    return 0; 
} 
+1

@Pete:2x經驗教訓。 – 2009-09-13 10:58:59

+1

實際上,你可以在不初始化的情況下聲明非extern const * global *變量。 'const int a;'被稱爲「暫定義」,或類似的東西。如果變量稍後被明確定義,那麼它就被視爲一個聲明。如果不是,這是一個定義。所以你可以在頭文件中包含'const int a;',然後在.c文件中包含'const int a = 12;'。不是你經常會想要的,因爲你可能會忘記定義,並且在某些編譯單元中爲'a'爲12,而在其他編譯單元中爲0 ... – 2009-09-13 11:13:18

+0

@onebyone:實際上,「暫定義」不會導致不同值在不同的編譯單元中。嘗試編譯「int x;」在一個文件中並且「int x = 1;」在另一個。變量x在鏈接後包含兩個文件(包括我試過的所有編譯器)。這是廣泛實施的行爲,雖然我沒有閱讀C99作爲具體說明。 – 2009-09-13 11:25:51

3

const變量是隻讀的,必須在定義它們的位置進行初始化。

這段代碼產生error: assignment of read-only variable 'foo'(GCC4):

const int foo; 
foo = 4; 

同去的常量指針(注意這裏:const int *不是一個常量指針,但指針常數):

int * const foo; 
foo = 4; 
+0

最後一個例子是否需要* foo = 4;生成錯誤?因爲分配4只會使用不可能的指針值... – 2009-09-13 11:29:52

+0

取決於您的意思。 'foo = 4'是一個編譯錯誤,因爲foo是const。 '* foo = 4'是一個運行時錯誤(希望編譯時警告),因爲foo是未初始化的(或者如果是全局的,則初始化爲NULL)。 – 2009-09-13 12:31:13

1

如果你正在談論分裂定義

const int x = 2; 

成兩個部分:

const int x; 

x=2; 

恐怕這是不可能的C.

如果我是你,我會嘗試,以確保我的理解,你描述的編碼規則的意圖。我懷疑明智的編碼規則會阻止初始化變量(即使是非const變量)。

響應於各種評論:

const int * p; 

不是一個常量變量的聲明。它是一個const const的非const指針變量的聲明。

你可以聲明

extern const int x; 

但在已執行的代碼,斷言檢查,之後你仍然不能初始化X ...

+0

+1。 「初始化在使用之前初始化」的意圖可能是(1),使得初始值在物理上接近於依賴它的代碼,和/或(2)如果初始化確實有效,有時可以避免,首先使用的是最好的經驗法則來最大限度地避免它的可能性。所以你必須衡量這兩個東西與標記爲const的值。 – 2009-09-13 10:43:04

+0

是的,我花了Pavel Shved的回答才意識到問題可能與本地const變量有關。我一直在考慮全局變量。 – 2009-09-13 10:48:48

2

你不能在函數體內聲明後,初始化常量,但您可以斷言後只需打開一個塊:

void func() 
{ 
    int y; 
    //do assertions 
    assert(something); 
    { 
     int const x = 5; 
     // function body 
    } 
} 
3

要知道,即使是在C89,你可以經常移動的定義更接近於由introduci首先使用點只爲額外的範圍提供一個裸塊。之前:

int a, b, c; 

a = 12; 
// do some stuff with a 

b = 17; 
// do some stuff with a and b 

c = 23; 
// do some stuff with a, b, and c 

後:

int a = 12; 
// do some stuff with a 
{ 
    int b = 17 
    // do some stuff with a and b 
    { 
     int c = 23; 
     // do some stuff with a, b and c 
    } 
} 

當然了C99,您可以定義其他變量不是在塊的開頭:

int a = 12; 
// do some stuff with a 

int b = 17 
// do some stuff with a and b 

int c = 23; 
// do some stuff with a, b and c 
2

短塊範圍和C99申報方法其他人已經表明,答案是否定的;你不能推遲const變量的初始化。無論如何,const對局部變量不是很有用。我在C中使用const關鍵字的主要時間是:

  • 函數參數指針(或基於參數的局部變量指針)其中函數遵守不修改指向數據的協定。 const關鍵字有助於確保函數實現遵守不需要修改的要求(它需要特殊的工作強制轉換以擺脫const),並允許通過多個函數調用來傳播此需求。
  • 用於聲明我希望存儲在二進制文件的只讀部分中的編譯時常量表(查找表,預定義的永久對象等),以便它們不會在運行時使用額外的物理資源。

我有時會聲明局部變量const,如果我認爲它會幫助讀者理解函數,但這很少見。

0

如果你想在LHS上拋棄const,那麼這個怎麼樣?

const int n = 0; 

*((int*)&n) = 23; 
+1

這個怎麼樣?非常糟糕的主意。它繞過了語言決定實施的限制。甚至可能導致錯誤,具體取決於您的編譯器實現。 – einpoklum 2015-11-19 09:39:12