1
#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?

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • [Please see this discussion on why not to cast the return value of malloc() and family in C..](https://stackoverflow.com/q/605845/2173917) – Sourav Ghosh Oct 16 '20 at 10:39
  • @SouravGhosh this is not the problem. – Dimitris Dimitriadis Oct 16 '20 at 10:41
  • and that is why that's a comment, not an answer! – Sourav Ghosh Oct 16 '20 at 10:42
  • In the `main` function `container` is a pointer to the first element of an array of `X` structures. You need to pass this pointer *as is* to the function. Basically, what the argument declaration `struct X** a` is saying is that `a` is an array of pointers to `X` structures". But that is not what you are passing in the call. – Some programmer dude Oct 16 '20 at 10:43
  • If you opt to keep passing pointer to pointer, you need to dereference that pointer to get the original pointer: `(*a)[i].deg = 0;` – Some programmer dude Oct 16 '20 at 10:47

5 Answers5

4

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);
    }
}
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
2

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.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • Why? there is no need to use fragmented memory. – David Ranieri Oct 16 '20 at 11:15
  • 1
    @DavidRanieri I guess I somewhat missread the question, returning the pointer is a solid solution, I corrected my answer and posted this allocation done with double pointer as an alternative to your good answer. – anastaciu Oct 16 '20 at 11:46
1

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 );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

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;
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

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
}
Ashish Khurange
  • 903
  • 7
  • 17