【问题标题】:Have I applied the rule of strict aliasing correctly?我是否正确应用了严格别名规则?
【发布时间】:2021-12-10 11:27:27
【问题描述】:

我当前的(简化的)缓冲区 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_BIT8,那么这个带有(void *) 的调整代码应该与uint8_t 版本完全相同。

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

【问题讨论】:

    标签: c buffer strict-aliasing


    【解决方案1】:

    如果uint8_tunsigned char 不同,那将是UB。假设uint8_t 存在,这是不太可能的,因为

    但是,该标准并未明确要求 uint8_tunsigned char 的类型相同。因此,它是由实现定义的。

    考虑从以下线程应用解决方案来检查上述类型是否相同。 How to assert two types are equal in c?

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

    【讨论】:

    • 感谢您的回答。我知道这种情况不太可能发生。但是,我不想养成编写技术上未定义行为的代码的习惯。在 C 中很难做出假设。我猜后面的代码(带字符)就可以了?
    • @marco-a,最好使用char*,但是如果重构很麻烦,您可以添加uint8_tunsigned char 相同的断言
    • 太棒了。我会花时间重构我的代码。
    • @marco-a:几乎每个独立实现的重要程序都依赖于通常由给定平台的几乎所有独立实现定义相同的行为,但标准没有强加要求。唯一应该关心“技术上” UB 的人是那些想要向后弯腰以适应无端聪明的编译器或疯狂的滥用语言的废话生成器的人,他们将短语“不可移植或错误”解释为“不可移植” ,因此是错误的”。
    猜你喜欢
    • 2020-04-11
    • 1970-01-01
    • 1970-01-01
    • 2015-10-15
    • 2019-09-05
    • 2017-02-25
    • 2016-10-09
    相关资源
    最近更新 更多