0

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 smith@jasonS-pc ~/src/c $ ./a.exe 0 1 2 3 4

jason smith@jasonS-pc ~/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.

5
  • What kind of result did you expect? Do you know what "undefined behavior" is? Commented Aug 4, 2016 at 4:59
  • jasmith@lab:~/src/c$ ./a.out Row1: 0 0 0 Row2: 1 1 1 Row3: 2 2 2 Row4: 3 3 3 Row5: 4 4 4 Row6: 5 5 5 jasmith@lab:~/src/c$ These values that display correspond to the value of y in that last for-loop. I do know about unexpected behavior, but this to me looks like expected behavior. I don't see how I'm even able to do nums3[y].num1 which is why I asked what I'm actually getting from that malloc'ed memory block pointer without doing the **. Commented Aug 4, 2016 at 5:01
  • 1
    You say "My issue is ... actually prints expected results". If it's an issue then you really expect it to print something else. What kind of unexpected result do you expect? Commented Aug 4, 2016 at 5:08
  • I'm not sure what unexpected result I'd expect here. Anything other than what I'm seeing, I suppose. Commented Aug 4, 2016 at 5:13
  • So if an expected result is 1,2,3, then the expected unexpected results would be 1,2,4 or 1,2,5 or 2,1,3 or... but not 1,2,3? Why? What's so special about 1,2,3? Commented Aug 4, 2016 at 5:18

2 Answers 2

3

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.)

Sign up to request clarification or add additional context in comments.

5 Comments

I understand that using the double pointer is the way to go here. I'm just trying to learn/understand why the code as is was wrong and what was happening to get the result that I see.
The code was wrong because you allocated memory for the array of pointers but assigned it to the wrong type. malloc() returns a void *, so it doesn't do any checking to see if you made a mistake.
Okay... I still don't understand then why I'm able to do nums3[y].num1 = y in that last for-loop. If the allocation of nums3 is wrong, then how does a struct even exist for the .num1 in the for-loop to act on?
When you make a mistake in C, the best possible outcome is for the program to exit or crash. In this case, the error did not happen to cause a crash. If you tried the same program on many different computers and compilers, maybe it would crash sometimes, maybe it would work sometimes. That is the danger of C, that your program may appear to work even though it is wrong.
Think of it like this: you asked for a sheet of paper from malloc(), but you asked for a piece of paper that was too small. Since you thought the paper was bigger, you ended up writing on the desk. Nobody is stopping you from writing on the desk. If you had kept writing, eventually your pencil would have gotten to the edge of the desk and you would have fallen over (crashed). However, you were unlucky, and this didn't happen.
0

You are correct by saying you are not allocating enough space as sizeof(struct number) = 12 while sizeof(struct number*) = 8

Malloc finds a free memory according to the size you asked and (if successful) returns you a pointer to the first address (this is virtual memory). If you exceed the size created you enter the realm of unexpected behavior. Meaning you either will be able to write and read data from the memory or you won't and even if you manage to do that, you can accidentally overwrite areas in memory storing other data.

In this case, although printing passed with no special behavior, when you try to free(nums3) you will get an error.

Also, if you will reverse the order of nums2 and nums3 declaration and print nums2 after nums3 loop, you will probably be able to see this corruption of data.

Hope this is helpful

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.