5

Consider:

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

struct node {
    int val;
    struct node *next;
};

typedef struct node item;

void main() {
    item * curr, *head;
    int i;

    head = NULL;
    for (i = 1; i <= 10; i++) {
        curr = (item *)malloc(sizeof(item));

        curr->val = i;
        curr->next = head;
        head = curr;
    }

   curr = head;
   while (curr) {
        printf("%d\n", curr->val);
        printf("%d\n", *curr);
        curr = curr->next;
   }

   getchar();
}

As I print out, *curr and curr->val are the same. Why?

I am confused about *curr. I think *curr is the value &curr points to. (I am newbie in C.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vancake
  • 151
  • 1
  • 7
  • If you are compiling with warnings, the line "printf("%d\n", *curr);" should print a warning since struct node is not an integer. While there is a reason you see this behavior, it's a bit complicated and has to do with how varargs are handled in C. Probably you should just avoid doing this so that the behavior is more understandable. – Ben Braun Jun 29 '16 at 05:18
  • 3
    `printf("%d\n", *curr);` is undefined behaviour because `*curr` is not an int. – user253751 Jun 29 '16 at 05:18
  • 1
    In addition to the what the above comments correctly pointed out, for reference, accessing elements with `(*curr).val` is same as `curr->val` – wolfsgang Jun 29 '16 at 05:21
  • 1
    `warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘item’ [-Wformat=] printf("%d\n", *curr);` – Support Ukraine Jun 29 '16 at 05:32
  • Related: http://stackoverflow.com/questions/32597425/are-struct-names-pointers-to-first-element and http://stackoverflow.com/questions/19776731/why-use-address-of-first-element-of-struct-rather-than-struct-itself . –  Jun 29 '16 at 05:32
  • 1
    Possible duplicate of [In C, does a pointer to a structure always point to its first member?](http://stackoverflow.com/questions/7312555/in-c-does-a-pointer-to-a-structure-always-point-to-its-first-member) –  Jun 29 '16 at 05:33
  • [Don't cast the result of `malloc` in C](http://stackoverflow.com/q/605845/995714) – phuclv Jun 29 '16 at 05:51
  • Try `printf("%d %s\n", cur->val, "hello")` vs `printf("%d %s\n", *curr, "hello");` if you think they're the same. – Paul Hankin Jun 29 '16 at 05:56

3 Answers3

9

First as a beginner, you should try to write correct code that raises no warning before trying to understand what happens under the hood and why some incorrect expressions still give correct results.

C is a very permissive language, that is as long as the compiler can translate what it reads it generally assumes that the programmer knows why he wrote it. It's what happens here.

In C, the address of a struct can be cast to an address to its first element. So (void *) &curr is the same as (void *) &(curr->val). Here I cast everything to void * to make sure to have compatible pointer types.

That means that *((int *) curr) is a perfectly defined C expression and its value is indeed curr->val. But it is correct, because I wrote an explicit pointer cast.

What you wrote works only by accident, because as curr is an aggregate type; you pass an int and an item * to printf and use only its first value - this is no longer standard C and I can only assume a classic implementation to guess that. But just replace your print commands by that:

printf("%d - %d\n", curr->val, 12);
printf("%d - %d\n", *curr, 15);

You should see garbage in the second printf, because with %d you declare that you pass a simple int and actually pass an aggregate. Here again, nothing is explicitly mandated per standard, but common implementation should give this result.

TL;DR: As *curr is not an int, printf("%d\n", *curr); is Undefined Behaviour. This means that anything can happen, even the expected result, but a different version of same compiler, or another compiler, or even a change in compilation parameters could give a different result, as could a minor change in the code.

And C allows automatic pointer cast to and from void * and you should never cast the return value of malloc as it can hide bugs. Please write:

curr = malloc(sizeof(item));

It is no longer C++ compatible, but is it correct C.

honk
  • 9,137
  • 11
  • 75
  • 83
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

This is undefined behaviour and will give you warnings.

printf gets the type of argument being passed to it with the help of format specifiers. In your case the first element of the structure is a intand hence has a int at the top of the stack. Hence when printf looks for a int it finds one and gives the correct output, even though you passed the entire structure.

If there was any other data type than int as the first element of your structure, it would have given an undefined output. Hence as I mentioned in the comments, the correct ways to access the structure elements are with (*curr).value or as you have used curr->value.

wolfsgang
  • 884
  • 6
  • 12
  • 1
    You say that the code is undefined behavior, but later say that "if there was any other data type than `int`... it would have given an undefined output". Surely if it's undefined behavior the output is already undefined, no matter the type? – Paul Hankin Jun 29 '16 at 05:57
  • @PaulHankin True, I wrote that just to clarify about the correct output in this case, and how it will not carry for other cases. – wolfsgang Jun 29 '16 at 05:59
-2

Here

curr->val = i;  
curr->next = head;
head = curr;

first you set (*curr).val to i. Then you make curr->next point to head. At this time still NULL. Then you make head point to curr. And here the interesting part, curr points to its beginning and *curr dereferences it which is essentially curr->val, because from there starts the struct curr.

As per Standard and this answer

(C1x §6.7.2.1.13: "A pointer to a structure object, suitably converted, points to its initial member ... and vice versa. There may be unnamed padding within as structure object, but not at its beginning.")

Which means that there isn't any space (padding) (or also e.g. some HEAD information) between the actual structure start and the start of its first element.

EDIT:
I don't say anything about UB, not because I don't see it. OP asked for something else. It should be pointed out, but not be THE answer! That's why we have Comments Section!

Community
  • 1
  • 1
Dimitar
  • 4,402
  • 4
  • 31
  • 47
  • 1
    `curr` does point to `&(curr->val)` but `*curr` is **not** `curr->val` it just starts with `curr->val` but it is longer. See my answer for more details – Serge Ballesta Jun 29 '16 at 08:56