2

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}
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Mark
  • 205
  • 1
  • 4
  • 11
  • 1
    It seems like you are trying to implement a `vector` (in c++ speaking), a dynamic array that grows and shrink on demand. this an ADT, and the shrink and growth can be achieved via `malloc()` and `realloc()` sys calls. – Adam May 29 '20 at 10:37
  • 4
    Read the [*Modern C*](https://modernc.gforge.inria.fr/) book. Show an [MRE] in your question. Read about [C dynamic memory allocation](https://en.wikipedia.org/wiki/C_dynamic_memory_allocation) and [*How to debug small programs*](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) and wikipedia on [segmentation fault](https://en.wikipedia.org/wiki/Segmentation_fault) – Basile Starynkevitch May 29 '20 at 10:37
  • "_such that, the size of the array is changed over time_" - that's not how arrays work in C... you need to manage the memory yourself, extending or shrinking when necessary. – Attie May 29 '20 at 10:41
  • @Adam I've heard about ```malloc``` and ```realloc``` but haven't learnt them yet. Would you mind showing me how to achieve the result using ```malloc``` or ```realloc```? – Mark May 29 '20 at 10:41
  • 2
    You can find similar code examples on StackOverflow; for example https://stackoverflow.com/questions/3536153/c-dynamically-growing-array – pifor May 29 '20 at 10:43
  • 1
    Generally, if you often need to add/remove items at the back or front, then an array isn't very suitable. Linked lists exist for this very reason... eh, but learn about malloc before you do anything else. – Lundin May 29 '20 at 11:41

4 Answers4

1

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++);
Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • This is such an amazing answer @PaulOgilvie . Thank you so so so much for the help. I cound't realize that ```j++``` can be the savior. Again appreciate for your amazing help. Have a nice day sir – Mark Jun 01 '20 at 04:22
1

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);
}
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • 1
    Hi @DavidRaniery . Thank you so so much for your help. It made my day when I see a person who is so willingly and enthusiastic to help me out. Your code looks so beautiful, although there're many things I haven't learnt about yet. You made me realized that. Also, you can check the answer by chqrlie which I've marked as an answer. His solution is very nice and easy to understand. Really appreciate your amazing help. Have a nice day sir – Mark Jun 01 '20 at 04:18
1

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.

Shubham
  • 1,153
  • 8
  • 20
  • 1
    And for the ```Segmentation fault``` error, that's occurring because in ```for( int i=0; ;i++ ) { if(temp[i]) push(tmp, temp[i], sizeof(tmp)/sizeof(tmp[0])); }``` section you are continuing your loop to infinity and you don't have access to ```temp[i]``` or I can say that you have access to access only ```10 * sizeof(int)``` bytes of memory where ```temp``` is stored after that memory block or **array bound** your program can't access the memory so it's throwing a ```Segmentation fault``` error which is equivalent to **Unable to access the memory**. – Shubham May 29 '20 at 12:21
  • 1
    Love your code and explanation about the error . I know what I need to learn more now thanks to you. Really appreciate your amazing and enthusiastic help. Btw, you can have a look at @chqrlie and Paul Ogilvie code, which are very easy to understand and super neat. Have a nice day man – Mark Jun 01 '20 at 04:26
1

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;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Thank you so so so much . The code is pretty much explanatory and very easy to understand. I can acknowledge what I need to learn more now. Again really appreciate you for the help. Have a nice day my sir – Mark Jun 01 '20 at 04:12
  • Also, the solution by @PaulOgilvie is super easy to understand and very nice. You can have a look at his code. – Mark Jun 01 '20 at 04:24