[英]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.