перераспределить изнутри функции - PullRequest
1 голос
/ 26 апреля 2009

У меня есть структура с именем 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()

Ответы [ 5 ]

4 голосов
/ 27 апреля 2009

В вашей модифицированной функции add_balls вы увеличиваете указатель num_balls, вы действительно хотите увеличить содержимое указателя, (* num_balls) ++;

3 голосов
/ 27 апреля 2009

Ваш исходный код был очень близок к правильности. Первая проблема относительно проста для решения. Линия

num_balls++;

должно быть

(*num_balls)++;

Вы увеличивали местоположение, на которое указывал num_balls, вместо того, чтобы увеличивать значение, на которое оно указывало. Когда вы позже разыменовали его, он указывал на какой-то другой фрагмент памяти - в этом конкретном сценарии использования он, вероятно, указывал на следующую локальную переменную в стеке в main(), переменную bg.

Другая ваша проблема - это проблема порядка операций. Индексирование массива имеет более высокий приоритет оператора, чем разыменование, поэтому строка

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

эквивалентно

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

myballs - указатель на указатель на массив ball с. Если *num_balls-1 отличается от 0, вы получите доступ к неправильной памяти. В любом случае, переменная-член x не является указателем, поэтому вы не можете разыменовать ее, поэтому компилятор спасет вас здесь с синтаксической ошибкой. Правильный код, чтобы убедиться, что вы разыменовываете перед взятием индекса массива:

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

Это делает то, что вы хотите, а именно: разыменование указателя для получения указателя на исходный массив, получение нужного элемента и присвоение переменной-члену x. Остальные строки в add_ball() должны быть переписаны аналогично.

Еще одна проблема с вашим кодом, которая больше связана с производительностью, чем с правильностью, заключается в том, что вы перезагружаете изображение blue_ball.bmp для каждого шара. Было бы лучше экземпляр растрового изображения, загрузив его только один раз, а затем каждый шарик ссылаться на этот экземпляр вместо того, чтобы каждый раз перезагружать его.

3 голосов
/ 27 апреля 2009

В дополнение к ответам Адама Розенфилда и Баруха Эвена, вы должны учитывать, что происходит, когда realloc() терпит неудачу.

p = realloc(p, s) опасно, потому что при неудаче он устанавливает указатель на NULL, но не освобождает память, на которую он указывал. Если это произойдет, ваша программа утечет исходный блок памяти (если он не потерпит крах вначале). Вместо этого вы должны присвоить результат realloc() другой переменной, чтобы вы могли обработать случай, когда он возвращает NULL.

3 голосов
/ 26 апреля 2009
*myballs[*num_balls-1]->x

Это разыменовывается слишком много раз - myballs - это массив указателей (или, иначе говоря, указатель на первый элемент массива указателей) - просто потеряйте звездочку перед myballs, и теперь вы получите указатель, из которого вы можете получить доступ к структуре (или, альтернативно, превратить -> в.). Символ -> является кратким обозначением доступа к членам структуры из указателя на эту структуру, а не из разыменованного указателя на структуру.

myballs[*num_balls-1]->x

или

(*myballs[*num_balls-1]).x
2 голосов
/ 27 апреля 2009

Если я понимаю, что вы пытаетесь сделать, что-то вроде следующего (непроверенного) лучше:

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

Хотя было бы возможно получить правильный приоритет всех операторов с достаточным количеством скобок в ссылках на элементы массива, как было написано изначально, результат будет по существу нечитаемым для людей. Как и было раньше, ассоциативность справа налево -> странным образом смешивается с ассоциативностью слева направо * и еще больше путается с индексом массива.

Возможно, вы захотите улучшить обработку ошибок. В частности, может иметь смысл просто вообще отказаться от добавления мяча, если память не была доступна. В этом случае было бы лучше не хранить новый указатель (или новый счетчик) до тех пор, пока он не сработает. realloc().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...