0% found this document useful (0 votes)
8 views11 pages

Understanding Pointers in C Programming

Chapter 1 discusses pointers in programming, explaining memory addresses, pointer operations, and the advantages and disadvantages of using pointers. It highlights the importance of dynamic memory allocation and the differences between static and dynamic allocation in C, along with examples of pointer usage and memory management. The chapter also covers the relationship between arrays and pointers, as well as the use of the malloc() function for dynamic memory allocation.

Uploaded by

Zinou Géo
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views11 pages

Understanding Pointers in C Programming

Chapter 1 discusses pointers in programming, explaining memory addresses, pointer operations, and the advantages and disadvantages of using pointers. It highlights the importance of dynamic memory allocation and the differences between static and dynamic allocation in C, along with examples of pointer usage and memory management. The chapter also covers the relationship between arrays and pointers, as well as the use of the malloc() function for dynamic memory allocation.

Uploaded by

Zinou Géo
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Chapter 1

Pointers

1 Variable address
A memory address represents the variables manipulated in a program. This mem-
ory consists of a series of boxes. In these boxes are stored the names and values of
the variables or the program’s instructions. In order to access an object (the value
of a variable or the instructions to be executed), one must access the content of
the corresponding memory box and, therefore, know the number of this box and
know the memory location of the object to be manipulated. This location is called
the memory address.
Memory
Address
Case

276314
276315
100
276316
276317
276318
276319
200
276320
276321
276322
One
276323
300 box = 1
276324 byte
276325
276326
276327
276328
276329
276330
char ch= ’c’
276331 est représentée
276332 65 par
276333 (67 = code
ascii )
276334

Figure 1.1: Memory representation

Figure 1.1 shows a representation of the memory. This diagram illustrates the
memory addresses in the left part and the memory cells just after. Notice that 4
memory cells represent a variable coded on 4 bytes (in another case, we can find a

1
variable on 8 bytes, like the double type). In the same figure, when declaring the
variable ch, it is represented in memory in ASCII code, and its value is 65 on one
byte, whereas in the case of the type int, the size in memory is 4 bytes.
The memory address is unique; it points to a single content. When using a
variable or a function, the compiler manipulates the address of the latter to access
it. He is the one who knows this address. The user does not have to worry about
it.

2 Pointers
A pointer is an object in programming that stores a memory address of another
variable of defined type. Somehow, it is indirect addressing instead of directly
using the value of the variable by using its address. Figure 1.2 shows how an
integer variable that has the value 2 and P is a pointer containing the address of a.
Pointers are a powerful tool for defining dynamic structures, thus allowing these

p a 2

0x60fefc 0x60fef4

Figure 1.2: Indirect addressing

structures to evolve over time. Unlike the array, which is a static structure, the
size is fixed and cannot be modified. In C language, a pointer must have a precise
and explicit type.

3 Advantages and disadvantages of pointers


The use of a pointer means that the system does not manage plus these variables,
on the other hand, it is the program that allocates or frees the memory.
There are huge speed benefits and predictable performance, but it requires more
programming skills because the programmer has to manage memory resources in
code. Speed comes from pointer indirection, while performance comes from freeing
up memory.

2
4 Pointer operations
Address operator & and *
To declare a pointer, we use the * operator with an explicit type. The * operator
is also used to return the value of the pointed variable. The * operator is called
the dereferencing operator.
1 # include < stdio .h >
2 intmain ()
3 {
4 int n = 20;
5 printf ( " \ nThe address of n is % u " ,& n ) ;
6 printf ( " \ nThe address of n is % d " ,n ) ;
7 printf ( " \ nThe address of n is % d " ,*(& n ) ) ;
8 }
Result

The address of n is 1002


The address of n is 20
The address of n is 20

The declaration of a pointer in C makes it possible to initiate a variable that points


to an integer, for example, and its value contains an address of another integer
variable, as every pointer has a type, it also has a size, as shown in the table:

Declaration Meaning Size in memory


