Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 2

Understanding Pointers: What They Store and How They Differ

Recommended resources

Hello! Welcome back to our course on C pointers.

In our previous lesson, we established a clear mental model of a C program's memory layout—the stack, heap, and static data segments. We discussed where variables and data live during a program's execution. This forms the essential backdrop for today's topic.

Today, we will focus on the variables that are designed to work with this memory layout directly: pointers. This lesson tackles a core learning outcome: to describe what a pointer variable stores and how it differs from regular variables. By the end of this session, you'll understand the fundamental concept of indirection and why pointers are a cornerstone of C programming, particularly in domains like embedded systems and automotive software where direct memory manipulation is common.

Regular Variables: The Direct Approach

Let's start with what you already know well. When you declare a regular variable, the compiler allocates a piece of memory and stores the variable's value directly in that location.

Consider this simple declaration:

int vehicle_speed_kmh = 90;

Based on our last lesson, since this is a local variable, memory for it is allocated on the stack. Conceptually, you can picture it like this: a specific memory location is reserved, and the value 90 is placed inside it.

C Pointers: Memory Representation
This diagram illustrates how a regular variable, `var` (similar to our `vehicle_speed_kmh`), directly stores its value (10) at a specific memory address. The variable's name is your direct handle to its value.

When you use vehicle_speed_kmh in your code, you are accessing the value 90 directly. It's a straightforward, one-step process.


Pointer Variables: The Indirect Approach

A pointer is different. A pointer is a variable whose value is the memory address of another variable.

Instead of holding data like an integer or a character, it holds the location of that data. This introduces a level of indirection. You don't get the data directly; you first get the address, and then you can go to that address to find the data.

Let's imagine we have a pointer to our vehicle_speed_kmh variable.

// We will cover the & and * operators in detail in the next lessons.
// For now, just know that &vehicle_speed_kmh gets the memory address of the variable.
int* speed_ptr = &vehicle_speed_kmh; 

Here, speed_ptr is the pointer variable. Its value is not 90. Its value is the memory address where vehicle_speed_kmh is stored.

The image you saw before perfectly illustrates this two-part relationship. The variable p is the pointer. Its own value is a memory address (0x7fff5ed98c4c), and that address is the location of the integer variable var.

This concept of indirection is fundamental to many features you've worked with, such as using shared memory for Inter-Process Communication (IPC). The OS provides your process with a pointer (a starting address) to a region of physical RAM, and you use that pointer to read from and write to that shared region.

To delve deeper into this core distinction, the following reading provides a clear, illustrated explanation.

Pointers and Memory

The document 'Pointers and Memory' from Rose-Hulman Institute of Technology offers a great conceptual introduction. It uses simple diagrams to contrast a regular variable with a pointer variable.

Please read the section titled 'What Is A Pointer?' (page 3). Focus on the drawing that shows the num and numPtr variables and the textual explanation of how a pointer stores a 'reference' to its 'pointee'.


Under the Hood: What is an Address?

Your work with debuggers like Trace32 has likely exposed you to raw memory addresses, usually represented as hexadecimal numbers (e.g., 0x200010F4). This is exactly what a pointer stores. At the machine level, a pointer is essentially an integer variable whose value is interpreted by the CPU as a memory address.

The size of this integer—and thus the size of the pointer itself—depends on the system's architecture, which we will cover in a later lesson. For now, the key idea is that a pointer holds the numerical address of a byte in memory.

The following reading explains this underlying implementation.

Pointers and Memory

Let's continue with the same 'Pointers and Memory' document to see how this concept maps to the machine level. This should resonate with your low-level systems experience.

Read the short section at the end titled 'Extra: How Are Pointers Implemented In The Machine?' (page 10). It concisely explains that a pointer is just an integer storing an address.


The Special Case: The NULL Pointer

What if a pointer doesn't point to anything? A regular uninitialized int variable holds a "garbage" numerical value. An uninitialized pointer also holds a garbage address, which is extremely dangerous to use.

To handle the case of "points to nothing" explicitly and safely, C provides a special value: NULL. A NULL pointer is a pointer that is guaranteed not to point to any valid memory location. It's a convention used to indicate that a pointer is intentionally empty.

This is a critical difference from regular variables, which don't have a universally recognized "empty" value. Checking if a pointer is NULL before using it is a fundamental defensive programming practice to prevent crashes.

Pointers and Memory

The 'Pointers and Memory' document also has a great section on this special NULL value.

Please read the section titled 'The NULL Pointer' (page 4). Note its purpose and the fact that attempting to access the data it 'points to' is a runtime error.

Test your understanding!

Imagine the following C code is executed:

int sensor_id = 12;
float temperature = 25.5f;
int* id_ptr = &sensor_id; 

Assume sensor_id is stored at memory address 0x4000.

  1. What is the value stored in the variable sensor_id?
  2. What is the value stored in the variable id_ptr?
  3. How does id_ptr differ from the temperature variable in terms of what they store?
Show answer
  1. The value stored in sensor_id is 12.
  2. The value stored in id_ptr is the memory address 0x4000.
  3. temperature is a regular float variable; it directly stores a floating-point value (25.5). id_ptr is a pointer variable; it does not store an integer value but rather stores the memory address of an integer variable. It provides one level of indirection to get to the value 12.

Conclusion

Today we've defined the fundamental nature of a pointer and contrasted it with a regular variable. This concept of indirection is what gives pointers their power and flexibility.

Key Takeaways:

  • Regular variables store a data value directly (e.g., 42, 'A').
  • Pointer variables store a memory address, which is the location of another variable.
  • This relationship is called indirection: a pointer refers to its data indirectly, via an address.
  • At the machine level, a pointer's value is simply an integer representing a memory address.
  • A special value, NULL, is used to indicate that a pointer intentionally points to nothing.

Now that we understand what a pointer is, our next lesson will focus on the mechanics of using them. We will formally introduce and explore two crucial operators:

  1. The address-of operator (&): How to get the memory address of a variable to store in a pointer.
  2. The dereference operator (*): How to use a pointer's stored address to access the value of the variable it points to.