使用`const`元素设计C容器?

时间:2014-12-19 16:34:30

标签: c interface const containers

容器接口是否应声明指向包含的指针 元素为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}}。 强调文字

4 个答案:

答案 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_getpthread_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);