2010-04-09 49 views
16

我讀過,靜態變量在函數內部使用時,當不希望變量值每次調用函數時更改/初始化。但是,在「main」之前在主程序中定義一個變量static的例子呢?靜態,定義和常量在C

#include <stdio.h> 

static double m = 30000; 

int main(void) 
{ 
value = m * 2 + 3; 
} 

這裏變量m有一個常量值,它在稍後的主程序中不會被修改。在同樣的思路有什麼區別呢有這些,而不是使用靜態定義的:

const double m = 30000; 

#define m 30000 //m or M 

,然後確保在這裏的主代碼使用雙操作,使將m轉換爲正確的數據類型。

+0

謝謝大家的有趣答案。所以我認爲在我的情況最好的事情是有靜態const double = 30000。 – yCalleecharan 2010-04-09 21:39:49

回答

12
static double m = 30000; 

double foo(double x, double y) { 
    return x/m + y; 
} 

這不會贏得任何東西。 m的副本必須作出計算。 此外,如果你這樣做:

double bar(double x, double y) { 
    m += x + y; 
    return m; 
} 

則所有吧將改變微米。 函數(或類)之外的靜態變量實際上是具有文件範圍的全局變量。其他文件不能通過外部獲取

函數內部的靜態變量仍然像全局變量,除了同一文件中的其他函數不能直接看到它們。

const double m = 30000; 

這是更好的,在許多情況下最好。如果編譯器看到這個全局常量,然後看到一個對m的引用,那麼它知道,不是生成代碼來從它以前的地方加載該值(這可能需要首先將一個文字地址加載到寄存器中)到一個寄存器或堆棧位置做計算它可以使一個寄存器爲30000,或者有時在那裏產生一個30000編碼的指令。

這樣做的缺點是編譯器必須假定其他源文件將要讀取m並且必須在目標文件中實際存儲一個副本作爲變量(但是是一個常量變量)。

我不確定它是否是標準的,但您有時可以做extern const double m = 30000;,編譯器將使用30000來優化並假設另一個文件實際上有一個將存儲在可執行文件中的m的副本。你也可以做static const double m = 30000;,編譯器可以假設沒有其他人會期望m的副本存儲在從源文件生成的目標代碼中。

#define m 30000 

的風險更高。如果先前有另一個m聲明爲變量,常量或函數,則不會收到警告或錯誤。另外,對於像這樣的預處理宏,很容易搞砸。 例如:

#define BASE_ADDRESS 48 
#define MY_OFFSET 9 
#define MY_ADDRESS BASE_ADDRESS+MY_OFFSET 
... 
    return MY_ADDRESS*4; 

是的,這是一個愚蠢的例子,但是這是什麼樣子之後的預處理器被用它做是

... 
    return 48+9*4; 

這是

return 48+(9*4); 

而這不是你可能想要的。

宏是壞的另一個地方是當你有很大的常量,如字符串。字符串要求它們可以通過指針進行尋址,並且比整數和浮點文字或常數更難於優化。你可以很容易地使一個非常大的項目,如果你有很多的東西,如:

#define JIM "Jim" 
#define JOHN "John" 

,然後用JIM和約翰都在你的程序,因爲編譯器可能無法看到你真的只需要字符串「 Jom「和」John「一次在節目中。

這就是說,看到常量是這樣宣佈的並不少見,而且常常是由知道自己在做什麼的人以正確的方式完成的。

+0

感謝您的長時間解釋。所以如果我在一個文件中包含所有代碼,那麼靜態const double m = 30000是我所假設的最佳答案。 – yCalleecharan 2010-04-09 22:11:57

+0

所有編譯器都會識別兩個相同的字符串,並且只會將它們存儲一次。 – Tomas 2010-05-11 11:28:26

+0

在某些情況下,您必須使用'define'而不是'const'。 'static const uint8_t ARRAY_SIZE = 16U; uint8_t數組[ARRAY_SIZE]'不起作用,因爲'ARRAY_SIZE'不是一個顯式的常量值。與'const uint8_t SECONDS_PER_MINUTE = 60U; const uint16_t SECONDS_PER_HOUR = 60U * SECONDS_PER_MINUTE;',它不起作用。這是一個很大的恥辱。 – Gauthier 2011-03-01 08:35:57

