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?

2 Answers

7
chux On Best Solutions

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
Ian Abbott On

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).