Pointers and Arrays

The relationship between pointers and arrays is very close. Consider the following example:

1

2

char str[80], *p1;

p1 = str;

Here, p1 is set to the address of the first element of the str array . To access the fifth element of the array we can write

1

2

3

str[4];

/* atau */

*(p1+4)

The two statements above will return the value of the fifth element. Remember, arrays start with 0. To access the fifth element, we must use 4 to index str . We also add 4 to the p1 pointer to access the fifth element because p1 previously pointed to the first element of the str array . (Remember that an array name without an index returns the first address of that array, which is the address of the first element.)

C / C ++ provides two ways to access arrays: pointer arithmetic and array-indexing. Although array-indexing seems easier, pointer arithmetic is faster. Because speed is a comparison in C / C ++ programming, programmers usually use pointers to access arrays.

Following are the two versions of the putstr () function , the first using array-indexing and the second with pointers. This putstr () function writes the string entered as an argument to the console.

1

2

3

4

5

6

7

8

9

10

11

12

/* Dengan mengindekskan array s. */

void putstr(char *s)

{

register int t;

for (t = 0; s[t]; ++t) putchar(s[t]);

}

 

/* Mengakses s dengan pointer. */

void putstr(char *s)

{

while (*s) putchar(*s++);

}

Pointer Array

Pointers can also be arrayed like other data types. For example the declaration for an array of pointers to int with 10 elements is

1 int *x[10];

To assign the address of a variable (for example the var variable ) to the third element of the pointer array, write

1 x[2] = var;

to find the value of var , write it

1 *x[2]

To pass an array of pointers to a function, we can use the same method used to pass other arrays, just call the function with an array name without an index. For example, a function that can accept array x will look like this

1

2

3

4

5

6

void display_array(int *q[])

{

int t;

for(t=0; t<10; t++)

printf(“%d”, *q[t]);

}

Remember, q is not a pointer to an integer, but q is a pointer to a pointer to an integer array (See picture below.) Parameter q will not be able to accept an argument other than a pointer to an integer, because that q is declared as an array of pointers.

A pointer array is often used to store pointers to strings. We can create functions that generate error messages based on numeric codes, such as the following:

1

2

3

4

5

6

7

8

9

10

void syntax_error(int num)

{

static char *err[] = {

“Cannot Open File\n”,

“Read Error\n”,

“Write Error\n”,

“Media Failure\n”

};

printf(“%s”, err[num]);

}

array err stores a pointer to each string. As we can see, printf () in syntax_error () is called with a character pointer pointing to one of the various error messages indexed based on the numbers passed to the function. For example, if num is given a value of 2, the Write Error message will be displayed.

Multiple Indirection

We can create a pointer that points to a pointer that points to the target value. This situation is called multiple indirection , or pointers to pointers . Look at the picture to better understand the concept. The value of a normal pointer is the address of the object that contains the desired value. In the case of a pointer to pointer, the first pointer contains the address of the second pointer, which points to the object containing the desired value.

Multiple Indirection can be done at any level needed, but moreover pointers to pointers are rarely needed. In fact, excessive indirection is difficult to control and vulnerable to conceptual errors.

Variables that are pointers to pointers must be declared as such. This is done by adding an asterisk in front of the variable name. For example, the following declaration tells the compiler that newbalance is a pointer to a float type pointer :

1           float **newbalance;

It should be understood that newbalance is not a pointer to a floating-point number but is a pointer to a float pointer .

To access the target value that is not directly pointed by the pointer to the pointer, we must use the asterisk twice, as in the following example:

1

2

3

4

5

6

7

8

9

10

#include <stdio.h>

int main(void)

{

int x, *p, **q;

x = 10;

p = &x;

q = &p;

printf(“%d”, **q); /* tampilkan nilai x */

return 0;

}

Here, p is declared as pointer to integer and q as pointer to pointer to interger. The call printf () displays the number 10 on the console.

Pointer Initialization

After a non-static local pointer is declared but before it is given a value, then the pointer contains an unknown value. (Global and static local pointers are automatically initialized to zero.) So when using a pointer before providing a valid value, the possibility of the program getting an error and its severity might have an impact on the operating system, an extremely dangerous type of error.

There is an important rule that is followed by most C / C ++ programmers when dealing with pointers: Pointers that do not point to any memory location are given an empty value (zero). By rule, all pointers that are empty mean that they don’t point anywhere and shouldn’t be used. However, just because a pointer is empty doesn’t make it “safe”. Using this blank value is just a rule followed by the programmer. That is not a rule made by C or C ++ languages. For example, if we use the empty pointer to the left of the assignment statement, we still run the risk of program or operating system failure.

Because empty pointers are considered unused, we can use empty pointers to make pointer routines easier to encode and more efficient. For example, we can use an empty pointer to mark the end of the pointer array. The routine that accesses the array will know that it has reached the end when it encounters an empty value. The following search () function illustrates this type of approach.

1

