DEV Community

Cover image for How to Work with Pointers in C Programming
Jiayi Su
Jiayi Su

Posted on

How to Work with Pointers in C Programming

The true power and potential of dealing directly with memory are unlocked by the pointers feature of the C programming language. With pointers, you can design complicated data structures like arrays, strings, and structures that can optimize memory usage because you have direct access to the memory addresses of your data.

In this post, we'll go in-depth on pointers in C programming and look at how they function with different data types. You'll be able to create more effective and robust programs that fully utilize the enormous computational power that C offers by grasping the complexities of pointers.


Basic Usage of Pointers

Pointers with Integers

A pointer is a variable that stores the memory address of another variable. In other words, a pointer points to a specific memory location in the computer's memory. The kind of data that a pointer variable will point to must be specified when the variable is declared. Use the following syntax, for instance, to declare a pointer that points to an integer:

int *ptr;
Enter fullscreen mode Exit fullscreen mode

This declares a pointer variable named ptr that has the ability to point to an integer value.

The & operator, which retrieves a variable's memory address, can be used here to fetch the memory address and then assign it to a pointer variable. For instance, you would use the following syntax to provide the pointer variable ptr the memory address of a variable named a:

int a = 9;
int *ptr = &a;
Enter fullscreen mode Exit fullscreen mode

The memory address of the variable a is now stored in ptr. The value stored at the memory address that the pointer points to can be accessed using the * operator. For instance, you would use the following syntax to display the value of the variable a using the pointer:

printf("%d", *ptr);
Enter fullscreen mode Exit fullscreen mode

This will print the value 9, which is the value stored in the variable a.

Pointers with Strings

In C, a string is implemented as a null-terminated characters array. To work with strings using pointers, you can declare a pointer variable that points to the first character in the string. For instance, the syntax as follows would be used to declare a pointer that points to a string literal:

char *str = "hello";
Enter fullscreen mode Exit fullscreen mode

The memory address of the first character, h, in the string hello is now stored in str. To access the characters in the string, just use * operator. Dereferencing is accomplished via the * operator. For instance, you could apply the following syntax to print the string's first character:

printf("%c", *str);
Enter fullscreen mode Exit fullscreen mode

This will print the character h.

Note: In the above example, str is a string literal instead of a string array. They are different because string literals are string constants, which cannot be changed, and are stored in the global's read-only memory.

✅ If you want to modify the characters in the string, you can declare str as a string array instead of a string literal with code like the following examples:

char str[] = "hello";
Enter fullscreen mode Exit fullscreen mode

Or alternatively,

char str[] = {'h', 'e', 'l', 'l', 'o', '\0'};
Enter fullscreen mode Exit fullscreen mode

Pointers with Arrays of Integers

Arrays in C are stored in a contiguous block of memory. You can declare a pointer variable that refers to the array's first element to work with arrays via pointers. For example, the following syntax would be used to declare a pointer that points to an array of integers:

int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr;
Enter fullscreen mode Exit fullscreen mode

Now, ptr contains the memory address of the first element in the array. You can use pointer arithmetic to access the other elements in the array. For example, to print the second element in the array, you would use the following syntax:

printf("%d", *(ptr + 1));
Enter fullscreen mode Exit fullscreen mode

This will print the value 2, which is the second element in the array.

💎 Bonus question: how to print the fourth element? Here is the solution:

printf("%d", *(ptr + 3));
Enter fullscreen mode Exit fullscreen mode

Pointers with Nested Array of Strings

An array that comprises other arrays is referred to as a nested array in C. You can declare a pointer variable that points to the first element in the outer array to work with nested arrays using pointers. For example, the following syntax would be used to declare a pointer that points to a nested array of strings:

char *arr[][2] = {{"hello", "world"}, {"I'm", "Jenny"}, {"from", "UofT"}};
char *(*ptr)[2] = arr;
Enter fullscreen mode Exit fullscreen mode

In this two dimensional array called arr, it is an array of pointers to characters.

