4

So let's say:

int n = 50;
int *p = &n;

Why do we use * and not & in C? What's the key difference between * and &?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • A “\*” is used to denote a pointer type because that is the *syntax*. No other reason need exist. The unary \* operator (which is _different_ from a type declaration, regardless of using the same symbol) is a pointer-dereference and the & unary operator is address-of. – user2864740 May 18 '20 at 00:06
  • What do you mean "why"? Because the language syntax is like that, that's why. – Lundin May 18 '20 at 08:57

3 Answers3

9

Using * to declare pointers is mostly a matter of convention, but there is a reason of consistency: the * in the declaration int *p means int is the type of *p.

It might seem more consistent to write int &p = &n as p is initialized to the address of n but this convention would not hold for double pointers: int **pp defines pp as a pointer to a pointer to an int, yet pp cannot be initialized with &(&n).

Note that int& p = n; is a valid definition in C++ for a reference to an int, which is a pointer in disguise. Modifying p would then modify n. References are implemented as pointers without the indirection notation.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • The declaration of `p` you've shown (`int *p`) means `p is a pointer-to-int`, I'd read it as "int pointer p", and the `*` in `int *p` means 'pointer', at least to me. YMMV. – Bob Jarvis - Слава Україні May 18 '20 at 00:33
  • @BobJarvis-ReinstateMonica: interesting remark. I don't usually read code aloud, and I would probably read *int star p*, or when teaching French programmers *int étoile p*. Your vocalisation is more sensible and less confusing. How would you pronounce `char *argv[]`? – chqrlie May 18 '20 at 00:38
  • I'd pronounce that as "char (pronounced 'care' - my code doesn't smolder :-) pointer array argv", or maybe as "array of char pointers argv". – Bob Jarvis - Слава Україні May 18 '20 at 17:56
2

C declarations follow a convention often described as “declaration mimics use”. The structure of a declaration matches (as closely as possible) the structure of an expression in the code.

For example, let’s say you have a pointer to an int named p. To access the value stored in the pointed-to object, we dereference the pointer with the unary * operator, like so:

printf( "value of what p points to is %d\n", *p );

The expression *p has type int, so we declare p as

int *p;

This conveys that the variable p has type "pointer to int", because the combination of p and the dereference operator * in the declarator yield an expression of type int.

And this is primarily why pointers aren't declared using the & operator in C, because the type of the expression &p wouldn't be int.

C++ uses the unary & operator to declare references, which are not the same thing as pointers:

void foo( int &ref )
{
  ref = new_value(); // writes a new value to the actual parameter ref references
}

int main( void )
{
  int x = 1;
  std::cout << "x before foo = " << x << std::endl;
  foo( x );
  std::cout << "x after foo = " << x << std::endl;
  return 0;
}

References kind of break this system - there's no &ref expression that has type int, it just notes that ref resolves to the same object as some other identifier.


C declarations are made up of two parts - a sequence of declaration specifiers (storage class specifier, type qualifier(s), type specifier(s)) followed by a sequence of (possibly initialized) declarators. Pointer-ness, array-ness, and function-ness are specified in the declarator:

            declaration specifiers           declarators
                     |                           |
+--------------------+----------------+ +--------+--------+
|                                     | |                 |
static const volatile unsigned long int a[10], *p, f(void);
   |   |            | |               | |      |   |
   |   |            | |               | |      |   +––––––– function declarator
   |   |            | |               | |      +––––––––––– pointer declarator
   |   |            | |               | +––––––––—–––––——–– array declarator
   |   |            | +–––––––+––––––—+
   |   |            |         |
   |   |            |         +––––––––––––––——–––––––––––— type specifiers
   |   +––––––+–––––+
   |          |
   |          +–––––––––––––––––––––––––––––––––––––––––––– type qualifiers
   +–––––––––––––—––––––––––––––––––––––––––––––––––––––––– storage class specifier

This is important - there's no "pointer to" type specifier. The pointerness is specified by the declarator. If you write something like

int* a, b;

it will be parsed as

int (*a), b;

and only a will be declared as a pointer.

John Bode
  • 119,563
  • 19
  • 122
  • 198
1

In C * in a variable definition means "pointer to". In an expression the & operator means "address of", while * operator means "dereference".

So in effect:

int n = 1;
int* p = &n; // Create a pointer and initialize with the address of n

if (*p == 1) {
  // This is true when dereferencing the pointer
}

Why not use & as in int& p? Mostly because the syntax isn't set up that way.

It's worth noting that in C++ the & comes back to mean "reference":

int n = 1;
int& r = n;

Note there's no need for any special treatment on the right-hand side, it just figures it out.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • Thank you! So this is the matter of syntax and convention? I’m learning so I want to make sure... – A Z I N E Z May 19 '20 at 00:16
  • It's how C was defined by the [creators of C](https://en.wikipedia.org/wiki/The_C_Programming_Language). – tadman May 19 '20 at 00:17