我是否正确应用了严格别名规则?

3

我目前的(简化版)缓冲区API如下:

typedef struct {
    size_t offset;
    size_t size;
    uint8_t *data;
} my_buffer;

// Writes an unsigned int 8 to the buffer
bool my_buffer_write_u8(my_buffer *buffer, uint8_t value) {
    if (buffer->offset >= buffer->size) return false;

    buffer->data[buffer->offset] = value;
    ++buffer->offset;
    return true;
}

然而,在我重新了解C语言中的严格别名规则后,我对这种用法并不确定:

    char string[32];

    my_buffer buffer;
    buffer.size = sizeof(string);
    buffer.data = string; // <-- I think this violates the strict aliasing rule
    buffer.offset = 0;

    // the function calls access buffer.data which is defined to be `uint8_t *` and not `char *`
    // in other words, I'm manipulating a `char *` through a `uint8_t *`:
    // even though uint8_t is almost always unsigned char, it is nevertheless not the same as unsigned char
    my_buffer_write_u8(&buffer, 'h');
    my_buffer_write_u8(&buffer, 'e');
    my_buffer_write_u8(&buffer, 'l');
    my_buffer_write_u8(&buffer, 'l');
    my_buffer_write_u8(&buffer, 'o');
    my_buffer_write_u8(&buffer, '\0');

我认为在缓冲区结构体中应该使用 void *,然后使用 (char *) 类型转换来访问底层数据:

typedef struct {
    size_t offset;
    size_t size;
    void *data;
} my_buffer;

// Writes an unsigned int 8 to the buffer
bool my_buffer_write_u8(my_buffer *buffer, uint8_t value) {
    if (buffer->offset >= buffer->size) return false;

    unsigned char *data = (unsigned char *)buffer->data;

    data[buffer->offset] = value;
    ++buffer->offset;

    return true;
}

char *unsigned char *以及signed char * 总是被假定为与其他数据类型别名相同。

但是对于uint8_t *来说,情况不尽相同(按照标准来说)。

如果CHAR_BIT等于8,那么带有(void *)的这个调整后的代码应该与uint8_t版本完全相同。

现在问题来了:我是否正确地应用了严格别名规则?

1个回答

1
如果uint8_tunsigned char不同,将会导致未定义行为。假设uint8_t存在,这是非常不可能的,因为:

然而,标准并没有明确要求uint8_t是与unsigned char相同的类型。因此它更多地是由实现定义的。

考虑应用以下主题中的解决方案来检查上述类型是否相同。 如何在c中断言两种类型相等?

最好使用char*/unsigned char*来访问数据。但是,如果重构代码会很麻烦,那么只需添加一个检查,检查类型uint8_tunsigned char是否相同,如果不同则拒绝编译。

谢谢你的回答。我知道这种情况很少见。但是,我不想养成编写具有技术上未定义行为的代码的习惯。在 C 中很难做出假设。我猜后面的代码(使用 char)就可以了? - Marco
1
@marco-a,最好使用char *,但如果重构麻烦,您可以添加断言,即uint8_tunsigned char是相同的。 - tstanisl
太棒了。我会花时间重构我的代码。 - Marco
1
@marco-a:几乎所有的独立实现的非平凡程序都依赖于行为,这些行为通常会被给定平台上的几乎所有独立实现相同地定义,但标准对其没有任何要求。唯一应该关心“技术上”UB的人是那些想要屈服于过度聪明的编译器或疯狂滥用语言的荒谬生成器的人,他们将“不可移植或错误”的短语解释为“不可移植,因此错误”。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接