Week 6: Pointer (1)
Last updated
Last updated
A pointer in C is a type of variable that stores the memory address of another variable. Unlike regular variables that hold a specific value like an integer or a character, pointers hold addresses where these values are stored.
Efficiency in Handling Arrays and Strings:
Pointers provide a more efficient way to handle arrays and strings. Since they directly access memory locations, operations like traversing an array or string can be done more quickly.
Dynamic Memory Allocation:
Pointers are essential for dynamic memory allocation in C. This allows programs to allocate memory while running, which is crucial for applications where the size of memory needed isn't known in advance.
Building Complex Data Structures:
They are key in constructing complex data structures such as linked lists, trees, and graphs. These structures require dynamic memory allocation and pointers provide the flexibility to efficiently manage memory.
Functionality and Flexibility in Functions:
Pointers enable passing large structures or arrays to functions with efficiency and ease. They also allow returning multiple values from a function through pointer arguments.
Direct Memory Access:
They offer a way to directly read and write in memory, which can lead to highly efficient code in system-level programming.
Compatibility with Low-level System Hardware:
Pointers are crucial in systems programming for interacting directly with memory, which is essential for hardware-level operations.
Defining a pointer in C involves specifying the type of data the pointer will reference and then using a specific syntax to declare the pointer variable. Here's a step-by-step guide on how to define a pointer in C:
The general syntax for declaring a pointer in C is as follows:
type
: The data type of the variable that the pointer will point to. This could be any of C's fundamental data types (e.g., int
, float
, char
) or derived types like structures.
*
: The asterisk (*) is the dereference operator and is used in the declaration to indicate that this is a pointer variable.
pointerName
: The name of the pointer variable. Like any variable name, it should be descriptive and follow C naming conventions.
Here are a few examples of pointer declarations in C:
A pointer is usually initialized to the address of a variable using the address-of operator (&
). Here's an example:
&y
gives the address of the variable y
, and yPtr
is initialized with this address. Now, yPtr
points to y
.
To use the pointer:
Dereferencing: To access the value at the address a pointer is holding, you use the dereference operator (*
) again, but this time in an expression:
Assignment: You can change the value of the variable pointed to by a pointer by dereferencing the pointer on the left-hand side of an assignment:
The example demonstrates the pointer operators &
and *
. The printf
conversion spec- ification %p
outputs a memory location as a hexadecimal integer on most platforms. The output shows that the address of a
and the value of aPtr
are identical, confirming that a
’s address was indeed assigned to the pointer variable aPtr
. The &
and *
operators are complements of one another. Applying both consecutively to aPtr in either order produces the same result. The addresses in the output will vary across systems that use different processor architectures, different compilers and even different compiler settings.
Output will look like
Function arguments in C can be passed in two ways: pass-by-value and pass-by-reference. Generally, arguments are passed by value, with the exception of arrays, which are passed by reference. This method allows functions to alter variables in the calling function and handle pointers to large data objects without incurring the overhead associated with copying these objects, as is the case with pass-by-value. While a return statement in a function is limited to returning a single value to the caller, passing arguments by reference provides a means for a function to effectively 'return' multiple values by modifying the variables of the caller.
Passing by value means that the function receives a copy of the argument, not the original variable itself. Changes made to the parameter inside the function do not affect the original argument.
In C, passing by reference means passing the address of a variable (usually using pointers) to a function so that the function can directly modify the variable's value.
Pass by Value: The original variable is not affected by what happens inside the function. It's a safe way to ensure that the original data cannot be accidentally modified. However, it can be less efficient for large data structures since it involves creating copies.
Pass by Reference: The function has direct access to and can modify the original variable. This approach is more efficient for large data structures but requires careful management to avoid unintended side-effects.
Null Pointers: Always check if pointers are NULL
before dereferencing to avoid segmentation faults.
Scope: Variables should have a scope beyond the function call (e.g., not local to another function) to ensure they exist throughout the function execution.
Const Correctness: If a function should not modify the data pointed to by the pointer, use const
keyword to enforce this.
NULL
PointersThe concept of a NULL
pointer is an important aspect of pointer usage in C programming. A NULL
pointer is a special pointer that points to nothing or no valid memory location. It is used as a sentinel value to indicate that the pointer is not intended to point to an accessible memory location.
NULL
Pointers?Safety: Prevents accidental use of uninitialized pointers, which can lead to undefined behavior or program crashes.
Error Handling: Indicates that a function that returns a pointer failed to perform its intended task.
End of Structures: Marks the end of a data structure, like linked lists or trees.
NULL
PointerIn C, you can define a NULL
pointer by initializing a pointer variable with the NULL
macro, which is defined in several standard headers like <stdio.h>
, <stdlib.h>
, and <stddef.h>
.
Dereferencing a NULL
Pointer: Attempting to dereference (access the value pointed by) a NULL
pointer will lead to undefined behavior, often resulting in a segmentation fault or program crash.
Checking for NULL
: Always check if a pointer is NULL
before dereferencing it, especially if there is a possibility that it might not point to a valid memory location.
Suppose we have a function that dynamically allocates memory and returns a pointer to it. Using a NULL
pointer is a good practice to handle situations where memory allocation might fail.
In the above example,
The malloc
function is used for dynamic memory allocation. If malloc
fails (e.g., due to insufficient memory), it returns NULL
. The function checks if arr
is NULL
to determine if memory allocation was successful. If not, it prints an error message and returns NULL
.
Checking for a NULL
pointer after calling malloc
(or similar functions like calloc
and realloc
) is crucial for robust error handling in dynamic memory allocation.
Always free dynamically allocated memory to prevent memory leaks.
The check for NULL
before using the returned pointer in main
ensures that the program does not attempt to dereference a NULL
pointer, which would lead to undefined behavior.
Pointer arithmetic in C involves operations on pointer values. Unlike regular arithmetic, pointer arithmetic is performed with respect to the data type to which the pointer points. Here's a brief summary:
Type-Specific Scaling:
When you increment or decrement a pointer, it moves by the number of bytes of its data type. For example, an int *
moves by sizeof(int)
bytes with each increment.
Pointer Increment and Decrement:
ptr++
or ++ptr
: Moves the pointer to the next memory location for its type.
ptr--
or --ptr
: Moves the pointer to the previous memory location for its type.
Pointer Addition and Subtraction:
ptr + n
: Moves the pointer forward by n
elements.
ptr - n
: Moves the pointer backward by n
elements.
Difference Between Two Pointers:
ptr2 - ptr1
: Computes the number of elements between two pointers (of the same type).
An example demonstrating pointer arithmetic in C. This example will use an array of integers and a pointer to traverse (ravel across or through) the array.
Example of output
Reminder: Hexadecimal number system is a type of number system, that has a base value equal to 16. Hexadecimal numbers are represented by only 16 symbols. These symbols or values are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E and F.
Swapping Two Numbers
Write a function void swap(int *x, int *y)
that swaps the values of two integers.
In your main
function, declare two integers, initialize them, and call swap
using their addresses.
Print the values before and after the swap to verify the function works.
Array Sum Using Pointers
Create an int
array and initialize it with some values.
Write a function int arraySum(int *arr, int size)
that returns the sum of the array elements. Use pointer arithmetic to navigate the array.
In main
, call this function and print the result.
Null Pointer Check
Write a function void printValue(int *ptr)
that prints the value pointed to by ptr
. The function should first check if ptr
is not a null pointer before attempting to print.
In main
, create an int
pointer, initialize it to NULL
, and then call printValue
with it.
Afterward, assign a valid address to the pointer and call printValue
again.