0

I was trying to get my old C-skills up to speed, but I have a small problem with pointers in arrays which are located in structs. Example:

#define ARRAYSIZE 3

struct B;
struct A {
    int val;
};


struct B {
    struct A *ptr;
    int iter;
};


int main() {
    struct A *a1;
    struct A *a2;
    struct A *a3;
    struct A *a_array[3];
    struct B *b_imp;
    short i;

    a1 = malloc(sizeof(struct A));
    a2 = malloc(sizeof(struct A));
    a3 = malloc(sizeof(struct A));

    a1->val = 1;
    a2->val = 2;
    a3->val = 3;

    a_array[0] = a1;
    a_array[1] = a2;
    a_array[2] = a3;

    b_imp = malloc(sizeof(struct B));
    b_imp->ptr = calloc(ARRAYSIZE, sizeof(struct A));
    b_imp->iter = 0;

    for (i = 0; i < ARRAYSIZE; i++) {
        b_imp->ptr[b_imp->iter++] = *a_array[i];
    }

    a3->val = 5;

    for (i = 0; i < b_imp->iter; i++) {
        printf("Value: %d\n", b_imp->ptr[i].val);
    }

    return 0;
}

The problem is, that even though I change a3's value to 5, it still prints 3. And this seems logical too; when I do b_imp->ptr[b_imp->iter++] = *a_array[i]; I think I'm copying the struct. But if try to *b_imp->ptr[b_imp->iter++] = a_array[i]; It just says pointer type required - of course.

What am I doing wrong? How can I just add a pointer to my structs, and not copy them?

3
  • 1
    Have you stepped through your program in a debugger, analysing the values and what gets changed where? Commented Feb 4, 2018 at 17:58
  • I know it's not the focus of the post but please remember to also free any allocated memory at the end of your program Commented Feb 4, 2018 at 18:19
  • You are right, I should have done that, even as an example. It's always good to have good C-manners ;-) Thank you, Commented Feb 4, 2018 at 18:22

2 Answers 2

3

That is not possible to change one variable's instance and make it appear in another in this setup. When you write a3->val = 5; it is making the changes to a different structure instance than that you store inside of struct B instance. So thet won't be reflected in the other variable.

 b_imp->ptr[b_imp->iter++] = *a_array[i];

This line is basically copying member wise the content of the struct A instance.(shallow copy). From this line onwards b_imp->ptr[b_imp->iter++] is different from that of the struct instance in a_array[i].

To make those changes

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define ARRAYSIZE 3

struct B;
struct A {
    int val;
};


struct B {
    struct A **ptr;
    int iter;
};


int main() {
    struct A *a1;
    struct A *a2;
    struct A *a3;
    struct A *a_array[3];
    struct B *b_imp;
    short i;

    a1 = malloc(sizeof(struct A));
    a2 = malloc(sizeof(struct A));
    a3 = malloc(sizeof(struct A));

    a1->val = 1;
    a2->val = 2;
    a3->val = 3;

    a_array[0] = a1;
    a_array[1] = a2;
    a_array[2] = a3;

    b_imp = malloc(sizeof(struct B));
    b_imp->ptr = calloc(ARRAYSIZE, sizeof(struct A*));
    b_imp->iter = 0;

    for (i = 0; i < ARRAYSIZE; i++) {
        b_imp->ptr[b_imp->iter++] = a_array[i];
    }

    a3->val = 5;

    for (i = 0; i < b_imp->iter; i++) {
        printf("Value: %d\n", b_imp->ptr[i]->val);
    }

    return 0;
}

Here instead of a single pointer we used double pointer and allocated memory to it. And then we put the pointer variable itself. Now we are addressing the same thing using these variables. The same should be written like this with all checks:-

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define ARRAYSIZE 3


struct A {
    int val;
};


struct B {
    struct A **ptr;
    int iter;
};


int main(void) {
    struct A *a1;
    struct A *a2;
    struct A *a3;
    struct A *a_array[3];
    struct B *b_imp;
    short i;

    a1 = malloc(sizeof(struct A));
    a2 = malloc(sizeof(struct A));
    a3 = malloc(sizeof(struct A));
    if(!a1 || !a2 || !a3){
        perror("malloc");
        exit(EXIT_FAILURE);
    }

    a1->val = 1;
    a2->val = 2;
    a3->val = 3;

    a_array[0] = a1;
    a_array[1] = a2;
    a_array[2] = a3;

    b_imp = malloc(sizeof(struct B));
    if(!b_imp){
        perror("malloc");
        exit(EXIT_FAILURE);     
    }
    b_imp->ptr = calloc(ARRAYSIZE, sizeof(struct A*));
    if(!b_imp->ptr){
        perror("calloc");
        exit(EXIT_FAILURE);     
    }

    b_imp->iter = 0;

    for (i = 0; i < ARRAYSIZE; i++) {
        b_imp->ptr[b_imp->iter++] = a_array[i];
    }

    a3->val = 5;

    for (i = 0; i < b_imp->iter; i++) {
        printf("Value: %d\n", b_imp->ptr[i]->val);
    }

    for (i = 0; i < b_imp->iter; i++) {
        free(a_array[i]);
        b_imp->ptr[i] = NULL;
    }
    free(b_imp->ptr);
    free(b_imp);

    return 0;
}
Sign up to request clarification or add additional context in comments.

Comments

2

You are right, your a making a copy, you need an array of pointers...

#define ARRAYSIZE 3

struct A {
    int val;
};


struct B {
    struct A **ptr; // Pointer to a pointer (array of A pointers)
    // or struct A *ptr[];
    int iter;
};


int main() {
    struct A *a1;
    struct A *a2;
    struct A *a3;
    struct A *a_array[ARRAYSIZE];
    struct B *b_imp;
    short i;

    a1 = (struct A *)malloc(sizeof(struct A));
    a2 = (struct A *)malloc(sizeof(struct A));
    a3 = (struct A *)malloc(sizeof(struct A));

    a1->val = 1;
    a2->val = 2;
    a3->val = 3;

    a_array[0] = a1;
    a_array[1] = a2;
    a_array[2] = a3;

    b_imp = (struct B *)malloc(sizeof(struct B));
    b_imp->ptr = (struct A **)calloc(ARRAYSIZE, sizeof(struct A *)); // Array entries are now pointers
    b_imp->iter = 0;

    for (i = 0; i < ARRAYSIZE; i++) {
        b_imp->ptr[b_imp->iter++] = a_array[i];
    }

    a3->val = 5;

    for (i = 0; i < b_imp->iter; i++) {
        printf("Value: %d\n", b_imp->ptr[i]->val); // ptr[i] is now a pointer
    }

    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.