Understanding of pointers an usage in C - c

In the code I wrote:
int *p1=&a,**p3=&p1;
after passing p3 as an argument to the function 'change', p1 gets
changed...then the main function has printed p1, p1 takes the value
at double pointer p3(as wrote int **p3=&p1), but it does not take address
of a(int *p1=&a).
This means that value of p1 will be the value at p3, not
the address of variable 'a'? Why the value at double pointer 'p3'
will be considered??
Why not the value of address(&a) which the pointer stored? can anyone clarify
these two lines....
int *p1=&a;
int **p3=&p1;
I understood the concept of double pointers, but need clarification
regarding these two lines...
#include<stdio.h>
void main()
{
int a=2,b=3;
int *p1=&a,*p2=&b,**p3=&p1;
printf("p1=%d, p2=%d, p3=%d\n",p1,p2,p3);
change(p3,p2);
printf("p1=%d, p2=%d, p3=%d\n",p1,p2,p3);
printf("p1=%d\n",p1);
printf("a=%d, b=%d\n",*p1,*p2);
}
void change(int **x,int *z)
{
*x=z;
printf("p1=%d, p2=%d\n",*x,z);
printf("a=%d, b=%d\n",**x,*z);
}
output:
p1=-840577016,p2=-840577012,p3=-840577008
p1=-840577012,p2=-840577012
a=3,b=3
p1=-840577012,p2=-840577012,p3=-840577008
p1=-840577012
a=3,b=3

Because you changed p1 in this line. *x=z;
To explain a bit more you have done this
change(p3,p2)
Now p3 has address of p1. And then you just changed it by assigning to it something else..
That something else is nothing but b's address.
void change(int **x,int *z)
x contains address of p1.
z contains address of b.
Then you said that whatever is the content of x - go there and write there whatever is the content of z. z contained address of b. So now we changed p1-s content to the address of b.
..but in the main will it ignore the line int *p1=&a after p1 gets changed in the function??
Well to the OP - do you think that p1 will get changed here?
int *p1 = &a;
p1 = &b;
We have lastly assigned b's address to p1. The value earlier written os overwritten.
Nothing is ignored - the program did whatever it is said to do.
Another point
Use %p to print a pointer like this
printf("p1=%p, p2=%p, p3=%p\n", (void*)p1, (void*)p2, (void*)p3);

Related

how to pass one pointer value to another pointer?like can I do int *a=*b;where b is defined already but the pointer a is not addressing to any integer

Actually I was reading about pointers and wanted to try something ,so I wrote a small code
int main(){
int x = 10;
int *ptr;
ptr = &x;
printf("%d is stored at address %d\n",x,ptr );
int *c=*ptr;
c=&ptr;
printf("location is %d\n",c);
printf("value of c= %d",*c);
}
the result I expected was value of c would be 10 but instead the value came as the location of x.
output: 10 is stored at address 997523644 location is 997523648 value of c= 997523644
does this problem arise because I didnt pass any location as first?or is it something else or my question is quite silly I know:D,Can anyone help me?
The first thing that is missing in the code that you have provided, is the declaration of ptr. You haven't declared the ptr variable yet, so first you need to do
int *ptr;
, which tells that you need a pointer variable to point to a int variable.
Since you declare another pointer int *c and ptr is already a pointer, you only need to assign int *c = ptr (or c=ptr in case you want to declare int *c seperately first), which will store the value of ptr (location of x) to c. So, doing int *c=*ptr is wrong. Now you can access the value of x by doing derefencing (*c or *ptr), which you already did.
Doing c=&ptr is also wrong, because c is of type int * and &ptr is of type int **. In case you are trying to store the address of ptr, you have to first declare a variable of type int **, for example int **d. Now d=&ptr is valid and d holds a value which is the address of ptr. Dereferencing d (*d) should give you the value that the address of ptr is holding, which is nothing but the address of x (same as the value of c). If you dereference d twice (**d), you should get the back the value of x (10).
int* c = ptr should be enough, you can read it as: c is pointer to integer, c = ptr since ptr is already a pointer this will make both c and ptr point to the same value.
What you are currently doing: c=&ptr; is making c point to the address of ptr, so if you want to access x value you will need to dereference twice, once to get the address of ptr, and once again to get the content of x.
Changed it to this:
#include <stdio.h>
int main(void) {
int x = 10;
int *ptr = &x;
printf("%d is stored at address %p\n",x,ptr );
int *c=ptr;
int d = &ptr;
printf("location is %p\n", c);
printf("value of c= %d", *c);
return 0;
}
As ptr is an address already, you can just do it:
int *c=ptr;
Output is:
10 is stored at address 0x7ffd6127dd54
location is 0x7ffd6127dd54
value of c= 10
To print variable address, you can check this answer:
Stackoverflow Answer
You just need to remove the line
c=&ptr;
The above line means that you're setting C as the location of ptr, which is some random value. So C points to the location of ptr, and ptr points to the location of x. C is a double-pointer
Alternatively, if you keep that line in there, you can make it work by instead printing
printf("value of c= %d",**c);
either solution would get the output you expect