5

static對於在函數外聲明的對象僅僅使對象位於翻譯單元本地(即,它不能從其他.c文件訪問)。它並沒有使它保持不變。這是const是。它們是正交的,所以你可以有一個或另一個或兩個。

例如

static const double m = 5; 

#define聲明瞭(在這種情況下)可以被用作一個恆定值的宏。沒有任何對象,所以const不適用,因爲沒有要更改的對象。因此,您也無法獲取宏的地址。

+0

只需添加一點:''const'和'static'在C++中不像C中那樣正交。在C++中,默認情況下,在函數外定義的'const'變量也是'static'。 – 2010-04-09 21:37:21

+0

@Jerry:當然,但這是一個C問題和一個初學者問題,所以它可能是一個不值得複雜答案的細節。 – 2010-04-09 21:40:19

+0

是正確的詞嗎?還是你的意思是相互?正如你所說的我們可以一方或另一方,或兩者兼而有之。 – yCalleecharan 2010-04-09 21:46:33

7

static意味着變量將靜態存儲時間,和當地的知名度。在這種情況下,它被用於「本地可見性」部分 - 即這意味着m僅在該翻譯單元內可見(基本上在該文件被預先處理之後)。

1

在頂級範圍static表示變量(或函數)不能在該源文件之外訪問 - 它不會被鏈接器使用,並且鏈接時不會引起任何名稱衝突。對變量是否是常量沒有影響 - 事實上,這樣的變量通常是非常量的,所以初始化可以被緩存。

使用const#define的區別在於前者允許編譯器鍵入檢查常量的使用情況。

1

主要區別在於#define會離開類型系統。預處理器沒有類型安全性,範圍等概念。如果您稍後嘗試寫這樣

爲(INT M = 0; M <大小; M +)循環{...}

你是到一個討厭的驚喜...

而且如果您使用#defines,則只會在調試代碼時看到30000的值,而不是名稱m。在這種情況下,這似乎沒有太大的區別,但是當使用有意義的常量和變量名稱時,確實如此。

+0

謝謝。知道這個調試技巧很有趣。 – yCalleecharan 2010-04-09 22:01:05

2

...變化/每個函數調用時初始化

您使用的話「變」與「初始化」,好像他們是相同的,但它們不是

void f(void) { 
    static int a = 0; 
    a++; // changed! 
    printf("%d\n", a); 
} 

int main(void) { 
    f(); f(); 
} 

/* 
    # 1 
    # 2 
*/ 

當文件範圍(外部函數)static不等於「靜態值」中的「const」,但這意味着標識符只能在該翻譯單元中引用。

所以你的第一個m沒有const仍然可以改變。只有const防範變化。但是,如果您省略了static,那麼如果鏈接庫或另一個在文件範圍內具有相同非靜態標識符的對象文件,您將在鏈接時發生衝突。

2

#define是一個預處理器操作,並且會在編譯階段發生之前導致所有發生的m30000替代。另外兩個例子是善意的變量。變量static存在於聲明它的翻譯單元中,並且可以被修改const變量是隻讀的。

5

當您編寫const double m=3000;時,您告訴編譯器在可從其他文件訪問的對象文件中創建一個符號m。編譯器可以在定義該文件的文件中內聯m的值,但仍然爲的符號將分配用於單獨編譯的目的。

當您編寫#define m 3000時,您只是在源文件的幾個位置使用語法便利性來編寫相同的常量。

2

如果m值必須永遠保持不變,那麼你當然可以使用

static const double m = 30000; 

#define m 30000 

只要注意,在C const對象都默認外部鏈接,所以要獲得等效的const聲明,您必須使用static const,而不僅僅是const

另請注意,在C語言中const對象不是常量,而是「常量變量」。如果您需要一個真實的常量(即形成常量表達式的實體),則必須使用#define或枚舉常量。

後者通常只有積分常數的問題。在你的情況下,double[static] const的方法可能效果最好。