繁体   English   中英

在结构内重新分配数组

[英]realloc of array inside a struct

我正在尝试编写一个使用realloc()来扩展结构实例中指向的数组的函数,但是我似乎无法使其正常工作。

我的代码的相关部分是:

struct data_t {
  int data_size;
  uint16_t *data;
};

void extend_data(data_t container, uint16_t value) {
    // adds an additional uint16_t to the array of DATA, updates its internal
    // variables, and initialises the new uint to VALUE.

    int len_data = sizeof(*(container->data)) / sizeof(uint16_t);
    printf("LENGTH OF DATA: %d\n", len_data);

    container->data = realloc(container->data, sizeof(*(container->data))+sizeof(uint16_t));
    container->data_size++;
    container->data[container->data_size-1] = value;

    len_data = sizeof(*(container->data)) / sizeof(uint16_t);
    printf("LENGTH OF DATA: %d\n", len_data);
    printf("data_size: %d\n", container->data_size);

    return;
}

有人可以看到这是什么问题吗?

编辑

正如R. Sahu所指出的, container不是此函数的指针-当您说代码“不起作用”时,我以为您是在说您不是在扩展数组,但是您在此处编写的内容不会甚至编译

您确定已正确复制此代码? 如果是这样,“不起作用”是否意味着您遇到编译时错误,运行时错误或只是意外输出?

如果您已经复制了编写的代码,那么您要做的第一件事就是将函数原型更改为

void extend_data(data_t *container, uint16_t value) {

并确保您传递的是指向 data_t类型的指针 ,否则更新不会反映在调用代码中。

原版的

在行中

container->data = realloc(container->data, sizeof(*(container->data))+sizeof(uint16_t));

sizeof(*(container->data))sizeof (uint16_t) container->datauint16_t指针 ,而不是uint16_t的数组; sizeof将为您提供指针对象的大小,而不是分配的元素数。 您要执行的操作类似于以下内容:

/**
 * Don't assign the result of a realloc call back to the original
 * pointer - if the call fails, realloc will return NULL and you'll
 * lose the reference to your original buffer.  Assign the result to
 * a temporary, then after making sure the temporary is not NULL,
 * assign that back to your original pointer.
 */
uint16_t *tmp = realloc(container-data, sizeof *container->data * (container->data_size + 1) );
if ( tmp ) 
{
  /**
   * Only add to container->data and update the value of container->data_size
   * if the realloc call succeeded.
   */
  container->data = tmp;
  container->data[container->data_size++] = value;
}

您没有正确计算新尺寸。 考虑一下:

typedef struct {
    size_t  size;
    int    *data;
} int_array;
#define  INT_ARRAY_INIT  { 0, NULL}

void int_array_resize(int_array *const  array,
                      const size_t      newsize)
{
    if (!array) {
        fprintf(stderr, "int_array_resize(): NULL int_array.\n");
        exit(EXIT_FAILURE);
    }
    if (!newsize) {
        free(array->data);
        array->data = 0;
        array->size = 0;
    } else
    if (newsize != array->size) {
        void *temp;

        temp = realloc(array->data, newsize * sizeof array->data[0]);
        if (!temp) {
            fprintf(stderr, "int_array_resize(): Out of memory.\n");
            exit(EXIT_FAILURE);
        }
        array->data = temp;
        array->size = newsize;
    }
}

/* int_array  my_array = INT_ARRAY_INIT;
      is equivalent to
   int_array  my_array;
   int_array_init(&my_array);
*/
void int_array_init(int_array *const array)
{
    if (array) {
        array->size = 0;
        array->data = NULL;
    }
}

void int_array_free(int_array *const array)
{
    if (array) {
        free(array->data);
        array->size = 0;
        array->data = NULL;
    }
}

关键是newsize * sizeof array->data[0] 这是array->data[0]类型的newsize元素所需的字符数。 malloc()realloc()均以char为单位。

如果使用int_array my_array = INT_ARRAY_INIT;初始化该类型的新结构int_array my_array = INT_ARRAY_INIT; 您只需调用int_array_resize()即可调整其大小。 realloc(NULL, size)等效于malloc(size) ; free(NULL)是安全的,什么也不做。)

int_array_init()int_array_free()只是用于初始化和释放此类数组的辅助函数。


就个人而言,每当我动态调整数组大小时,我都会保留分配的大小( size )和使用的大小( used ):

typedef struct {
    size_t  size;   /* Number of elements allocated for */
    size_t  used;   /* Number of elements used */
    int    *data;
} int_array;
#define  INT_ARRAY_INIT { 0, 0, NULL }

