简体   繁体   English

在结构内重新分配数组

[英]realloc of array inside a struct

I'm trying to write a function that uses realloc() to extend the array as pointed to within in instance of a struct, however I can't seem to get it to work. 我正在尝试编写一个使用realloc()来扩展结构实例中指向的数组的函数,但是我似乎无法使其正常工作。

The relevant part of my code is: 我的代码的相关部分是:

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;
}

Can anybody see what the problem is with this? 有人可以看到这是什么问题吗?

Edit 编辑

As R. Sahu points out, container is not a pointer in this function - when you said the code "wasn't working", I assumed you meant that you weren't growing your array, but what you've written here won't even compile . 正如R. Sahu所指出的, container不是此函数的指针-当您说代码“不起作用”时,我以为您是在说您不是在扩展数组,但是您在此处编写的内容不会甚至编译

Are you sure you've copied this code correctly? 您确定已正确复制此代码? If so, does "not working" mean you're getting a compile-time error, a run-time error, or just unexpected output? 如果是这样,“不起作用”是否意味着您遇到编译时错误,运行时错误或只是意外输出?

If you've copied the code as written, then the first thing you need to do is change the function prototype to 如果您已经复制了编写的代码,那么您要做的第一件事就是将函数原型更改为

void extend_data(data_t *container, uint16_t value) {

and make sure you're passing a pointer to your data_t type, otherwise the update won't be reflected in calling code. 并确保您传递的是指向 data_t类型的指针 ,否则更新不会反映在调用代码中。

Original 原版的

In the line 在行中

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

sizeof(*(container->data)) evaluates to sizeof (uint16_t) . sizeof(*(container->data))sizeof (uint16_t) container->data is a pointer to, not an array of, uint16_t ; container->datauint16_t指针 ,而不是uint16_t的数组; sizeof will give you the size of the pointer object, not the number of elements you've allocated. sizeof将为您提供指针对象的大小,而不是分配的元素数。 What you want to do is something like the following: 您要执行的操作类似于以下内容:

/**
 * 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;
}

You don't calculate the new size correctly. 您没有正确计算新尺寸。 Consider this: 考虑一下:

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;
    }
}

The key point is newsize * sizeof array->data[0] . 关键是newsize * sizeof array->data[0] This is the number of chars needed for newsize elements of whatever type array->data[0] has. 这是array->data[0]类型的newsize元素所需的字符数。 Both malloc() and realloc() take the size in chars. malloc()realloc()均以char为单位。

If you initialize new structures of that type using int_array my_array = INT_ARRAY_INIT; 如果使用int_array my_array = INT_ARRAY_INIT;初始化该类型的新结构int_array my_array = INT_ARRAY_INIT; you can just call int_array_resize() to resize it. 您只需调用int_array_resize()即可调整其大小。 ( realloc(NULL, size) is equivalent to malloc(size) ; free(NULL) is safe and does nothing.) realloc(NULL, size)等效于malloc(size) ; free(NULL)是安全的,什么也不做。)

The int_array_init() and int_array_free() are just helper functions to initialize and free such arrays. int_array_init()int_array_free()只是用于初始化和释放此类数组的辅助函数。


Personally, whenever I have dynamically resized arrays, I keep both the allocated size ( size ) and the size used ( used ): 就个人而言,每当我动态调整数组大小时,我都会保留分配的大小( 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 }

A function that ensures there are at least need elements that can be added is then particularly useful. 因此,确保至少need添加元素的功能特别有用。 To avoid unnecessary reallocations, the function implements a policy that calculates the new size to allocate for, as a balance between amount of memory "wasted" (allocated but not used) and number of potentially slow realloc() calls: 为了避免不必要的重新分配,该函数实现了一个策略,该策略计算要分配的新大小,以“浪费”(已分配但未使用)的内存量与可能会很慢的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;
}

There are many opinions on what kind of reallocation policy you should use, but it really depends on the use case. 对于应该使用哪种重新分配策略,有很多意见,但这实际上取决于用例。

There are three things in the balance: number of realloc() calls, as they might be "slow"; 剩下的三件事是: realloc()调用的数量,因为它们可能是“缓慢的”。 memory fragmentation if different arrays are grown requiring many realloc() calls; 如果增长了不同的数组并需要许多realloc()调用,则内存碎片; and amount of memory allocated but not used. 以及已分配但未使用的内存量。

My policy above tries to do many things at once. 我上面的政策试图一次做很多事情。 For small allocations (up to 256 elements), it rounds the size up to the next multiple of 16. That is my attempt at a good balance between memory used for small arrays, and not very many realloc() calls. 对于小分配(最多256个元素),它会将大小四舍五入到下一个16的倍数。这是我试图在用于小数组的内存和不多的realloc()调用之间取得良好平衡的尝试。

For larger allocations, 50% is added to the size. 对于较大的分配,将添加50%的大小。 This reduces the number of realloc() calls, while keeping the allocated but unused/unneeded memory below 50%. 这减少了realloc()调用的次数,同时将已分配但未使用/不需要的内存保持在50%以下。

For really large allocations, when you have 2 21 elements or more, the size is rounded up to the next multiple of 2 20 , less a few elements. 对于非常大的分配,当您有2 21个或更多元素时,大小会四舍五入到2 20的下一个倍数,再减去一些元素。 This caps the number of allocated but unused elements to about 2 21 , or two million elements. 这将已分配但未使用的元素的数量限制为大约2 21或200万个元素。

(Why less a few elements? Because it does not harm on any systems, and on certain systems it may help a lot. Some systems, including x86-64 (64-bit Intel/AMD) on certain operating systems and configurations, support large ("huge") pages that can be more efficient in some ways than normal pages. If they are used to satisfy an allocation, I want to avoid the case where an extra large page is allocated just to cater for the few bytes the C library needs internally for the allocation metadata.) (为什么要减少一些元素?因为它不会损害任何系统,并且在某些系统上可能会有所帮助。某些系统,包括某些操作系统和配置上的x86-64(64位Intel / AMD),支持大型(“巨大”)页面在某些方面可能比普通页面更有效。如果使用它们来满足分配,我想避免这样一种情况:分配一个超大页面只是为了满足C库的几个字节内部需要分配元数据。)

void extend_data(data_t container, ...

In your function container is not the pointer but the struct itself passed by the value so you cant use the -> operator. 在函数container中,指针不是指针,而是值本身传递的结构体,因此您不能使用->运算符。

The realloced memory will be lost as you work on the local copy of the passed strucure and it will be lost on the function return. 当您对传递的结构的本地副本进行操作时,重新分配的内存将丢失,并且在函数返回时将丢失。

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

it will be always 1 as the *(uint16_t *) / sizeof(uint16_t) is always one. 它将始终为1因为*(uint16_t *) / sizeof(uint16_t)始终为1。

Why: data member is pointer to the uint16_t . 原因: data成员是指向uint16_t指针。 *data has the type of uint16_t *data的类型为uint16_t

sizeof is calculated during the compilation not the runtime and it does not return the ammount of memory allocated by the malloc . sizeof是在编译期间(而不是运行时)计算的,并且不会返回malloc分配的malloc

It appears you aren't using sizeof correctly. 看来您没有正确使用sizeof In your struct you've defined a uint16_t pointer , not an array. 在您的结构中,您定义了uint16_t 指针 ,而不是数组。 The size of the uint16_t* data type is the size of a pointer on your system. uint16_t*数据类型的大小是系统上指针的大小。 You need to store the size of the allocated memory along with the pointer if you want to be able to accurately resize it. 如果希望能够精确调整其大小,则需要将分配的内存的大小与指针一起存储。 It appears you already have a field for this with data_size . 看来您已经有一个具有data_size的字段。 Your example might be able to be fixed as, 您的示例可能可以固定为

// 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