容器接口是否应声明指向包含的指针
元素为const
?
任务:用C设计一个容器(注意:这是明确的关于普通C的, 不关于C ++,也不是C#)。容器用指针喂食 items,应返回指向项目的指针。
有点无意义的MWE,但这个想法扩展到有用的容器 好:
#include <stdio.h>
typedef struct {
void *data;
} container_t;
void put(container_t *c, void *data)
{
c->data = data;
}
void *get(container_t *c)
{
return c->data;
}
int main(void)
{
container_t c;
int x = 42;
int *y;
put(&c, &x);
y = get(&c);
printf("value = %d\n", *y);
return 0;
}
到目前为止,非常好。
好吧,容器不应该使用指针来修改存储的 数据。我想通过一个小的界面清楚地说明这一点 改变:
void put(container_t *c, const void *data)
^new
现在编译器要求我进行另一次更改,我确实这样做了 同意:
typedef struct { const void *data; } container_t;
^new
然后编译器要求我再做一次更改,这是相当的 合乎逻辑的:
const void *get(container_t *c)
^new
现在,编译器抱怨y
不是const int *
,
这让我有点不高兴。什么是正确的处理方式
此?
设计没有const
的容器?我有时会看到这个
库文档,例如Glib
[https://developer.gnome.org/glib/2.42/glib-Double-ended-Queues.html#g-queue-push-tail]。
但我真的很喜欢const
提供的“安全性”。
记录get
的返回值可能需要const
抛弃?即打电话给
y = (int *)get(&c);
我不想抛弃const
里面的get
return (void *)c->data;
功能,如
const
因为我不知道调用者是否应该考虑 项目{{1}}。 强调文字
答案 0 :(得分:2)
容器类型就是 - 容器。它允许用户添加/删除/访问包含的项目。用户使用它做什么取决于用户。
如果你仍然希望指针指向常数,那么你的第二种方法就是最好的方法。但是如果用户可以抛弃const,那么容器的代码也是如此,因此不修改数据的承诺很浅。
另请注意,容器存储指向void的指针。它本身就强烈表明用户容器的代码不能做太多,因为它不知道指针指向的数据类型。是的,它仍然可以在该内存位置写入垃圾。
鉴于const不保证常量,即编译器可能不将const数据放在只读存储器中,容器代码的用户仍然可以尝试使用const。现在,const数据实际上可能最终存在于只读存储器中,或者用户可能正在从容器中为只读存储器提供地址。也就是说,用户已确保没有代码可以修改常量数据。在这种情况下,您无需担心定义指向常量数据的指针。如果用户已经验证数据不能被设置为只读,那么他/她仍然可以向数据中添加CRC之类的内容。这样,当他/她抓住数据时,他可以验证数据是否可靠。
总结:如果你有指向void的指针,那么使它成为一个指向const void的指针是没有意义的。此外,由用户保护他/她的数据。数据结构应该只关心它的用途。
答案 1 :(得分:2)
您的界面正在签订合同,即传递给它的对象永远不会通过该访问进行修改。所以本质上你需要两种容器类型,或者更普遍的是接口,一种假设对象是可变的(void*
版本),另一种假设它可以被修改(void const*
版本
这是在许多地方出现的问题,即使在C库中也是如此。 E.g memchr
就是这样一个界面,可以静默地对对象进行const
转换。这种直接的叛国行为可以在_Generic
的现代C中被规避。
无法避免存储值然后在代码中完全不相关的位置重现它的问题。 C和POSIX标准对tss_set/tss_get
和pthread_setspecific/pthread_getspecific
存在问题,并以不兼容的方式解决问题。 C变体两者都有void*
,POSIX变体的设置为void const*
,获取时为void*
。
答案 2 :(得分:1)
const *
表示您可以访问和修改该地址中的数据,但无法修改您指定的地址。
这意味着如果用户想要更改您的数据内容,就能够做到。但地址将保持不变。
但是,每个人都可以自由地将指针设置为某个地址。你不能阻止这种情况。
有关更多信息,请查看this链接(The C Book - Const and Volatile)第8.4.1节。
答案 3 :(得分:1)
如果你想要y 指针的常量,正确的方法是:
int * const y = get(&c);
如果你想要明白y 指向的内容,那么,正如你所提到的:
const int * y = get(&c);