Is using while(*p++) for checking if an array has more elements dangerous?
Could there be problems if on the next memory location there is some value and this value is not a part of an array.
This simple code:
#include <stdio.h>
void f(float *p) {
printf("%p : %.f\n", p, *p);
while(*p++){
printf("%p : %.f\n", p, *p);
}
printf("%p : %.f\n", p, *p);
}
int main() {
float p[] = {2.2, 3.2};
f(p);
return 0;
}
gives this output:
0x7fffed4e8f10 : 2
0x7fffed4e8f14 : 3
0x7fffed4e8f18 : 0
0x7fffed4e8f1c : 0
So, if on 0x7fffed4e8f18 the value was ≠ 0 would it make my code wrong?
As soon as p goes outside of the bounds the array, dereferencing it invokes undefined behavior. So yes, doing this is dangerous.
There are already a lot of answers that explain the undefined behavior that arises from reading past the array's boundaries pretty well. But there is yet another (potential) problem nobody has mentioned yet.
If your array can contain 0 as a valid value, you'll assume the wrong size. That is,
void
f(float * p)
{
do
{
printf("% .01f\n", *p);
}
while(*p++);
}
int
main()
{
float p[] = {-1.0f, -0.5f, 0.0f, 0.5f, 1.0f};
f(p);
return 0;
}
will output
-1.00
-0.50
0.00
and “overlook” the remaining elements. While this particular example does not invoke undefined behavior, it might not be what you've expected.
I guess you've come over this pattern with string programming. By convention, we require a character array that is meant to represent a text string to
not contain any NUL bytes and
be terminated by placing a NUL byte after the last byte.
If both requirements are fulfilled, doing
void
roll_all_over_it(char * string)
{
while(*string);
{
putchar(*string++);
}
}
is safe by contract.
This makes using strings a little more convenient since we don't have to maintain an integer alongside every string to keep track of its length. On the other hand, it has made quite a few (carelessly written) programs vulnerable to buffer overrun attacks and there are other problems that arise from this assumption. See for example the discussion of fgets in the GNU libc manual:
Warning: If the input data has a null character, you can't tell. So don't use fgets unless you know the data cannot contain a null. Don't use it to read files edited by the user because, if the user inserts a null character, you should either handle it properly or print a clear error message. We recommend using getline instead of fgets.
In C language there's only one way to determine how many elements an array has: it is to look up that value from in original array type, the one that was used in array definition. Any other attempts to figure out the number of elements by some invented run-time mechanisms are useless.
In your specific example, function f has no access to the original array type (since you converted your array to pointer as you were passing it to f). This means that it is impossible to restore the original size of the array inside f, regardless of what you do.
If you insist on passing your array to f as a float * pointer, a good idea would be to pass the original array size manually from the caller (i.e. from main), as an extra parameter of function f.
The "terminating value" technique, where you use some sort of special element value to designate the last element of the array (zero, for example) can also be used for that purpose, but is generally worse that just passing the size from the outside. In any case, you have make sure to include that terminating value into the array manually. It won't appear there by itself.
Yes, it is: your function takes a pointer argument, but you're not checking to make sure it's not a NULL-pointer. Dereferencing a null pointer is not allowed.
As others have pointed out: dereferencing an invalid pointer (out of bounds) results in undefined bahviour, which is bad.
Also, you are using the correct format string to print a pointer (%p), but compile your code with -Wall -pedantic. printing a pointer value is one of the few cases where you have to cast a pointer to void *. IE change:
printf("%p : %.f\n", p, *p);
to
printf("%p : %.f\n", (void *) p, *p);
update:
In response to your comment: it would seem that you're actually trying to determine the length of an array that is passed as an argument. The simple fact of the matter is that you can't. An array decays into a pointer. A pointer is not an array, and therefore, so you can't determine the length of the original array. At least: not reliably.
If you are working on an array, and you want to know its length: have the caller of your function pass the length as an argument:
void f(float *arr, size_t arr_len)
{
if (arr == NULL)
exit( EXIT_FAILURE );//handle error
//do stuff
}
In short, your function relies heavily on there being a 0 after the array. The function itself can be invoked passing NULL, too and dereferencing null is illegal. So yes, your code is dangerous.
//example setup
float foo = 123.4f
float *bar = malloc(123 * sizeof *foo);//<-- uninitialized memory, contains junk
//omitting if (bar == NULL) check, so bar might be null
//dangerous calls:
f(&foo);
f(bar);//<-- bar could be null if malloc failed, and contains junk if it didn't dangerous
f(NULL);
What you did is indeed highly dangerous !
You make no control of the last element of the array to be null, and you dereference a pointer that will be after the last element of your array. Two points to Undefinit Behaviour !
Here is what you should do :
#include <stdio.h>
void f(float *p) {
printf("%p : %.f\n", p, *p);
while(*p++){
printf("%p : %.f\n", p, *p);
}
/* because of ++, p is now one element to far : stop and do not print ... */
}
int main() {
float p[] = {2.2, 3.2, 0}; /* force a 0 as a last element marker */
f(p);
return 0;
}
Yes, the way you have written your code yields undefined behavior if there is no zero element in your array.
That is, while(*p++) in and of itself is not bad, but applying it to an array that you have not explicitly terminated with a zero element is. I. e. the following version is safe:
#include <stdio.h>
//p must point to a zero terminated array
void f(float *p) {
printf("%p : %.f\n", p, *p);
while(*p++){
printf("%p : %.f\n", p, *p);
}
//printf("%p : %.f\n", p, *p); //p points past the end of the array here, so dereferencing it is undefined behavior
}
int main() {
float array[] = {2.2, 3.2, 0}; //add a terminating element
f(array);
return 0;
}
However, I usually prefer explicitly passing the size of the array:
#include <stdio.h>
void f(int floatCount, float *p) {
printf("%p : %.f\n", p, *p);
for(int i = 0; i < floatCount; i++) {
printf("%p : %.f\n", p+i, p[i]);
}
}
int main() {
float array[] = {2.2, 3.2};
int count = sizeof(array)/sizeof(*array);
f(count, array);
}
Btw: Arrays are not pointers, but they decay into pointers when you pass them to a function. I changed the naming to reflect this.
Related
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
typedef char trgm[3];
char test[2][3] = {'a','b','c','d','e','f'};
int main(void)
{
trgm *t;
t = test;
printf("%c\n",*(char *)t);
printf("%c\n",*(char *)*t);
printf("%c\n",**t);
return 0;
}
the output of three printf is same. I don't understand why the t and *t are same? what does complier do internal?
There are three paths of interpretation of the same value - the starting address of the memory extent occupied by the array. So dereferencing the value casted to char * you will get the value of the first element of the type char of the array.
You can think of an array variable as containing the memory address of its first element. In your case since the array is a 2D array, the "first element" t[0] is in-fact pointer to the 'first row'. Note that in terms of memory there is no special storage for element t[0] and t[1], it gets converted to appropriate memory offset, but it helps to think that way. Hence when you deference the pointer (*t), you get the same value as you would get for the array variable.
t = [0] --> 'a','b','c'
[1] --> 'd','e','f'
Try this printf("%p %p %p\n", t,*t, t[0]);, you will get same value for all.
When you declare the typedef char trgm[3];, then trgm refers to *an array of char [3]`. This is important.
When you declare trgm *t = test, the pointer t is a pointer to array of char [3]. By setting t = test; you make t a pointer that points to the first row of test (essentially equivalent to test[0]. Since an array variable holds (or points to) the first element of test, both t and test point to the same value. Dereferncing the either the pointer t or test (e.g. *t or *test) will access the first element of test.
Now since t is a pointer to array of char [3], it is a pointer with two-levels of indirection and can therefore be dereferenced twice (as can test). Take test[row][col]. Let's look at the pointer equivalent for the array. test can be written as
*(*(test + col) + row)
(both col and row are 0 when referencing the first element (e.g. test[0][0]) so the above is simply
*(*(test + 0) + 0)
note: anything plus 0 is just that thing again, so it simplifies to:
**test
For t it is just:
(*t)[col] /* to index cols 0 - 2 */
It too can be rewritten as:
*((*t) + col)
With col being zero for the first element, you get
**t
However, t by pointing to the first row, allows pointer arithmetic (e.g. t = t + 1;, or just t++) to update t to point to the second row, and so on.
So, in sum, whether you are looking at test or you are looking at t, both initially reference the address for the first element in test, both are (or can be considered) pointers with two levels of indirection, and dereferencing each once, or twice, will still point at the original element in test. You can use t to access all values in your array, for instance:
for (; t < &test[2]; t++)
printf (" %c, %c, %c\n", (*t)[0], (*t)[1], (*t)[2]);
putchar ('\n');
Which will output:
a, b, c
d, e, f
Lastly, there is no need to make test a global variable. Avoid global variables to reduce the chance of name collision. Declare test in main() and pass it pointer to it to any other function it is required.
Putting that altogether, you could do something like:
#include <stdio.h>
typedef char trgm[3];
int main(void)
{
char test[][3] = {{'a','b','c'},
{'d','e','f'}};
trgm *t;
t = test;
printf("%c\n",*(char *)t);
printf("%c\n",*(char *)*t);
printf("%c\n",**t);
for (; t < &test[2]; t++)
printf (" %c, %c, %c\n", (*t)[0], (*t)[1], (*t)[2]);
putchar ('\n');
return 0;
}
Example Use/Output
$ ./bin/p2x3
a
a
a
a, b, c
d, e, f
Look things over and let me know if you have any further questions.
I have this code:
void alloc2(int** p) {
*p = (int*)malloc(sizeof(int));
**p = 10;
}
void alloc1(int* p) {
p = (int*)malloc(sizeof(int));
*p = 10;
}
int main(){
int *p;
alloc1(p);
//printf("%d ",*p);//value is undefined
alloc2(&p);
printf("%d ",*p);//will print 10
free(p);
return 0;
}
So, I understand that alloc1 just makes a local copy so it's not effecting outside the function the pointer which is given as a parameter.
But what is happening with alloc2?
tl;dr;
And why this alloc1(&p); won't work?
Update
I think i answered my question. The crucial thing is that & makes you a pointer and then a was built to a double pointer bei dereferencing once. Then the double pointer points to the address given be malloc. Finally the address was filled with 10.
And alloc1(&p); would work, but you couldn't derefence the double pointer since it takes a single pointer.
Thanks to all of you
It didn't become a double pointer, in alloc2() you are passing a pointer containing the address of main()'s p. When you dereference it you are actually modifying the address stored in main()'s p. And that's why it's working.
Since there is no pass by reference in c, the only way you can modify a parameter inside a function is by passing a pointer with it's address, for example if you have to pass an integer to a function and the function needs to modify it then you make a pointer using the address of & operator and pass that pointer to the function, example
void
modify(int *pointer)
{
*pointer += 1;
}
int
main(void)
{
int value;
value = 0;
modify(&value);
printf("%d\n", value);
modify(&value);
printf("%d\n", value);
}
would output
1
2
A double pointer is a pointer to a pointer, so you are making a pointer from p in main() which stores the address of p in main(), the pointer itself is stored somewhere, so you are passing the address where the pointer is stored and hence you can modify it's contents from within alloc2().
Note: It's bad style to cast the return valud of malloc(), read more about it here.
It would be clearer if you gave the variables in different functions different names. Since you have multiple variables and arguments named p, and they are distinct from each other, it is easy to confuse yourself.
void alloc2(int** pa2)
{
*pa2 = (int*)malloc(sizeof(int));
**pa2 = 10;
}
void alloc1(int* pa1)
{
pa1 = (int*)malloc(sizeof(int));
*pa1 = 10;
}
int main()
{
int *p = 0;
alloc1(p);
//printf("%d ",*p);//value is undefined
alloc2(&p);
printf("%d ",*p);//will print 10
free(p);
return 0;
}
Apart from renaming the arguments of functions, I've also initialised p in main() to zero (the NULL pointer). You had it uninitialised, which means that even accessing its value (to pass it to alloc1()) gives undefined behaviour.
With p being NULL, alloc1() also receives the NULL pointer as the value of pa1. This is a local copy of the value of p from main(). The malloc() call then changes the value of pa1 (and has no effect on p in main(), since it is a different variable). The statement *pa1 = 10 sets the malloced int to be 10. Since pa1 is local to alloc1() it ceases to exist when alloc1() returns. The memory returned by malloc() is not free()d though (pa1 ceases to exist, but what it points to doesn't) so the result is a memory leak. When control passes back to main(), the value of p is still zero (NULL).
The call of alloc2() is different, since main() passes the address of p. That is the value of pa2 in alloc2(). The *pa2 = (int *)malloc(sizeof(int)) statement does change the value of p in main() - to be the value returned by malloc(). The statement **pa2 = 10 then changes that dynamically allocated int to be 10.
Note also that the (int *) on the result of malloc() is unnecessary in C. If you need it, it means one of
You have not done #include <stdlib.h>. The type conversion forces the code to compile, but any usage of the int - strictly speaking - gives undefined behaviour. If this is the case, remove the int * and add #include <stdlib.h>.
You are compiling your C code using a C++ compiler.
EDIT:
What should I do to have a correct code then ?
EDIT2:
Ok, I correct the code below
Context
Fiddling with memcpy.
Linux, 64 bits.
gcc 4.8.x
Code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void d(char ** restrict a, char ** restrict b){
char r[20];
memcpy(r,*a, strlen(*a)+1);
// it is the same thing as before since *c is equivalent to &r (or put simply r).
char *restrict c = malloc(20);
memcpy(c,*a, strlen(*a)+1);
// that one bugs me. why b alone and not *b ??
// EDIT : this is incorrect
memcpy(b,*a, strlen(*a)+1);
// EDIT : this is correct
memcpy(*b,*a, strlen(*a)+1);
printf("pointer c -> hey %s\n",c);
printf("array r -> hey %s\n",r);
// EDIT : this is incorrect
printf("double pointer -> hey %s\n",b);
// EDIT : this is correct
printf("double pointer -> hey %s\n",*b);
}
int main(void)
{
char a[] = "YOU";
char * b = a;
char * c = malloc(20);
d(&b, &c);
return 0;
}
Question
I would like to undertsand why memcpy doesn't complain about me passing double pointer to it, while it needs a pointer only.
I know that with chars *b == &a == a and that an array is referenced by its first member up to '\0'. The problem really is with passing a double pointer to memcpy.
why didn't I have to do
memcpy(*b, *a, strlen(*a)+1);
since memcpy signature is
void * memcpy ( void * destination, const void * source, size_t num );
and first argument is a "Pointer to the destination array where the content is to be copied, type-casted to a pointer of type void*", according to cplusplus.com.
What is the "catch" here please ?
Thanks a lot
Well, a double pointer is a single pointer to single pointer, so it can be passed to a function that expects a void pointer.
It is of course another thing whether or not your code is correct... It's not and works only by coincidence. Note that not only you use memcpy() to write to a wrong location, but you also print the same wrong location of memory as a string in your printf(). The "coincidence" here is that both of these "wrong" locations are the same, so you falsely assumed that it works fine.
Try to really print the right thing and you'll see the mayhem:
printf("double pointer -> hey %s\n",*b);
Consider what would happen if you wanted to copy the representation of a pointer to another one, like this:
char *p;
char *q = NULL;
memcpy(&p, &q, sizeof q);
should the compiler really complain in this case? Nope.
The point is that void * is untyped. It can point to any object type. It's not a constraint that a void * can't point to a pointer-to-pointer. It absolutely can.
As to why it "works": It does not work. It only appears to be working. Because of the invalid pointer operation, the code has undefined behavior, so it can do anything. In the better case, it crashes and makes the problem apparent. In your case, the error remained silent and the program was pretending it worked.
" Double pointers are also sometimes employed to pass pointers to functions by reference "
can somebody can explain me the above statement, what exactly does point to function by reference means ?
I believe this example makes it clearer :
//Double pointer is taken as argument
void allocate(int** p, int n)
{
//Change the value of *p, this modification is available outside the function
*p = (int*)malloc(sizeof(int) * n);
}
int main()
{
int* p = NULL;
//Pass the address of the pointer
allocate(&p,1);
//The pointer has been modified to point to proper memory location
//Hence this statement will work
*p=10;
//Free the memory allocated
free(p);
return 0;
}
It means that you have a function that takes a pointer pointer (type int ** for example). This allows you to modify the pointer (what data it is pointing to) much in the way passing a pointer by reference would allow.
void change (int *p) {*p = 7;}
void Really_Change (int **pp) {*pp = null;}
int p = 1;
int *pp = &p;
// now, pp is pointing to p. Let's say it has address 0x10;
// this makes a copy of the address of p. The value of &p is still 0x10 (points to p).
// but, it uses that address to change p to 7.
change(&p);
printf("%d\n", p); // prints 7;
// this call gets the address of pp. It can change pp's value
// much like p was changed above.
Really_Change(&pp);
// pp has been set to null, much like p was set to 7.
printf("%d\n", *pp); // error dereference null. Ka-BOOM!!!
So, in the same way that you can pass a pointer to an int and change the value, you can pass a pointer to a pointer and change its value (which changes what it points to.)
I'll try to explain with both code and plain english :). The explanation may get long, but it will be worth the while.
Suppose we have a program, running its main() function, and we make a call to another function that takes an int parameter.
Conceptually, When you pass a variable as a parameter to a function, you can do so in (roughly speaking) two ways: by value, or by reference.
"By value" means giving the function a copy of your variable. The function will receive its "content" (value), but it won't be able to change the actual variable outside its own body of code, because it was only given a copy.
"By reference", on the other hand, means giving the function the actual memory address of our variable. Using that, the function can find out the variable's value, but it can also go to that specified address and modify the variable's content.
In our C program, "by value" means passing a copy of the int (just taking int as argument), and "by reference" means passing a pointer to it.
Let's see a small code example:
void foo(int n) {
n = 10;
printf("%d\n", n);
}
int main() {
int n = 5;
foo(n);
printf("%d\n", n);
return 0;
}
What will the output of this program be? 10 10? Nope. 10 5! Because we passed a copy of the int, by value and not by reference, foo() only modified the number stored in its copy, unable to reach main()'s copy.
Now, if we do it this way:
void foo(int* n) {
*n = 10;
printf("%d\n", *n);
}
int main() {
int n = 5;
foo(&n);
printf("%d\n", n);
return 0;
}
This time we gave foo() our integer by reference: it's actual memory address. foo() has full power to modify it by accessing it's position in memory, foo() and main() are working with the same copy, and so the output will be 10 10.
As you see, a pointer is a referece,... but also a numerical position in memory. It's similar to an int, only the number contained inside is interpreted differently. Think of it this way: when we pass our int by reference, we're passing an int pointer by value!. So the same by value/by reference logic can be applied to pointers, even though they already are references.
If our actual variable was not an int, but an int reference (pointer), and we wanted main() and foo() to share the same copy of that reference so that foo() can modifiy it, what would we do? Why of course, we'd need a reference to our reference! A pointer to a pointer. That is:
int n; /* integer */
int* n; /* integer reference(pointer). Stores an int's position in memory */
int** n; /* reference to integer reference, or double pointer.
Stores int*'s memory address so we can pass int*s by reference. */
I hope this was useful.