何时以及出于什么目的在C中将const关键字用于变量?


58

这里审核我的代码时const出现了使用关键字的问题。我了解它用于对变量执行只读行为。

我对它可能有用时的各种情况感到困惑。

  • 是否应该在功能原型中为了清楚起见使用它?
  • 在代码开发期间是否应将其用作安全措施?
  • 是否应该在各种函数的范围内使用它们来声明运行时常量?
  • 应该完全使用吗?

这些问题仅仅是我所面临的困惑的例子。一般的困惑是

  • 什么时候const在C编程中使用关键字?
  • 在C语言中使用此关键字可以获得哪些好处?
  • 使用const关键字有什么弊端吗?


有人指出,由于所有这些问题,在我的问题的细节中,这个问题可能太广泛了。我只是想澄清这些问题只是为了澄清对主要问题的困惑。

何时以及出于什么目的在C中将const关键字用于变量?

也可以改写为

const在C`中正确使用关键字具有相同的优缺点。


对于那些投票关闭的人,请解释为什么它不属于类别Specific issues with software development。我很具体。
Aseem Bansal

我猜是因为您在同一帖子中问了多个问题,因此您的问题属于“太宽泛
罗伯特·哈维

1
@RobertHarvey所有这些问题只是为了解释我的困惑。我唯一的问题仍然是这个问题的标题。一个很好的答案将涵盖所有这些相关问题。因此它仍然是一个specific issueconst在C`中正确使用关键字具有相同的优缺点。
Aseem Bansal

@RobertHarvey现在好点了吗?
Aseem Bansal

Answers:


64

查看代码时,我遵循以下规则:

  • const当函数不修改(或释放)所指向的数据时,请始终用于通过引用传递的函数参数。

    int find(const int *data, size_t size, int value);
  • 始终const用于可能使用#define或枚举定义的常量。结果,编译器可以将数据定位在只读存储器(ROM)中(尽管在嵌入式系统中,链接器通常是用于此目的的更好的工具)。

    const double PI = 3.14;
  • 切勿在函数原型中对通过value传递的参数 使用const 。它没有意义,因此只是“噪音”。

    // don't add const to 'value' or 'size'
    int find(const int *data, size_t size, const int value); 
  • 在适当的const volatile地方,在程序无法更改但仍可能更改的位置上使用。硬件寄存器是此处的典型用例,例如反映设备状态的状态寄存器:

    const volatile int32_t *DEVICE_STATUS =  (int32_t*) 0x100;

其他用途是可选的。例如,函数实现中函数的参数可以标记为const。

// 'value' and 'size can be marked as const here
int find(const int *data, const size_t size, const int value)  
{
     ... etc

或获得的函数返回值或计算结果,然后再不更改:

char *repeat_str(const char *str, size_t n) 
{
    const size_t len = strlen(str);
    const size_t buf_size = 1 + (len * n);
    char *buf = malloc(buf_size);
    ...

的这些用法const只是表明您不会更改变量;它们不会更改变量的存储方式或位置。编译器当然可以确定变量没有更改,但是通过添加const允许它强制执行。这可以帮助读者并增加一些安全性(尽管如果您的功能足够大或过于复杂以至于有很大的不同,则可以说还有其他问题)。编辑-例如 一个200行密集编码的函数,该函数带有嵌套循环和许多长的或相似的变量名,知道某些变量永远不会改变可能会大大简化其含义。此类功能设计或维护不当。


问题const。您可能会听到“常量中毒”一词。当添加const到函数参数导致“常数”传播时,会发生这种情况。

编辑-const中毒:例如在​​函数中:

int function_a(char * str, int n)
{
    ...
    function_b(str);
    ...
}

如果我们更改strconst,则必须确保fuction_b还需要一个const。依此类推,如果function_b将继续传递strfunction_c,等等。可以想象,如果它传播到许多单独的文件/模块中,可能会很痛苦。如果它传播到无法更改的功能(例如系统库),则必须进行强制转换。因此,散布 const在现有代码中可能会带来麻烦。但是,在新代码中,最好const在适当的情况下保持一致。

更为隐蔽的问题const是它不是使用原始语言编写的。作为附加组件,它不太适合。首先,它有两个含义(如上述规则所示,意思是“我不会更改”和“无法修改”)。但是,除此之外,它还很危险。例如,编译并运行以下代码,并且(取决于编译器/选项)在运行时可能会崩溃:

const char str[] = "hello world\n";
char *s = strchr(str, '\n');
*s = '\0';

strchr返回char*而不是const char*。由于 const必须其调用参数强制转换为char*。在这种情况下,这将放弃真正的只读存储属性。编辑:-这通常适用于只读存储器中的vars。“ ROM”不仅指物理ROM,还指任何写保护的内存,就像在典型OS上运行的程序的代码部分一样。

许多标准库函数的行为方式相同,所以要当心:当您拥有常数(即存储在ROM中)时,必须非常小心,不要失去其常数。


它实际上没有两个含义。正如您所说,这仅表示“ 不会改变这一点”。例如,拥有一个const volatile变量是完全有效的。
2013年

2
函数参数的确如此,但是对于文件范围内的常量,例如,const变量实际上是只读的:const说“ 无法修改”。const volatile另一方面,A 表示“ 无法修改,但可能会更改”。我将在答案中提及挥发物。
威廉·莫里斯

我不同意您的评论,只是认为这是向编译器发出将其放入ROM的指令。这意味着某种运行时防范未定义行为的方式(即通过某种方式修改const),这可能导致误解,例如“为什么我可以const通过非const指针更改此变量”(SO和其他C论坛上的常见问题) !)。总的来说,我还是喜欢这个答案:)
2013年

没错,“将其放入ROM”是不正确的(尽管很简洁:-)-我将其更改为“无法修改”,这更准确。感谢您的意见。
威廉·莫里斯

这是一个很棒的概述。我喜欢用短语来const声明宣告只读数据和只读数据视图,区别在于“无法修改”与“您不能修改”。
乔恩·普迪

8

通常,在任何编程语言中,建议使用它const或等效的修饰符,因为

  • 可以向呼叫者说明,他们传递的内容不会改变
  • 潜在的速度改进,因为编译器可以肯定地知道可以省略某些仅在参数可以更改时才有意义的事情
  • 保护自己免受意外改变价值

1

是的,基本上是TheLQ的答案。

对程序员来说是一种安全措施,因此您无需修改​​变量,也不要调用可能会修改变量的函数。在数组或结构中,const说明符指示其内容的值不会被修改,甚至编译器也不允许您这样做。但是,您仍然可以通过强制转换轻松地更改变量的值。

在我通常看到的情况下,它通常用于在代码中添加常量值,并表示如果调用特定函数,则不会修改数组或结构。最后一部分很重要,因为当您调用将要修改数组或结构的函数时,您可能希望保留原始版本,因此需要创建变量的副本,然后将其传递给函数。如果不是这种情况,显然您不需要副本,因此例如可以更改,

int foo(Structure s);

int foo(const Structure * s);

并且没有得到复制开销。

只是要补充一点,请注意C具有const说明符的特定规则。例如,

int b = 1;
const int * a = &b;

与...不同

int b = 1;
int * const a = &b;

第一个代码不允许您修改a。在第二种情况下,指针是常量,但其内容不是常量,因此编译器将允许您在* a = 3;没有编译器错误的情况下进行声明,但是您不能a将其作为对另一件事的引用。


0

与TheLQ的声明一致:

与一组程序员一起工作时,声明const是一个好的方法,它表明不应修改所述变量,或者只是为了提醒自己在大型项目中。从这个意义上说,它很有用,并且可以节省很多麻烦。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.