这是在 C 中适当使用 const 限定符吗?

Posted

技术标签:

【中文标题】这是在 C 中适当使用 const 限定符吗?【英文标题】:Is this an appropriate use of const qualifiers in C? 【发布时间】:2015-06-18 21:39:18 【问题描述】:

我在 C 中有一个简单的向量实现,它包含一个 void* 数组。由用户来处理实际类型。

我想“承诺”向量不会改变其内容,所以我将数据存储为:

Struct _Vector
    UInt32 used;
    UInt32 size;
    const void** arr;
;

我不知道我的 constness 正确性是否被认为是“过度使用”,但我尝试使用我的访问器/修饰符来保持 const 正确性:

void vector_add(Vector *v, const void* const elem);   
void vector_set(Vector *v, const UInt32 idx, const void* const elem);

当我从向量返回一个元素时,它是一个指向用户数据的指针,所以我让用户修改指向的数据,方法是丢弃内部数组中的常量。

void* vector_get(const Vector *v, const UInt32 idx)
    if ( idx >= v->used ) 
        exitAtError("Vector","Array out of bounds");
    return (void*)v->arr[idx];

我没有返回 const void*,而是返回 void*。这个想法是在内部,我想确保我不会更改指向的数据,但我不在乎向量之外的数据会发生什么。

这被认为是对 const 的正确使用吗?我认为可以在一个地方将数据声明为 const,而在其他地方让它是可变的。在阅读了许多声称永远不会抛弃 const 的教条文章后,我不确定,如果它被抛弃了,那么一开始就不适合使用 const。

【问题讨论】:

我不会认为它是“教条的”以避免抛弃 constness。 constness 的全部意义在于让编译器强制执行它。如果您通过将const 抛开来规避它,那将变得毫无用处。 我不会考虑返回值,因为它可以由调用者强制转换。 你们都说得很好。 const 限定符的设计方式是有原因的。我不确定我是否通过抛弃 const 来“绕过它”,但无论如何我意识到我的推理是不够的。 【参考方案1】:

我想“承诺”该向量不会改变其内容

这是 C。数据结构没有行为。您可以定义一个向量数据结构,使得最终指向的数据不能通过向量进行修改,这就是您所做的。当然,这有其含义。

我不知道我的 constness 正确性是否被认为是“过度使用”,但我尝试使用我的访问器/修饰符来保持 const 正确性:

如果您完全不介意const,那么一定要严格遵守const-正确性。否则,没有多大意义。

当我从向量返回一个元素时,它是一个指向用户数据的指针,所以我让用户修改指向的数据,方法是丢弃内部数组中的常量。

不。你吹过了。 const-正确性必须是全部或全部才有价值。

通过抛弃const,您违反了您的Vector 类型与用户签订的合同,允许通过实例修改指向的值。可以肯定的是,间接如此,但这并不重要。此外,这可能导致更广泛地违反const 正确性——例如,假设输入到您的向量中的数据首先是const。然后将它们存储在您的数据结构中就可以了,但是您提供可以允许修改它们的函数(或至少允许尝试这样做)。

您可以考虑使用不透明的数据结构而不是const-限定成员以防止通过向量直接访问元素。但最终,对于包含const 数据和非const 数据的向量,const 正确性将需要单独的数据结构和函数。

【讨论】:

谢谢,这是我的担忧,我误解了 const 合约的强度。你的例子很好地证明了它的滥用是如何产生问题的。您是否会说 C 只是不支持我想要的那种强制执行,即只有向量的范围不修改数据?还是只是我设计代码的方式? 另外我想指出 Vector 结构确实是不透明的。任何实现它的人,只能看到 Vector 的 typedef 和函数声明。 @MitchLaskis:请注意,违反const 合同将表现出未定义的行为const 变量首先可能被放置在只读内存中或被写保护,因此任何写入都会导致灾难(甚至是字面意思!)。 @Olaf :我现在看到 const 确实是在说“在程序结束或变量超出范围之前,这永远不会改变”,并不是要强制执行临时访问权限。我真正在寻找的是一种说法,“除非通过这些特定的函数,否则永远不要修改这个指针”,而 const 并不是为之设计的。 @MitchLaskis:你应该搜索一下,我可能搜索过answered that already。请注意,这是针对 int 的,但可以轻松扩展。如果有不清楚的地方,您可以在那里发表评论。【参考方案2】:

我认为这是一个坏主意,因为它允许在没有警告的情况下写入 const 对象。例如:

const int x = 5;
vector_add(&v, &x);

int *p = vector_get(&v, 0);
*p = 6;   // oops

改为返回const void *。然后你可以让调用者决定何时丢弃 const。

您最终可能会拥有两个版本的向量,一个持有void *,一个持有const void *

【讨论】:

【参考方案3】:

虽然其他答案在有限的 const 正确性方面是正确的,但您应该注意,在 C 中,一旦指针进入此向量,就无法知道数据是否 [im] 可变。在这种情况下,vector 不应该关心这一点,并且总是获取并返回非常量指针。如果有人想在那里传递 const-pointer,he 将在执行 get() 时进行强制转换并注意取消强制转换。用户有责任恢复初始存储语义,因为他确实知道。

您在这里的实际主张是您永远不会取消引用指针,但不是说它是 const。只需在文档中写下即可。

【讨论】:

以上是关于这是在 C 中适当使用 const 限定符吗?的主要内容,如果未能解决你的问题,请参考以下文章

为啥允许我在 C 中使用 const 限定变量作为数组大小?

精通C语言ANSI C 类型限定符const,volatile,restrict,_Atomic

精通C语言ANSI C 类型限定符const,volatile,restrict,_Atomic

C++ | const修饰的变量

QT & C++:传递 'const QString' 丢弃限定符

跟我一起学C++之从C到C++(const限定符)