The first set of empty square brackets indicates that arr is an array with some number of rows. Actually, the number of rows will be inferred by the compiler when you give it an initial value, which is 3 in this case. (Thanks to the compiler, we can use this short-cut to omit the array size.)

The second set of square brackets specifies the number of columns, which is 2 in this case.

After we initialize the array with three rows and two columns, the first row contains the strings "hello" and "world", the second row contains the strings "I'm" and "Jenny", and the third row contains the strings "from" and "UofT", here, each element is a string literal (i.e., char *).

Notice that each row of the array arr is an array of pointer to char. This is because each row of arr contains a pointer to a one-dimensional array of length two, a pointer to an array of strings is effectively a pointer to the first string of that array of strings, and a pointer to a string is actually the memory address of that string.

Then, we declare the variable ptr, which is a pointer to a two-dimensional character array, it is initialized to point to the array arr. So ptr is an alias of arr, because they store the same memory address.

The memory address of the first element of the outer array is now stored in ptr. To access the inner arrays and their elements, use pointer arithmetic and dereferencing. For example, you would use this syntax to output the second string in the first inner array:

printf("%s", *(*ptr + 1));
Enter fullscreen mode Exit fullscreen mode

This line of code will print the string "world".

Let's look at the expression *(*ptr + 1). First of all, *ptr dereferences ptr to obtain the two-dimensional array arr, and then, *ptr + 1 is the memory address of the second element in the first row of the arr array, and then with *(*ptr + 1), we dereference the pointer to get the string literal stored at that address, which is "world". (More about pointer arithmetic at the later part of the blog, stay tuned 😊)

Using Pointers as Parameters to Functions

Pointers are a type of parameter that functions in C can accept. This enables the function to make changes to the original variable outside the function. A function must be declared to accept a pointer argument in order to accept a pointer as a parameter. As an illustration, you would use the following syntax to send a pointer to an integer variable to the increment function:

void increment(int *ptr) {
    (*ptr)++;
}
Enter fullscreen mode Exit fullscreen mode

This function takes a pointer to an integer as a parameter, and then it increments the value stored at the memory location pointed to by this pointer named ptr.

By giving a pointer to an integer variable, you can invoke the function. For example, the following syntax could be applied to increase the value of the integer num:

int num = 9;
int *ptr = #
increment(ptr);
printf("%d", num);
Enter fullscreen mode Exit fullscreen mode

This will print out the value 10.

Passing Arrays as Parameters to Functions

Arrays can also be passed as parameters to functions in C. In this case, you effectively send a pointer to the array's first element when you pass an array to a function. For example, the following syntax would be used to pass an array of integers to the sum function:

int sum(int *arr, int size) {
    int res = 0;
    for (int i = 0; i < size; i++) {
        res += arr[i];
    }
    return res;
}
Enter fullscreen mode Exit fullscreen mode

This function accepts an array of integers and the size of the array as inputs and returns the sum of the elements.

You can call the function by passing an array of integers and the size of the array. For example, the following code computes the sum of an array of integers named nums:

int nums[] = {1, 2, 3, 4, 5};
int size = sizeof(nums) / sizeof(nums[0]);
int result = sum(nums, size);
printf("%d", result);
Enter fullscreen mode Exit fullscreen mode

This will output the value 15, which is the sum of the values in the nums array.


Advanced Usage of Pointers

Pointers Arithmetic

As you have seen earlier, you can perform arithmetic operations on pointers thanks to the powerful pointer arithmetic capability of pointers in C. You can access an array's elements using pointer syntax and traverse through them using pointer arithmetic. The size of the data type that the pointer is pointing to determines how pointer arithmetic works in C.

Think about the following code, for instance:

int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr;
Enter fullscreen mode Exit fullscreen mode

In this code, ptr points to the first integer of the arr array. To access the second integer of the array using pointer arithmetic, you can use the following code:

int second = *(ptr + 1);
Enter fullscreen mode Exit fullscreen mode

