Able to print the expected results of an array of pointers to structures that are not put online - Confused

advertisements

My issue is with nums3 array and why the last for-loop actually prints expected results. The nums3 array as I understand it contains an array of pointers to the struct but these pointers have not yet been initialized to any specific instance of a struct. But in this for-loop I can assign values and see the expected results display.

Also, I've read that with the pointer returned by malloc I can use the [index] after the pointer and iterate over the allocated memory. I assume this feature is using the fact it has a type multiplied by some value and it does the division automatically to know how this block of memory is split up and therefore how far to advance to the next index. But I'm still confused as to why I'm getting expected results on that last for-loop when I haven't initialized or pointed those pointers to anything specific.

I know that if I were to add ** and change the to -> then I could operate on those pointers directly, but with the code now I'm able to use the . operator to access structure members. So, without the ** in the nums3 malloc line, what exactly does the pointer returned from malloc return?

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

#define ARRAY_MAX 5

int main(void)
{
    struct number {
        int num1;
        int num2;
        int num3;
    };

    struct number n;
    n.num1 = 5;
    printf("n.num1: %d\n", n.num1);
    n.num2 = 6;
    printf("n.num2: %d\n", n.num2);
    n.num3 = 7;
    printf("n.num3: %d\n", n.num3);

    struct number nums1[5];

    struct number* nums2 = malloc(sizeof(struct number) * ARRAY_MAX);
    struct number* nums3 = malloc(sizeof(struct number*) * ARRAY_MAX);

    int x;
    for(x = 0; x <= 5; x++) {
        nums1[x].num1 = x;
        nums1[x].num2 = x;
        nums1[x].num3 = x;
    }

    int y;
    for(y = 0; y <= ARRAY_MAX; y++) {
        nums2[y].num1 = x;
        nums2[y].num2 = x;
        nums2[y].num3 = x;
    }

    for(y=0; y<=ARRAY_MAX; y++) {
        nums3[y].num1 = y;
        nums3[y].num2 = y;
        nums3[y].num3 = y;
        printf("%d ", nums3[y].num1);
        printf("%d ", nums3[y].num2);
        printf("%d \n", nums3[y].num3);
    }

Here is a simpler test case of my question:

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

#define MAX 5

int main(void)
{
    struct number {
        int num1;
    };

    struct number* n = malloc(sizeof(struct number*) * MAX);

    int i;
    for(i=0; i<MAX; i++) {
        n[i].num1 = i;
        printf("%d\n", n[i]);
    }

    free(n);

}

Result of running this code:

jason [email protected] ~/src/c $ ./a.exe 0 1 2 3 4

jason [email protected] ~/src/c $

Questions:

  1. How does n[i] work with n being the pointer returned from malloc? How does C know what to do with n[i]? How does C know how to get to n[i+1]? Does it look at what sizeof() is being called on and divide by however many times it is multiplied and use that result to know where the next cell starts?

  2. Why does n[i].num1 = i; even compile? If all I have done is specify a block of memory containing size for x number of pointers to the struct (pointers which would be smaller than the size of the struct itself) and certainly have not initialized anything to point to an actual instance of this struct. Why isn't this a syntax or some other compiler generated error? What exists at cell n[i] that .num1 is working on? Doesn't n[i] right now just contain a pointer without a valid address since it's not yet initialized? How do we go from that to n[i].num1 = i? Is it valid syntax to do "some memory address".num1 = "some value"?

I understand this is not the correct way to do this, and you all have provided great information, but I'm still puzzled as to why this code even compiles. It just doesn't make sense to me.


In general, if you access memory incorrectly you cannot expect anything. You can't expect to get the right answer, and you can't expect to get the wrong answer. You can't expect your program to crash and you can't expect it to run. Again, it can do anything.

The error here is that you allocated an array of pointers but then chose the wrong type to hold the result.

// This is wrong!
struct number* nums3 = malloc(sizeof(struct number*) * ARRAY_MAX);

You want this:

struct number **nums3 = malloc(sizeof(struct number*) * ARRAY_MAX);
//            ^^

Or really, this way is better:

struct number **nums3 = malloc(sizeof(*nums3) * ARRAY_MAX);

Then you have an array of (uninitialized) pointers to play with. For example,

for (int i = 0; i < ARRAY_MAX; i++) {
    nums3[i] = malloc(sizeof(*nums3[i]));
    nums3[i]->num1 = i;
    nums3[i]->num2 = i;
    nums3[i]->num3 = i;
}

or...

for (int i = 0; i < ARRAY_MAX; i++) {
    nums3[i] = &nums2[i];
}

Whatever you want.

(We're pretending here that malloc() doesn't return NULL which is not guaranteed.)