首页 > 解决方案 > 如何处理 C 中动态数组的标头对齐

问题描述

我正在用 C 实现一个任意类型的动态数组。该实现通过在数组数据之前分配一个标头来工作:

typedef struct
{
    unsigned char* begin; // Pointer to data
    unsigned char* end; // Pointer to last element
    size_t typeSize; // Size of type
    size_t capacity; // Capacity of array (not number of elements in array)
} dynamicArr;

#define dynamicArr_ConstructBase(dest, src, newCapacity) \
dest = src;                                              \
dest->capacity = newCapacity;                            \
dest->begin = (unsigned char*)dest + sizeof *dest

#define dynamicArr_Construct(dest, src, newCapacity, currSize, typeSize) \
dynamicArr_ConstructBase(dest, src, newCapacity);                        \
dest->end = dest->begin + typeSize * (currSize)

#define dynamicArr_Header(arr) ((dynamicArr*)((unsigned char*)(arr) - sizeof(dynamicArr)))
static inline size_t dynamicArr_Size(dynamicArr* arr)
{
    return (arr->end - arr->begin) / arr->typeSize;
}

#define dynamicArr_Create(typename, arr) typename* arr = (typename*)dynamicArr_Create_(sizeof(typename))
static inline unsigned char* dynamicArr_Create_(size_t typeSize)
{
    dynamicArr* dynArr;
    void* tmp = malloc(sizeof * dynArr + typeSize * 10);
    if (!tmp) return NULL;

    dynArr = tmp;
    dynArr->begin = (unsigned char*)dynArr + sizeof * dynArr;
    dynArr->end = dynArr->begin;
    dynArr->capacity = 10;
    dynArr->typeSize = typeSize;

    return dynArr->begin;
}

#define dynamicArr_Free(arr) free(dynamicArr_Header(arr))

#define dynamicArr_Push(arr, item) \
do                                                                                                \
{                                                                                                 \
    if (arr)                                                                                      \
    {                                                                                             \
        dynamicArr* header = dynamicArr_Header(arr);                                              \
        if (header->typeSize && header->typeSize == sizeof item)                                  \
        {                                                                                         \
            size_t arrSize = dynamicArr_Size(header);                                             \
            if (arrSize == header->capacity)                                                      \
            {                                                                                     \
                size_t newCapacity = (size_t)(header->capacity * 1.5f);                           \
                if (newCapacity == header->capacity) ++newCapacity;                               \
                void* tmp = realloc(header, sizeof(dynamicArr) + header->typeSize * newCapacity); \
                if (tmp)                                                                          \
                {                                                                                 \
                    dynamicArr_Construct(header, tmp, newCapacity, arrSize, header->typeSize);    \
                    *((void**)&(arr)) = header->begin;                                            \
                    arr[arrSize] = item;                                                          \
                    header->end += header->typeSize;                                              \
                }                                                                                 \
                else                                                                              \
                {                                                                                 \
                    free(header);                                                                 \
                    arr = NULL;                                                                   \
                }                                                                                 \
            }                                                                                     \
            else                                                                                  \
            {                                                                                     \
                arr[arrSize] = item;                                                              \
                header->end += header->typeSize;                                                  \
            }                                                                                     \
        }                                                                                         \
    }                                                                                             \
} while(0)

使用示例:

void Func()
{
    dynamicArr_Create(int, intArr);
    dynamicArr_Push(intArr, 10);
    printf("%i\n", intArr[0]);
    dynamicArr_Free(intArr);
}

这样做的问题是,我认为这是未定义的行为,因为如果编译器选择使用对齐指令作为数组的项目,则在标头之后可能未对齐。

有没有办法保证项目的对齐?

我已经研究过使用灵活的数组成员,但是我不确定如何实现它以使其适用于任意类型。

例如,我可以像这样声明标题:

#define dynamicArr_Header(type) \
struct                          \
{                               \
    size_t typeSize;            \
    size_t capacity;            \
    type data[];                \
}

但是,如果我要在代码中使用它,我的编译器会警告我'=': incompatible types - from '*' to '*'

导致警告的示例:

dynamicArr_Header(int)* A = malloc(sizeof(dynamicArr_Header(int)));
dynamicArr_Header(int)* B = A; //Causes warning

这样做危险吗?这是因为匿名结构真的不是同一类型,还是会导致更多问题(例如我是否可以保证编译器以相同的方式填充和打包这些结构)?

灵活的数组成员方法的另一个“问题”是我必须传递头指针,这与之前的方法不同,我只能提供指向数据的指针。我知道使用灵活的数组版本可以:

int* pntrToIntArr = &header->data[0];

但是我希望将标头抽象出来,但是如果我提供指向数据数组的指针,我将如何取回标头?我可以(dynamicArr_Header(int)*)((unsigned char*)pntrToIntArr - sizeof(dynamicArr_Header(int)))吗?

如果不违反严格的混叠/对齐规则,我在 C 中所做的事情是不可能的吗?我知道我可能用指针语义打破了很多严格的别名规则。

标签: arrayscpointersdynamicmemory-alignment

解决方案


推荐阅读