1 int *p p will point to integer value 4 bytes
2 float *q q points to a real variable address 4 bytes
3 char *ch ch points to a character 4 bytes
4 double *pt pt points to a real variable 8 bytes

The address of a variable is denoted by the operator &. When we use the
operator & as a prefix of a variable, it gives the address of this variable in memory.
1 # include < stdio .h >
2 void main ()
3 {
4 int n = 10;
5 printf ( " \ nValue of n is : % d " ,n ) ;
6 printf ( " \ nValue of & n is : % u " ,& n ) ;
7 }
Result

3
Value of n is: 10
Value of &n is: 6356720

Initializing pointers
A pointer can be initialized a pointer. We can either assign a zero or NULL
1 int * ptr2 ,* ptr1 ;
2 ptr1 = 0;
3 tptr2 = NULL ;

Pointer arithmetic
Using pointers, we can afford only two operations addition, subtraction, and com-
parison. However, its two operations operate differently. If we have p as a pointer
to an integer, p+1 means that we obtain the address of the integer, which imme-
diately follows the one pointed to by p. The address of the new element is relative
to the size of the integer in bytes. If by the same concept, we p = p ∗ i, the pointer
p will have to point to the integer whose address is i*size (sizeof()) of the integer.
The same goes for subtracting an integer from a pointer and for the increment and
decrement operators ++ and –.

4.1 Array and pointer


The declaration of an array in C is seen as declaring a constant pointer, meaning
it is an unmodifiable pointer. The address of the array is the same address of its
first element. If we have a pointer ptr that points to an array tab, we have written:
ptr = tab, which is equivalent to ptr =&tab[0]. Similarly, an array can be used as
a pointer, as shown in the following code:

Listing 1.1: Relation between array and pointer


1 hand () {
2 int tab [5];
3 int * ptr = tab ;
4 * ptr =3; // the element tab [0] = 3
5 ptr [4] = 2; // the element tab [4] i . e . tab [4] = 2
6 array [2]=7;
7 array [3]=5;
8 *( tab +3) =4; // the third position of tab initializes to 4
9 array [1]= 0;
10 for ( int i =0; i <5; i ++)

4
11 printf ( " % d " , tab [ i ]) ;
12 }
Result

3 0 7 4 2

Note that the array has taken values either by using direct assignment to array
elements (as in lines: 7, 8) or by pointer using *ptr, which means the address of the
first element of the array tab[ 0], ptr[4] a pointer used as an element of the array
or even *(tab+3) which corresponds to the third element of the array. According
to the program 1.1, we see that the array structure is very close to the pointer and
can be entirely manipulated by a pointer (see the result of exercise 5).
The manipulation of arrays shows some weaknesses since it is a static structure.
It cannot allow the creation of arrays whose size can vary in the program. Also,
creating two-dimensional arrays whose rows do not all have the same number of
elements is still impossible.
Along with array usage limits, dynamic allocation solves the listed issues of the
array.
However, an array differs from a pointer because we must always initialize it
by assigning the address like ptr =&x or by dynamic allocation, and the second
difference is that we cannot use the operators of the pointer to an array, so writing
tab++ is forbidden.

4.2 String, array, and pointer


char * and char[] are both used to access the character array. Although function-
ally, the two are the same, they are syntactically different. See how both work to
access the channel.

Listing 1.2: Example: character array declaration and initialization


1 char ch []= " Example " ;
Consider the 1.2 example for storing and accessing a string using a character
array. In this example, the string "Hello" is stored in the character array "ch".
The character array is used to store characters in a contiguous memory location.
It will take the following form after initialization. We did not specify the size of
the array in this example. Each array location will get the following values.

ch[0] = ’E’
ch[1] = ’x’
ch[2] = ’e’
ch[3] = ’m’

5
ch[4] = ’p’
ch[5] = ’l’
ch[6] = ’e’
ch[7] = ’\0’

