簡體   English   中英

C 虛擬結構,嚴格別名和 static 初始化

[英]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)應該相同,並且基於事實上VectorRealVector具有相同的 alignment 也應該沒問題。

如果這是ub ,我如何以安全的方式在C中創建一種暫存器結構? 在 C++ 中,我們有alignasalignof和 place placement new ,這有助於使這種考驗更加安全。

如果在C99中無法做到這一點,那么在C11中使用alignasalignof會更安全嗎?

#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 中創建一種暫存器結構?

真正做到安全的唯一正確方法是兩步過程:

  • 首先編譯一個測試可執行文件,它將 output 的大小和結構 RealVector 的struct RealVector
  • 然后生成具有正確結構定義的 header 文件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使用帶有alignofstruct
  • 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM