How to insert an element at the last position in an array dynamically in C?

Viewed 4743

I'm a newbie in C and trying to insert a number at the last position in C such that, the size of the array is changed over time.

The first array is like this:

temp[10] = {1, 2, 0, 0, 5, 6, 0, 8, 0, 0};

Now how can we insert those values in temp that are != 0 to a new array which has a defined length of 5: tmp

Here's what I'm trying:

void push(int arr[], int value, int current){
  arr[current] = value;
}

int main(void) { 

  int temp[10] = {1, 2, 0, 0, 5, 6, 0, 8, 0, 0};
  int tmp[5];

  for(int i=0;;i++){
    if(temp[i]) push(tmp, temp[i],sizeof(tmp)/sizeof(tmp[0]));
    // I don't put i < 10 int the loop initialization. Since the tmp's length is just 5

  }
  // I put sizeof(tmp)/sizeof(tmp[0]) there because we want at every time the tmp is inserted a new value,
  // it's length will change (increase by 1). 
  // So next loop round, the new value will be added to the last position
  // But I failed to do so

} 

Current output:

exited segmentation fault
// I'd be very grateful if someone can also explain for me why this error happens

Desired output:

tmp[5] = {1, 2, 5, 6, 8}
4 Answers

Your definition of push requires the 3rd parameter to be the position to place the element. However, the expression sizeof(tmp)/sizeof(tmp[0]) provides the size of the array and it will thus index past the array's last element (because indexes in C go from 0..n-1). This in itself can cause a segmentation fault.

Neither did you provide a terminating condition for your loop. It will continue forever.

Your solution could simply be:

for(int i=0, j=0; i<10; i++)
   if(temp[i]) push(tmp, temp[i], j++);

The problem is that you are accessing outside of the bounds of temp because you don't specify when to stop in the loop.

For a dynamic array, when you don't know the number of elements before hand, you can use realloc:

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

void push(int **arr, size_t *size, int value)
{
    int *ptr = realloc(*arr, sizeof(*ptr) * (*size + 1));

    if (ptr == NULL)
    {
        free(*arr);
        perror("push");
        exit(EXIT_FAILURE);
    }
    ptr[*size] = value;
    *size += 1;
    *arr = ptr;
}

int main(void)
{ 
    int temp[10] = {1, 2, 0, 0, 5, 6, 0, 8, 0, 0};
    int *arr = NULL;
    size_t size = 0;

    for (size_t i = 0; i < sizeof temp / sizeof temp[0]; i++)
    {
        if (temp[i])
        {
            push(&arr, &size, temp[i]);
        }
    }
    for (size_t i = 0; i < size; i++)
    {
        printf("%d\n", arr[i]);
    }
    free(arr);
} 

Notice that even if it ilustrates the use of a growable array, this example is considered bad code, a more robust design will take care of the size and is able to manage the allocation and deallocation by itself:

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

typedef struct
{
    int *data;
    size_t size;
}  dyn_array;

dyn_array *create_dyn_array(void)
{
    dyn_array *arr = calloc(1, sizeof *arr);

    if (arr == NULL)
    {
        perror("create_dyn_array");
        exit(EXIT_FAILURE);
    }
    return arr;
}

void destroy_dyn_array(dyn_array *arr)
{
    free(arr->data);
    free(arr);
}

void push_dyn_array(dyn_array *arr, int value)
{
    int *ptr = realloc(arr->data, sizeof(*ptr) * (arr->size + 1));

    if (ptr == NULL)
    {
        destroy_dyn_array(arr);
        perror("push_dyn_array");
        exit(EXIT_FAILURE);
    }
    ptr[arr->size++] = value;
    arr->data = ptr;
}

int main(void)
{ 
    int temp[10] = {1, 2, 0, 0, 5, 6, 0, 8, 0, 0};
    dyn_array *arr = create_dyn_array();

    for (size_t i = 0; i < sizeof temp / sizeof temp[0]; i++)
    {
        if (temp[i])
        {
            push_dyn_array(arr, temp[i]);
        }
    }
    for (size_t i = 0; i < arr->size; i++)
    {
        printf("%d\n", arr->data[i]);
    }
    destroy_dyn_array(arr);
}

This is better, but still not perfect because we are limited to a fixed type (int), with some extra memory we can adapt our code to support any type (using the generic type void *), as an optimization, the dynamic array grows by a factor of 2 instead of calling realloc on each iteration:

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

/* Begin - This API is supposed to be in a header */

