You're correct that & gets the address of a variable.
This is probably most useful when you want to pass the variable as an argument to a function, for a few different reasons:
1. You want the function to modify the variable.
Everything in C is passed by value, The following function will not change a:
| void func(int arg){
arg += 4;
}
int main(int argc, char** argv){
int a = 0;
func(a);
}
|
But you can work around this by passing a pointer to the variable - func can dereference the pointer to get a modifyable "lvalue".
| void func(int* arg){
*arg += 4;
}
int main(int argc, char** argv){
int a = 0;
func(&a);
printf("%d\n", a); // it's 4
}
|
2. You're passing a big struct and don't want all that memory to be copied onto the stack frame.
Since everything is passed by value, if you have a struct with 100 uint32_t's inside, that's gonna effectively do a 400 byte memcpy every time you call the function. If you pass a pointer to the struct it'll probably do no copying at all, because the pointer can be passed in a register.
3. Combining 1 and 2, you have a struct and want the func to modify some of its fields.
As for argv, it's an array of character pointers. And since arrays in C are closely related to pointers (and you can't pass a plain array as an argument - they "decay" to pointers), argv appears as a "pointer to pointers to char".
Here is a better visualization for running a program called 'ab' given one argument 'c':
Also whitespace between the *, typename and identifier doesn't matter, it's personal preference whether you use "int* a", "int *a", "int * a" or "int*a", though the last two are ususal.