Функция добавления элемента в динамический массив Си
Пытаюсь написать функцию добавления элемента в целочисленный массив языка Си, требований к эффективности выделяемой памяти нет. Пробовал сделать это двумя способами: с помощью realloc() или вручную (т.е. с помощью free() и malloc()). В обоих случаях при прогоне программы через valgrind он выдавал одну и ту же ошибку:
Conditional jump or move depends on uninitialised value(s)
Вот код этой функции:
void add_item(int ** p_arr, int * size, int item)
{
printf("\nThe array is expanded by 1 item.\n");
// First way.
/* ++(*size);
*p_arr = (int *) realloc(*p_arr, *size * sizeof(int));
assert(*p_arr);
*p_arr[*size - 1] = item;
*/
// Second way.
int copy_arr[*size];
arr_cpy(copy_arr, *p_arr, *size);
free(*p_arr);
++(*size);
*p_arr = (int *) malloc(*size * sizeof(int));
assert(*p_arr);
arr_cpy(*p_arr, copy_arr, *size - 1);
*p_arr[*size - 1] = item;
}
Здесь используется вспомогательная функция arr_cpy(), которая копирует size элементов из массива arr_2 в массив arr_1:
void arr_cpy(int arr_1[], const int arr_2[], int size)
{
int i;
for (i = 0; i < size; ++i)
{
arr_1[i] = arr_2[i];
}
}
Вот более подробный вывод valgrind:
==7630== Conditional jump or move depends on uninitialised value(s)
==7630== at 0x4E9A8AA: vfprintf (vfprintf.c:1642)
==7630== by 0x4EA2EE5: printf (printf.c:33)
==7630== by 0x108A7E: main (dynamic_array.c:56)
==7630== Uninitialised value was created by a heap allocation
==7630== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7630== by 0x108E6B: add_item (dynamic_array.c:161)
==7630== by 0x108A4C: main (dynamic_array.c:55)
==7630==
==7630== Use of uninitialised value of size 8
==7630== at 0x4E9683B: _itoa_word (_itoa.c:179)
==7630== by 0x4E99EDD: vfprintf (vfprintf.c:1642)
==7630== by 0x4EA2EE5: printf (printf.c:33)
==7630== by 0x108A7E: main (dynamic_array.c:56)
==7630== Uninitialised value was created by a heap allocation
==7630== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7630== by 0x108E6B: add_item (dynamic_array.c:161)
==7630== by 0x108A4C: main (dynamic_array.c:55)
==7630==
==7630== Conditional jump or move depends on uninitialised value(s)
==7630== at 0x4E96845: _itoa_word (_itoa.c:179)
==7630== by 0x4E99EDD: vfprintf (vfprintf.c:1642)
==7630== by 0x4EA2EE5: printf (printf.c:33)
==7630== by 0x108A7E: main (dynamic_array.c:56)
==7630== Uninitialised value was created by a heap allocation
==7630== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7630== by 0x108E6B: add_item (dynamic_array.c:161)
==7630== by 0x108A4C: main (dynamic_array.c:55)
==7630==
==7630== Conditional jump or move depends on uninitialised value(s)
==7630== at 0x4E99FE4: vfprintf (vfprintf.c:1642)
==7630== by 0x4EA2EE5: printf (printf.c:33)
==7630== by 0x108A7E: main (dynamic_array.c:56)
==7630== Uninitialised value was created by a heap allocation
==7630== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7630== by 0x108E6B: add_item (dynamic_array.c:161)
==7630== by 0x108A4C: main (dynamic_array.c:55)
==7630==
==7630== Conditional jump or move depends on uninitialised value(s)
==7630== at 0x4E9AB1C: vfprintf (vfprintf.c:1642)
==7630== by 0x4EA2EE5: printf (printf.c:33)
==7630== by 0x108A7E: main (dynamic_array.c:56)
==7630== Uninitialised value was created by a heap allocation
==7630== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7630== by 0x108E6B: add_item (dynamic_array.c:161)
==7630== by 0x108A4C: main (dynamic_array.c:55)
==7630==
Проблема в том, что после вывода содержимого массива последний элемент равен нулю (хотя добавлялся не ноль). Значение последнего элемента внутри функции совпадает с тем, что вводил, а после работы функции оно равно нулю, причём адреса соответствующих ячеек памяти внутри и вне функции совпадают (проверял операторами вывода printf()). С чем связана проблема, объясните, пожалуйста.
Ответы (2 шт):
Окончательный код функции:
void add_item(int ** p_arr, int * size, int item)
{
printf("\nThe array is expanded by 1 item.\n");
// First way.
/* ++(*size);
*p_arr = (int *) realloc(*p_arr, *size * sizeof(int));
assert(*p_arr);
(*p_arr)[*size - 1] = item;
*/
// Second way.
int copy_arr[*size];
arr_cpy(copy_arr, *p_arr, *size);
free(*p_arr);
++(*size);
*p_arr = (int *) malloc(*size * sizeof(int));
assert(*p_arr);
arr_cpy(*p_arr, copy_arr, *size - 1);
(*p_arr)[*size - 1] = item;
}
Проблема была из-за невнимательности: так как приоритет унарной операции (*) ниже, чем операции ([]), то выражение с разыменованием необходимо заключать в круглые скобки, то есть необходимо было исправить строку кода
*p_arr[*size - 1] = item;
на
(*p_arr)[*size - 1] = item;
Основная идея функции верная.
Теперь код работает обоими способами, проверено в т.ч. через valgrind.
Хочу отдельно поблагодарить @AlexGlebe за соответствующее замечание!
Вариант с realloc:
bool add_item(int ** p_arr, int * size, int item)
{
printf("\nThe array is expanded by 1 item.\n");
int *new_arr = realloc(*p_arr, (*size + 1) * sizeof(**p_arr));
if (new_arr == NULL)
{
return false;
}
*p_arr = new_arr;
p_arr[*size] = item;
++*size;
return true;
}
Вариант с malloc/free:
bool add_item(int ** p_arr, int * size, int item)
{
printf("\nThe array is expanded by 1 item.\n");
int *new_arr = malloc((*size + 1) * sizeof(**p_arr));
if (new_arr == NULL)
{
return false;
}
arr_cpy(new_arr, *p_arr, *size);
free(*p_arr);
*p_arr = new_arr;
p_arr[*size] = item;
++*size;
return true;
}