Understanding pointers in C using example code

I am learning pointers in C, so I am looking at one example. I tried to add comments to understand what is going on. Is the following code correct? In other words, do my comments describe the operation correctly?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int main(){
int x, y;
int *p1, *p2;
x=-42;
y=163;
p1=&x; //pointer 1 points to the address of x
p2=&y; //pointer 2 points to the address of y
*p1=17; //this just made x=17 because it says that the value at the address where p1 points should be 17
p1=p2; //this just made pointer 1 point at the same place as pointer 2, which means that p1 now points at y
p2=&x; //this just made pointer 2 point at the address of x
//NOW WE HAVE: p1 points at y, p2 points at x, y=17 because of p1
*p1=*p2; //now x=17 as well as y (because the value at the place p2 points, which is x, just became 17)
printf("x=%d, \ny=%d\n", x, y);
return 0;
}
"Check the debugger", for sure. You can also just copy your printf statement after each time you set a value to check, since you're just learning, but this isn't good long-term practice.
I'm not sure if you've quite got the concept and just made a typo mixing up x and y, or if you're making an incorrect assumption, but yes, in your comments:
//NOW WE HAVE: p1 points at y, p2 points at x, y=17 because of p1
you have the location of p1 and p2 correct at this point, but y has not received an assignment since you gave it the value of 163 initially. Assignments to *p1 will affect y, but only after the p1=p2 line.
At the point of this comment, x=17, because of the *p1=17 line a few line up. y is unchanged from the initial assignment.
*p1=*p2; //now x=17 as well as y (because the value at the place p2 points, which is x, just became 17)
y becomes 17 here, as *p1 (which is now y) is assigned the value in *p2, which, as you state, is x.

Double pointers as arguments in C

