Функция добавления элемента в динамический массив Си

Пытаюсь написать функцию добавления элемента в целочисленный массив языка Си, требований к эффективности выделяемой памяти нет. Пробовал сделать это двумя способами: с помощью 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 шт):

Автор решения: Konstantin Ruzov

Окончательный код функции:

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 за соответствующее замечание!

→ Ссылка
Автор решения: Stanislav Volodarskiy

Вариант с 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;
}
→ Ссылка