C 中 const 限定变量有哪些实际用途?
正如最近几个问题中所讨论的,在 C 中声明 const 限定变量(与 C++ 中的 const 变量或指向 const 的指针相对) C)中的内容通常没有多大作用。最重要的是,它们不能用于常量表达式。
话虽如此,C 中 const 限定变量的合法用途有哪些?我能想到最近在我使用过的代码中出现的一些,但肯定还有其他的。这是我的列表:
使用它们的地址作为指针的特殊哨兵值,以便永远不会与任何其他指针进行比较。例如: char *sentinel(void) { static const char s;返回&s; } 或简单地
const char sentinel[1];
因为我们只关心地址,实际上对象是否被写入并不重要,所以const 的唯一好处
是编译器通常会将其存储在由可执行文件的mmap
或零页副本支持的只读内存中。当值可能随库的新版本而更改时,使用
const
限定变量从库(尤其是共享库)导出值。在这种情况下,简单地在库的接口标头中使用#define 并不是一个好方法,因为它会使应用程序依赖于所构建的库的特定版本中的常量值.与之前的使用密切相关,有时您希望将库中的预定义对象公开给应用程序(典型的示例是
stdin
、stdout
和 <来自标准库的 code>stderr)。使用该示例,extern FILE __stdin; #define stdin (&__stdin) 将是一个非常糟糕的实现,因为大多数系统实现共享库的方式 - 通常它们需要将整个对象(此处为FILE
)复制到链接应用程序时确定的地址,并引入对对象大小的依赖(如果重建库并且对象大小发生变化,程序将中断)。此处使用const
指针(不是指向const
的指针)可以解决所有问题:extern FILE *const stdin;
,其中>const
指针被初始化为指向库内部某处的预定义对象(其本身可能被声明为static
)。数学函数、字符属性等的查找表。这是我最初忘记包含的明显表,可能是因为我正在考虑算术/指针类型的单个
const
变量,因为这就是问题的主题首先出现了。感谢 Aidan 激发了我的记忆。作为查找表的变体,状态机的实现。艾丹提供了一个详细的例子作为答案。我发现相同的概念在没有任何函数指针的情况下通常也非常有用,如果您可以根据一些数字参数对每个状态的行为/转换进行编码。
还有人对 C 中的 const 限定变量有一些巧妙、实用的用途吗?
As has been discussed in several recent questions, declaring const
-qualified variables in C (as opposed to const
variables in C++, or pointers to const
in C) usually serves very little purpose. Most importantly, they cannot be used in constant expressions.
With that said, what are some legitimate uses of const
qualified variables in C? I can think of a few which have come up recently in code I've worked with, but surely there must be others. Here's my list:
Using their addresses as special sentinel values for a pointer, so as never to compare equal to any other pointer. For example:
char *sentinel(void) { static const char s; return &s; }
or simplyconst char sentinel[1];
Since we only care about the address and it actually wouldn't matter if the object were written to, the only benefit ofconst
is that compilers will generally store it in read-only memory that's backed bymmap
of the executable file or a copy of the zero page.Using
const
qualified variables to export values from a library (especially shared libraries), when the values could change with new versions of the library. In such cases, simply using#define
in the library's interface header would not be a good approach because it would make the application dependent on the values of the constants in the particular version of the library it was built with.Closely related to the previous use, sometimes you want to expose pre-defined objects from a library to the application (the quintessential examples being
stdin
,stdout
, andstderr
from the standard library). Using that example,extern FILE __stdin; #define stdin (&__stdin)
would be a very bad implementation due to the way most systems implement shared libraries - usually they require the entire object (here,FILE
) to be copied to an address determined when the application is linked, and introduce a dependency on the size of the object (the program will break if the library is rebuilt and the size of the object changes). Using aconst
pointer (not pointer-to-const
) here fixes all the problems:extern FILE *const stdin;
, where theconst
pointer is initialized to point to the pre-defined object (which itself is likely declaredstatic
) somewhere internal to the library.Lookup tables for mathematical functions, character properties, etc. This is the obvious one I originally forgot to include, probably because I was thinking of individual
const
variables of arithmetic/pointer type, as that's where the question topic first came up. Thanks to Aidan for triggering me to remember.As a variant on lookup tables, implementation of state machines. Aidan provided a detailed example as an answer. I've found the same concept is also often very useful without any function pointers, if you can encode the behavior/transitions from each state in terms of a few numeric parameters.
Anyone else have some clever, practical uses for const
-qualified variables in C?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
const
在嵌入式编程中经常用于映射到微控制器的 GPIO 引脚。例如:这两者都可以防止程序员意外更改指针本身,这在某些情况下可能会造成灾难性的后果。
tInPort
声明还可以防止程序员更改输入值。使用
易失性
可以防止编译器假设my_input
的最新值将存在于缓存中。因此,从my_input
进行的任何读取都将直接进入总线,因此始终从设备的 IO 引脚读取。const
is quite often used in embedded programming for mapping onto GPIO pins of a microcontroller. For example:Both of these prevent the programmer from accidentally changing the pointer itself which could be disastrous in some cases. The
tInPort
declaration also prevents the programmer from changing the input value.Using
volatile
prevents the compiler from assuming that the most recent value formy_input
will exist in cache. So any read frommy_input
will go directly to the bus and hence always read from the IO pins of the device.例如:
last
不能成为常量表达式的一部分这一事实既不存在也不存在。const
是类型系统的一部分,用于指示值不会改变的变量。没有理由修改我的函数中last
的值,因此我将其声明为const
。我懒得声明它
const
,但是我根本懒得使用静态类型语言;-)For example:
The fact that
last
can't be part of a constant-expression is neither here nor there.const
is part of the type system, used to indicate a variable whose value will not change. There's no reason to modify the value oflast
in my function, so I declare itconst
.I could not bother declaring it
const
, but then I could not bother using a statically typed language at all ;-)PC-lint 警告 429 源于预期,指向已分配对象的本地指针应该是 来消耗
我所说的“脏”是指其对应的指针参数具有非常量基类型的函数。警告的描述免除了诸如 strcpy() 之类的库函数的“脏”标签,显然是因为此类库函数没有一个获得指向对象的所有权。
因此,当使用 PC-lint 等静态分析工具时,被调用函数参数的 const 限定符会保留本地分配的内存区域。
PC-lint warning 429 follows from the expectation that a local pointer to an allocated object should be consumed
By "dirty" I mean a function whose corresponding pointer parameter has a non-const base type. The description of the warning absolves library functions such as strcpy() from the "dirty" label, apparently because none of such library functions takes ownership of the pointed object.
So when using static analysis tools such as PC-lint, the const qualifier of parameters of called functions keeps locally allocated memory regions accounted.
const
对于某些我们使用数据以特定方式指导代码的情况很有用。例如,这是我在编写状态机时使用的模式:这类似于以下内容:
但对我来说更容易维护,特别是在与状态相关的更复杂行为的情况下。例如,如果我们想要一个特定于状态的中止机制,我们可以将结构体定义更改为:
并且我不需要同时修改
abort
和dispatch
新状态的例程。我使用 const 来防止表在运行时发生更改。const
can be useful for some cases where we use data to direct code in a specific way. For example, here's a pattern I use when writing state machines:This is analogous to something like:
but is easier for me to maintain, especially in cases where there's more complicated behavior associated with the states. For example, if we wanted to have a state-specific abort mechanism, we'd change the struct definition to:
and I don't need to modify both the
abort
anddispatch
routines for the new state. I useconst
to prevent the table from changing at runtime.当类型不具有可用文字(即数字以外的任何类型)时,
const
变量非常有用。对于指针,您已经给出了一个示例(stdin
和 co),您可以在其中使用#define
,但您会得到一个可以轻松分配的左值。另一个例子是 struct 和 union 类型,它们没有可分配的文字(只有初始值设定项)。例如,考虑复数的合理 C89 实现:有时您只需要一个存储的对象而不是文字,因为您需要将数据传递给需要
void*
和一个size_t
。以下是 cryptoki API(又名 PKCS#11)的示例:许多函数需要以CK_ATTRIBUTE
数组形式传递的参数列表,其基本上定义为因此,在您的应用程序中,对于布尔值属性,您需要传递一个指向包含 0 或 1 的字节的指针:
A
const
variable is useful when the type is not one that has usable literals, i.e., anything other than a number. For pointers, you already give an example (stdin
and co) where you could use#define
, but you'd get an lvalue that could easily be assigned to. Another example isstruct
andunion
types, for which there are no assignable literals (only initializers). Consider for instance a reasonable C89 implementation of complex numbers:Sometimes you just need to have a stored object and not a literal, because you need to pass the data to a polymorphic function that expects a
void*
and asize_t
. Here's an example from the cryptoki API (a.k.a. PKCS#11): many functions require a list of arguments passed as an array ofCK_ATTRIBUTE
, which is basically defined asso in your application, for a boolean-valued attribute you need to pass a pointer to a byte containing 0 or 1:
它们可用于无法由用户代码更改的内存映射外设或寄存器,仅用于微处理器的某些内部机制。例如。在 PIC32MX 上,指示程序状态的某些寄存器被限定为 const 易失性 - 因此您可以读取它们,编译器不会尝试优化重复访问,但您的代码无法写入它们。
(我手头没有任何代码,所以我现在无法引用一个好的例子。)
They can be used for memory-mapped peripherals or registers that cannot be changed by user code, only some internal mechanism of the microprocessor. Eg. on the PIC32MX, certain registers indicating program state are qualified
const volatile
- so you can read them, and the compiler won't try to optimise out, say, repeated accesses, but your code cannot write to them.(I don't any code to hand, so I can't cite a good example right now.)