Is the address of malloc + size_t * 3 aligned for any type?

此生再无相见时 提交于 2019-12-10 21:36:50

问题


I'm builing a kind of dynamic array (vector), but instead of embedding the data (typically void *) into a struct vector, I'm reserving space for a struct vector + a chunk of bytes, an example using an array of size_t's:

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

struct vector {
    size_t capacity;
    size_t typesize;
    size_t size;
};

#define VECTOR(v) ((struct vector *)((unsigned char *)v - sizeof(struct vector)))

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

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

static void *vadd(void *data)
{
    struct vector *vector = VECTOR(data);
    unsigned char *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;
        }
        vector = (struct vector *)new;
        vector->capacity = capacity;
        vector->size++;
        return new + sizeof(*vector);
    }
    vector->size++;
    return data;
}

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

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(*(void **)((unsigned char *)data + iter * vector->typesize));
        }
    }
    free(vector);
}

int main(void)
{
    size_t *data;

    data = valloc(sizeof(size_t), 1);
    if (data == NULL) {
        perror("valloc");
        exit(EXIT_FAILURE);
    }
    for (size_t i = 0; i < 10; i++) {
        data = vadd(data);
        if (data == NULL) {
            perror("vadd");
            exit(EXIT_FAILURE);
        }
        data[i] = i;
    }
    for (size_t i = 0; i < vsize(data); i++) {
        printf("%zu\n", data[i]);
    }
    vfree(data, NULL);
    return 0;
}

The question is: Is the address of the result of malloc (void *) + the size of the struct well aligned for any type? Is the behaviour of this code well defined?


回答1:


Is the address of malloc + size_t * 3 aligned for any type?

No, not certainly.

Make certain of fundamental alignment by making the prefix data also of fundamental alignment.

One way is to use of union of struct vector and max_align_t. The size of union uvector will be a multiple of the fundamental alignment.

max_align_t which is an object type whose alignment is as great as is supported by the implementation in all contexts;

union uvector {
  max_align_t a;
  struct vector {
    size_t capacity;
    size_t typesize;
    size_t size;
  } v;
};

union uvector *uvector;
unsigned char *data;

data = calloc(1, sizeof *uvector ...
...
return data + sizeof *uvector;

Answer has some inefficiencies - Reworked in another answer..




回答2:


Is the address of malloc + size_t * 3 aligned for any type?

No, because there may be standard types which have greater alignment requirements than size_t. The alignment requirement of a standard type will be one of a number of fundamental alignments used by the compiler.

C11's <stddef.h> defines a standard type max_align_t whose alignment is as great as any standard type supported by the implementation. (There may be extended types that have a greater alignment requirement than max_align_t.) C11 also has the _Alignof and _Alignas keywords.

_Alignof is an operator that operates on a type and its result is an integer constant expression equal to the alignment requirement of that type in bytes. The largest fundamental alignment is given by _Alignof(max_align_t).

The _Alignas keyword is used in alignment specifiers as part of a declaration to increase the alignment requirement of the declared object or member. (It cannot be used to decrease the alignment requirement of an object or member.) An alignment specifier has one of the following forms:

  • _Alignas ( type-name )
  • _Alignas ( constant-expression )

where constant-expression specifies the required alignment in bytes. The first form with type-name is equivalent to _Alignas (_Alignof ( type-name ) ).

You can use an alignment specifier to increase the alignment requirement of the first member of struct vector to the maximum fundamental alignment as follows:

struct vector {
    _Alignas(max_align_t)
    size_t capacity;
    size_t typesize;
    size_t size;
};

Since the first member of struct vector now has the greatest possible fundamental alignment, then struct vector as a whole has the same alignment requirement (unless it contains non-standard types that require extended alignment). If necessary. the compiler adds extra padding to the end of struct vector to ensure that sizeof(struct vector) is a multiple of _Alignof(max_align_t).



来源:https://stackoverflow.com/questions/55867320/is-the-address-of-malloc-size-t-3-aligned-for-any-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!