C中未定义类型的结构字段

时间:2020-07-24 11:09:17

标签: c generics struct void

是否可以定义一个结构成员来保存未定义类型/大小的数据?

typedef struct my_data {
size_t size;
??? data;
} my_data

int main (void) {
my_data *toto;
void *test;

test = malloc(100);
toto = (my_data*)test;

基本上,我想让size字段保留前8个字节,而让第二个字段保留其余8个字节吗?是否可以用C这样定义结构?

2 个答案:

答案 0 :(得分:0)

是的!您可以使用Zero length array。它基本上允许您在结构的末尾具有可变长度的数组。仅当放置在结构的最后一个元素中时,它才起作用。

typedef struct my_data {
    size_t size;
    int data[0];
} my_data;

int main (void) {
    my_data *toto;
    void *test;

    test = malloc(sizeof(my_data) + sizeof(int) * 100);
    toto = (my_data*)test;
    toto->size = 100;
    toto->data[43] = 123;
    printf("%d\n", toto->data[43]);
    free(toto); //don't forget to free dynamically allocated memory
}

因此,这是最适合您的示例的方法。但是,如果您只想存储可变长度的数组,只需使用指向动态分配的数组的指针即可:

int main (void) {
    int *data;
    size_t size = 100;
    data = malloc(sizeof(int) * size);
    data[42] = 123;
    printf("%d\n", data[42]);
    free(data); //don't forget to free dynamically allocated memory
}

答案 1 :(得分:0)

要处理的主要问题是对齐。对于任何对象类型,指向malloc返回的内存块的指针都正确对齐,但是对于某些对象类型,刚好超过类型的size成员末尾的指针可能未正确对齐。您可以通过在size成员和其他max_align_t类型的对象之间使用联合来解决此问题:

typedef union {
    size_t size;
    max_align_t reserved__;
} my_data_header;

然后分配一个对象,增加my_data_header的大小,并使用一个指针,该指针恰好位于my_data_header的末尾以指向您的对象:

my_data_header *head = malloc(sizeof(*head) + object_size);
if (head) {
    head->size = object_size;
    object_ptr = (void *)&head[1];
} else {
    object_ptr = NULL;
}

但是,释放内存时,您需要释放头,而不是对象指针。或者,可以从对象指针中检索指向头部的指针:

if (object_ptr) {
    head = &((my_data_head *)(void *)object_ptr)[-1];
} else {
    head = NULL;
}

可以定义一组接口函数,以允许分配和释放任何大小的对象并检索当前大小:

obj_alloc.c

#include "obj_alloc.h"
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

typedef union {
    size_t size;
    max_align_t reserved__;
} obj_hdr;

static obj_hdr *obj_to_hdr(void *obj)
{
    if (!obj) {
        return NULL;
    }
    return (obj_hdr *)obj - 1;
}

size_t obj_size(const void *obj)
{
    if (!obj) {
        return 0;
    }
    return obj_to_hdr((void *)obj)->size;
}

void *obj_reallocarray(void *obj, size_t nmemb, size_t size)
{
    obj_hdr *hdr = obj_to_hdr(obj);

    if (!(nmemb && size)) {
        /* New size is zero. */
        free(hdr);
        return NULL;
    }
    if ((SIZE_MAX - sizeof(*hdr)) / nmemb < size) {
        /* Too big */
        errno = ENOMEM;
        return NULL;
    }
    hdr = realloc(hdr, sizeof(*hdr) + nmemb * size);
    if (!hdr) {
        /* Allocation failure. */
        errno = ENOMEM;
        return NULL;
    }
    /* Record size and return pointer to object. */
    hdr->size = nmemb * size;
    return hdr + 1;
}

void *obj_allocarray(size_t nmemb, size_t size)
{
    return obj_reallocarray(NULL, nmemb, size);
}

void *obj_calloc(size_t nmemb, size_t size)
{
    void *obj = obj_reallocarray(NULL, nmemb, size);

    if (obj) {
        memset(obj, 0, nmemb * size);
    }
    return obj;
}

void *obj_malloc(size_t size)
{
    return obj_reallocarray(NULL, 1, size);
}

void *obj_realloc(void *obj, size_t size)
{
    return obj_reallocarray(obj, 1, size);
}

void obj_free(void *obj)
{
    free(obj_to_hdr(obj));
}

obj_alloc.h

#ifndef OBJ_ALLOC_H__INCLUDED
#define OBJ_ALLOC_H__INCLUDED

#include <stddef.h>

/**
 * \brief Get current size of object.
 *
 * Get current size of object allocated by obj_malloc(),
 * obj_realloc(), obj_calloc(), obj_allocarray(), or
 * obj_reallocarray().
 *
 * \param[in] obj
 *   Pointer to object, or null pointer.
 *
 * \return current size of object.
 */ 
size_t obj_size(const void *obj);

/**
 * \brief Free object.
 *
 * Free object allocated by obj_malloc(), obj_realloc(),
 * obj_calloc(), obj_allocarray(), or obj_reallocarray().
 *
 * \param[in] obj
 *   Pointer to object, or null pointer.
 */
void obj_free(void *obj);

/**
 * \brief Allocate an object from dynamic memory.
 *
 * Allocate an object of specified size from dynamic memory and
 * record its size.
 *
 * \param[in] size
 *   Size of object to be allocated.
 *
 * \return On success, returns a pointer to the allocated object,
 * or a null pointer if \p size is 0. On failure, returns a null
 * pointer and sets \c errno to \c ENOMEM.
 */
void *obj_malloc(size_t size);

/**
 * \brief Reallocate an object from dynamic memory.
 *
 * Reallocate an existing object to a new, specified size from
 * dynamic memory and record its new size.
 *
 * obj_realloc(NULL, size) behaves like obj_malloc(size).
 *
 * obj_realloc(obj, 0) behaves like obj_free(obj) and returns a
 * null pointer.
 *
 * If a pointer to an object is given, it must have been returned
 * by a previous call to obj_malloc(), obj_realloc(),
 * obj_calloc(), obj_allocarray(), or obj_reallocarray().
 *
 * \param[in] obj
 *   Pointer to existing object, or a null pointer.
 * \param[in] size
 *   New size of object to be reallocated.
 *
 * \return On success, returns a pointer to the reallocated
 * object, or a null pointer if \p size is 0. On failure, returns
 * a null pointer and sets \c errno to \c ENOMEM, but the original
 * object remains unchanged.
 */
void *obj_realloc(void *obj, size_t size);

/**
 * \brief Allocate a zeroed array of objects from dynamic memory.
 *
 * Allocate memory for an array of objects from from dynamic
 * memory and with a specified number of elements and a specified
 * element size, recording the total size, and setting the memory
 * contents to zero.
 *
 * obj_calloc(nmemb, size) is equivalent to
 * obj_malloc(nmemb * size) followed by
 * memset(ptr, 0, nmemb * size) if the return value is non-null.
 * The call is valid even if the multiplication of \p nmemb by
 * \p size would result in arithmetic overflow, but the function
 * will fail to allocate memory in that case.
 *
 * \param[in] nmemb
 *   Number of elements to allocate.
 * \param[in] size
 *   Size of each element.
 *
 * \return On success, returns a pointer to the allocated memory,
 * or a null pointer if \p size is 0 or \p nmemb is 0. On failure,
 * returns a null pointer and sets \c errno to \c ENOMEM.
 */
void *obj_calloc(size_t nmenb, size_t size);

/**
 * \brief Allocate an array of objects from dynamic memory.
 *
 * Allocate memory for an array of objects from from dynamic
 * memory and with a specified number of elements and a specified
 * element size, recording the total size.
 *
 * obj_allocarray(nmemb, size) is equivalent to
 * obj_malloc(nmemb * size).  The call is valid even if the
 * multiplication of \p nmemb by \p size would result in
 * arithmetic overflow, but the function will fail to allocate
 * memory in that case.
 *
 * \param[in] nmemb
 *   Number of elements to allocate.
 * \param[in] size
 *   Size of each element.
 *
 * \return On success, returns a pointer to the allocated memory,
 * or a null pointer if \p size is 0 or \p nmemb is 0. On failure,
 * returns a null pointer and sets \c errno to \c ENOMEM.
 */
void *obj_allocarray(size_t nmemb, size_t size);

/**
 * \brief Reallocate an array of objects from dynamic memory.
 *
 * Reallocate an existing object to an array of objects from
 * dynamic memory with a specified number of elements and a
 * specified element size, recording the new total size.
 *
 * obj_reallocarray(obj, nmemb, size) is equivalent to
 * obj_realloc(obj, nmemb * size).  The call is valid even if the
 * multiplication of \p nmemb by \p size would result in
 * arithmetic overflow, but the function will fail to reallocate
 * memory in that case.
 *
 * obj_realloc(obj, 0, size) and obj_realloc(obj, nmemb, 0)
 * behave like obj_free(obj) and return a null pointer.
 *
 * If a pointer to an object is given, it must have been returned
 * by a previous call to obj_malloc(), obj_realloc(),
 * obj_calloc(), obj_allocarray(), or obj_reallocarray().
 *
 * \param[in] obj
 *   Pointer to existing object, or a null pointer.
 * \param[in] nmemb
 *   Number of elements to allocate.
 * \param[in] size
 *   Size of each element.
 *
 * \return On success, returns a pointer to the reallocated
 * memory, or a null pointer if \p nmemb is 0 or \p size is 0.
 * On failure, returns a null pointer and sets \c errno to \c
 * ENOMEM, but the original object remains unchanged.
 */
void *obj_reallocarray(void *obj, size_t n_memb, size_t size);

#endif