赋值是否会更改变量的有效类型?

时间:2016-12-22 20:20:28

标签: casting c11

潜在相关309702517687082

我正在考虑编写一个内存分配器,并试图找出如何导航现代C对类型惩罚和别名的限制。我认为只要分配器底层的缓冲区最初是从malloc检索的,因为malloc的指针没有声明的类型,我认为我很清楚。

过度对齐的char缓冲区确实具有声明的类型。我不认为我可以将指针转换为任意类型,而必须通过char指针仔细写入它,例如使用memcpy。这很痛苦,因为我看不到通过来自调用者的memcpy hack隐藏写入的方法。

请考虑以下事项:

#include <assert.h>
#include <stdalign.h>
#include <stdint.h>
#include <string.h>

static_assert(sizeof(double) == sizeof(uint64_t), "");
static_assert(alignof(double) == alignof(uint64_t), "");

int main(void)
{
  alignas(alignof(double)) char buffer[sizeof(double)];
  // effective type of buffer is char [8]                                                                                                                                                                                                 

  {
    double x = 3.14;
    memcpy(&buffer, &x, sizeof(x));
    // effective type of buffer is now double                                                                                                                                                                                             
  }

  {
    uint64_t* ptr = (uint64_t*)&buffer;
    // effective type of buffer is still double                                                                                                                                                                                           
    // reading from *ptr would be undefined behaviour                                                                                                                                                                                     
    uint64_t y = 42;
    memcpy(ptr, &y, sizeof(y));
    // effective type of buffer is now uint64_t                                                                                                                                                                                           
  }

  {
    double* ptr = (double*)&buffer;
    // effective type of buffer is still uint64_t                                                                                                                                                                                         
    uint64_t retrieve = *(uint64_t*)ptr;  // OK                                                                                                                                                                                           
    assert(retrieve == 42);

    double one = 1.0;
    *ptr = one;  // Unsure if OK to dereference pointer of wrong type                                                                                                                                                                     
    // What is the effective type of buffer now?                                                                                                                                                                                          
    assert(*ptr == one);
  }
}

这是可行的,因为我可以努力确保每次自定义分配器返回一个void指针时,它都会用memcpy写入,而不是强制转换为所需的类型。也就是说,替换

double * x = my_malloc(sizeof(double));
*x = 3.14;

使用:

double tmp = 3.14;
void * y = my_malloc(sizeof(double));
memcpy(y, &tmp, sizeof(double));
double * x = (double*)y;

所有这些线路噪声都被编译器中的优化传递所扼杀,但确实看起来很愚蠢。是否有必要符合标准?

这绝对可以通过在asm中而不是在C中编写分配器来解决,但我并不是特别热衷于这样做。如果问题不明确,请告诉我。

1 个答案:

答案 0 :(得分:1)

不,一般情况下。它只会更改分配时没有类型的对象的有效类型,即通过malloc和朋友分配的对象。

因此,如果您执行编译器和库实现的 user 之类的操作,则程序的行为是未定义的。分配为char[]的数组始终具有该类型的有效类型。

如果您是编译器或库编写器,则不受这些限制的约束。您只需说服您的工具链不要过分优化。通常,您可以通过确保您的分配器功能位于其自己的TU中,仅导出void*,并确保您没有链接时间优化或打开的内容。

如果您提供此类功能作为C库(替换)的一部分,则您必须作为必须向您的用户提供保证的实施者。