2016-08-24 3 views
3

Я хочу динамически добавлять новые элементы в массив структур. Я добавил минимальный пример того, что segfaults. Я думаю, что мне нужно передать указатель struct data **arr функции перераспределения и добавить новый элемент правильно, но я не смог сделать это правильно.Изменить размер массива структур внутри функции

#include <string.h> 
#include <stdlib.h> 
#include <stdio.h> 

struct data { 
    char s[10]; 
    int arr[10]; 
}; 

void add_one_element(struct data *arr, int *num_elements) 
{ 
    /*increment counter*/ 
    *num_elements +=1; 
    arr = realloc(arr, *num_elements * sizeof(struct data)); 

    strcpy(arr[*num_elements-1].s, "ABC"); 
    for(int i = 0; i < 10; i++) 
    arr[*num_elements-1].arr[i] = i; 
} 


int main() 
{ 
    struct data *arr = NULL; 
    int num_elements = 0; 
    add_one_element(arr, &num_elements); 

    printf("arr.s = %s\n", arr[num_elements-1].s); 
    for(int i = 0; i < 10; i++) 
     printf("arr[%d].arr[%d] = %d\n", num_elements-1, i, arr[num_elements-1].arr[i]); 

    free(arr); 
    return 0; 
} 

EDIT 1: попытался исправить проблему. На этот раз я получаю test(91537,0x7fff79532000) malloc: *** error for object 0x7fff5f5c0ad0: pointer being realloc'd was not allocated. Что свидетельствует о том, что перераспределение не удалось.

#include <string.h> 
#include <stdlib.h> 
#include <stdio.h> 

struct data { 
    char s[10]; 
    int arr[10]; 
}; 

void add_one_element(struct data **arr, int *num_elements) 
{ 
    /*increment counter*/ 
    *num_elements +=1; 
    arr = realloc(arr, *num_elements * sizeof(struct data)); 

    strcpy(arr[*num_elements-1]->s, "ABC"); 
    for(int i = 0; i < 10; i++) 
    arr[*num_elements]->arr[i] = i; 
} 


int main() 
{ 
    struct data *arr = NULL; 
    int num_elements = 0; 
    add_one_element(&arr, &num_elements); 
    printf("arr.s => %s\n", arr[num_elements-1].s); 
    for(int i = 0; i < 10; i++) 
     printf("arr[%d].arr[%d] = %d\n", num_elements-1, i, arr[num_elements-1].arr[i]); 
    return 0; 
} 
+0

Вы 'realloc'ating адрес локальной переменной вместо' 'arr' в main', передать указатель на указатель:' недействительным add_one_element (структура данных ** обр, Int * num_elements) 'и 'add_one_element (& arr, & num_elements);' –

+0

Тщательно перешагнув код в отладчике, вы быстро укажете на эту проблему. –

+0

Что-то вроде 'arr = realloc (arr, ...' очень плохая практика! Что делать, если 'realloc' возвращает нулевой указатель? Вы теряете массив. Первое, что нужно для правильной обработки ошибок! – Olaf

ответ

3

Вы правы, что вам нужно передать в struct data ** вашей функции. Вы бы сделать это следующим образом:

void add_one_element(struct data **arr, int *num_elements) 
{ 
    /*increment counter*/ 
    *num_elements +=1; 
    *arr = realloc(*arr, *num_elements * sizeof(struct data)); 

    strcpy((*arr)[*num_elements-1].s, "ABC"); 
    for(int i = 0; i < 10; i++) 
    (*arr)[*num_elements-1].arr[i] = i; 
} 

Так везде вы ссылки в этой функции будет меняться в *arrarr. Обратите внимание, что это выражение заключено в скобки для доступа к элементам массива.

Затем вызывается функция, как это:

add_one_element(&arr, &num_elements); 
2

С С проходит по значению, вы не можете изменить значение переменной для вызывающей без ссылки. В C это делается путем разыменования указателя на переменную, которую функция должна изменить. Таким образом, вам нужно передать указатель на переменную указателя.

(В отредактированных изменениях, вы не разыменования переменного указателя.)

realloc может возвращать NULL, и в этом случае, оригинальный указатель не был освобожден. Итак, вам нужно зафиксировать и проверить результат или realloc в отдельной переменной.

void add_one_element(struct data **arr, int *num_elements) 
{ 
    struct data *tmp; 
    int tmp_num = *num_elements + 1; 

    /*increment counter*/ 
    tmp = realloc(*arr, tmp_num * sizeof(struct data)); 
    if (tmp == NULL) { 
     /* XXX: handle the error */ 
     return; 
    } 

    strcpy(tmp[tmp_num-1].s, "ABC"); 
    for(int i = 0; i < 10; i++) 
    tmp[tmp_num-1].arr[i] = i; 

    *arr = tmp; 
    *num_elements = tmp_num; 
} 
+0

В качестве альтернативы, можно просто заставить ненормальное завершение, если 'realloc' возвращает null, если таргетинг на реализацию где ar отказ ealloc указывает на то, что неустранимый сбой был, вероятно, неизбежным. – supercat

+0

@supercat: Это правильное решение сделать, но не то, что я хотел бы получить API, который выполняет выполнение распределения. Я предпочел бы, чтобы сбой был зарегистрирован и передан обратно вызывающему абоненту, чтобы решить, требуется ли завершение. – jxh

+0

Если требования программы будут удовлетворены, если она закончится ненормально в случае нехватки памяти, но не будет удовлетворена, если она будет записывать в память, которой она не принадлежит, API-интерфейс распределения, который гарантированно либо успешно, либо завершится ненормально, может облегчить отвечают этим требованиям, чем это было бы возможно по запросу, которое требует, чтобы звонящий проверял каждый звонок на предмет возможного отказа. – supercat

Смежные вопросы