What does ** do in C language?
Q: what is this (**)?
A: Yes, it's exactly that. A pointer to a pointer.
Q: what use does it have?
A: It has a number of uses. Particularly in representing 2 dimensional data (images, etc). In the case of your example char** argv
can be thought of as an array of an array of char
s. In this case each char*
points to the beginning of a string. You could actually declare this data yourself explicitly like so.
char* myStrings[] = {
"Hello",
"World"
};
char** argv = myStrings;
// argv[0] -> "Hello"
// argv[1] -> "World"
When you access a pointer like an array the number that you index it with and the size of the element itself are used to offset to the address of the next element in the array. You could also access all of your numbers like so, and in fact this is basically what C is doing. Keep in mind, the compiler knows how many bytes a type like int
uses at compile time. So it knows how big each step should be to the next element.
*(numbers + 0) = 1, address 0x0061FF1C
*(numbers + 1) = 3, address 0x0061FF20
*(numbers + 2) = 4, address 0x0061FF24
*(numbers + 3) = 5, address 0x0061FF28
The *
operator is called the dereference operator. It is used to retrieve the value from memory that is pointed to by a pointer. numbers
is literally just a pointer to the first element in your array.
In the case of my example myStrings
could look something like this assuming that a pointer/address is 4 bytes, meaning we are on a 32 bit machine.
myStrings = 0x0061FF14
// these are just 4 byte addresses
(myStrings + 0) -> 0x0061FF14 // 0 bytes from beginning of myStrings
(myStrings + 1) -> 0x0061FF18 // 4 bytes from beginning of myStrings
myStrings[0] -> 0x0061FF1C // de-references myStrings @ 0 returning the address that points to the beginning of 'Hello'
myStrings[1] -> 0x0061FF21 // de-references myStrings @ 1 returning the address that points to the beginning of 'World'
// The address of each letter is 1 char, or 1 byte apart
myStrings[0] + 0 -> 0x0061FF1C which means... *(myStrings[0] + 0) = 'H'
myStrings[0] + 1 -> 0x0061FF1D which means... *(myStrings[0] + 1) = 'e'
myStrings[0] + 2 -> 0x0061FF1E which means... *(myStrings[0] + 2) = 'l'
myStrings[0] + 3 -> 0x0061FF1F which means... *(myStrings[0] + 3) = 'l'
myStrings[0] + 4 -> 0x0061FF20 which means... *(myStrings[0] + 4) = 'o'
In C arguments are passed by values. For example if you have an integer varaible in main
int main( void )
{
int x = 10;
//...
and the following function
void f( int x )
{
x = 20;
printf( "x = %d\n", x );
}
then if you call the function in main like this
f( x );
then the parameter gets the value of variable x
in main. However the parameter itself occupies a different extent in memory than the argument. So any changes of the parameter in the function do not influence to the original variable in main because these changes occur in different memory extent.
So how to change the varible in main in the function?
You need to pass a reference to the variable using pointers.
In this case the function declaration will look like
void f( int *px );
and the function definition will be
void f( int *px )
{
*px = 20;
printf( "*px = %d\n", *px );
}
In this case it is the memory extent occupied by the original variable x
is changed because within the function we get access to this extent using the pointer
*px = 20;
Naturally the function must be called in main like
f( &x );
Take into account that the parameter itself that is the pointer px
is as usual a local variable of the function. That is the function creates this variable and initializes it with the address of variable x
.
Now let's assume that in main you declared a pointer for example the following way
int main( void )
{
int *px = malloc( sizeof( int ) );
//..
And the function defined like
void f( int *px )
{
px = malloc( sizeof( int ) );
printf( "px = %p\n", px );
}
As parameter px
is a local variable assigning to it any value does not influence to the original pointer. The function changes a different extent of memory than the extent occupied by the original pointer px
in main.
How to change the original pointer in the function? Just pass it by reference!
For example
f( &px );
//...
void f( int **px )
{
*px = malloc( sizeof( int ) );
printf( "*px = %p\n", *px );
}
In this case the value stored in the original pointer will be changed within the function because the function using dereferencing access the same memory extent where the original pointer was defined.