2010-04-09 79 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的方法可能效果最好。