Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to explain C pointers (declaration vs. unary operators) to a beginner?

Tags:

c

pointers

People also ask

What is the pointer declarations used in C explain with example?

The declarator names the variable and can include a type modifier. For example, if declarator represents an array, the type of the pointer is modified to be a pointer to an array. You can declare a pointer to a structure, union, or enumeration type before you define the structure, union, or enumeration type.

What are unary operators explain with an example?

In mathematics, an unary operation is an operation with only one operand, i.e. a single input. This is in contrast to binary operations, which use two operands. An example is any function f : A → A, where A is a set.

What is the meaning of Unary Operator in C?

These are the type of operators that act upon just a single operand for producing a new value. All the unary operators have equal precedence, and their associativity is from right to left. When we combine the unary operator with an operand, we get the unary expression.

How Unary Operator works?

The unary operators require only one operand; they perform various operations such as incrementing/decrementing a value by one, negating an expression, or inverting the value of a boolean. The increment/decrement operators can be applied before (prefix) or after (postfix) the operand.


The reason why the shorthand:

int *bar = &foo;

in your example can be confusing is that it's easy to misread it as being equivalent to:

int *bar;
*bar = &foo;    // error: use of uninitialized pointer bar!

when it actually means:

int *bar;
bar = &foo;

Written out like this, with the variable declaration and assignment separated, there is no such potential for confusion, and the use ↔ declaration parallelism described in your K&R quote works perfectly:

  • The first line declares a variable bar, such that *bar is an int.

  • The second line assigns the address of foo to bar, making *bar (an int) an alias for foo (also an int).

When introducing C pointer syntax to beginners, it may be helpful to initially stick to this style of separating pointer declarations from assignments, and only introduce the combined shorthand syntax (with appropriate warnings about its potential for confusion) once the basic concepts of pointer use in C have been adequately internalized.


For your student to understand the meaning of the * symbol in different contexts, they must first understand that the contexts are indeed different. Once they understand that the contexts are different (i.e. the difference between the left hand side of an assignment and a general expression) it isn't too much of a cognitive leap to understand what the differences are.

Firstly explain that the declaration of a variable cannot contain operators (demonstrate this by showing that putting a - or + symbol in a variable declaration simply causes an error). Then go on to show that an expression (i.e. on the right hand side of an assignment) can contain operators. Make sure the student understands that an expression and a variable declaration are two completely different contexts.

When they understand that the contexts are different, you can go on to explain that when the * symbol is in a variable declaration in front of the variable identifier, it means 'declare this variable as a pointer'. Then you can explain that when used in an expression (as a unary operator) the * symbol is the 'dereference operator' and it means 'the value at the address of' rather than its earlier meaning.

To truly convince your student, explain that the creators of C could have used any symbol to mean the dereference operator (i.e. they could have used @ instead) but for whatever reason they made the design decision to use *.

All in all, there's no way around explaining that the contexts are different. If the student doesn't understand the contexts are different, they can't understand why the * symbol can mean different things.


Short on declarations

It is nice to know the difference between declaration and initialization. We declare variables as types and initialize them with values. If we do both at the same time we often call it a definition.

1. int a; a = 42;

int a;
a = 42;

We declare an int named a. Then we initialize it by giving it a value 42.

2. int a = 42;

We declare and int named a and give it the value 42. It is initialized with 42. A definition.

3. a = 43;

When we use the variables we say we operate on them. a = 43 is an assignment operation. We assign the number 43 to the variable a.

By saying

int *bar;

we declare bar to be a pointer to an int. By saying

int *bar = &foo;

we declare bar and initialize it with the address of foo.

After we have initialized bar we can use the same operator, the asterisk, to access and operate on the value of foo. Without the operator we access and operate on the address the pointer is pointing to.

Besides that I let the picture speak.

What

A simplified ASCIIMATION on what is going on. (And here a player version if you want to pause etc.)

          ASCIIMATION


The 2nd statement int *bar = &foo; can be viewed pictorially in memory as,

   bar           foo
  +-----+      +-----+
  |0x100| ---> |  1  |
  +-----+      +-----+ 
   0x200        0x100

Now bar is a pointer of type int containing address & of foo. Using the unary operator * we deference to retrieve the value contained in 'foo' by using the pointer bar.

EDIT: My approach with beginners is to explain the memory address of a variable i.e

Memory Address: Every variable has an address associated with it provided by the OS. In int a;, &a is address of variable a.

Continue explaining basic types of variables in C as,

Types of variables: Variables can hold values of respective types but not addresses.

int a = 10; float b = 10.8; char ch = 'c'; `a, b, c` are variables. 

Introducing pointers: As said above variables, for example

 int a = 10; // a contains value 10
 int b; 
 b = &a;      // ERROR

It is possible assigning b = a but not b = &a, since variable b can hold value but not address, Hence we require Pointers.

Pointer or Pointer variables : If a variable contains an address it is known as a pointer variable. Use * in the declaration to inform that it is a pointer.

• Pointer can hold address but not value
• Pointer contains the address of an existing variable.
• Pointer points to an existing variable

Looking at the answers and comments here, there seems to be a general agreement that the syntax in question can be confusing for a beginner. Most of them propose something along these lines:

  • Before showing any code, use diagrams, sketches or animations to illustrate how pointers work.
  • When presenting the syntax, explain the two different roles of the asterisk symbol. Many tutorials are missing or evading that part. Confusion ensues ("When you break an initialized pointer declaration up into a declaration and a later assignment, you have to remember to remove the *" – comp.lang.c FAQ) I hoped to find an alternative approach, but I guess this is the way to go.

You may write int* bar instead of int *bar to highlight the difference. This means you won't follow the K&R "declaration mimics use" approach, but the Stroustrup C++ approach:

We don't declare *bar to be an integer. We declare bar to be an int*. If we want to initialize a newly created variable in the same line, it is clear that we are dealing with bar, not *bar. int* bar = &foo;

The drawbacks:

  • You have to warn your student about the multiple pointer declaration issue (int* foo, bar vs int *foo, *bar).
  • You have to prepare them for a world of hurt. Many programmers want to see the asterisk adjacent to the name of the variable, and they will take great lengths to justify their style. And many style guides enforce this notation explicitly (Linux kernel coding style, NASA C Style Guide, etc.).

Edit: A different approach that has been suggested, is to go the K&R "mimic" way, but without the "shorthand" syntax (see here). As soon as you omit doing a declaration and an assignment in the same line, everything will look much more coherent.

However, sooner or later the student will have to deal with pointers as function arguments. And pointers as return types. And pointers to functions. You will have to explain the difference between int *func(); and int (*func)();. I think sooner or later things will fall apart. And maybe sooner is better than later.