I am having some problems with double pointers as arguments in C.
From what I know so far:
when I have a function that takes a pointer as an argument, say a function called functionX(int *y)
then when I call functionX, let's say by: functionX(&randomvar),where randomvar is an integer containing a certain value (let's say 5), then C will create a pointer that is also called 'randomvar' that contains the address of randomvar. So the output of *randomvar will be 5.
Is my understanding correct?
If so, then when the function has double pointers : functionX(int **y) and by doing functionX(&randomvar) creates 2 pointers, one that contains the address of randomvar, another that contains the address of the first pointer.
I'm a bit stumped here I'm not sure if it's correct.
From what I know so far: when I have a function that takes a pointer
as argument, says a function called functionX(int *y) then when I
call functionX, let's say by: functionX(&randomvar),where
randomvar is an integer containing a certain value (let's say 5),
then C will create a pointer that is also called 'randomvar' that
contains the addresse of randomvar. So the ouput of *randomvar
will be 5.
Is my understanding correct?
No.
The situation you describe would be along these lines:
void functionX(int *y) {
// ...
}
int main(void) {
int randomvar = 5;
functionX(&randomvar);
}
The expression &randomvar in the main() function of that code does evaluate to the address of that function's local variable randomvar. The expression has type int *, the same as parameter y to function functionX(), so it is well suited for use as an argument to that function. All well and good so far.
But the expression &randomvar designates only a value, not an object. There is no storage reserved for it, and therefore it does not have an address. It also has no name, and in particular, it is not named 'randomvar'. Inside function functionX(), (a copy of) that value can be accessed as y, and the expression *y will evaluate to 5. Nowhere in the code presented is *randomvar a semantically valid expression.
If so
It is not so.
, then when the function has double pointers : functionX(int **y) and by doing functionX(&randomvar) creates 2 pointers, one that contains the adresse of randomvar, another that contains the adresse of the first pointer.
Not at all. &randomvar is a single expression. Evaluating it produces one value, which, as I've already covered, has type int *. This is does not match a function parameter of type int **. An int ** is a pointer to an int *. To obtain one, you might take the address of an object (not a value) of type int *:
void functionY(int **z) {
// ...
}
int main(void) {
int randomvar = 5;
int *varptr = &randomvar;
functionY(&varptr);
}
When done that way, there are indeed two pointers, one an int * and the other an int **, but the former needs to be declared explicitly.
Visualize it this way:
void foo(int a);
void bar(int *b);
void baz(int **c);
// main function
{
int x = 5;
int *p = &x;
foo(x);
bar(p);
baz(&p);
}
// **main** mem space
virtual mem address var name (conceptual) value
=================== ===================== =====
ABCD:4000 x 5
ABCD:4008 p ABCD:4000
// **foo** mem space
virtual mem address var name (conceptual) value
=================== ===================== =====
BCDE:2000 a 5
// a new variable is created.
// any changes made on 'a' will not affect 'x' in 'main'
// **bar** mem space
virtual mem address var name (conceptual) value
=================== ===================== =====
BCDE:4000 b ABCD:4000
// a new pointer is created pointing to 'x'
// 'b' points to 'x' and '*b' means 'the value stored in ABCD:4000'
// any changes made on '*b' will affect 'x' in main
// **baz** mem space
virtual mem address var name (conceptual) value
=================== ===================== =====
BCDE:8000 c ABCD:4008
// a new pointer is created pointing to 'p'
// 'c' points to 'p' and '*c' means 'the value stored in ABCD:4008'
// any changes made on '*c' will change the value of 'p' in main
// if '**c = 7' is executed, x will be assigned '7'
// if '*c = ABCD:8000' is executed, p will no longer point to 'x'
hen when I call functionX, let's say by: functionX(&randomvar),where
randomvar is an integer containing a certain value (let's say 5), then
C will create a pointer that is also called 'randomvar' that contains
the addresse of randomvar
The name of the pointer in the function functionX is y because you wrote yourself that it is the name of the function parameter functionX(int *y).
If so, then when the function has double pointers : functionX(int **y)
and by doing functionX(&randomvar) creates 2 pointers, one that
contains the adresse of randomvar, another that contains the adresse
of the first pointer. If you have a function declared for example like
The operator & creates one pointer not two pointers.
If you have a function declared for example like
void functionX(int **y);
and a variable declared like
int randomvar = 5;
then such a call of the function like
functionX( &randomvar );
generates a compilation error because the type of the argument is int * while the type of the parameter according to the function declaration is int **.
You may not write for example like
functionX( &&randomvar );
because using the first operator & creates a temporary object of the type int *. And you may not apply the operator & to a temporary object.
To call the function you could write
int *p = &randomvar;
functionX( &p );
In this case the type of the argument will be int ** as it is required by the parameter type.
Here is a demonstrative program.
#include <stdio.h>
void f( int *p )
{
printf( "The value of p is %p\n"
"the pointed value is %d\n",
( void * )p, *p );
}
void g( int **p )
{
printf( "The value of p is %p\n"
"the pointed value is also a pointer %p\n"
"the pointed value by the dereferenced pointer is %d\n",
( void * )p, ( void * )*p, **p );
}
int main(void)
{
int x = 5;
f( &x );
putchar( '\n' );
int *p = &x;
g( &p );
return 0;
}
Its output might look for example like
The value of p is 0x7ffced55005c
the pointed value is 5
The value of p is 0x7ffced550060
the pointed value is also a pointer 0x7ffced55005c
the pointed value by the dereferenced pointer is 5
You can just keep chaining pointers forever if you like. Here's some code that demonstrates this:
#include<stdio.h>
void foo(int ***A, int **B, int *C) {
printf(" %p A\n %p *A\n %p **A\n %d ***A\n", A, *A, **A, ***A);
printf(" %p B\n %p *B\n %d **B\n", B, *B, **B);
printf(" %p C\n %d *C\n ", C, *C);
}
int main(void) {
int D = 8;
int* C = &D;
int** B = &C;
int*** A = &B;
foo (A,B,C);
printf("%p &D (in main)\n", &D);
return 0;
This will give results like this for the dereferenced pointers, each '*' comes off just as it goes on, so notice how **A = *B = C = &D
0x7ffc81b06210 A
0x7ffc81b06218 *A
0x7ffc81b06224 **A
8 ***A
0x7ffc81b06218 B
0x7ffc81b06224 *B
8 **B
0x7ffc81b06224 C
8 *C
0x7ffc81b06224 &D (in main)
Note finally that the call foo (&B, &C, &D) will produce the same result.
There is no pointer made of the same name as the passed variable when invoking a function with an address of a passed variable as argument. It simply passes the address of a certain variable when preceded by the & operator. Inside the called function the pointer which holds this address can have any valid identifier.
when the function has double pointers : functionX(int **y) and by doing functionX(&randomvar) creates 2 pointers, one that contains the adresse of randomvar, another that contains the adresse of the first pointer.
You cannot pass the address of an int object, which is of type int*, when the function expects an object of type int**.
Speaking for passing double pointers in general, the concept is the same as said above.
No, your understanding is not correct. C doesn't "create" anything in the sense you assumed -- most variables are simply labels of memory locations (not only though, they may "alias", or label, CPU registers) -- your randomvar in the scope where functionX is called is a label of a memory area allocated to store an integer and which is interpreted as integer. The value of &randomvar unary expression (operator & with a single operand randomvar) is the address of the data. That address is what is passed to functionX as y, meaning that a memory location (or CPU register) is reserved and stores the address (not the value) of randomvar.
As an example, say your program declares a variable like int randomvar; -- when executed, a part of RAM -- 4 bytes for an int typically -- is reserved to hold the variable value of randomvar. The address isn't known until the program is executing in memory, but for the sake of the example let's imagine the address 0xCAFEBABEDEADBEEF (8 bytes) is the one that points to the 4 bytes to hold the integer value. Before the variable is assigned a value, the value at the address is indeterminate -- a declaration of a variable only reserves the space to hold the value, it doesn't write anything at the address, so before you assign a value to the variable, you shouldn't even be using the value at all (and most C compilers can warn you about this).
Now, when the address is passed to functionX this means that for the function, label y is 8 bytes reserved at some memory location, to store an address of an integer variable. When called like functionX(&randomvar), y stores 0xCAFEBABEDEADBEEF. However, y also [typically] has an address -- the former value (address of randomvar) has to be stored somewhere! Unless a CPU register stores the value, in which case there is no [RAM] address, naturally.
For a pointer to a pointer like int * * y, y labels a memory location reserved to store an address to an address of an integer variable.

In the following program the invocation of change_it() seems to have no effect. Please explain and correct the code? [duplicate]

This question already has answers here:
How to change a variable in a calling function from a called function? [duplicate]
(3 answers)
Closed 4 years ago.
void change_it(int[]);
int main()
{
int a[5],*p=1;
void change_it(int[]);
printf("p has the value %u \n",(int)p);
change_it(a);
p=a;
printf("p has the value %u \n",(int)p);
return 0;
}
void change_it(int[]) {
int i=777, *q=&i;
a = q; // a is assigned a different value
}
For starters, when you initialize p, you're giving a pointer the value of 1, when it needs a memory location. NULL uses 0, but that doesn't mean you can -or should- just assign integer values to pointers.
Just as an fyi, you can cast the value of 1 like this:
int a[5], *p = (int *) 1;
There's like -2 reasons for doing this, though, the -1th reason being that the minimal type safety that C provides should be respected, and the -2th being that it makes the code hard to understand for other people.
I'm going to assume what you meant to do was not declare a pointer with an address value of 1 though, and say you meant to declare a pointer that holds a value of 1. Unless you have another variable that holds the value of 1 already, you're going to have to first dynamically allocate the pointer, then set its value.
int* p = malloc(sizeof(int));
*p = 1;
If you had another variable to use, you could instead create the pointer on the stack rather than dynamically allocating it, like this:
int* q;
q = p;
Now, calling the same print function on both would yield this:
printf("p has the value %d\n", *p);
printf("q has the value %d\n", *q);
Output:
p has the value 1
q has the value 1
Addressing your main problem, you need to name the parameter in the change_it function, for example:
void change_it(int arr[])
Your program needs the parameter to be named, otherwise it has no idea of knowing you're trying to reference the array. The a variable you reference in the function is not bound to anything; the compiler will know be able to deduce what you're talking about.
Also, you don't need to redeclare the function prototype in your main function. The reason this is not a compiler error is that you can have as many declarations as you want, but only one definition. Again though, there's no reason to do this.
Another fyi, you don't have to name the parameters in your function prototypes, but it's good practice to both name them and be consistent with the names between the prototypes and the actual implementations so that people reading your code understand what's going on.
Also, you're using the %u specifier for the printf function, when you're not actually using unsigned decimal numbers. You're using signed decimals so you should use %d.
Lastly, your change_it function commits one crucial error preventing it from correctly changing the value of the passed-in array properly: you're setting the array that you passed in to the value of q.
Look at the function in your original code closely (pretend you named the input array a, as it looks like you mean to). You first declare an integer variable i and set its value to 777. Then, you create an integer-pointer variable q on the stack and correctly set its value to i. Note: You're not setting q to the value of i, but rather the address of i.
Why does this small but significant distinction matter? When you set a to q in the next line, you're changing the address of the array, specifically the first element of a five-element integer array, to point to the address of an integer variable. This is bad for a few reasons. First, the array is five integers long, but now it points to a single element. If and when you try to access elements 2-5, you'll get either meaningless garbage or a segmentation fault for trying to access memory you don't own. Even worse, the variable i is allocated on the stack, so when the function change_it exists, the function's data will be popped off the stack, and trying to access the address of i will yield either garbage or a segmentation fault for trying to access memory you don't own. See a pattern?
I'm not really sure how to correct this code, as I'm not sure what you were trying to accomplish, but correcting the aforementioned errors, your code now looks something like this:
#include <stdio.h>
void change_it(int arr[]);
int main()
{
int a[5];
int *p = a; // Equivalent to int *p = &a[0];
printf("a address: %p\n", a); // Should be equal to p
printf("p address: %p\n", p); // Should be equal to a
a[0] = 1;
printf("a[0] = %d\n", a[0]); // 1
printf("p has the value %d\n", *p); // 1
change_it(a);
p = a;
printf("a address: %p\n", a);
printf("p address: %p\n", p);
printf("a[0] = %d\n", a[0]);
printf("p has the value %d \n", *p);
return 0;
}
void change_it(int arr[])
{
int i=777;
arr[0] = i;
// Could be just:
// arr[0] = 777;
}
Output:
p address: 0x7fffc951e0b0
a[0] = 1
p has the value 1
a address: 0x7fffc951e0b0
p address: 0x7fffc951e0b0
a[0] = 777
p has the value 777
Note: Your memory address can and probably will be different from these, all it matters is that p and a are equal in both.
Anyways, hope this helps. Let me know if you have any questions.
Alright, you I believe do not have basic understanding of a function: First lets start with declaration and definition:
void change_it(int[]); // THIS IS DECLARATION
int main ()
{
void change_it(int[]); // THIS IS DECLARATION (duplicate and unnecessary
....
}
void change_it(int[] a) // THIS IS DEFINITION
{
int i=777, *q=&i;
a = q; // a is assigned a different value
}
declaration of the function only needs (you can put parameter name for readability) a parameter type, where as definition has to have name of the parameter because in definition parameters are local variables.
printf("p has the value %u \n",(int)p);
This will print the address of p not the value of p. So this should be
printf("p has the value %u \n", *p);
And finally we get to the body of a function. Where you are depending on somthing that have been locally assigned and putting it back into parameters
void change_it(int[] a)
{
int i=777, *q=&i;
a = q; // a is assigned a different value
}
so q is pointer and you are assigning address of local variable i to it. Well what happens when your program exists the function? i might disappear thus loosing its values and its address, which is assigned to q which means q is loosing its variable and value, and which is assigned to a which might loos its variable because it is pointing to i in your function.
This part here:
int a[5],*p=1;
void change_it(int[]); // Here, doesn't compile
printf("p has the value %u \n",(int)p);
That statement isn't just valid, as far as I know, you can't declare a function inside another function in C.
Also:
void change_it(int[]) // Here, an error
{
int i = 777, *q = &i;
a = q;
}
This function needs an argument, but you supplied only its type (being int[]),
void change_it(int a[]) fixes the problem
Your program does not compile and produce warnings. It would not work as you intended.
1) p is a pointer. To access value which it points to you have to dereference it using * dereference opearator.
2)
void change_it(int[]);
is not needed in the body of main.
3)
the invocation of change_it() seems to have no effect
If you want to change a[0] element inside the function change_it name the passing parameter to a and dereference the q pointer,
The working program may look as this:
#include <stdio.h>
void change_it(int a[]);
int main()
{
int a[5] = {0}; // init all element of `a` to `0`
int *p; // declare int pointer
p = a; // p point to array `a`
// print the first element of array `a`
printf("a[0] has the value %d \n",(int)*p);
// call function change_it, pass `a` as the argument
change_it(a);
printf("a[0] has the value %d \n",(int)*p);
return 0;
}
// change the value of the first element of array `a` to 777
void change_it(int a[]) {
int i=777, *q; // declare int i and pointer
q = &i; // pointer `q` points to the `i` now
a[0] = *q; // a[0] is assigned value = 777;
}
Output:
a[0] has the value 0
a[0] has the value 777

