Proper way to read 'int * ptr=&x'

Viewed 899

The following code is easily interpreted as

int x=4;
int *ptr;  // ptr is a pointer to an int
ptr=&x; // the address of x  is assigned to the pointer ptr

But the following equivalent code, may read misleadingly like

int *ptr=&x; //  the address of x is assigned to  *ptr, which is an integer

that is it seems to be interpreted as assigning the address of x to the pointed value *ptr (int *ptr )= &x The correct interpretation as the one when declaration and initialization are separated should be written something like int *(ptr = &x),to make evident that the assignment is to the pointer and not to the pointed location but this gives an error, why is that? And what is the best way to read and think of int *ptr=&x?

5 Answers

This is, admittedly a weird part of C and it's mostly due to C's evolution.

In C's syntax, declarations aim to mirror use, so that after int *x;, *x resolves to an int and *x=42 assigns to that int. But initializations, which are started with = after the specifiers declarator part of a declaration, are syntactically and semantically different from an assignments.

(Initialization can "assign" to static/filescope variables and such an assignment generates no code: it simply contributes to the makeup of the resulting binary. Assignments always generate code unless optimizations can delete it)

Initializations and assignments used to be very differently looking in prehistoric C, where you'd initialize without the = sign as in int x 42;. (Even after the = was added to the syntax of initializations, it was long impossible to initialize local, nonstatic, variables which meant situations such as int *p = &x; didn't arise all that often.)

The old syntax had problems (would int x (42); also declare and initialize x or would it be a function declaration?) and that's why it was replaced, but I like how it emphasized that initializations are different from assignments.

Unfortunately with the new syntax (as in int x, *p = &x;) this distinction is not all that apparent, and you simply have to remember that when you have type specifiers (int) at the left end, then the = does not denote an assignment where you can just look at *p = &x but rather that it's an initialization where you have to look at the whole declaration and see what's declared (x as an int and p as a pointer to int) . The = in that context then initializes the declared identifier.

int * is the type, so it makes perfect sense. It's just that pointer notation in C can take some effort to get used to. But look at this code

typedef int* int_ptr;
int x;
int_ptr ptr = &x;

Same thing. However, it's often advised to NOT typedef pointers.

The confusion comes from that * serves two roles. It is BOTH to name a type AND to dereference a pointer. An example of the first is sizeof(int*)

If you declare several pointers at once it looks messier, but it's still the same. In general, it's recommended to not declare more than one pointer at once. Because if we would like to do the above thing with two pointers, it would look like this:

int *pa = &x, *pb = &x;

That's the same as

int *pa = &x;
int *pb = &x;

And this does something completely different and will generate a warning because you're assigning the address of a variable to the pb variable that has type int

int *pa = &x, pb = &x;

However, using the typedef from above, you can (but probably shouldn't) do this:

int_ptr pa=&x, pb = &x;

But one way to think of it is that it makes no sense at all in any situation to dereference an uninitialized pointer.

And what is the best way to read and think of int *ptr=&x?

Take the fact that it does not make sense to dereference an uninitialized pointer. And you're doing an initialization of a pointer, and therefore you should initialize it with a (valid) address.

Ok, I see that, another thing. If the type is int* why is it almost always written like with the * next to the pointer variable instead, like int *ptr?, it would make more sense, even if it is the same to write it like int* ptr.

Because, then it would be MUCH easier to forget the asterisk if you declare several pointers at once. That would give the impression that int* p,q; declares two pointers.

C pointer syntax is clunky. It's a very old language. Just get used to it. It will never change. Just for fun, here is a page that can tell what a declaration is https://cdecl.org/ so try these:

int (*p)[3]
int *p[3]
const int *p[3]
int *const p[3]
const int (*p)[3]
int (*const p)[3]

You could write

int ( *ptr ) = &x;

or even like

int ( * ( ptr ) ) = &x;

Though this record

int *ptr = &x;

is clear for each C programmer because it is a declaration and not an assignment statement. Moreover in C opposite to C++ declarations are not statements.

That is 1) you may not enclose a declaration in parentheses as you wrote (int *ptr )= &x and 2) you may enclose a declarator in parentheses.

If to follow your logic then you should write the declaration like

int *p = ( p = &x );

that makes the declaration more confusing.:)

Here is a demonstrative program that shows some examples of declarations and initializations of a pointer.

#include <stdio.h>

int main(void) 
{
    int x = 10;
    
    int *p1 = &x;
    int ( *p2 ) = &x;
    int ( *( p3 ) ) = &x;
    
    int typedef *T;
    
    T ( p4 ) = &x;
    
    printf( "*p1 = %d\n", *p1 );
    printf( "*p2 = %d\n", *p2 );
    printf( "*p3 = %d\n", *p3 );
    printf( "*p4 = %d\n", *p4 );

    return 0;
}

The program output is

*p1 = 10
*p2 = 10
*p3 = 10
*p4 = 10

Pay attention to that in C as in many other languages some symbols are overloaded and their meanings depend on the context. For example the symbol & can denote the address of operator and the bitwise AND operator and by the way in C++ this symbol also can denote a reference.

Compare the following.

int n = 5;     // (a) defines 'int' variable 'n' and initializes it to '5'

int *p;        // (b) defines 'int*' pointer variable 'p`
p = &n;        //     initializes 'p' to '&n'

int *p1 = p;   // (c) defines 'int*' pointer variable 'p1' and initializes it to 'p' 
               //     syntactically, it looks just like (a), but for a pointer type

int *p2 = &n;  // (d) same as (b) and (c) combined into a one-liner

int n3 = 7, *p3 = &n3;  // (e) same as (a), (b) and (c) combined into a one-liner

when declare a pointer, int p is equal to int p; int* is a type.

Related