因此,确保至少need添加元素的功能特别有用。 为了避免不必要的重新分配,该函数实现了一个策略,该策略计算要分配的新大小,以“浪费”(已分配但未使用)的内存量与可能会很慢的realloc()调用次数之间取得平衡:

void int_array_need(int_array *const  array,
                    const size_t      need)
{
    size_t  size;
    void   *data;

    if (!array) {
        fprintf(stderr, "int_array_need(): NULL int_array.\n");
        exit(EXIT_FAILURE);
    }

    /* Large enough already? */
    if (array->size >= array->used + need)
        return;

    /* Start with the minimum size. */
    size = array->used + need;

    /* Apply growth/reallocation policy. This is mine. */
    if (size < 256)
        size = (size | 15) + 1;
    else
    if (size < 2097152)
        size = (3 * size) / 2;
    else
        size = (size | 1048575) + 1048577 - 8;

    /* TODO: Verify (size * sizeof array->data[0]) does not overflow. */

    data = realloc(array->data, size * sizeof array->data[0]);
    if (!data) {
        /* Fallback: Try minimum allocation. */
        size = array->used + need;
        data = realloc(array->data, size * sizeof array->data[0]);
    }
    if (!data) {
        fprintf(stderr, "int_array_need(): Out of memory.\n");
        exit(EXIT_FAILURE);
    }

    array->data = data;
    array->size = size;
}

对于应该使用哪种重新分配策略,有很多意见,但这实际上取决于用例。

剩下的三件事是: realloc()调用的数量,因为它们可能是“缓慢的”。 如果增长了不同的数组并需要许多realloc()调用,则内存碎片; 以及已分配但未使用的内存量。

我上面的政策试图一次做很多事情。 对于小分配(最多256个元素),它会将大小四舍五入到下一个16的倍数。这是我试图在用于小数组的内存和不多的realloc()调用之间取得良好平衡的尝试。

对于较大的分配,将添加50%的大小。 这减少了realloc()调用的次数,同时将已分配但未使用/不需要的内存保持在50%以下。

对于非常大的分配,当您有2 21个或更多元素时,大小会四舍五入到2 20的下一个倍数,再减去一些元素。 这将已分配但未使用的元素的数量限制为大约2 21或200万个元素。

(为什么要减少一些元素?因为它不会损害任何系统,并且在某些系统上可能会有所帮助。某些系统,包括某些操作系统和配置上的x86-64(64位Intel / AMD),支持大型(“巨大”)页面在某些方面可能比普通页面更有效。如果使用它们来满足分配,我想避免这样一种情况:分配一个超大页面只是为了满足C库的几个字节内部需要分配元数据。)

void extend_data(data_t container, ...

在函数container中,指针不是指针,而是值本身传递的结构体,因此您不能使用->运算符。

当您对传递的结构的本地副本进行操作时,重新分配的内存将丢失,并且在函数返回时将丢失。

sizeof(*(container.data)) / sizeof(uint16_t)

它将始终为1因为*(uint16_t *) / sizeof(uint16_t)始终为1。

原因: data成员是指向uint16_t指针。 *data的类型为uint16_t

sizeof是在编译期间(而不是运行时)计算的,并且不会返回malloc分配的malloc

看来您没有正确使用sizeof 在您的结构中,您定义了uint16_t 指针 ,而不是数组。 uint16_t*数据类型的大小是系统上指针的大小。 如果希望能够精确调整其大小,则需要将分配的内存的大小与指针一起存储。 看来您已经有一个具有data_size的字段。 您的示例可能可以固定为

// I was unsure of the typedef-ing happening with data_t so I made it more explicit in this example
typedef struct {
    int data_size;
    uint16_t* data;
} data_t;

void extend_data(data_t* container, uint16_t value) {
    // adds an additional uint16_t to the array of DATA, updates its internal
    // variables, and initialises the new uint to VALUE.

    // CURRENT LENGTH OF DATA
    int len_data = container->data_size * sizeof(uint16_t);
    printf("LENGTH OF DATA: %d\n", len_data);

    uint16_t* tmp = realloc(container->data, (container->data_size + 1) * sizeof(uint16_t));
    if (tmp) {
        // realloc could fail and return false.
        // If this is not handled it could overwrite the pointer in `container` and cause a memory leak
        container->data = tmp;
        container->data_size++;
        container->data[container->data_size-1] = value;
    } else {
        // Handle allocation failure
    }

    len_data = container->data_size * sizeof(uint16_t);
    printf("LENGTH OF DATA: %d\n", len_data);
    printf("data_size: %d\n", container->data_size);

    return;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM