Использование max_align_t для хранения фрагмента байтов - PullRequest
0 голосов
/ 26 апреля 2019

В этой теме Мне было предложено использовать max_align_t, чтобы правильно выровнять адрес для любого типа, в итоге я создал эту реализацию динамического массива:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>

struct vector {
    size_t capacity;
    size_t typesize;
    size_t size;
    max_align_t data[];
};

#define VECTOR(v) ((struct vector *)((unsigned char *)v - offsetof(struct vector, data)))

static void *valloc(size_t typesize, size_t size)
{
    struct vector *vector;

    vector = calloc(1, sizeof(*vector) + typesize * size);
    if (vector == NULL) {
        return NULL;
    }
    vector->typesize = typesize;
    vector->capacity = size;
    vector->size = 0;
    return vector->data;
}

static void vfree(void *data, void (*func)(void *))
{
    struct vector *vector = VECTOR(data);

    if (func != NULL) {
        for (size_t iter = 0; iter < vector->size; iter++) {
            func((unsigned char *)vector->data + vector->typesize * iter);
        }
    }
    free(vector);
}

static void *vadd(void *data)
{
    struct vector *vector = VECTOR(data);
    struct vector *new;
    size_t capacity;

    if (vector->size >= vector->capacity) {
        capacity = vector->capacity * 2;
        new = realloc(vector, sizeof(*vector) + vector->typesize * capacity);
        if (new == NULL) {
            return NULL;
        }
        new->capacity = capacity;
        new->size++;
        return new->data;
    }
    vector->size++;
    return vector->data;
}

static size_t vsize(void *data)
{
    return VECTOR(data)->size;
}

static void vsort(void *data, int (*comp)(const void *, const void *))
{
    struct vector *vector = VECTOR(data);

    if (vector->size > 1) {
        qsort(vector->data, vector->size, vector->typesize, comp);
    }
}

static char *vgetline(FILE *file)
{
    char *data = valloc(sizeof(char), 32);
    size_t i = 0;
    int c;

    while (((c = fgetc(file)) != '\n') && (c != EOF)) {
        data = vadd(data);
        data[i++] = (char)c;
    }
    data = vadd(data);
    data[i] = '\0';
    return data;
}

struct data {
    int key;
    char *value;
};

static int comp_data(const void *pa, const void *pb)
{
    const struct data *a = pa;
    const struct data *b = pb;

    return strcmp(a->value, b->value);
}

static void free_data(void *ptr)
{
    struct data *data = ptr;

    vfree(data->value, NULL);
}

int main(void)
{
    struct data *data;

    data = valloc(sizeof(struct data), 1);
    if (data == NULL) {
        perror("valloc");
        exit(EXIT_FAILURE);
    }
    for (size_t i = 0; i < 5; i++) {
        data = vadd(data);
        if (data == NULL) {
            perror("vadd");
            exit(EXIT_FAILURE);
        }
        data[i].value = vgetline(stdin);
        data[i].key = (int)vsize(data[i].value);
    }
    vsort(data, comp_data);
    for (size_t i = 0; i < vsize(data); i++) {
        printf("%d %s\n", data[i].key, data[i].value);
    }
    vfree(data, free_data);
    return 0;
}

Но я не уверен, что смогу использовать max_align_t для хранения фрагмента байтов:

struct vector {
    size_t capacity;
    size_t typesize;
    size_t size;
    max_align_t data[]; // Used to store any array,
                        // for example an array of 127 chars
}; 

Разрывает ли он один за последним элементом правила массива?

1 Ответ

2 голосов
/ 27 апреля 2019

Разрывает ли он один за последним элементом правила массива?

номер

Использование max_align_t для хранения фрагмента байтов

Проблема OP не является особенной, поскольку в ней используется гибкий элемент массива .

В особом случае последние элементы структуры ... имеют тип неполного массива; это называется членом гибкого массива. ... Однако когда. (или ->) оператор имеет левый операнд, который является (указателем) на структуру с элементом гибкого массива, а правый операнд называет этот член, он ведет себя так, как если бы этот элемент был заменен самым длинным массивом (с тот же тип элемента) ...

Это та же проблема, что и доступ к любой выделенной памяти или массиву одного типа, как если бы это был другой тип.

Преобразование из max_align_t * в char * в void * хорошо определено, когда выравнивание выполнено правильно.

Указатель на тип объекта может быть преобразован в указатель на другой тип объекта. Если результирующий указатель неправильно выровнен для ссылочного типа, поведение не определено. C11dr §6.3.2.3 7

Все просмотренные обращения в коде не пытаются получить доступ вне массива «как будто».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...