To access an element of the array, for example, ch[2], the compiler checks if
"ch" is an array and returns the second element of the array ch.
On the other hand, using char*ch ="Example", the string will be stored in
any random location as an array. We don’t even know where the string is stored.
However, the chain would be known by its starting address. To declare a character
type pointer. The base address of the unknown array is stored in a character
pointer variable. ’ch’ stores the base address of the unknown array. To access
the elements of the array, we write ch[3] or *(ch+3). This means that we want to
access the start of the pointer by moving 3 memory cells (the third element).

Dynamic Allocation
Memory allocation so far is done automatically by the compiler, i.e., it is the
compiler that sets the size of the variable at compile time. The motivation for
dynamic allocation can be summarized in the following points:

• The maximum amount of memory to use cannot be determined at compile


time;

• We want to allocate a very large object;

• We want to create flexible data structures (containers) with no fixed upper


size;

Dynamic allocation is required when the worst-case memory requirements are not
known, so it is not possible to statically allocate the required memory because the
required memory size is not known.
Even if we know the worst case, it may be desirable to use dynamic memory
allocation because it allows it to be used more efficiently by several processes. All
processes can statically commit their worst-case memory requirements, but this
limits the number of running processes that can exist on the system.
Memory allocation in programming is crucial for storing values when assigning
them to variables. The allocation is done either in the compilation phase or during
program execution. The 1.1 array represents some differences between static and
dynamic allocation. Despite the advantages of dynamic allocation, the program-
mer must manage memory carefully since he must free the memory as soon as the
variable is no longer needed. Otherwise, he may overflow the memory.

6
Static Allocation Dynamic Allocation
Permanent memory allocation Allocation is done at the request of the program
Allocation before execution Allocation during execution
It uses the stack It uses the heap
Less Efficient More Efficient
No memory reuse Memory reuse (free cells)

Table 1.1: Difference between static allocation and dynamic allocation

Dynamic allocation in C

The C language is a structured language; it follows fixed rules for programming.


One of them is not being able to change the size of a static array. An array is a
collection of elements stored in continuous memory locations. As we can see in the
t[0] t[1] t[2] t[3] t[4] t[5] t[6] t[7]

1234 56 1212 33 1434 80 1312 78

65508 65512 65516 65520 65524 65528 65532 65536

figure that the length (size) of the above table is 8. But what if it is necessary to
modify this length (size)? For example, in case only 5 elements are needed to be
entered in this array. While the remaining 3 indices waste memory in this array.
So it is necessary to reduce the length (size) of the array from 8 to 5. Another case
is where we have the same array with the 8 elements filled. But it is necessary to
enter 3 other elements in this array. In this case, we need 3 additional slots. So
the length (size) of the array should be changed from 8 to 12. As the C language
does not allow modifying the size of the static array directly, it is advisable to use
pointers to remedy the problems mentioned above.

sizeof() sizeof is a compilation unary operator that can be used to calculate


the size of its operand. The result of sizeof is an unsigned integer type which
is usually denoted by size_t. sizeof can be applied to any data type, including
primitive types such as integer and real types, pointer types, or compound data
types such as Structure, union, etc.
The code demonstrates how sizeof() works and returns the size in bytes of a
few data types.
1 # include < stdio .h >
2 intmain ()
3 {
4 printf ( " % lu \ n " , sizeof ( char ) ) ;
5 printf ( " % lu \ n " , sizeof ( int ) ) ;
6 printf ( " % lu \ n " , sizeof ( float ) ) ;

7
7 printf ( " % lu " , sizeof ( double ) ) ;
8 return 0;
9 }
Result

1
4
4
8

malloc() The “malloc” or “memory allocation” function in C is used to dynam-


