赋值会改变变量的有效类型吗?
Does assignment change the effective type of a variable?
可能相关 30970251, 7687082。
我正在考虑编写一个内存分配器并尝试解决现代 C 对类型双关和别名施加的限制。我认为只要分配器底层的缓冲区最初是从 malloc 检索到的,因为来自 malloc 的指针没有声明类型。
过度对齐的字符缓冲区确实有声明的类型。我不 认为 我可以将一个指针转换为任意类型,而是必须通过 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);
}
}
这是可行的,因为我可以努力确保每次使用 memcpy 将自定义分配器 returns 写入一个空指针,而不是强制转换为所需的类型。即替换
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 中编写分配器来解决,但我并不是特别热衷于这样做。如果问题未明确说明,请告诉我。
不,一般不会。它只会更改分配时没有类型的对象的有效类型,即通过 malloc
和朋友分配的对象。
因此,如果您执行编译器和库实现的 user 之类的操作,则程序的行为是未定义的。分配为 char[]
的数组始终具有有效类型。
如果您是编译器或库编写者,则不受这些限制的约束。你只需要说服你的工具链不要优化太多。通常你可以通过确保你的分配器函数存在于它自己的一个 TU 中来做到这一点,它只导出一个 void*
,并确保你没有 link 时间优化或类似的东西打开.
如果您提供这样的功能作为 C 库的一部分(替换),那么您作为实现者必须向您的用户提供保证。
可能相关 30970251, 7687082。
我正在考虑编写一个内存分配器并尝试解决现代 C 对类型双关和别名施加的限制。我认为只要分配器底层的缓冲区最初是从 malloc 检索到的,因为来自 malloc 的指针没有声明类型。
过度对齐的字符缓冲区确实有声明的类型。我不 认为 我可以将一个指针转换为任意类型,而是必须通过 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);
}
}
这是可行的,因为我可以努力确保每次使用 memcpy 将自定义分配器 returns 写入一个空指针,而不是强制转换为所需的类型。即替换
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 中编写分配器来解决,但我并不是特别热衷于这样做。如果问题未明确说明,请告诉我。
不,一般不会。它只会更改分配时没有类型的对象的有效类型,即通过 malloc
和朋友分配的对象。
因此,如果您执行编译器和库实现的 user 之类的操作,则程序的行为是未定义的。分配为 char[]
的数组始终具有有效类型。
如果您是编译器或库编写者,则不受这些限制的约束。你只需要说服你的工具链不要优化太多。通常你可以通过确保你的分配器函数存在于它自己的一个 TU 中来做到这一点,它只导出一个 void*
,并确保你没有 link 时间优化或类似的东西打开.
如果您提供这样的功能作为 C 库的一部分(替换),那么您作为实现者必须向您的用户提供保证。