C中的Static,define和const
我已经读过,当每次调用函数时不希望变量值改变/初始化时,静态变量在函数内部使用。 但是如何在“main”之前在主程序中定义变量static,例如
#include static double m = 30000; int main(void) { value = m * 2 + 3; }
这里变量m具有一个常数值,以后不会在主程序中修改。 在同一思路中,它有什么不同,而不是使用静态定义:
const double m = 30000;
要么
#define m 30000 //m or M
然后确保在主代码中使用双重操作,以便将m转换为正确的数据类型。
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; }
然后所有对bar的调用都会改变m。 函数(或类)之外的静态变量实际上是具有文件范围的全局变量。 其他文件无法通过extern获取
函数内部的静态变量仍然类似于全局变量,除了即使同一文件中的其他函数也无法直接看到它们。
const double m = 30000;
这是更好的,在许多情况下是最好的。 如果编译器看到这个全局const,然后看到对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和JOHN,因为编译器可能无法在程序中看到你真的只需要字符串“Jom”和“John”。
话虽这么说,看到常量被这样宣告的情况并不少见,而且通常他们是那些知道自己在做什么的人那样做的。
static
意味着变量将具有静态存储持续时间和本地可见性。 在这种情况下,它被用于“局部可见性”部分 – 即它意味着m
仅在该翻译单元内可见(在其预处理后基本上是该文件)。
当你写const double m=3000;
您告诉编译器在目标文件中创建一个可以从其他文件访问的符号m
。 编译器可以在定义它的文件中内联m
的值,但是为了单独编译的目的,仍然必须分配符号。
当您编写#define m 3000
您只是使用语法方便在源文件中的几个位置写入相同的常量。
对于在函数外部声明的对象, static
仅使对象成为转换单元的本地对象(即,无法从其他.c
文件访问它)。 它不会使它保持不变。 那是const
是为了。 它们是正交的,因此您可以拥有一个或另一个或两者。
例如
static const double m = 5;
#define
声明一个宏(在这种情况下)可以用作常量值。 没有对象,因此const
不适用,因为没有对象需要更改。 因此,您也无法获取宏的地址。
…每次调用函数时更改/初始化
您使用“更改”和“初始化”这两个词,就好像它们是相同的,但它们不是
void f(void) { static int a = 0; a++; // changed! printf("%d\n", a); } int main(void) { f(); f(); } /* # 1 # 2 */
当在文件范围(外部函数)时, static
并不像“静态值”那样意味着“const”,但这意味着标识符只能在该转换单元中引用。
所以你的第一个没有const
m
仍然可以改变。 只有const
防范变化。 但是如果省略static
那么如果链接到库或另一个在文件范围内具有相同非静态标识符的目标文件,则会在链接时发生冲突。
#define
是一个预处理器操作,在编译阶段发生之前会导致所有出现的m
被30000
替换。 另外两个例子是真正的变量。 static
变量存在于声明它并可以修改的转换单元中。 const
变量是只读的。
如果m
的值必须永远保持不变,那么你当然可以使用
static const double m = 30000;
要么
#define m 30000
请注意,在C const
对象中默认有外部链接,因此要获得等效的const
声明,您必须使用static const
,而不仅仅是const
。
还要注意,在C语言中, const
对象不是常量 ,而是“常量变量”。 如果需要一个真常量 (即形成常量表达式的实体),则必须使用#define
或enum常量。
后者通常只是积分常数的问题。 在你的情况下,使用[static] const
可能效果最好。
在顶层范围内, static
意味着变量(或函数)不能在此源文件之外访问 – 它不会对链接器可用,并且在链接时不会导致任何名称冲突。它对是否有影响变量是否是常量 – 实际上,这些变量通常是非常量的,因此可以缓存初始化。
使用const
和#define
之间的区别在于前者允许编译器键入 – 检查常量的用法。
主要的区别在于,使用#define可以离开类型系统。 预处理器没有类型安全,范围等概念。因此,例如,如果您稍后尝试编写类似的循环
for(int m = 0; m 你是一个令人讨厌的惊喜…… 此外,如果您使用#defines,则在调试代码时只会看到值30000,而不是名称m
。 在这种情况下,这似乎没有太大的区别,但是当使用有意义的常量和变量名称时,确实如此。