【问题标题】:realloc from within a function从函数内重新分配
【发布时间】:2009-04-26 19:06:11
【问题描述】:

我有一个名为 ball 的结构、多个球、一个球数组和一个我想向数组添加新球的函数:

结构:

typedef struct ball{
    BITMAP *image;
    int x;
    int y;
    int vector_x;
    int vector_y;
} ball;

不工作功能):

void add_balls(int *num_balls, ball **myballs){
    num_balls++;
    *myballs = realloc(*myballs, *num_balls * sizeof(ball));
    *myballs[*num_balls-1]->x =  rand() % 640;
    *myballs[*num_balls-1]->y = rand() % 480;
    *myballs[*num_balls-1]->vector_x = rand() % 10;
    *myballs[*num_balls-1]->vector_y = rand() % 10;
    *myballs[*num_balls-1]->image = load_bitmap("blue_ball.bmp", NULL); 
 }

以及main中的函数调用:

add_balls(&num_balls, &myballs);

对函数的调用失败并显示以下错误消息:

p.c: In function ‘add_balls’:
p.c:19: error: invalid type argument of ‘unary *’
p.c:20: error: invalid type argument of ‘unary *’
p.c:21: error: invalid type argument of ‘unary *’
p.c:22: error: invalid type argument of ‘unary *’
p.c:23: error: incompatible types in assignment

有什么帮助吗?

这有帮助。它现在可以编译,但在运行时出现分段错误错误。以下是完整的代码,如果感兴趣的话:

#include <allegro.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#define NUM_BALLS 10

typedef struct ball{
    BITMAP *image;
    int x;
    int y;
    int vector_x;
    int vector_y;
} ball;

void add_balls(int *num_balls, ball **myballs){
    num_balls++;
    myballs = realloc(*myballs, *num_balls * sizeof(ball));
    myballs[*num_balls-1]->x =  rand() % 640;
    myballs[*num_balls-1]->y = rand() % 480;
    myballs[*num_balls-1]->vector_x = rand() % 10;
    myballs[*num_balls-1]->vector_y = rand() % 10;
    myballs[*num_balls-1]->image = load_bitmap("blue_ball.bmp", NULL);  
 }

int main() 
{
    allegro_init();
    install_keyboard();
    srand(time(0));

    install_timer();

    set_color_depth(32);
    set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640,480,0,0);

    BITMAP *buffer = NULL;
    buffer = create_bitmap(640,480);

    ball *myballs;
    myballs  = malloc(NUM_BALLS * sizeof(ball));
    int num_balls = NUM_BALLS;

    BITMAP *bg = NULL;
    bg = load_bitmap("bg.bmp",NULL);

    int i;
    for(i=0;i<num_balls;i++){
        myballs[i].x =  rand() % 640;
        myballs[i].y = rand() % 480;
        myballs[i].vector_x = rand() % 10;
        myballs[i].vector_y = rand() % 10;
        myballs[i].image = load_bitmap("blue_ball.bmp", NULL);
    }

    int bg_vector_x;
    float bg_vector_y;

    while(!key[KEY_ESC]){
        vsync();
        for(i=0;i<num_balls;i++){

            if(myballs[i].x + myballs[i].vector_x > 640 || myballs[i].x + myballs[i].vector_x < 0){
                myballs[i].vector_x *= -1;
            }
            if(myballs[i].y + myballs[i].vector_y > 480 || myballs[i].y + myballs[i].vector_y < 0){
                myballs[i].vector_y *= -1;
            }

            myballs[i].x += myballs[i].vector_x;
            myballs[i].y += myballs[i].vector_y;
        }

        if(key[KEY_UP]){
            add_balls(&num_balls, &myballs);
        }

        clear_bitmap(buffer);

        int ii;     
        for(i=0;i<3;i++){
            for(ii=-1;ii<3;ii++){
                draw_sprite(buffer, bg ,(bg_vector_x%528)+(i*528),100*cos(bg_vector_y) + ii*353);       
            }
        }
        bg_vector_x++;
        bg_vector_y+=0.1;

        for(i=0;i<num_balls;i++){
            draw_sprite(buffer, myballs[i].image, myballs[i].x,myballs[i].y);
        }

        blit(buffer, screen, 0,0,0,0,640,480);
    }

    for(i=0;i<num_balls;i++){
        destroy_bitmap(myballs[i].image);
    }
    free(myballs);
    destroy_bitmap(buffer);
}

END_OF_MAIN()

【问题讨论】:

  • 没看更深,第一个函数行“num_balls++;”应该是“*num_balls++;”。例如。递增整数而不是其地址
  • 您应该始终检查 malloc 和 realloc 的返回值。如果您每次都懒得检查结果,请使用中止程序的包装器。
  • 替换myballs = realloc(*myballs, *num_balls * sizeof(ball));' with *myballs = realloc(*myballs, *num_balls * sizeof(ball));'
  • 它会在 myballs 之前给出带和不带“*”的错误

