在C语言中定义常量值

5
我有一个C项目,所有的代码都是按照*.c/*.h文件对组织的。我需要在一个文件中定义一个常量值,但是这个值也会在其他文件中使用。应该如何声明和定义这个值呢?
这个值应该在*.h文件中声明为static const ...吗?还是应该在*.h文件中声明为extern const ...并在*.c文件中定义?如果这个值不是原始数据类型(如intdouble等),而是char *struct,那么它的定义方式是否有所不同呢?(尽管在我的情况下它是一个double。)
一般来说,在*.h文件中定义东西似乎不是一个好主意;应该在*.h文件中声明事物,但在*.c文件中定义它们。然而,extern const ...方法似乎效率低下,因为编译器无法内联该值,而必须始终通过其地址访问它。
我想这个问题的本质是:在C语言中,应该在*.h文件中定义static const ...值,以便在多个地方使用吗?
9个回答

8
我遵循的规则是H文件中只声明,C文件中才定义。如果它只在一个文件中使用,则可以在单个C文件中声明和定义。
所谓声明,是指告诉编译器其存在,但不为其分配空间。这包括#define、typedef、extern int x等。
定义会为声明赋值并为其分配空间,例如int x和const int x。这还包括函数定义;将其包含在标头文件中经常导致代码浪费空间。
我见过太多初级程序员因将const int x = 7;放入标头文件中而感到困惑,然后惊奇地发现x被定义了多次而产生链接错误。我认为至少需要static const int x才能避免这个问题。
我不会过于担心代码的速度。计算机的主要问题(以速度和成本为基础)很久以前就从执行速度转移到了开发的便捷性上。

3
如果您需要常量(实际的、编译时的常量),有三种方法可以实现,将它们放入头文件中(这样做没有任何问题):
enum {
    FOO_SIZE = 1234,
    BAR_SIZE = 5678
};

#define FOO_SIZE 1234
#define BAR_SIZE 5678

static const int FOO_SIZE = 1234;
static const int BAR_SIZE = 5678;

在C++中,我倾向于使用枚举的方式,因为它可以被作用域限定到命名空间中。对于C语言,我使用宏。但这基本上只是个人口味问题。如果需要浮点常量,就不能再使用枚举了。在C++中,我会使用最后一种方式——静态常量double,在这种情况下(注意在C++中static将是多余的,因为它们是const,所以会自动变成static)。在C语言中,我仍然会继续使用宏。
使用第三种方法会减慢程序运行速度,这是一个“谬论”。我只是喜欢使用枚举,因为你得到的值是rvalues——你不能获得它们的地址,这样更加安全。此外,写的模板代码更少,眼睛也更容易集中在常量上。

1

你真的需要担心inline的优势吗?除非你在编写嵌入式代码,否则请坚持可读性。如果它确实是某个魔法数字,我会使用define;我认为const更适用于像const版本字符串和修改函数调用参数之类的东西。话虽如此,在.c中定义,在.h中声明的规则绝对是一个被广泛接受的惯例,我不会因为你可能节省了一次内存查找而打破它。


1

通常情况下,不应该在头文件中定义static。如果你在头文件中定义了static变量,那么每个使用该头文件的文件都会有自己的私有副本,这与DRY原则相悖:不要重复自己。

因此,你应该使用替代方案。对于整数类型,使用在头文件中定义的枚举非常强大;它也可以很好地与调试器配合使用(尽管更好的调试器也可以帮助处理#define宏值)。对于非整数类型,通常最好的方法是在头文件中使用extern声明(可选加上const限定符),并在一个C文件中进行单一定义。


1
这并不违反 DRY 原则。你没有在重复自己,而是编译器在重复你的代码。DRY 原则并不是为了节省空间效率,请查阅相关资料。-1. - rix0rrr
@rix0rr:显然,你对这种情况有不同的看法。但是,如果你的头文件定义了静态对象,那么我的代码很少会使用它们。当然也有特殊情况,但那只是例外。 - Jonathan Leffler

0

我可以给你一个间接的答案。在 C++ 中(与 C 相反),const 意味着 static。也就是说,在 C++ 中,static constconst 是一样的。这告诉了你 C++ 标准组织对此问题的看法,即所有的 const 都应该是静态的。


0

我想看到更多与你的问题相关的上下文。值的类型至关重要,但你却没有提到它。在C语言中,const关键字的含义非常微妙;例如:     const char *p; 并不意味着指针p是一个常量;你可以随便写p。你不能写的是p所指向的内存,即使p的值发生变化,这个规则仍然适用。这是我真正理解的唯一情况;总的来说,const的微妙放置让我感到困惑。但是对于函数参数来说,这种特殊情况非常有用,因为它从函数中提取了一个承诺,即参数指向的内存不会被改变。

还有一个每个人都应该知道的特殊情况:整数。几乎总是,常量命名的整数应该在.h文件中定义为枚举文字。枚举类型不仅允许您以自然的方式将相关的常量分组在一起,而且还允许您在调试器中查看这些常量的名称,这是一个巨大的优势。

我已经写了数万行的C代码,如果我尝试追踪它们的话,可能有数百行。(wc ~/src/c/*.c显示85000行,但其中一些是生成的,当然还有很多C代码潜伏在其他地方)。除了上述两种情况,我从未发现const有太多用处。我很乐意学习一个新的、有用的例子。

0

关于autoconf环境: 您可以在configure文件中定义常量。我猜AC_DEFINE()是定义整个构建过程的宏。


0
回答你问题的实质:
通常情况下不建议在头文件中定义静态变量。
这会导致每个包含该头文件的翻译单元(C文件)中都有重复的变量。

头文件中的变量应该声明为extern,因为这是隐含的可见性。 请参阅this question以获得良好的解释。

实际上,情况可能并不那么严重,因为编译器可能会将const类型转换为字面值。但是,您可能不希望依赖于该行为,特别是如果关闭了优化。


0
在C++中,你应该始终使用
const int SOME_CONST = 17;

对于常量和不变量

#define SOME_CONST 17

定义通常会在以后带来麻烦。常量是语言中的一部分,并且具有强类型,因此您不会因为某些隐藏的交互而出现奇怪的错误。我会将常量放在适当的头文件中。只要它是#pragma once(或#ifndef x / #define x / #endif),您就不会遇到任何编译错误。

在普通的C语言中,您可能会遇到必须使用#define的兼容性问题。


const不能作为case标签、数组大小或用于创建新的const(例如:const int SEC_PER_MIN = 60; const int SEC_PER_HOUR = SEC_PER_MIN * 60; 是不起作用的)。请参考Leffler的精彩列表:https://dev59.com/onI-5IYBdhLWcg3wxruQ。 - Gauthier
我习惯使用C++。我已经修改了我的答案以明确这一点,因为大多数人在提到C时,使用C++编译器进行编译。 - FryGuy
@FryGuy:真的吗?我通常在使用C时使用C编译器,在使用C++时使用C++编译器。 <耸肩> - Michael Rawson
你应该使用const static,这样在每次使用时不会创建多个实例,尤其是当它是一个对象的时候。 - paulm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接