Type Casts

You now know enough about dynamic memory to reserve blocks of it, but what you actually want to do is to store and retrieve data in these blocks.

The void pointer variable introduced in the previous section allows you to store and retrieve any kind of address, but it specifically does not allow you to read or write any data through it using the dereferencing operator (*) as you learned how to do in Chapter 9. The reason for this is that the void pointer variable lacks data type information. Since a void pointer can point to anything, the system cannot know if it should read or write a 1-byte char or a 4-byte integer, or something completely different.

What the system needs in such a situation is a hint from you, the programmer. You have to tell it how you would like that memory address accessed: as a character or as an integer, for example.

The mechanism for giving the system such a hint is called a cast or type cast. The syntax used for a cast is a pair of parentheses with the desired pointer type between them, followed by the address value:

void x*;
(int *)x

This tells the system that the address stored in the x pointer variable should be interpreted as the address of an integer value. You can precede this construct with the dereferencing operator to actually read an integer value from that memory address:

printf("The value of x is %p.",
 *(int *)x);

Let's try an example so that this idea will make better sense.

To use the type cast operator

1.
Create a new file or project in your text editor or IDE.

2.
Type the standard beginning lines of code (Script 10.2):

/* typecast.c - Script 10.2 */
#include <stdio.h>
#include <stdlib.h>

Script 10.2. Type casts supply missing type information to the compiler and are critical when working with dynamic memory.


3.
Begin the main function:

int main (void) {

4.
Define and initialize a void pointer variable:

void *x = NULL;

It's good practice to initialize pointer variables with the NULL pointer value. Much like setting such a pointer to NULL after freeing memory, this makes it clear that x points to nothing.

5.
Call the malloc() function and ask for a block of memory big enough to hold an integer. Store the address of the block returned by malloc() in the void pointer variable:

x = malloc(sizeof(int));

Since the amount of memory required to store an integer is machine-dependent, you have to use the sizeof() operator to find out how many bytes will be required. On most contemporary systems, sizeof(int) will return 4.

We have again omitted the check for a NULL return value from malloc(), but you can include it here because you should always check for NULL in a real program.

6.
Print the address of the block:

printf("Starting address: %p.
", x);

7.
Store an integer value into the memory block using the type cast and dereferencing operators:

*(int *)x = 1234;

The x refers to the address of the dynamically reserved memory block. The (int *) before the x type-casts that as an integer. Finally, the dereferencing operator (the initial *) allows you to read from or write to the memory block. In this case, the value 1234 will be stored there.

8.
Print the value stored in the memory block:

printf("Value stored in the memory
 block: %d.
", *(int *)x);

Just as you can use a combination of the type cast and dereferencing operators to assign a value to a memory block, the combination of the two can be used to read in a value stored in a memory block. The %d signifier is used since the value stored is an integer.

9.
Return the block to the pool using the free() function:

free(x);
x = NULL;

10.
Complete the main function:

    getchar();
    return 0;
}

11.
Save the file as typecast.c, compile, and debug as necessary.

12.
Run the application (Figure 10.3).

Figure 10.3. Thanks to type casting, a void pointer can be used like an int pointer.


✓ Tips

  • In more complex expressions involving type casts and the dereferencing operator, you might want to add a pair of parentheses: *((int *)x). This ensures that the expression is evaluated the way you meant it to be.

  • If the combination of the dereferencing and type cast operators looks too confusing to you, you can always assign the address stored in the void pointer to a nonvoid pointer variable of the appropriate type:

    void *x; // void pointer variable
    int *y; // integer pointer variable
    y = x;
    Now you can use *y instead of
     *(int *)x.
    

When You Don't Need Casts

After you've seen type casting in action, here's an important simplification: You do not need the type casts when assigning or comparing address values if one of the pointers is a void pointer. This means that these statements are all legal without casts:

int a, *b;
void *x;
x = &a;
b = x;
if (x == &a) ...

It also means that the previous example could be simplified by using an integer pointer variable instead of a void pointer variable:

int *x;
x = malloc(sizeof(int));
*x = 1234;
printf("Value stored in the memory block: %d.
", *x);
free(x);
x = NULL;

It is important to note that the malloc() function still returns a void pointer. You are allowed to omit the type cast in the assignment to x because the fact that you are storing the (untyped) address in an integer pointer variable already tells the system how you intend to use the block of memory.

Also note that the sizeof(int) expression is not what tells the system that you are planning to use the memory block to store an integer; it just specifies how to determine the size (in bytes) of an integer on a particular machine.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset