HPlogo HP C/HP-UX Reference Manual: Version A.05.55.02 > Chapter 5 Expressions and Operators

Pointer Operators (*, ->, &)

» 

Technical documentation

Complete book in PDF

 » Table of Contents

 » Index

Syntax

*ptr_exp

Dereferences a pointer. That is, finds the contents stored at the virtual address that ptr_exp holds.

ptr->member

Dereferences a ptr to a structure or union where member is a member of that structure or union.

&lvalue

Finds the virtual address where the lvalue stored.

Description

A pointer variable is a variable that can hold the address of an object.

Assigning an Address Value to a Pointer

To declare a pointer variable, you precede the variable name with an asterisk. The following declaration, for example, makes ptr a variable that can hold addresses of long int variables:

long *ptr;

The data type, long in this case, refers to the type of variable that ptr can point to. To assign a pointer variable with the virtual address of a variable, you can use the address-of operator &. For instance, the following is legal:

long *ptr;
long long_var;
ptr = &long_var; /* Assign the address of long_var to ptr. */

But this is illegal:

long *ptr;
float float_var;
ptr = &float_var; /* ILLEGAL - because ptr can only store the
address of a long int. */

The following program illustrates the difference between a pointer variable and an integer variable.

/* Program name is "ptr_example1". */
#include <stdio.h>
int main()
{
int j = 1;
int *pj;
pj = &j; /* Assign the address of j to pj */
printf("The value of j is: %d\n", j);
printf("The address of j is: %p\n", pj);
}

If you run this program (in 32-bit mode), the output looks something like this:

The value of j is: 1
The address of j is: 7b033240

Dereferencing a Pointer

To dereference a pointer (get the value stored at the pointer address), use the * operator. The program below shows how dereferencing works:

/* Program name is "ptr_example2". */

#include <stdio.h>

int main(void)
{
char *p_ch;
char ch1 = 'A', ch2;
printf("The address of p_ch is %p\n", &p_ch);
p_ch = &ch1;
printf("The value of p_ch is %p\n", p_ch);
printf("The dereferenced value of p_ch is %c\n",
*p_ch);
}

The output from this program looks something like this:

The address of p_ch is 7b033240
The value of p_ch is 7b033244
The dereferenced value of p_ch is A

This is a roundabout and somewhat contrived example that assigns the character A to both ch1 and ch2. It does, however, illustrate the effect of the dereference (*) operator. The variable ch1 is initialized to A. The first printf() call displays the address of the pointer variable p_ch. In the next step, p_ch is assigned the address of ch1, which is also displayed. Finally, the dereferenced value of p_ch is displayed and ch2 is assigned to it.

The expression *p_ch is interpreted as “Take the address value stored in p_ch and get the value stored at that address.” This gives us a new way to look at the declaration. The data type in the pointer declaration indicates what type of value results when the pointer is dereferenced. For instance, the declaration

float *fp;

means that when *fp appears as an expression, the result will be a float value.

The expression *fp can also appear on the left side of an expression:

*fp = 3.15;

In this case, we are storing a value (3.15) at the location designated by the pointer fp. This is different from

fp = 3.15;

which attempts to store the address 3.15 in fp. This, by the way, is illegal, because addresses are not the same as floating-point values.

When you assign a value through a dereferenced pointer, make sure that the data types agree. For example:

/* Program name is "ptr_example3". */

#include <stdio.h>

int main(void)
{
float f = 1.17e3, g;
int *ip;
ip = &f;
g = *ip;
printf("The value of f is: %f\n", f);
printf("The value of g is %f\n", g);
}

The result is

The value of f is: 1170.000000
The value of g is: 1150435328.000000

In the preceding example, instead of getting the value of f, g gets an erroneous value because ip is a pointer to an int, not a float. The HP C compiler issues a warning message when a pointer type is unmatched. If you compile the preceding program, for instance, you receive the following message:

cc: "ptr_example3.c", line 9: warning 604: Pointers are not
assignment-compatible.

Pointer Arithmetic

The following arithmetic operations with pointers are legal:

  • You may add an integer to a pointer or subtract an integer from a pointer.

  • You may use a pointer as an operand to the ++ and operators.

  • You may subtract one pointer from another pointer, if they point to objects of the same type.

  • You may compare two pointers

All other arithmetic operations with pointers are illegal.

When you add or subtract an integer to or from a pointer, the compiler automatically scales the integer to the pointer's type. In this way, the integer always represents the number of objects to jump, not the number of bytes. For example, consider the following program fragment:

int x[10], *p1x = x, *p2x;

p2x = p1x + 3;

Since pointer p1x points to a variable (x) that is 4 bytes long, then the expression p1x + 3 actually increments p1x by 12 (4 * 3), rather than by 3.

It is legal to subtract one pointer value from another, provided that the pointers point to the same type of object. This operation yields an integer value that represents the number of objects between the two pointers. If the first pointer represents a lower address than the second pointer, the result is negative. For example,

&a[3] - &a[0]

evaluates to 3, but

&a[0] - &a[3]

evaluates to -3.

It is also legal to subtract an integral value from a pointer value. This type of expression yields a pointer value. The following examples illustrate some legal and illegal pointer expressions:

long *p1, *p2;
int a[5], j;
char *p3;

p1 = a; /* Same as p1 = &a[0] */
p2 = p1 + 4; /* legal */
j = p2 - p1; /* legal -- j is assigned 4 */
j = p1 - p2; /* legal -- j is assigned -4 */
p1 = p2 - 2; /* legal -- p2 points to a[2] */
p3 = p1 - 1; /* ILLEGAL -- different pointer types*/
j = p1 - p3; /* ILLEGAL -- different pointer types*/
j = p1 + p2; /* ILLEGAL -- can't add pointers */

Arrays and Pointers

Arrays and pointers have a close relationship in the C language. You can exploit this relationship in order to write more efficient code. See the discussion of “Array Subscripting ([ ]) ” for more information.

Casting a Pointer's Type

A pointer to one type may be cast to a pointer to any other type. For example, in the following statements, a pointer to an int is cast to a pointer to a char. Presumably, the function func() expects a pointer to a char, not a pointer to an int.

int i, *p = &i;
func((char *) p);

As a second example, a pointer to a char is cast to a pointer to struct H:

struct H {
int q;
} x, y;
char *genp = &x;
y = (struct H *)genp->q;

See “Cast Operator ” for more information about the cast operator.

It is always legal to assign any pointer type to a generic pointer, and vice versa, without a cast. For example:

float x, *fp = &x;
int j, *pj = &j;
void *pv;
pv = fp; /* legal */
fp = pv; /* legal */

In both these cases, the pointers are implicitly cast to the target type before being assigned.

Null Pointers

The C language supports the notion of a null pointer — that is, a pointer that is guaranteed not to point to a valid object. A null pointer is any pointer assigned the integral value 0. For example:

char *p;
p = 0; /* make p a null pointer */

In this one case — assignment of 0 — you do not need to cast the integral expression to the pointer type.

Null pointers are particularly useful in control-flow statements, since the zero-valued pointer evaluates to false, whereas all other pointer values evaluate to true. For example, the following while loop continues iterating until p is a null pointer:

char *p;
. . .
while (p) {
. . .
/* iterate until p is a null pointer */
. . .
}

This use of null pointers is particularly prevalent in applications that use arrays of pointers.

The compiler does not prevent you from attempting to dereference a null pointer; however, doing so may trigger a run-time access violation. Therefore, if it is possible that a pointer variable is a null pointer, you should make some sort of test like the following when dereferencing it:

if (px && *px) /* if px = 0, expression will short-circuit
. . . before dereferencing occurs*/

Null pointers are a portable feature.

Example 1

/* Program name is "pointer_array_example1". This program
* shows how to access a 1-dimensional array through
* pointers. Function count_chars returns the number of
* characters in the string passed to it.
* Note that *arg is equivalent to a_word[0];
* arg + 1 is equivalent to a_word[1]...
*/
#include <stdio.h>

int count_chars(char *arg)
{
int count = 0;
while (*arg++)
count++;
return count;
}

int main(void)
{
char a_word[30];
int number_of_characters;
printf("Enter a word -- ");
scanf("%s", a_word);
number_of_characters = count_chars(a_word);
printf("%s contains %d characters.\n", a_word,
number_of_characters);
}

If you execute this program, you get the following output:

Enter a word -- Marilyn
Marilyn contains 7 characters.

Example 2

/* Program name is "pointer_array_example2". This program
* demonstrates two ways to access a 2-dimensional array.
*/
#include <stdio.h>

int main(void)
{
int count = 0, which_name;
char c1, c2;
static char str[5][10] = {"Phil", "Sandi", "Barry",
"David", "Amy"};
static char *pstr[5] = { str[0], str[1], str[2],
str[3], str[4]};
/* pstr is an array of pointers. Each element in the array
* points to the beginning of one of the arrays in str.
*/
/* Prompt for information. */
printf("Which name do you want to retrieve?\n");
printf("Enter 0 for the first name,\n");
printf(" 1 for the second name, etc. -- ");
scanf("%d", &which_name);
/* Print name directly through array. */
while (c1 = str[which_name][count++])
printf("%c", c1);
printf("\n");

/* Print same name indirectly through an array of pointers. */
while (c2 = *(pstr[which_name]++))
printf("%c", c2);
/* We could also have used the following statement instead of
* the two previous ones: printf("%s", pstr[which_name]);
*/
printf("\n");
}

If you execute this program, you get the following output:

Which name do you want to retrieve?
Enter 0 for the first name,
1 for the second name, etc. -- 1
Sandi
Sandi

© Hewlett-Packard Development Company, L.P.