2

3

4

5

6

7

8

/* Mencari nama */

int search(char *p[], char *name)

{

register int t;

for(t=0; p[t]; ++t)

if(!strcmp(p[t], name)) return t;

return -1; /* tidak ditemukan*/

}

The for loop in the search () function will continue until a match or empty pointer is found. Assuming that the end of the array is empty, the loop control condition is false when this empty value is found.

C / C ++ programmers usually initialize strings. We can see an example of the syntax_error () function above. Other variations of this initialization are:

1 char *p = “hello world”;

As we can see, pointer p is not an array. The reason why this initialization was successful was because of how the compiler operates. All C / C ++ compilers create a string table , which is used to store the constant string used by the program. That way, the above declaration places the hello world address , stored in a string table, into the p pointer . This p pointer can be used the same as any other string (but this string cannot be changed). In order to better understand it, consider the following sample program:

1

2

3

4

5

6

7

8

9

10

11

#include <stdio.h>

#include <string.h>

char *p = “hello world”;

int main(void)

{

register int t;

/* print the string forward and backwards */

printf(p);

for(t=strlen(p)-1; t>-1; t–) printf(“%c”, p[t]);

return 0;

}

In the C ++ standard, the type of string literals is const char * . But C ++ provides automatic conversion to char * . Thus, the above program is still valid. However, this automatic conversion is an outdated feature, which means that we shouldn’t rely on this feature for new code. For new programs, we must assume that string literals are constant and the declaration of p in the above program must be written like this.

1 const char *p = “hello world”;

Pointer to Function

Another great feature of C ++ is the function pointer , though it’s confusing. Functions are not the same as variables, but functions also have physical locations in memory that can be assigned to pointers. This address is the entry point to the function and is the address used when the function is called. When a pointer points to a function, the function can be called via a pointer. Function pointers also allow functions to be passed as arguments to other functions.

To get the function address, we only need to write the function name without parentheses or arguments. Consider the following example for more details:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#include <stdio.h>

#include <string.h>

void check(char *a, char *b,

int (*cmp)(const char *, const char *));

int main(void)

{

char s1[80], s2[80];

int (*p)(const char *, const char *);

p = strcmp;

gets(s1);

gets(s2);

check(s1, s2, p);

return 0;

}

void check(char *a, char *b,

int (*cmp)(const char *, const char *))

{

printf(“Testing for equality.\n”);

if(!(*cmp)(a, b)) printf(“&quot;Equal”);

else printf(“Not Equal”);

}

When the check () function is called, two character pointers and one function pointer are given as parameters. In the check () function , arguments are declared as character pointers and function pointers. Notice how the function pointer is declared. We must use the same form when deciphering other function pointers, although the return type and function parameters may be different. The brackets in * cmp are needed so that the compiler can interpret the statement correctly.

In check () , expression

1 (*cmp) (a, b)

call strcmp () , designated by cmp , with arguments a and b. Parentheses in * cmp must be present. This is one way to call functions through pointers. Secondly, the simpler syntax, as follows, can also be used.

1 cmp(a, b);

But we will more often encounter the first syntax style, this is because it clearly states that a function is being called using a pointer. The second form can make us assume that cmp is another function. Apart from that, these two expressions are the same.

Note that check () can be called using strcmp () directly, like this:

1 check(s1, s2, strcmp);

This eliminates the need for additional pointer variables.

We might wonder why there are people who write programs like this. Of course, nothing is found and there are significant errors made in the previous example. However, sometimes it is more beneficial to pass functions as parameters or create arrays of functions. For example, when a compiler or translator is written, the parser (the part that checks the expression) often uses various support functions, for example functions that calculate mathematical operations, perform I / O, or access to the system. Instead of using a switch statementwith many of these functions in it, an array of function pointers can be created. In this way, the right function is chosen based on its index. To better understand it we will use the development version of the previous example. In this program, check () can be made to check alphabetical equations or numeric equations by calling them with different comparison functions.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

#include <stdio.h>

#include <ctype.h>

#include <stdlib.h>

#include <string.h>

void check(char *a, char *b,

int (*cmp)(const char *, const char *));

int numcmp(const char *a, const char *b);

int main(void)

{

char s1[80], s2[80];

gets(s1);

gets(s2);

if(isalpha(*s1))

check(s1, s2, strcmp);

else

check(s1, s2, numcmp);

return 0;

}

void check(char *a, char *b,

int (*cmp)(const char *, const char *))

{

printf(“Testing for equality.\n”);

if(!(*cmp)(a, b)) printf(“Equal”);

else printf(“Not Equal”);

}

int numcmp(const char *a, const char *b)

{

if(atoi(a)==atoi(b)) return 0;

else return 1;

}

In this program, if we enter the alphabet, strcmp () is given to check () . If not, numcmp () is used. Because check () calls the function it receives as an argument, it can use comparisons of different functions in different cases.

 

Leave a Comment

Scroll to Top