Dimitris Dimitriadis
Dimitris Dimitriadis

Reputation: 163

Passing array of structs with reference - segmentation fault

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

struct X {
    char surname[30];
    int deg;
};

void read_record(struct X** a, int size){
    for (int i = 0;i < size; i++){
        a[i]->deg = 0;
    }
}

int main(){
    int n = 10;
    struct X *container = (struct X*)malloc(sizeof(struct X) * n);
    read_record(&container, n);
}

I created a 1D array of size n, then I passed it by reference to the function read_record. However, when I execute the program, there is a segmentation fault. What is the problem?

EDIT:

As a next step, I want to reallocate the array of 10 elements in the function with size of 20. That's why I want to send the array as a reference. If I did it in main then I would write:

container = realloc(container, (n + 10) * sizeof(Struct X));

How can I do this in the function?

Upvotes: 1

Views: 411

Answers (5)

anastaciu
anastaciu

Reputation: 23802

As a next step, I want to reallocate the array of 10 elements in the function with size of 20. (in the function). That's why I want to send the array as a reference.

The pointer is passed by value, so to save the changes and have them usable outside the function scope, after the function ends, i.e. in main, a pointer to pointer must be the argument, and the address of the pointer must be passed, your overall assessment is correct.

Your implementation, however, is not correct, here's how you shoud do it:

Live demo

void read_record(struct X **a, int size) //double pointer
{
    *a = realloc(*a, sizeof **a * (size + 10)); //reallocate memory for 20 ints

    if (*a == NULL)
    {
        perror("malloc");
    }

    for (int i = 0; i < size + 10; i++) //assing new values
    {
        (*a)[i].deg = 1;
    }
}
int main()
{
    int n = 10;
    struct X *container = malloc(sizeof *container * n); //original allocation
    //the pointer now has space for 10 ints

    if (container == NULL)
    { //check allocation errors
        perror("malloc");
    }

    for (int i = 0; i < n; i++) //assign values
    {
        container[i].deg = 0;
    }
    
    read_record(&container, n); //pass by reference
    //the pointer now has space for 20 ints
    
}

Alternatively you can return the pointer instead, refering to David Ranieri's answer.

Upvotes: 2

David Ranieri
David Ranieri

Reputation: 41017

container is already a pointer, you don't need to pass the address-of the pointer, instead:

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

struct X {
    char surname[30];
    int deg;
};

void read_record(struct X *a, size_t size)
{
    for (size_t i = 0; i < size; i++) {
        a[i].deg = 0;
    }
}

int main(void)
{
    size_t n = 10;
    struct X *container = malloc(sizeof(struct X) * n);

    read_record(container, n);
}

also, prefer size_t to store the number of allocated objects.

Nitpick: read_record doesn't seem a good name for a function that modifies the contents of the records.

EDIT: As a next step, I want to reallocate the array of 10 elements in the function with size of 20. (in the function). That's why I want to send the array as a reference.

Same approach but returning a reallocated container:

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

struct X {
    char surname[30];
    int deg;
};

struct X *read_record(struct X *a, size_t size)
{
    struct X *new = realloc(a, sizeof(struct X) * size);

    if (new != NULL)
    {
        for (size_t i = 0; i < size; i++) {
            new[i].deg = 0;
        }
    }
    return new;
}

int main(void)
{
    size_t n = 10;
    struct X *container = malloc(sizeof(struct X) * n);

    container = read_record(container, n * 2);
    if (container == NULL)
    {
        fprintf(stderr, "Can't read record\n");
        exit(EXIT_FAILURE);
    }
}

Upvotes: 4

Some programmer dude
Some programmer dude

Reputation: 409196

When you call read_record you pass a pointer to a pointer to the first element of an array of X structures.

But inside the read_record you treat it as a pointer to the first element of an array of pointers to X structures (i.e. as an array of pointers to X). There's a subtle but very important difference here.

If you want to emulate pass-by-reference for the pointer variable, you need to dereference it inside the read_record to get the original pointer (and remember that then you have an array of objects, not pointers):

(*a)[i].deg = 0;

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 310980

The first function parameter has the pointer to pointer type struct X**. So dereferencing the parameter a you will get a pointer of the type struct X*. Now you may apply the subscript operator that yields lvalue of the type struct X..

That is the function definition will look like

void read_record(struct X** a,int size){
    for (int i=0;i<size;i++){
        ( *a )[i].deg = 0;
    }
}

Or this statement

( *a )[i].deg = 0;

may be substituted for this statement

a[0][i].deg = 0;

On the other hand, there is no great sense to declare the first parameter as having the type struct X**. The function can look simpler as for example

void read_record(struct X* a,int size){
    for (int i=0;i<size;i++){
        a[i].deg = 0;
    }
}

and be called like

read_record( container, n );

Upvotes: 1

Ashish Khurange
Ashish Khurange

Reputation: 923

Double pointer is the problem. The code should be:

void read_record(struct X* a,int size){ // Check the change
    for (int i=0;i<size;i++){
        a[i]->deg = 0;
    }
}

int main(){
    int n = 10;
    struct X *container=(struct X*)malloc(sizeof(struct X)*n);
    read_record(container,n); // Check the change
}

Upvotes: 0

Related Questions