Hello! Welcome to your fifth lesson.
In our last session, we explored the dereference operator (*), learning how to use it to "follow" a pointer to its target address to read or modify the data stored there. This powerful indirection is central to C programming.
Today, we'll formalize the process of creating and preparing these pointers for use. This lesson focuses on the precise syntax and best practices for creating pointer variables for different kinds of data. Our learning outcome is to declare and initialize pointers correctly for different data types. Getting this right is the first step in writing safe and predictable pointer-based code, a non-negotiable requirement in a field like automotive software development.
The Anatomy of a Pointer Declaration
A pointer declaration tells the compiler three essential things:
- That you are creating a pointer variable.
- The name of that variable.
- The type of data it is intended to point to.
The general syntax is:
data_type *pointer_name;
Let's break this down:
data_type: This is the base type of the pointer. It specifies the type of the data that the pointer will hold the address of.*: The asterisk is the special character that tells the compilerpointer_nameis a pointer, not a regular variable ofdata_type.pointer_name: This is the identifier you will use to refer to your pointer variable.
Here are some examples with common data types:
int an_integer = 42;
double a_double = 3.14;
char a_character = 'A';
int *p_int = &an_integer;
double *p_double = &a_double;
char *p_char = &a_character;
This image provides a clear visual summary of the declaration and initialization process.

A Note on Style: int* p vs. int *p
You will encounter both int* p; and int *p; in C code. Both are syntactically correct and compile to the same machine code. However, the int *p; style is generally preferred because it aligns more closely with how the C compiler interprets declarations. Consider this line:
int *p, q;
Here, p is declared as a pointer to an int, but q is declared as a regular int. The * binds to the variable name (p), not the type (int). This can be a source of confusion, so it's often clearer to declare one pointer per line.
The Critical Role of the Base Type
You might wonder why we need to specify a pointer's type. After all, isn't an address just an address, regardless of what's stored there? The base type is crucial for two main reasons:
-
Dereferencing: When you write
*p_int = 100;, the compiler uses the pointer's base type (int) to know how many bytes to write to memory (e.g., 4 bytes on a typical 32-bit system). If it were achar*, it would only write 1 byte. Without the type, the compiler wouldn't know the size of the data at the target address. -
Pointer Arithmetic: As we will see in the next module, when you perform arithmetic on a pointer (e.g.,
p_int + 1), the compiler doesn't just add 1 to the raw memory address. It adds1 * sizeof(int). The base type provides the scale for all pointer arithmetic operations.
The following reading provides a great explanation of pointer declaration and the importance of its type.
A TUTORIAL ON POINTERS AND ARRAYS IN C by Ted ...
To solidify your understanding of pointer declaration and the significance of the base type, please read the following sections from 'A TUTORIAL ON POINTERS AND ARRAYS IN C'.
First, read the section that starts with 'In C when we define a pointer variable...' and ends just before the discussion of the 'NULL' macro. This covers the basic declaration syntax. Then, jump to the beginning of 'CHAPTER 2: Pointer types and Arrays' and read the first two paragraphs, which explain exactly why the base type is so important for dereferencing and arithmetic.
Safe Initialization: Avoiding Disaster
An uninitialized pointer is a ticking time bomb. It holds a random, garbage address. If you try to dereference it, your program will likely crash or, worse, corrupt some unknown part of memory. In safety-critical systems, this is an unacceptable risk. All pointers must be initialized to a known state before use.
There are three safe ways to initialize a pointer:
-
Initialize with the address of a variable:
This is the most common use case. You make the pointer point to a valid piece of data as soon as it's created.uint32_t can_id = 0x7DF; // A standard diagnostics CAN ID uint32_t *id_ptr = &can_id; // id_ptr is initialized to the address of can_id -
Initialize with
NULL:
If you don't have a valid address for the pointer at the time of declaration, initialize it toNULL.NULLis a special macro that represents a "points to nothing" state. This makes it easy to check if a pointer is valid before using it.struct SomeIpHeader *header_ptr = NULL; // ... later in the code ... if (header_ptr != NULL) { // Safe to dereference process_header(header_ptr); } -
Initialize with another pointer:
You can initialize a pointer with the value of another pointer of the same base type. Both pointers will then point to the same memory location.uint8_t *pdu_buffer_ptr = &pdu[0]; // Pointer to the start of a PDU buffer uint8_t *payload_ptr = pdu_buffer_ptr; // Both now point to the same buffer
The following reading walks through these initialization techniques with clear examples.
Pointer Basics in C - C Programming Tutorial
The article 'Pointer Basics in C' provides a practical overview of how to assign addresses and NULL to pointers.
Please read the sections 'Declaring Pointer Variables' and 'Assigning Address to Pointer Variable'. Focus on the syntax for declaration, the use of the & operator for assignment, and the concept of initializing pointers to NULL.
Test your understanding!
Your task is to write a small code snippet. Imagine you are defining part of an AUTOSAR COM module. You have the following variables:
// A variable holding the Data Length Code (DLC) of a CAN frame
uint8_t dlc = 8;
// A variable for a specific signal value, e.g., vehicle speed
float vehicle_speed = 95.5f;
// A pointer to a large data buffer that is not yet allocated
uint8_t *tx_buffer_ptr;
Write the C code to declare and initialize three new pointers:
- A pointer named
dlc_pthat points to thedlcvariable. - A pointer named
speed_pthat points to thevehicle_speedvariable. - A pointer named
rx_buffer_pwhich, liketx_buffer_ptr, is not yet pointing to a valid buffer. Initialize it to a safe default state.
Show answer
Here is the correct implementation:
// 1. Declare and initialize a pointer to the dlc variable
uint8_t *dlc_p = &dlc;
// 2. Declare and initialize a pointer to the vehicle_speed variable
float *speed_p = &vehicle_speed;
// 3. Declare a pointer for a receive buffer and initialize it to a safe state
uint8_t *rx_buffer_p = NULL;
Each pointer is declared with the correct base type that matches the variable it points to. rx_buffer_p is safely initialized to NULL because it does not yet have a valid memory address to hold.
Conclusion
In this lesson, we established the fundamental rules for creating and initializing pointers in C. Correctly declaring a pointer with its specific base type is not just a matter of syntax; it's what enables the compiler to handle memory operations safely and correctly.
Key Takeaways:
- The declaration syntax is
data_type *pointer_name;. - The base type is critical for the compiler to know the size of the data being pointed to, which affects both dereferencing and pointer arithmetic.
- An uninitialized pointer is a major source of bugs. Always initialize a pointer to a valid address (
&variable), toNULL, or to the value of another valid pointer. - Adhering to these rules is a cornerstone of the defensive programming required for robust and safe systems, aligning with principles you know from MISRA-C and ISO 26262.
In our next lesson, we will investigate an important property of pointer variables themselves: their size. We'll explore pointer size and its relationship to system architecture (32-bit vs. 64-bit). This will help you understand how pointers behave across the different microcontroller and processor architectures you encounter in your work.