Here, ptr is incremented by one, which moves the pointer to the memory location of the second element in the array. What is actually added to ptr is 1 * sizeof(int), so if an integer takes four bytes in your computer, then actually four bytes are added to the memory address stored at ptr. By moving ahead 4 * sizeof(int), now ptr is storing the memory address of the second integer. The * operator is used to dereference the pointer and access the value stored at that memory location.

Note that you can use the array notation to fetch the second element as well:

int second = ptr[1];
Enter fullscreen mode Exit fullscreen mode

Really, by ptr[1], what is happening behind the hood is that *(ptr + 1) is evaluated. You can substitute any other number n that is in-bound of the size of the array to replace 1. For example, you can write the following line of code to fetch the third element:

int third = *(ptr + 2);
Enter fullscreen mode Exit fullscreen mode

This is equivalent to:

int third = ptr[2];
Enter fullscreen mode Exit fullscreen mode

You can also use pointer arithmetic to print out each element while iterating through an array. For example, consider the following code:

for (int i = 0; i < 5; i++) {
    printf("%d ", *(ptr + i));
}
Enter fullscreen mode Exit fullscreen mode

Using pointer arithmetic, this code iterates through the arr array and outputs each element.

Pointers to Pointers

In C, pointers can also be used to hold another pointer's memory address. They are also referred to as double pointers or pointers to pointers. When you need to change a pointer variable inside a function or make a dynamic two-dimensional array, pointers to pointers come in handy.

You can use the following syntax to declare a pointer to another pointer:

int **pt_ptr;
Enter fullscreen mode Exit fullscreen mode

This declares the pointer variable pt_ptr, which can to hold the memory address of another pointer to integer.

Think about the following code, for instance:

int num = 9;
int *ptr = &num;
int **pt_ptr = &ptr;
Enter fullscreen mode Exit fullscreen mode

Here, pt_ptr is a pointer that stores the memory address of the pointer variable, which points to the integer num. You can access the value stored at the memory location pointed to by pt_ptr using the following code:

int value = **pt_ptr;
Enter fullscreen mode Exit fullscreen mode

Here, the * operator is used twice to dereference the pointer to access the value stored at the memory location pointed to indirectly by pt_ptr.

The first time you dereference pt_ptr with * pt_ptr, you can get the memory address of num, the second time you dereference * pt_ptr by saying ** pt_ptr, you can now get the value stored in num, which is 9, therefore 9 is then assigned to value, which is a variable of type int.

Conclusion

The C programming language has a powerful feature called pointers that lets you work with memory directly. We explored pointers in C programming and provided examples of how to use them with strings, integer arrays, nested arrays of strings, and nested arrays of arrays of numbers in this blog post. We also covered how to pass arrays as arguments and how to use pointers as parameters to functions. The use of pointers to pointers and pointer arithmetic was also covered, along with examples. You can create more effective and powerful applications by grasping how pointers operate in C programming.

Top comments (3)

Collapse
 
pauljlucas profile image
Paul J. Lucas

char *str = "hello";

That should be char const *str = "hello"; Without it, the compiler would have erroneously allowed you to modify the string — which is undefined behavior.

In the above example, str is a string literal instead of a string array.

A string literal is a string array (of char), just a constant one. Also note that any constant array also can not be changed:

int const a[] = { 1, 2, 3 };
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jiayisu profile image
Jiayi Su

Hi Paul,

Thank you for your comment. And it is a great suggestion to declare str as char const *str = "hello", I really like it, as it is more appropriate since it prevents attempts to modify the string literal which is undefined behaviour.

To add on to your comment, I should have said that str is a string literal instead of a normal string array. Thank you for pointing this out. It indeed is an array of null terminated char's, the main difference is that a string literal is stored in the read-only memory.

It would be better to define constant arrays with the keyword const to prevent later attempts to modify it, which would result in undefined behaviour.

That is a great comment. Thank you again for your great suggestion :D

Collapse
 
pauljlucas profile image
Paul J. Lucas

str isn't a string literal. The only things that are string literals are literally strings like "hello" — that's why they're called string literals. In this case, str is a pointer that points to a string literal.