【问题标题】:Assigning an instance of a struct to a new const struct instance将结构的实例分配给新的 const 结构实例
【发布时间】:2016-03-12 16:06:12
【问题描述】:

我有一个公共函数,它对缓冲区执行读取操作并执行摘要。它有一个const uint8_t * 参数。

void do_digest(const uint8_t *buf, size_t size);

在内部,我将所有指针大小对包装在一个简单的缓冲区结构中:

typedef struct {
    uint8_t *buf;
    size_t size;
} myBuffer;

但是,作为一个迂腐的开发人员,我想摆脱一个警告,指出当分配给 myBuffer 结构时,参数的常量被剥离。

myBuffer foo = { buf, size };  // Warning: discards 'const' qualifier 

因此,最简单和最明确的解决方案是创建一个与 const 缓冲区一起使用的新结构类型:

typedef struct {
    const uint8_t *buf;
    size_t size;
} myConstBuffer;

myConstBuffer cfoo = { buf, size }; // No warnings, woohoo!

但是,有一个问题;在内部,我传递指向这些缓冲区结构的指针。现在,如果我有一个可变的 myBuffer 实例并且我想将它传递给一个接受 myConstBuffer 指针的函数,我会收到一条警告,抱怨类型不兼容。

void some_internal_func(const myConstBuffer *b);

...

myBuffer mutable_buf = { ... };    
some_internal_func(&mutable_buf); // Warning: incompatible types.

好像我在用一个警告换另一个警告。有没有更好的解决方案?

提前致谢。

【问题讨论】:

  • 为什么需要 constBuffer 和 buffer?只需让 const 指针成为缓冲区的成员。
  • “警告”实际上是格式错误的代码(一些编译器说“警告”并插入虚构的强制转换),所以想要解决这个问题不仅仅是迂腐
  • 这个问题没有真正好的解决方案。您拥有的一个工具是创建一个接受myBuffer 并返回myConstBuffer 的函数;那么你至少避免强制转换

标签: c struct c99


【解决方案1】:

回答你的问题:标准库中的函数经常会遇到这种情况,我不知道有更好的解决方案来转换参数,使其包含const 限定符(也就是说,如果你不选择忽略警告:) )

当一个函数在它的原型中定义它的一个参数具有<type> const * 类型时,这并不是要求您提供const 类型的参数,因为这表明该函数不会尝试修改该地址的内容。函数通过指定const 向您提出的要求是,在该函数调用期间,您(可能来自不同的线程)不会修改该地址的内容。

通常您会在库函数中看到这一点,这些库函数通常不会在 C 中实现。但如果是这样,那么尝试修改 <type> *const param 指向的位置会导致编译器错误。

在我看来,与incompatible type 相关的警告方式incompatible type 更可取,因为通过在传递变量时将变量强制转换为const 来抑制它要安全得多作为参数而不是其他类型。

【讨论】:

    【解决方案2】:

    没有简单的解决方案,因为如果你有 const <type> *<type> * 的结构,它们是不兼容的。

    但是,如果您可以使用 C11 或至少 C99,您可以将 buff 自己制作为包含大小和数据的 struct

    typedef struct {
        size_t size;
        uint8_t data[];
    } Buffer;
    

    请注意,您不能从中创建静态或自动变量,因此您必须使用malloc 和朋友:

    size_t buffer_length = 10;
    Buffer *buff = malloc(sizeof(*buff) + buffer_length);
    
    if ( buff != NULL ) {
        buff->size = buffer_length;
    
        ...
    

    这个struct 可以像上面的buff 一样传递给const Buffer *Buffer *,但是您不需要再跟踪大小/长度,因此可以更改接口。

    这称为灵活数组成员。它必须是struct 的最后一个成员。 sizeof(Buffer) 就好像数组有 0 个条目(这在 C 中是不允许的,但它是 C99 之前代码的 gcc 扩展遗留物。

    更新:当然,这只有在您在所有级别都使用此struct 时才有意义。所以你只将这个struct 传递给所有使用这样一个数组加上长度/大小的函数。否则您将不得不复制数据,这通常是不想要的(没有例外)。

    在开发软件时,内部接口的设计通常会占用大量设计时间。但正如您现在所看到的,这是值得的。

    【讨论】:

    • 但是如果用户传入一个指针和一个大小,那么除非你复制数据,否则这根本不起作用。这将避免 const 错误,但可能不是应该做的事情。
    • @dave:你什么意思? “也”什么?为什么那行不通?
    • 用户传入指针和大小。您不能将缓冲区放在内存中的指针上(因为您不控制它之前的 size_t 字节),因此您唯一的解决方案是复制整个输入数据。这与当前的数据类型有很大不同,而且可能不是最优的。
    • @dave:这就是我写“没有简单的解决方案”的原因。当然,您必须更改接口,因为它们是不同的数据类型。但这是const-正确的唯一方法。 C 不能可靠地跟踪多个间接的const-正确性。这是您应该深思熟虑地设计界面的原因之一。当然,您也可以使用 Linux 内核中的 container_of 之类的东西,但这在这里很容易出错,而且您仍然有第二个参数。
    • @dave:这绝对是一个自定义接口,因为标准库不使用stdint.h 类型。我认为很明显您也必须更改函数参数,因为 struct 现在已经包含大小,所以第二个参数将无用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多