Author : Mkoubaa
Page : << Previous 7 Next >>
int *p; /* a pointer to an integer */
printf("%d\n",*p);
}
This code tells the compiler to print the value p points to. However, p has not been initialized yet; it contains the address 0. A segmentation fault results, which means that you have used a pointer that points to an invalid area of memory. Almost always, an uninitialized pointer or a bad pointer address is the cause of segmentation faults.
Part 10: Using Pointers for Variable Parameters
Most C programmers first need to use pointers for variable parameters. Suppose you have a simple procedure in Pascal that swaps two integer values:
program samp;
var
a,b:integer;
procedure swap(var i,j:integer);
var t:integer;
begin
t:=i;
i:=j;
j:=t;
end;
begin
a:=5;
b:=10;
writeln(a,b);
swap(a,b);
writeln(a,b);
end.
Because this code uses variable parameters, it swaps the values a and b correctly.
C has no formal mechanism for passing variable parameters: It passes everything by value. Enter and execute the following code and see what happens:
#include <stdio.h>
void swap(int i, int j)
{
int t;
t=i;
i=j;
j=t;
}
void main()
{
int a,b;
a=5;
b=10;
printf("%d %d\n",a,b);
swap(a,b);
printf("%d %d\n",a,b);
}
No swapping takes place. The values of a and b are passed to swap, but no values are returned.
To make this function work correctly, you must use pointers, as shown below:
#include <stdio.h>
void swap(int *i, int *j)
{
int t;
t = *i;
*i = *j;
*j = t;
}
void main()
{
int a,b;
a=5;
b=10;
printf("%d %d\n",a,b);
swap(&a,&b);
printf("%d %d\n",a,b);
}
To get an idea of what this code does, print it out, draw the two integers a and b, and enter 5 and 10 in them. Now draw the two pointers i and j, along with the integer t. When swap is called, it is passed the addresses of a and b. Thus, i points to a (draw an arrow from i to a) and j points to b (draw another arrow from b to j). Because the pointers have been established, *i is another name for a, and *j is another name for b. Now run the code in swap. When the code uses *i and *j, it really means a and b. When the function completes, a and b have been swapped.
Suppose you accidentally forget the & when the swap function is called, and that the swap line accidentally looks like this: swap(a,b);. This causes a segmentation fault. When you leave out the &, the value of a is passed instead of its address. Therefore, i points to an invalid location in memory and the system crashes when *i is used.
This is also why scanf crashes if you forget &--- scanf is using pointers to put the value it reads back into the variable you have passed. Without &, scanf is passed a bad address and crashes.
Part 11: Using Pointers for Dynamic Data Structures
Dynamic data structures-those that grow and shrink as you need them to by allocating and deallocating memory from the heap-are extremely important in C. If you have never seen them before, pick up a book on data structures so that you can learn about them in depth.
Dynamic data structures allocate blocks of memory from the heap as required and link those blocks together into some kind of structure that uses pointers. When a structure no longer needs a block, it will return it to the heap for reuse.
The following two examples show the correspondence between Pascal code and C code using the heap. The first example allocates an integer block, fills it, writes it, and disposes of it. In Pascal, it looks like this:
program samp;
var
p:^integer;
begin
new(p);
p^:=10;
writeln(p^);
dispose(p);
end.
The same code in C looks like this:
#include <stdio.h>
void main()
{
int *p;
p=(int *) malloc (sizeof(int));
*p=10;
printf("%d\n",*p);
free(p);
}
This code is really useful only for demonstrating the process of allocating, deallocating, and using a block in C. The malloc line does the same thing as the new statement does in Pascal. It allocates a block of memory of the size specified---in this case, sizeof(int) bytes. The sizeof command in C returns the size, in bytes, of any type. The code could just as easily have said malloc(4), since sizeof(int) equals four bytes on most UNIX machines. Using sizeof, however, makes the code much more portable and readable.
The malloc function returns a pointer to the allocated block. This pointer is generic. Using the pointer without typecasting generally produces a type warning from the compiler. The (int *) typecast converts the generic pointer returned by malloc into a pointer to an integer, which is what p expects. The dispose statement in Pascal is replaced by free in C. It returns the specified block to the heap for reuse.
The second example illustrates the same functions as the previous example, but it uses a record instead of an integer. In Pascal, the code looks like this:
program samp;
type
rec=record
i:integer;
f:real;
c:char;
end;
var
p:^rec;
begin
new(p);
p^.i:=10;
p^.f:=3.14;
p^.c='a';
writeln(p^.i,p^.f,p^.c);
dispose(p);
end.
In C, the code looks like this:
#include <stdio.h>
struct rec
{
int i;
float f;
char c;
};
void main()
{
struct rec *p;
p=(struct rec *) malloc (sizeof(struct rec));
(*p).i=10;
(*p).f=3.14;
(*p).c='a';
printf("%d %f %c\n",(*p).i,(*p).f,(*p).c);
free(p);
}
Note the following line:
(*p).i=10;
Many wonder why the following doesn't work:
*p.i=10;
The answer has to do with the precedence of operators in C. The result of the calculation 5+3*4 is 17, not 32, because the * operator has higher precedence than + in most computer languages. In C, the . operator has higher precedence than *, so parentheses force the proper precedence. See tutorial 14 for more information on precedence.
Most people tire of typing (*p).i all the time, so C provides a shorthand notation. The following two statements are exactly equivalent, but the second is easier to type:
(*p).i=10;
p->i=10;
You will see the second more often than the first when referencing records pointed to by a pointer.
A more complex example of dynamic data structures is a simple stack library, one that uses a dynamic list and includes
Page : << Previous 7 Next >>