标签: c malloc


【解决方案1】:

在你修改的add_balls函数中你增加了指针num_balls,你其实是想增加指针的内容,(*num_balls)++;

【讨论】:

    【解决方案2】:
    *myballs[*num_balls-1]->x
    

    这取消引用的次数太多了 - myballs 是一个指针数组(或者换句话说,指向指针数组的第一个元素的指针) - 只是失去 myballs 前面的星号,现在你会得到您可以从中访问结构的指针(或者,将 -> 转换为 .)。符号 -> 是您从指向该结构的指针访问结构成员的简写,而不是从指向该结构的取消引用指针访问。

    myballs[*num_balls-1]->x
    

    (*myballs[*num_balls-1]).x
    

    【讨论】:

    • 不,myballs /is/ 是一个指针数组(或者换句话说,它是一个指向球指针的指针)。如果它是指向指针数组的指针,则声明将是。球***我的球;但是,您提供的代码是正确的。 – Matthew Flaschen 18 秒前 [删除此评论]
    • 如果 myballs 应该是一个指针数组,那么需要另一个级别的间接来允许在该数组增长时重新定位该数组。我的钱在 (*myballs[*num_balls-1]).x 上。
    • 你确定第一个解决方案吗?
    • 我相信我们都错了。根据 bk1e,它是一个指向球数组的 /pointer/,它与 /array of ball pointers/ 具有相同的声明,但含义完全不同......
    • 糟糕,这也是不正确的。它是 (*myballs)[*num_balls-1].x,否则你索引传入的指针而不是它指向的指针。
    【解决方案3】:

    您的原始代码非常接近正确。第一个问题比较容易解决。线

    num_balls++;
    

    应该是

    (*num_balls)++;
    

    您正在递增num_balls 指向的位置,而不是递增它指向的值。当你后来取消引用它时,它指向了其他一些内存——在这个特定的使用场景中,它可能指向堆栈上的下一个局部变量main(),即变量bg

    您的另一个问题是操作顺序问题。数组索引比解引用具有更高的运算符优先级,所以行

    *myballs[*num_balls-1]->x = rand() % 640;
    

    等价于

    *(myballs[*num_balls-1]->x) = rand() % 640
    

    myballs 是指向balls 数组的指针。如果*num_balls-1 不是 0,您将访问错误的内存。无论如何,成员变量x 不是指针,因此您无法取消引用它,因此编译器会在此处将您保存为语法错误。正确的代码是确保在获取数组索引之前取消引用:

    (*myballs)[*num_balls-1].x = rand() % 640;
    

    这就是你想要的,即取消引用指针以获得指向原始数组的指针,获取你想要的元素,并分配给成员变量xadd_ball() 中的其余行应类似地重写。

    您的代码的另一个问题是性能问题而不是正确性问题,那就是您正在为每个球重新加载blue_ball.bmp 图像。 instance 位图最好只加载一次,然后让每个球都引用那个实例,而不是每次都重新加载它。

    【讨论】:

      【解决方案4】:

      除了 Adam Rosenfield 和 Baruch Even 的回答之外,您还应该考虑当 realloc() 失败时会发生什么。

      p = realloc(p, s) 很危险,因为当它失败时,它会将指针设置为NULL,但不会释放它曾经指向的内存。如果发生这种情况,您的程序最终将泄漏原始内存块(如果它没有首先崩溃)。相反,您应该将realloc() 的结果分配给另一个变量,以便您可以处理它返回NULL 的情况。

      【讨论】:

        【解决方案5】:

        假设我了解您要执行的操作,则以下内容(未经测试)会更好:

        #include <assert.h>
        
        void add_balls(int *num_balls, ball **myballs){
            int n = ++*num_balls;
            ball *pb = realloc(*myballs, n * sizeof(ball));
        
            /* fail loudly and early on lack of memory. */
            assert(pb); 
            *myballs = pb;
        
            /* note the pre-decrement of n here to make obvious the common n-1 */
            pb[--n]->x =  rand() % 640;
            pb[n]->y = rand() % 480;
            pb[n]->vector_x = rand() % 10;
            pb[n]->vector_y = rand() % 10;
            pb[n]->image = load_bitmap("blue_ball.bmp", NULL); 
         }
        

        尽管在最初编写的数组元素引用中使用足够的括号可以使所有运算符优先级正确,但结果基本上是人类无法阅读的。事实上,-&gt; 的从右到左的关联性与 * 的从左到右的关联性奇怪地混合在一起,并被数组索引进一步混淆。

        您可能希望改进错误处理。特别是,如果内存不可用,简单地拒绝添加球可能是有意义的。在这种情况下,最好在知道 realloc() 有效之前不要存储新指针(或新计数)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-08-10
          • 2019-05-02
          • 1970-01-01
          • 2020-08-16
          • 2014-01-30
          • 2021-02-09
          • 1970-01-01
          • 2023-03-21
          相关资源
          最近更新 更多