是否可以定义一个结构成员来保存未定义类型/大小的数据?
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这样定义结构?
答案 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