typedef struct
{
    void **data;
    size_t room;
    size_t size;
}  dynarray;

dynarray *dynarray_create(void)
{
    dynarray *array = calloc(1, sizeof *array);

    if (array == NULL)
    {
        return NULL;
    }
    array->data = malloc(sizeof(void *));
    if (array->data == NULL)
    {
        free(array);
        return NULL;
    }
    array->room = 1;
    return array;
}

void *dynarray_push(dynarray *array, void *data)
{
    if (data == NULL)
    {
        return NULL;
    }
    if (array->size == array->room)
    {
        array->room *= 2;

        void *ptr = realloc(array->data, array->room * sizeof(void *));

        if (ptr == NULL)
        {
            return NULL;
        }
        array->data = ptr;
    }
    array->data[array->size++] = data;
    return data;
}

void *dynarray_get(dynarray *array, size_t index)
{
    return array->data[index];
}

size_t dynarray_size(dynarray *array)
{
    return array->size;
}

void dynarray_destroy(dynarray *array, void (*func)(void *data))
{
    if (func != NULL)
    {
        for (size_t i = 0; i < array->size; i++)
        {
            func(array->data[i]);
        }
    }
    free(array->data);
    free(array);
}

/* End API */

int main(void)
{ 
    int temp[10] = {1, 2, 0, 0, 5, 6, 0, 8, 0, 0};
    dynarray *array = dynarray_create();

    if (array == NULL)
    {
        perror("dynarray_create");
        exit(EXIT_FAILURE);
    }
    for (size_t i = 0; i < sizeof temp / sizeof temp[0]; i++)
    {
        if (temp[i])
        {
            if (dynarray_push(array, &temp[i]) == NULL)
            {
                perror("dynarray_push");
                exit(EXIT_FAILURE);
            }
        }
    }

    size_t size = dynarray_size(array);

    for (size_t i = 0; i < size; i++)
    {
        int *data = dynarray_get(array, i);

        printf("%d\n", *data);
    }
    dynarray_destroy(array, NULL);
}

What is

trying to insert a number at the last position in C

Although you got an explanatory answer by David Ranieri, I want to show the simplest approach keeping in mind that you are a newbie. Look at this code:-

Code:

#include <stdio.h>
#include <stdlib.h>
#define SIZE 10

void push(int arr[], int value, int current){
  arr[current] = value;
}

int main(void) {

  int temp[SIZE] = {1, 2, 0, 0, 5, 6, 0, 8, 0, 0};
  int *tmp = NULL, count = 0, i, j;

  // Loop for counting non-zero values in temp array.
  for (i = 0; i < SIZE; ++i)
    if (temp[i])
        count++;

  // Dynamically allocating space for tmp along with checking errors if any.
  if((tmp = (int *)malloc(count * sizeof(int))) == NULL)
     {
         printf("Memory Not Available.\n");
         exit(-1);
     }

  for(i = 0, j = 0; i < SIZE; i++){
    if(temp[i])
        push(tmp, temp[i], j++);
  }

  // Printing your desired output.
  for(i = 0; i < count; ++i)
    printf("%d\t", tmp[i]);

    free(tmp);
}

Here the size of tmp will change according to the non-zero elements of temp. Now if you want to make the temp array a dynamic array, use the same approach as used for the tmp, just ask the user at run-time to input the size of temp and then its elements/values as well.

C does not have dynamic arrays. Arrays have a fixed size determined from their definition. You can allocate objects with malloc() that behave as arrays, but you must keep track of their allocated size separately. Appending an element requires reallocating the array so its address in memory may change.

In your code, tmp has a fixed size of 5 elements. You could maintain an index specifying how many elements are used and update that in the push function:

#include <stdio.h>

int push(int arr[], int value, size_t size, size_t *current) {
    if (*current < size) {
        arr[(*current)++] = value;
        return 0;
    } else {
        /* array is full */
        return -1;
    }
}

int main(void) { 
    int temp[10] = { 1, 2, 0, 0, 5, 6, 0, 8, 0, 0 };
    int tmp[5];
    size_t pos = 0;

    for (size_t i = 0; i < sizeof(temp) / sizeof(temp[0]); i++) {
        if (temp[i])
            push(tmp, temp[i], sizeof(tmp) / sizeof(tmp[0]), &pos);
    }

    printf("tmp:");
    for (size_t i = 0; i < pos; i++) {
        printf(" %d", tmp[i]);
    }
    printf("\n");
    return 0;
}
Related