What is wrong with the pointer usage in this simple program?

I'm using VS 2010 Express, and I'm on Win Vista.
Here is the code:
#include <stdio.h>
int somefunc(char *p);
int main()
{
char *p = 0;
int x;
x = somefunc (p);
printf("%c", *p);
getch();
return 0;
}
int somefunc(char *p)
{
char y = '4';
p = &y;
return 2;
}
After I return to main(), I see the following in the autos window for p:
p 0x00000000 <Bad Ptr>
Anyone know why? It's as if the pointer is being treated as an automatic variable.
Btw, the x was just to create a similar condition to the same problem I was having in a larger program, but I don't think people want to sift through all of that code just to get to the meat of the problem, which is displayed here.
p in main() is a null pointer. It is passed by value to somefunc(), but that means that the value in the variable p in the function is a copy of the value in p in the main program, not a pointer to p itself. You change what the copy of p in the function points at; fortunately, it doesn't change what's in the main program.
Then you try to print what's at the position pointed to by the null pointer; this is undefined behaviour and programs typically crash.
If you had managed to change p in the main program, it would be pointing to an automatic local variable in somefunc() that would be out of scope — which is more undefined behaviour.
This would work:
#include <stdio.h>
int somefunc(char **p);
int main(void)
{
char *p = 0;
int x = somefunc(&p);
printf("%c\n", *p);
return 0;
}
int somefunc(char **p)
{
static char y = '4';
*p = &y;
return 2;
}
There are many other, possibly better, ways to make this work too.
I'm still confused on what *p = &y; means. If p in somefunc is a pointer to the pointer p, let's say p2 is a pointer to p1, then *p2 = p1, so *p2 is p1, which stores &y? Is that the correct way of thinking about this?
Basically, yes. First, let's rewrite the code to disambiguate the use of p:
int main(void)
{
char *p1 = 0;
int x = somefunc(&p1);
printf("%c\n", *p1);
return 0;
}
int somefunc(char **ppc)
{
static char y = '4';
*ppc = &y;
return 2;
}
The variable ppc is a pointer to pointer to char (hence the name ppc — not a name I'd normally use except for pedagogical purposes). I'll also rename p in main() to pq1, and introduce char *p2; in somefunc() which is a pointer to char.
Revising your comment in terms of these variables:
I'm still confused on what *ppc = &y; means. If ppc in somefunc() is a pointer to the pointer p1 in main(), then let's say ppc is a pointer to p1, then *ppc = p1, so *ppc is p1, which stores &y? Is that the correct way of thinking about this?
And that strongly shows that you're thinking is correct. One of the design guidelines for the C type nomemclature was that if you write:
SomeType *stp;
then *stp represents a value of type SomeType — declaration mimics use.
Now, with ppc, the declaration is char **ppc; and the declaration mimics use rule means that **ppc is a value of type char. Consequently, it means that *ppc is a value of type char *. It means that when you write to *ppc, you are modifying the space that ppc points at, and it points at the address of char *p1; in main() in the sample code.
You are modifying p in somefunc but this won't modify p in main. You probably need to pass a pointer to p if you want to modify its value.
Complementing Jonathan's answer, for your question
What is the difference between char **p and char p for the function parameter? Why can I pass and modify an array without having to return it, but I have to with a pointer? They are both addresses.
In C, there's no pass by reference, there's only pass by value i.e. any argument passed to a function is copied to its parameters, but can't be made to refer to the original. This is the reason functions like swap is implemented using pointers. Example
void swap(int *x, int *y)
{
int t = *x;
*x = *y;
*y = t;
}
int a = 0, b = 1;
swap(&a, &b);
Here &a is a pointer whose value is copied onto x, thus it is pass by value (sometimes, incorrectly, called pass by reference). So I should be able to do the same when modifying pointers too, right?
Nope. Since a pointer is already pointing to something, making it point another thing needs another level of indirection. An analogy would be: you know somewhere a direction sign is pointing towards your home, by just knowing where it's pointing to (home), would you be able to change the direction it's pointing? No, you'd have to know where the sign is to go there and make it point in another direction.
Likewise, say if a variable lives at level 0 then to edit it, we go to a level further i.e. level 1 to modify it (from outside). This concept can be applied recursively; so to edit a variable at level 1, we've to go to level 2 to modify them from outside and so on. Pictorially
type: int* int int*
variable name: ptr a pptr
-------- ------- --------
value: | 1000 | --> | 1 | <-- | 1000 |
-------- ------- --------
address: 2000 1000 2400
Notice that pptr can't change where ptr is pointing to since pptr too is pointing at what ptr is pointing at a. However
int** int* int
pptr ptr a
-------- -------- -------
| 2000 | --> | 1000 | --> | 1 |
-------- -------- -------
2400 2000 1000
now pptr (int**) is in a position to modify where ptr (int*) is pointing to. If you'd to modify pptr then you to go another level higher ppptr (int***) and so on.

Resources