ically allocate a single large block of memory with a specified size. It returns a
pointer of type void, which can be converted into a pointer of any form. This
function returns NULL if necessary (insufficient memory space). Its syntax is:
1 ptr = ( type *) malloc ( byte - size )
In the following example, we call on the malloc() function to allocate 5 boxes of
integer type, as the size int is 4 bytes, i.e., we have reserved 20 bytes, and the
pointer ptr points to the first bytes in the allocated slot.
1 # include < stdio .h >
2 # include < stdlib .h >
3
4 int main ()
5 {
6 int * ptr ;
7 int n , i ;
8
9 // Number of elements required
10 n =5;
11 printf ( " Number of elements to reserve : % d \ n " , n ) ;
12
13 // Dynamic allocation using malloc ()
14 ptr = ( int *) malloc ( n * sizeof ( int ) ) ;
15
16 // test if memory reservation failed
17 if ( ptr == NULL ) {
18 printf ( " Failed to allocate memory .\ n " ) ;
19 exit (0) ;
20 }
21 else {

8
22
23 // Successfully allocate memory
24 printf ( " Memory successfully allocated .\ n " ) ;
25
26 // Filling the elements
27 for ( i = 0; i < n ; ++ i ) {
28 ptr [ i ] = i + 1;
29 }
30 // Display of elements
31 printf ( " The elements are : " ) ;
32 for ( i = 0; i < n ; ++ i ) {
33 printf ( " %d , " , ptr [ i ]) ;
34 }
35 }
36 return 0;
37 }
Result:

Number of items to book: 5


Memory allocated successfully.
The elements are: 1, 2, 3, 4, 5,

calloc() The calloc() (contiguous allocation) function in C is used to dynamically


allocate the specified number of memory blocks of the specified type. It initializes
each block with a default value of "0".
1 ptr = ( cast - type *) calloc (n , element - size ) ;
In the preceding code, if we replace line 14 with the line below, we obtain the same
result, the only difference is that the elements are initialized to 0.
1 ptr = ( int *) calloc (n , sizeof ( int ) ) ;

free() The free() function in C is used to dynamically free memory. Memory


allocated using the malloc() and calloc() functions is not freed by itself. Therefore,
the free() method is used whenever dynamic memory allocation takes place. It
helps to reduce memory waste by freeing it.
1 free ( ptr ) ;
Returning to the previous code, if we add the following line at the end of the
program (after line 35), we see that the ptr pointer has completely disappeared
from memory.

9
1 free ( ptr ) ;
The following program summarizes the use of calloc().
1 # include < stdio .h >
2 # include < stdlib .h >
3
4 intmain ()
5 {
6 int * ptr ;
7 int n , i ;
8
9 // Number of elements needed
10 n =5;
11 printf ( " Number of elements to reserve : % d \ n " , n ) ;
12
13 // Dynamic allocation using malloc ()
14 ptr = ( int *) malloc ( n * sizeof ( int ) ) ;
15
16 // test if memory reservation failed
17 if ( ptr == NULL ) {
18 printf ( " Failed to allocate memory .\ n " ) ;
19 exit (0) ;
20 }
21 else {
22
23 // Memory allocation successfully
24 printf ( " Memory allocated successfully .\ n " ) ;
25
26 // Fill elements
27 for ( i = 0; i < n ; ++ i ) {
28 ptr [ i ] = i + 1;
29 }
30 // Display of elements
31 printf ( " The elements are : " ) ;
32 for ( i = 0; i < n ; ++ i ) {
33 printf ( " %d , " , ptr [ i ]) ;
34 }
35 n =10;
36 printf ( " \ n \ nThe new size is : % d \ n " , n ) ;

10
37
38 // Dynamic memory reallocation
39 ptr = realloc ( ptr , n * sizeof ( int ) ) ;
40
41 // Fill elements
42 for ( i = 5; i < n ; ++ i ) {
43 ptr [ i ] = i + 1;
44 }
45
46 // Display of elements
47 printf ( " / the elements of the array are : " ) ;
48 for ( i = 0; i < n ; ++ i ) {
49 printf ( " %d , " , ptr [ i ]) ;
50 }
51 // free memory
52 free ( ptr ) ;
53 }
54 }
55 return 0;
56 }

realloc() The realloc() or “re-allocation” function in C is used to dynamically


change the memory allocation of previously allocated memory. In other words,
if the memory previously allocated using malloc or calloc is insufficient, realloc
can be used to dynamically reallocate the memory. It is used with the following
syntax:
1 ptr = realloc ( ptr , newSize ) ;

11

You might also like