Learn C Language – Part 2: Pointer and Array

Part 2 begins…

So, here we are. The second section of the C language’s pointer concept. In this post, we’ll take a closer look at how pointers can mimic arrays by referring to their addresses on the memory. Also, handling memories is something crucial and a privilege of C programmers. Great powers come with great responsibility. Let’s dive into the deeper journey of the legendary language, shall we?

This is the second installment in my C language tutorial. Since I’m still learning the language, so this post also works as my daily progress tracker. So, let’s begin the journey!

Pointer and array: basics

In the program below3 (sample_ex3-1.c), there are four variables:

int/int*char/char*
Regular variablear1ar2
Pointer variablep1p2
Table 01: variables

Here, what we want to show you is that pointer variables can also be array variables as well.

image 01: sample_ex3-1.c

Here is the output result. As you can see, by adding 1, the pointer variables also add up to its value accordingly.

image 02: output result

Let’s put the breakpoint in line 13.

image 03: breakpoint

This is the variables’ initial status. p1 and p2 are initialized as null, so the initial values are zero.

image 04: initial values

ar1 and ar2 are expanded. Since they are also initialized, random values are substituted in the variables.

image 05: initial values

When you step over once, zero is substituted in the variable i.

image 06: the first step over

And the zero is substituted in ar1[0] in the second step over.

image 07: the second step over

A is substituted in ar2[0] in the third step over. (65 is the ASCII number for the upper case A).

image 08: third step over

Here is the result when you finish executing stepovers until the end of the ar1 and ar2 values.

variablesvalues
ar10 to 4
ar2A to E
Table 02: stepovers for ar1 and ar2

image 07: stepovers for ar1 and ar2

And when you stop your stepover at the beginning of the p1-p2 loop, you can confirm that the very beginning address digits of p1 and p2 are exactly the same as that of ar1 and ar2. It’s because the p1 and p2 is referring to the memory addresses of ar1 and ar2, instead of their values.

image 08: p1 and p2 values

Change your IDE perspective to watch, and type p1. Then you can see the aforementioned memory address.

image 09: p1 on watch

By adding one by one, values are also increased accordingly. What you can confirm here is that p1[0] to p1[4] is equal to ar1[0] to ar1[4].

image 10: p1[0] to p1[4]

Since p1[5] is out of the array range, it doesn’t work properly.

image 11: p1[5]

The logic is the same in p2. By adding one by one, the alphabet is increased accordingly. by adding a number to the pointer variable, it refers to its counterpart in the ar1/ar2 array.

image 12: p2

The relationship between pointer and array

What’s important to know is that an array is an unusual version of a pointer. In the example below (sample_ex3-2.c), the pointer variables p1 and p2 work as the array variable d.

And also what’s important here is that by substituting an array variable for a pointer variable, the pointer can act like an array.

image 13: sample_ex3-2.c

Here is the output result. The results are all the same, but how they printout vary.

image 14: output result

Here, the breakpoint is inserted in line 8.

Here are some key points in image 14:

1: the below syntax in line 11 is similar to that of a pointer despite the fact that d is indeed an array. But this syntax is okay.

*(d + i)

2: Line 11’s below syntax is similar to that of an array despite the fact that p1 is a pointer. And still, it’s okay.

p1[i]

3: In line 11, the p2 indicator is incremented in line 12.

*p2 //line 11
p2++ //line 12
image 14: breakpoint

The first stop in the breakpoint indicates that the values from line 5 are inserted in the array variable d.

image 15: initial result

After executing stepovers a couple of times, you can see that p1 and p2 are now referring to the same address of d.

image 16: p1 and p2

Switching to the watch perspective, you can confirm the same values from d by typing through p1[0] to p1[2].

image 17: watch perspective for p1

d is also referring to the same address as shown in the above image. And you can confirm that by typing *d and *(d+1) and such.

image 18: p1 and d

Since p2 is updated (incremented) by p2++ in line 12, you can’t list all the values of the variable. At this point in the image below, p2 is currently stopped at &d[1].

image 19: p2

This is the current output result.

image 20: output result

When you finish executing the stepovers, you can see that p2 reached its endpoint.

Just remember, an array is a special version of a pointer.

image 21: end result of stepover

Generating and deleting memories

In the previous section, we confirmed the fact that pointers can work as arrays. Here, in the last section of this post, we’ll see that pointers can be arrays themselves. This happens when we dynamically generate pointers. So, what does it mean dynamically? Well, it means you can generate pointers whenever/wherever you like, and delete them whenever/wherever you like.

image 22: sample_ex3-3.c

Here is the output result where the program just prints out the values. What’s important here is the fact that we haven’t generated actual values in the program.

image 23: output result

In line 11 and 12, we’ve actually generated arrays. The malloc function stores a specified number of memories according to line 4 – SIZE = 3. So, in this case, we’ve generated 3 arrays. And, (int*) and (double*) cast the generated arrays into pointer-type.

What’s important to learn here is that malloc generates memories in the heap area, where you can generate data whenever/wherever you like. And memories generated in the area must be librated at some point within the program.

image 24: malloc

In line 23 and 24, the free function librates the memories. What we need to remember is that malloc and free always come together.

image 25: free

To see how the program works, I inserted the breakpoint in line 11.

image 26: breakpoint

In the first stop, 0 is substituted as they are initialized in line 7 and 8.

image 27: first stop

In the first stepovers, memory addresses are substituted for p1 and p2.

image 28: stepping over

Switching to the watch perspective, you can see that all the values inside p1 and p2 are just initialized.

image 29: watch perspective

By stepping over, you can confirm that 0 and 0.0 are substituted for p1[0] and p2[0] respectively.

image 30: stepping over

As you step over till the end, you can see that all the values are now substituted for arrays.

image 31: stepping over till the end

What we need to be careful of is the scary fact that values out of the arrays’ ranges can also be stored in the arrays. in this case, it might be impossible to free those areas in the memory. And worse it could destroy the memory system.

image 32: out of range

Here, by inserting another breakpoint in line 23, we can see how memory areas will be librated.

image 33: another breakpoint

As you execute stepovers, you can confirm that p1’s memories are now liberated.

image 34: freeing p1 memories

And p2’s memories are now also librated.

image 35: freeing p2 memories

Lastly, we need to learn something important about memories. The regular variables declared in line 7 and 8 are stored in the stack area, while the malloc variables in line 11 and 12 are stored in the heap area. And the qualities of the two different memories are a little different.

The stack area’s memories are automatically liberated when the main program processes are finished. This means that the memories in the stack area are out of your control.

On the other hand, heap area’s memories are allowed to be generated by programmers whenever/wherever they like, but also they must be manually deleted within the program process, meaning they are under your control.

image 36: variables

Leave a Reply