2016-10-21 4 views
0

У меня возникли проблемы с поиском правильного способа мутации элементов массива вложенных структур в C99 (после передачи массива другой функции). Массив объявляется внутри main() - так ли это в стеке, я полагаю? - и я могу просто показать соответствующие части кода.Каков правильный способ обновления массива вложенных структур C?

typedef struct 
{ 
    uint32_t x, y; 
} point; 

typedef struct 
{ 
    uint32_t r, g, b, a; 
} rgba; 

typedef struct 
{ 
    point a, b, c; 
    rgba p; 
} tri; 

Это структуры; вот как создать случайный треугольник (способность мне нужно в других частях программы):

tri triangle() 
{ 
    tri t; 

    t.a.x = rand() % xres; 
    t.a.y = rand() % yres; 
    t.b.x = rand() % xres; 
    t.b.y = rand() % yres; 
    t.c.x = rand() % xres; 
    t.c.y = rand() % yres; 
    t.p.r = rand() % 256; 
    t.p.g = rand() % 256; 
    t.p.b = rand() % 256; 
    t.p.a = rand() % 256; 

    return t; 
} 

А вот часть (несколько строк, вы можете проигнорировать), где я объявляю и населяющие массив:

int main(int argc, char *argv[]) 
{ 
    init(argc, argv[1]); 

    uint32_t img[xres][yres]; 

    copyInputImage(img); 

    srand(time(NULL)); 

    tri trigons[NUM_TRIGONS]; 

    for (int i = 0; i < NUM_TRIGONS; i++) 
    { 
     trigons[i] = triangle(); 
    } 

Все, что успешно доставит мне массив случайных треугольников.

Теперь вот моя проблема: в зависимости от параметров программы, иногда я хочу немедленно повторно заполнить массив данными из текстового файла. Формат текста прост: один символ без знака в каждой строке. Вот пример того, что данные для одного пикселя выглядит в этом файле:

0 
40 
0 
5 
37 
31 
134 
64 
167 
100 

Вот пример 10 примитивов треугольник структура в конечном счете нуждается. Все числовые строки имеют 3 цифры или меньше, но мой цикл с fgets() включает в себя буфер с 5 символами, который позволяет использовать терминатор линии и нулевой терминатор.

Функция, которая делает это выглядит следующим образом:

void read_SVG(char* filename, tri* ts) 
{ 
    FILE* fp = fopen(filename, "r"); 

    char str[5]; 

    for (int i = 0; i < NUM_TRIGONS; i++) 
    { 
     tri *t = &(ts[i]); 
     point *a = &(ts[i]).a; 
     point *b = &(ts[i]).b; 
     point *c = &(ts[i]).c; 
     rgba *p = &(ts[i]).p; 

     while (fgets(str, 5, fp) != NULL) 
     { 
      a->x = (uint32_t) strtol(str, NULL, 10); 
      a->y = (uint32_t) strtol(str, NULL, 10); 
      b->x = (uint32_t) strtol(str, NULL, 10); 
      b->y = (uint32_t) strtol(str, NULL, 10); 
      c->x = (uint32_t) strtol(str, NULL, 10); 
      c->y = (uint32_t) strtol(str, NULL, 10); 
      p->r = (uint32_t) strtol(str, NULL, 10); 
      p->g = (uint32_t) strtol(str, NULL, 10); 
      p->b = (uint32_t) strtol(str, NULL, 10); 
      p->a = (uint32_t) strtol(str, NULL, 10); 
     } 
     t->a = *a; 
     t->b = *b; 
     t->c = *c; 
     t->p = *p; 
    } 
    fclose(fp); 
} 

И, наконец, это то, как я призываю read_SVG(), показанный в его контексте в основной().

if (argc > 2) 
{ 
    read_SVG(argv[2], trigons); 

    draw(screen, trigons); 
    SDL_Flip(screen); 
    SDL_SaveBMP(screen, "wtf.bmp"); 
    exit(0); 
} 

Когда выходной массив внутри этого read_SVG(), это выглядит как данные, которые я ожидал. Но когда я исследую его в main(), он выглядит как случайные значения, которые я изначально заселял.

Таким образом, кажется, что начальный массив никогда не обновлялся. Что я сделал неправильно, и может ли кто-нибудь предложить общий лучший способ обработки этого типа вложенных проблем с передачей и обновлением структуры?

Я уверен, что многие вопросы об этом были заданы; Я могу только надеяться, что мой будет достаточно всеобъемлющим, чтобы помочь некоторым другим новичкам C. Я попытался широкий спектр подходов, прежде чем прийти сюда за помощью ...

+0

Одна из неправильных вещей в этом коде возвращается в локальную структуру от 'треугольника()'. Локальные вары создаются в стеке, и после возврата из функции его стоп-фрейм отбрасывается. Лучше передать указатель на структуру в качестве параметра. – hidefromkgb

+4

Он возвращает структуру, которая будет выполнена через копию в вызывающем коде. он неэффективен, но фактически ничем не отличается от возврата локального int. – kdopen

+0

Право; У меня есть -Wall, и я обычно получаю ошибку, когда возвращаю то, что будет потеряно после того, как кадр стека будет отброшен. Код, который я опубликовал в этом вопросе, не вызывает никаких предупреждений даже с -Wextra. – sacheie

ответ

2

Эта часть является излишним, так как а, Ь, с и р уже указывает на ту же память, что указывает на т:

t->a = *a; 
    t->b = *b; 
    t->c = *c; 
    t->p = *p; 

Но это всего лишь nitpick. ;) важный комментарий, вероятно, что fgets() будет читать все содержимое файла для первого элемента и ничего не читать для остальных элементов. Может быть, s/while/if /?

Редактировать, чтобы уточнить: Вот альтернативная версия функции read_SVG():

void read_SVG(char* filename, tri* ts) 
{ 
    FILE* fp = fopen(filename, "r"); 
    char str[5]; 
    int i = 0; 

    while (i < NUM_TRIGONS && fgets(str, 5, fp) != NULL) { 
     uint32_t val = (uint32_t) strtol(str, NULL, 10); 
     ts[i].a.x = ts[i].a.y = 
     ts[i].b.x = ts[i].b.y = 
     ts[i].c.x = ts[i].c.y = 
     ts[i].p.r = ts[i].p.g = ts[i].p.b = ts[i].p.a = val; 

     i++; 
    } 
    fclose(fp); 
} 
+0

Насколько я понимаю, 'fgets' возвращает одну строку и автоматически перебирается к следующей строке при следующем вызове? Поэтому я просто петлю, пока не вернет NULL, обозначая конец файла. Если вы говорите правду, почему я могу изменить функцию, чтобы заполнить свой собственный массив, который при распечатке покажет именно то, что я хочу: одно число на строку, идеально соответствующее введенному мной текстовому вводу. – sacheie

+2

Ваше понимание fgets() верное. Проблема в том, что индекс в три-массив увеличивается за пределами цикла while. Поскольку while-loop считывает все данные из файла для элемента # 1 (i == 0), функция fgets() возвращает NULL для всех других значений i. –

+0

Я вижу вашу точку зрения, и мне нравится ваша идея. Но я пробовал вставлять в вашу версию; Я также попробовал добавить 'rewind (fp)' для моего, и в обоих случаях у меня получилось полностью черное выходное изображение, а не то, которое должен был создать текстовый файл. – sacheie