C:使用realloc以获得高性能的结构数组

时间:2017-07-20 20:02:01

标签: c dynamic structure realloc

我正在使用realloc来调整包含3个点x,y和z的结构数组的大小。此结构封装在另一个包含数组的结构,数组的长度和一个“保留”值中,该值用于预分配策略,以便在更明显将更多结构点附加到预分配策略时实现更快的性能struct array。
我正在编译一个看起来像这样的Makefile:

CFLAGS = -g -Wall
LIBS = -lm

default: echo "You must specify a target, e.g. file1, file2" 


file2:
    gcc $(CFLAGS) -o $@ test.c file2.c $(LIBS)

我有一个初始化空数组结构的函数,一个用于将数组重置为空并释放任何动态分配的内存,一个用于将一个点附加到数组的末尾,另一个用于删除索引指定的一个点值。

我遇到了两个我无法找到原因的错误。一个是我的代码返回非零状态代码1,另一个是当我追加几千点时长度似乎是一个。 我让append函数完成所有工作但是如果我应该在初始化中分配动态内存,请告诉我。我很确定我的重置和删除功能正如他们应该的那样工作。请看一下附加物。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <assert.h>

typedef struct point
{
  int x, y, z;
} point_t;

typedef struct 
{
  // number of points in the array
  size_t len;

  // pointer to an array of point_t structs

  point_t* points;


  size_t reserved; 

} point_array_t;


void point_array_initial( point_array_t* pa )
{
    assert(pa);
    pa->len = 0;
    pa->reserved = 0;
    pa->points=NULL;
}   


void point_array_reset( point_array_t* pa )
{//just free the array and set pa to NULL

    assert(pa);

    pa->points = memset(pa->points, 0, sizeof(point_t)*(pa->len));
    pa->len = 0;
    pa->reserved=0;
    free(pa->points);
    pa->points=NULL;
}


int point_array_append( point_array_t* pa, point_t* p )
{

    assert(pa);
    assert(p);
    if(pa == NULL)//something wrong with intialization or reset
    {
        return 1;
    }
    if(p == NULL)//nothing to append
    {
        return 1;
    }
    //append the first point 
    if(pa->len == 0)
    {
        pa->len=1;
        pa->reserved=pa->len*2;
        pa->points = malloc(sizeof(point_t)* (pa->reserved));
        if(pa->points == NULL)//malloc failed
        {
            return 1;
        }

        pa->points[pa->len-1].x = p->x;
        pa->points[pa->len-1].y = p->y;
        pa->points[pa->len-1].z = p->z;
    }

    if (pa->reserved > pa->len )
    {
        pa->len+=1;
        pa->points[pa->len-1].x = p->x;//insert at index 0
        pa->points[pa->len-1].y = p->y;
        pa->points[pa->len-1].z = p->z;

    }
    //when we run out of space in reserved (len has caught up)
    else if(pa->reserved == pa->len)
    {
        pa->len+=1;
        pa->reserved=pa->len*2;
        pa->points=realloc(pa->points, sizeof(point_t)*(pa->reserved));//doubling size of array
        pa->points[pa->len-1].x = p->x;//TODO: change formula to find insertion point
        pa->points[pa->len-1].y = p->y;
        pa->points[pa->len-1].z = p->z;
    }



    return 0;
}


int point_array_remove( point_array_t* pa, unsigned int i )
{

    assert(pa);
    if (i >= pa->len)//out of bounds
    {
        return 1;
    }   

    if(pa->len==0)//0 elements trying to remove from empty array
    {
        //pa->len=0;
        //free(pa->points);
        //pa->points=NULL; 
        return 1;
    }
    else if(pa->len ==1)//remove only element
    {
        pa->len-=1;//no copying required, just shorten
        pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));
        //free(pa->points);
        //pa->points=NULL;
    }
    else//array size is longer than 1 or 0
    {
        pa->points[i].x = pa->points[pa->len-1].x;
        pa->points[i].y = pa->points[pa->len-1].y;
        pa->points[i].z = pa->points[pa->len-1].z;  
        pa->len-= 1;//shorten array size
        pa->reserved = pa->len*2;
        pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));//could reallocate for reserve here to increase speed.
    }   

    return 0;
}

2 个答案:

答案 0 :(得分:1)

追加函数中的else正文后缺少if(pa->len == 0):第一个点会被追加两次。

请注意,此功能中有太多特殊情况。它可以简化为一个测试:如果数组太小,重新分配它,并附加点。

