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

Is this an appropriate use of const qualifiers in C?

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

我想 'promise' 向量不会改变其内容,所以我将数据存储为:

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

我不知道我的 constness 正确性是否被认为是 "overdoing it",但我尝试保持我的 accessors/modifiers:

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

当我 return 向量中的一个元素时,它是一个指向用户数据的指针,所以我让用户通过放弃内部数组中的常量来修改指向的数据。

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

而不是 returning a const void*,我 return a void* 相反。这个想法是,在内部,我想确保我没有更改指向的数据,但我不关心向量外部的数据会发生什么。

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

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

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

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

相反,return const void *。然后你可以让调用者决定何时丢弃 const。

您最终可能会得到两个版本的矢量,一个是 void *,一个是 const void *

I want to 'promise' that the vector will not alter its contents

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

I don't know if it's considered "overdoing it" with my constness correctness, but I try to maintain const correctness with my accessors/modifiers:

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

When I return an element from the vector, it is a pointer to the user's data so I let the user modify the data being pointed to, by casting away the constness in the internal array.

没有。你搞砸了。 const-正确性必须是全有或全无才能有价值。

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

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

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

你在这里的实际声明是你永远不会取消引用指针,但不是说它是常量。只需将其写在文档中即可。