2

Let's assume I have a struct like this:

typedef struct {
    char* author;
    uint32_t isbn;
} Book;

int
main( int arg_c; char* arg_v[] ) {
    Book* b = malloc( sizeof *b );
    char* author = "Miller";

    //[...]

    free(b);
    return 0;
}

Now what kinda trips me up is line 8: Book* b = malloc( sizeof *b ); here, I'm creating a pointer b of type Book on the left side of the equals sign, but at the same time, I'm dereferencing b on the right side to allocate memory for b.

How does that work?

How can I define a pointer at the same time, as I use that pointer to allocate memory for it. If they line were Book* b = malloc( sizeof (Book) ); that is immediately intuitive for me, since sizeof interrogates the size of a type. But here, it's as if the identifier is used in two places at the same time, while it's being defined.

Is there some double-pass thing going on, that I'm missing?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
polemon
  • 4,722
  • 3
  • 37
  • 48
  • 1
    The operand of `sizeof` isn't evaluated, so `sizeof(*p)` doesn't dereference `p`. It only looks at the *type* of the expression `*p`. There must be a duplicate somewhere, but I don't immediately see a good one. Otherwise you're right, there would be a problem with dereferencing an uninitialized pointer. – Nate Eldredge Apr 30 '21 at 21:43
  • Generally it is not forbidden to use an identifier you're defining in its own initializer. `int x = x;` is syntactically and semantically legal, though (I believe) it causes undefined behavior if executed. – Nate Eldredge Apr 30 '21 at 21:45

2 Answers2

2

This expression

sizeof *b

is not evaluated. The compiler just tries to determine the type of the expression *b and this type is Book. So in fact this expression is equivalent to

sizeof( Book )

The type of the variable b is known after completion of the definition of its declarator. So the initializer expression already knows the type of the initialized object.

To make it more clear consider the following demonstrative program

#include <stdio.h>

int main(void) 
{
    int x = 10;
    
    size_t n = sizeof( x += 20 );
    
    printf( "n = %zu, x = %d\n", n, x );
    
    return 0;
}

The program output is

n = 4, x = 10

As this expression

sizeof( x += 20 )

is not evaluated (the compiler only determines the type of the expression) then the value of the variable x was not changed.

Another more complicated example.

#include <stdio.h>

int i = 1;

int main(void) 
{
    size_t i[i], b = sizeof( i );

    printf( "b = %zu\n", b );

    return 0;
}

The program output is

b = 8

Within the declaration of the array i there is used the variable i declared in the file scope because the declarator of the array i is not completed yet. But in the declaration of the variable b there is used the identifier i that denotes the array.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • I understand that, but what I'm not understanding, it is possible to interrogate the type of something that is *in the process* of being defined for it's own definition. So is the type defined while there is no allocation made for it, and it's at that point usable in the rest of the statement? – polemon Apr 30 '21 at 21:57
  • 1
    @polemon The type of the variable b is defined after its complete declarator definition. – Vlad from Moscow Apr 30 '21 at 21:58
  • Aaaah! so once the compiler interprets the left side of the equals sign it already has codified the type and registered the identifier? So that both the identifier is known to the rest of the statement as well as its type? – polemon Apr 30 '21 at 22:02
  • 1
    @polemon Yes as soon as the declarator is defined its type is known. – Vlad from Moscow Apr 30 '21 at 22:05
  • @polemon See my appended post. – Vlad from Moscow Apr 30 '21 at 22:15
  • I had to read the addendum several times to understand it, but I completely understand the matter now. Thanks! – polemon Apr 30 '21 at 22:30
0

It does not dereference the pointer.
It merely uses the size of what the pointer points to.
It might be plausible to you by looking at even more weird Book* b = malloc( sizeof (Book) );, which does not really access anything at all, using only a type.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
  • Doesn't work: my compiler (clang) throws an error: "error: expected parentheses around type name in sizeof expression Book* b = malloc( sizeof Book );" – polemon Apr 30 '21 at 21:50
  • And you do not get that warning for the code shown in your question? Please provide a [mre]. – Yunnosch Apr 30 '21 at 21:51
  • Nope, whenI use `Book* b = malloc( sizeof *b );` it compiles fine. – polemon Apr 30 '21 at 21:53
  • `sizeof` needs parentheses when used with a type but not with an expression, see https://stackoverflow.com/questions/5894892/why-and-when-do-i-need-to-use-parentheses-after-sizeof – Nate Eldredge Apr 30 '21 at 21:56
  • Sorry, I admit at not testing before posting. Fixed. Thanks @Nate, for putting a finger on what exactly I confused. – Yunnosch Apr 30 '21 at 21:56
  • Yeah, I know about this, and as I've written in my Op post, I know about this and I find it much more intuitive. The confusion comes from the fact that the same identifier is used on both sides of the definition statement. – polemon Apr 30 '21 at 21:59