其他简化是可能的:

  • 测试if (pa->len == 0)//0 elements trying to remove from empty array与之前的测试realloc(NULL, size)是多余的。

  • 利用malloc(size)相当于realloc(p, 0)free(p)相当于free(NULL)realloc()就可以了。

  • 请注意point_array_remove可能会失败,即使缩小块也是如此。

  • 你应该只在数组过于稀疏时收缩它,而不是每次调用#include <assert.h> #include <stdlib.h> typedef struct point { int x, y, z; } point_t; typedef struct { size_t len; // number of valid points in the array size_t reserved; // allocated number of points in the array point_t *points; // pointer to an array of point_t structs } point_array_t; void point_array_initial(point_array_t *pa) { assert(pa); pa->len = 0; pa->reserved = 0; pa->points = NULL; } void point_array_reset(point_array_t *pa) { assert(pa); free(pa->points); pa->len = 0; pa->reserved = 0; pa->points = NULL; } int point_array_append(point_array_t *pa, const point_t *p) { point_t *points; assert(pa); assert(p); // no need to test pa nor p, asserts would already abort points = pa->points; if (pa->len >= pa->reserved || points == NULL) { // reallocate of points array is too small size_t newsize = pa->reserved; if (newsize < pa->len) newsize = pa->len; if (newsize < 1) newsize = 1; newsize += newsize; points = realloc(points, newsize * sizeof(*points); if (points == NULL) return 1; pa->points = points; pa->reserved = newsize; } // append point structure points[pa->len++] = *p; return 0; } int point_array_remove(point_array_t *pa, unsigned int i) { point_t *points; assert(pa); if (i >= pa->len || pa->points == NULL) { //out of bounds or invalid array return 1; } if (pa->len - i > 1) { memmove(&pa->points + i, &pa->points + i + 1, sizeof(*pa->points) * (pa->len - i - 1)); } pa->len--; if (pa->reserved >= pa->len * 3) { size_t newsize = pa->len * 2; // shorten the array with care. // note that the array will be freed when it becomes empty // no special case needed. points = realloc(pa->points, sizeof(*points) * newsize); if (points != NULL) { pa->points = points; pa->reserved = newsize; } } return 0; } 时都收缩。

这是一个更简单的版本:

public static void main(String[] args) {

    //Init your String
    String str = "Your string here !";
    str = str.toLowerCase();

    //Create a table to store number of letters
    int letters [] = new int[ 26 ];

    //For each char in your string
    for(int i = 0; i < str.length(); i++){
        //Transphorm letter to is corect tab index
        int charCode = (int)str.charAt(i)-97;

        //Check if the char is a lettre
        if(charCode >= 0 && charCode < 26 ){
            //Count the letter
            letters[charCode]++;
        }
    }

    //Display the result
    for(int i = 0; i < 26; i ++){
        char letter = (char)(i+65);
        System.out.println("Letter " + letter + " count = " + letters[i]);
    }

}

答案 1 :(得分:1)

除了chqrlie指出的错误之外,还有一些关于代码的其他想法。

对于非调试版本,更好的CFLAGS选择是

-Wall -Wextra -O3

添加-pedantic以获取一些其他警告,您可以将-Ofast与gcc&gt; = 4.6一起使用。

从不realloc指针本身,如果realloc失败,则返回NULL并且您丢失了对原始内存块的引用 - 并因为您不再创建内存泄漏将块的起始地址设为free。在验证len成功之前,请不要递增reservedrealloc。相反,总是使用临时指针并仅在成功时增加值,例如

else if(pa->reserved == pa->len)
{
    void *tmp = realloc(pa->points, sizeof(point_t)*(pa->len + 1) * 2);
    if (!tmp) {
        /* handle error - exit or return */
    }
    pa->points = tmp;
    pa->len+=1;
    pa->reserved=pa->len*2;
}

如果您只是想将数组缩短一个,则以下内容会出现问题:

else if(pa->len ==1)//remove only element
{
    pa->len-=1;//no copying required, just shorten
    pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));
    //free(pa->points);
    //pa->points=NULL;
}
else//array size is longer than 1 or 0
{
    pa->points[i].x = pa->points[pa->len-1].x;
    pa->points[i].y = pa->points[pa->len-1].y;
    pa->points[i].z = pa->points[pa->len-1].z;  
    pa->len-= 1;//shorten array size
    pa->reserved = pa->len*2;
    pa->points=realloc(pa->points, sizeof(point_t)*(pa->len));//could reallocate for reserve here to increase speed.
}   

在上面的else中,你将前一个点分配给最后一个,然后砍掉最后一个 - 要么我不明白你想要完成什么,要么就是做你认为的事。在任何一种情况下,除非你有一些令人信服的理由希望realloc将数组缩短一个(我等待所有添加/删除操作完成,然后在{{1}上调用最终的realloc } element来精确调整你的内存使用量)。相反,我会用以下内容替换上面的全部内容:

len

不需要弄乱任何其他东西。您有效地忽略了最后一行中的数据 - 这不会伤害任何内容,直到您的下一个添加覆盖值。