[英]C dummy struct, strict aliasing and static initialization
我的第一个问题没有很好地表述,所以这次又来了,被问得更清楚,解释得更清楚。
我想隐藏结构的变量,同时能够在堆栈上静态初始化结构。 大多数解决方案都使用不透明指针习语和动态 memory 分配,这并不总是需要的。
这个例子的想法来自以下帖子:
https://www.reddit.com/r/C_Programming/comments/aimgei/opaque_types_and_static_allocation/
我知道这可能是ub
,但我相信它应该在大多数消费者架构中都能正常工作:32 位或 64 位。
现在您可能会告诉我,有时size_t
可能大于void *
并且联合中的void * alignment
union
union
alignment 为sizeof(void *)
可能是错误的,但通常情况并非如此,也许它会发生但我认为这是例外而不是规则。
基于大多数编译器添加填充以将其对齐到 4 或 8 的倍数,具体取决于您的架构,并且sizeof
使用填充返回正确的大小, sizeof(Vector)
和sizeof(RealVector)
应该相同,并且基于事实上Vector
和RealVector
具有相同的 alignment 也应该没问题。
如果这是ub
,我如何以安全的方式在C
中创建一种暂存器结构? 在 C++ 中,我们有alignas
、 alignof
和 place placement new
,这有助于使这种考验更加安全。
如果在C99
中无法做到这一点,那么在C11
中使用alignas
和alignof
会更安全吗?
#include <stdint.h>
#include <stdio.h>
/* In .h */
typedef union Vector {
uint8_t data[sizeof(void *) + 2 * sizeof(size_t)];
/* this is here to the force the alignment of the union to that of sizeof(void *) */
void * alignment;
} Vector;
void vector_initialize_version_a(Vector *);
void vector_initialize_version_b(Vector *);
void vector_debug(Vector const *);
/* In .c */
typedef struct RealVector {
uint64_t * data;
size_t length;
size_t capacity;
} RealVector;
void
vector_initialize_version_a(Vector * const t) {
RealVector * const v = (RealVector *)t;
v->data = NULL;
v->length = 0;
v->capacity = 8;
}
void
vector_initialize_version_b(Vector * const t) {
*(RealVector *)t = (RealVector) {
.data = NULL,
.length = 0,
.capacity = 16,
};
}
void
vector_debug(Vector const * const t) {
RealVector * v = (RealVector *)t;
printf("Length: %zu\n", v->length);
printf("Capacity: %zu\n", v->capacity);
}
/* In main.c */
int
main() {
/*
Compiled with:
clang -std=c99 -O3 -Wall -Werror -Wextra -Wpedantic test.c -o main.exe
*/
printf("%zu == %zu\n", sizeof(Vector), sizeof(RealVector));
Vector vector;
vector_initialize_version_a(&vector);
vector_debug(&vector);
vector_initialize_version_b(&vector);
vector_debug(&vector);
return 0;
}
为什么也不简单? 它避免了指针双关语
typedef struct RealVector {
uint64_t * data;
size_t length;
size_t capacity;
} RealVector;
typedef struct Vector {
uint8_t data[sizeof(RealVector)];
} Vector;
typedef union
{
Vector v;
RealVector rv;
} RealVector_union;
void vector_initialize_version_a(void * const t) {
RealVector_union * const v = t;
v -> rv.data = NULL;
v -> rv.length = 0;
v -> rv.capacity = 8;
}
和
我将发布上一个问题的答案,我没有时间发布:)
我这样做安全吗?
不,你不是。 但是,与其找到一种安全的方法,不如在不安全时出错:
#include <assert.h>
#include <stdalign.h>
static_assert(sizeof(Vector) == sizeof(RealVector), "");
static_assert(alignof(Vector) == alignof(RealVector), "");
通过以这种方式编写的检查,您将事先知道何时会出现问题,然后您可以处理特定环境来修复它。 如果支票不会触发,你就会知道没关系。
如何以安全的方式在 C 中创建一种暂存器结构?
真正做到安全的唯一正确方法是两步过程:
struct RealVector
struct Vector { alignas(REAL_VECTOR_ALIGNMENT) unigned char data[REAL_VECTOR_SIZE]; };
struct Vector { alignas(REAL_VECTOR_ALIGNMENT) unigned char data[REAL_VECTOR_SIZE]; };
笔记:
union
使用带有alignof
的struct
uint8_t
是一个 8 位的 integer。 使用char
或最好的unsigned char
来表示“字节”。sizeof(void*)
不保证为sizeof(uint64_t*)
where max alignment is either 4 or 8
- 通常在 x86_64 上alignof(long double)
为 16。一种可能性是在 .h 文件中按如下方式定义Vector
:
/* In vector.h file */
struct RealVector {
uint64_t * data;
size_t length;
size_t capacity;
};
typedef union Vector {
char data[sizeof(struct RealVector)];
/* these are here to the force the alignment of the union */
uint64_t * alignment1_;
size_t alignment2_;
} Vector;
这也定义了用于向量 implementation.c 文件的struct RealVector
:
/* In vector.c file */
typedef struct RealVector RealVector;
这样做的好处是Vector
的二进制内容实际上由RealVector
组成并且正确对齐。 缺点是狡猾的用户可以通过指针类型转换轻松地操纵Vector
的内容。
一个不太合法的替代方法是从 .h 文件中删除struct RealVector
并将其替换为相同形状的匿名struct
类型:
/* In vector.h file */
typedef union Vector {
char data[sizeof(struct { uint64_t * a; size_t b; size_t c; })];
/* these are here to the force the alignment of the union */
uint64_t * alignment1_;
size_t alignment2_;
} Vector;
然后需要在vector implementation.c文件中完整定义struct RealVector
:
/* In vector.c file */
typedef struct RealVector {
uint64_t * data;
size_t length;
size_t capacity;
} RealVector;
这样做的好处是,如果不首先定义另一个与匿名struct
类型具有相同形状的struct
类型,那么狡猾的用户就不能轻易地操纵Vector
的内容。 缺点是forms 的二进制表示Vector
的匿名struct
类型在技术上与向量实现中使用的RealVector
类型不兼容。c 文件因为标签和成员名称不同。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.