Double Pointer and Local Scope Confusion - c

I am taking a c programming course on Udemy and am quite confused when passing a double pointer into a function. In the example, the instructor passes the address of a pointer as an argument to a function. Then, he de-references that double pointer parameter in the function and sets it equal to the address of a local variable (a).
#include <stdio.h>
#include <malloc.h>
void foo(int **temp_ptr)
{
int a = 5;
*temp_ptr = &a;
}
int main()
{
int *ptr = NULL;
ptr = (int*) malloc(sizeof(int));
*ptr = 10;
foo(&ptr);
printf("%d\n", *ptr);
return 0;
}
So, what we are doing here is changing the value of ptr to hold the address of a. Then, when we de-reference ptr, it should display the value of 5 and not 10, since we changed the value of ptr to hold the address of a.
This is indeed correct: it displays 5 and not 10.
However, what doesn't make sense here is that the variable a is in the local scope of the foo function. When we declare a, the memory allocated is put onto the stack frame; thus, when leaving the local scope of the function that memory is deleted and the frame is popped off of the stack. How can we still access that variable a with the ptr variable in the main function? Shouldn't that memory be completely wiped out?

Then, when we de-reference ptr, it should display the value of 5
Should is incorrect. printf("%d\n", *ptr); is undefined behavior (UB) as the address stored in ptr is invalid with the return of foo(&ptr);.
Output may print 5, may print 42, may crash. It is UB.
... correct that the memory of the stack should be completely wiped out
No. There is not specified wiping of memory. The result is UB.
But sometimes undefined behavior works?
"undefined behavior" is undefined. Even if it "works" today, it may not "work" tomorrow.
When we declare a, the memory allocated is put onto the stack frame; thus, when leaving the local scope of the function that memory is deleted and the frame is popped off of the stack.
This assumes an underling code model that is not specified by C. Other real possibilities exist.
How can we still access that variable a with the ptr variable in the main function?
As is, no defined way. Alternatively keep the address of a valid by making a static.

In Foo function, we are updating the address of our pointer. int a declared in foo function will be stored at some memory location. Now, we are assigning that location to our ptr(which is okay, ptr can point to any memory location of type int). But the issue is the value at that location is not in our control and this location can be used for any other purpose which may update the value at that location.
It means that our pointer is now pointing to location that may be updated by any other source.

Related

What is the address of a pointer in C?

int main()
{
int *p;
printf("%p \n", &p);
printf("%p \n", p);
return 0;
}
By executing this code I am receiving the following output:
0x16b77f710
0x104683f4c
I expected to get the same memory address, because the &p id not referenced to any other variable.
Why i am getting two different memory address?
Thanks,
The pointer is a normal object having (in your case type of int *). It cant point to itself because the type of such pointer would have to be int **
*image stolen from internet.
A pointer is a variable like any other. It has an address, which is typically the address in memory where that variable sits.
Like any other variable, a pointer variable also has some data in it. For a pointer variable, that data is the address of some other variable, the variable at which the pointer points.
The address of a variable, and the contents of a variable, are two totally different things. They are almost never equal.
Try this program, in which I give your variable p something to point to:
int main()
{
int i = 5;
int *p = &i;
printf("p: %p, p's address: %p\n", p, &p);
printf("i: %d, i's address: %p\n", i, &i);
}
You should notice two things:
As in your first program, "p" and "p's address" will be different.
Whatever value you see for "p", it will be the same as "i's address".
The reason is that a pointer, when declared, does not point to itself by default. Simplified you can imagine it in such a way that a pointer occupies 2 memory cells. 1 memory cell has the virtual address where the pointer itself is located (&p in your case), the 2nd memory cell contains the virtual address where the pointer points to (p in your case).
Since memory cells retain their value when they are deallocated, the cell containing the destination address of your pointer still contains an obsolete value from another, already completed process.
You would have the same phenomenon if you declare a new integer variable and then print its value with printf, you will see that there will already be some number in the new variable that will appear completely random. This is also due to the fact that there is still an obsolete value in the corresponding memory cell.
Let assume there is random memory block for just understanding name it a. Now assume that p is pointing to that memory block.
&p returns the address of memory block where p is present.
p returns the address of memory block(&a) to the variable/memory block(a) which p is pointing.
So of course it will give different memory addresses.
I expected to get the same memory address, because the &p id not referenced to any other variable.
Pointer variables do not automatically point to themselves; if you don't explicitly initialize them, then their initial value will be indeterminate (or NULL, depending on how they are declared).
There's nothing magic about pointer variables - like any other scalar, they store some kind of value; it's just that in their case, that value is an address of another object.
If you really want p to store its own address, you'll have to do something like
p = (int *) &p;
The cast is necessary because the type of the expression &p is int **, and you can't assign a pointer value of one type to a variable of a different pointer type. But, pointers to different types are not guaranteed to have the same size, representation, or alignment. On modern commodity hardware like x86 you can probably count on int * and int ** having the same size and representation, just be aware that doesn't have to be the case everywhere.

Why is returning a stack allocated pointer variable in a function allowed in C?

I've read the followed post:
Is returning a heap-allocated pointer from function OK?
Which shows that a pointer pointing to a heap allocated variable is returned is alright. However, is the pointer technically a "stack allocated variable", which would then get deallocated upon returning of the function?
For example:
int* test(){
int arr[5];
int *ptr = arr;
return ptr; //deallocated ptr?
}
int *test2(){
int arr[5];
return arr;
}
In test
Also, is it right to say arr is a pointer that points to some newly created int array arr, pointing at &arr[0]. If arr is not a pointer, why is it valid to return it satisfying the function return type?
Since both ptr and arr are supposedly stack allocated, why does the code only work in test() and not test2()? Does test() give an undefined behavior?
They will both be undefined behaviour, if the returned value is accessed. So, none of them are "OK".
You're trying to return a pointer to a block-scoped variable which is of auto storage duration. So, once the scope ends, the lifetime of the variable comes to an end.
Quoting C11, chapter ยง6.2.4/P2, regarding the lifetime (emphasis mine)
The lifetime of an object is the portion of program execution during which storage is
guaranteed to be reserved for it. An object exists, has a constant address, and retains
its last-stored value throughout its lifetime. If an object is referred to outside of its
lifetime, the behavior is undefined [...]
Then, from P5,
An object whose identifier is declared with no linkage and without the storage-class
specifier static has automatic storage duration, [...]
and
For such an object that does not have a variable length array type, its lifetime extends
from entry into the block with which it is associated until execution of that block ends in
any way. [...]
So, in your case, the variable arr is having automatic storage and it's lifetime is limited to the function body. Once the address is returned to caller, attempt to access the memory at that address would be UB.
Oh, and there's no "stack" or "heap" in C standard, All we have is the lifetime of a variable.
Both test and test2() are equivalent. They return an implementation-defined pointer that you must not dereference, or else UB ensues.
If you don't dereference the returned pointer, calling test() or test2() does not result in undefined behavior, but such a function is probably not very useful.
Upon entering a function a new stack frame is added to the stack. The stack frame is where all autos (non static variables declared in the function) are stored. When we leave the function the return value is placed in a register (generally R0) in the CPU and the stack pointer is then decreased to remove the stack frame. We then return control to the point where we called the function and we get the return value from the register.
So in this case you have int arr[5], as the program enters the function a new stack frame is added to the stack. In this stack frame there is memory for 5 integers in an array, the variable arr is indeed now equivalent a pointer to the first element in the array. When you return the variable arr you are returning a pointer to the data in the stack frame, when the function exits and you return back to the previous function the stack pointer is then decreased to remove the stack frame of the function you just exited.
The pointer is still pointing to that place in memory where we previously had an array allocated. So when the stack is increased the memory arr is pointing to will be over written. Changing the data the returned value points to could result in some very "exciting" stuff happening as we don't know when the memory is now used for.
Array vs pointer example:
char arr[5];
char * ptr = arr;
In this case the compiler knows the size of arr and does not know the size of ptr so we can do sizeof(arr) and the compiler will do the calculation at compile time. When it comes to run time, they are equivalent values in memory.
Both cases are technically the same.
In both cases, a pointer to arr is returned. While the value of the returned pointer indeed points to the memory that used to contain arr, arr is already freed from the memory.
Therefore, sometime when you access the pointer you would still find there the contents of arr, which were just happened to not overridden yet. Other times, you might access it after this memory has been overridden, and get undefined data or even segmentation fault.
You still seem residually confused by the pointer being an automatic variable as well, so that you are afraid that returning it would be invalid even if it pointed to some valid memory (say, a static array).
It is important to remember that in C all parameter and return value passing is done by value. If you "return a pointer" as in return p; it is exactly the same mechanism as if you "returned an integer", as in return i;: The value of the variable is copied somewhere and obtained by the caller. In the case of i that value may be 42; in the case of p the value may be 3735928559 (or in other words, 0xdeadbeef). That value denotes the place in memory where e.g. your array was residing before it ceased to exist because the function returned. The address does not change when you copy it any more than 42 changes, and is completely independent of the lifetime of the variable p which once contained it โ€” it was, after all, copied out of it just in time.1
1This is beyond the scope of the question but technically conceptually, a temporary object is created for the return value. The lifetime and semantics of temporaries are more systematically categorized in modern C++.

Why is setting a derefernce pointer equal to a primitive illegal?

Why does setting the value of a dereferenced pointer raise a Segmentation fault 11? To make what I mean clear look a the follow code:
#include <stdio.h>
int *ptr;
*ptr = 2;
int main(){
printf("%d\n", *ptr);
return 0;
}
I thought that *ptr=2 would set the rvalue that the pointer ptr is point to to 2. Is that not the case? I apologize if for those c expert programmers, this is really easy/obvious.
Are we only allowed to set a dereferenced pointer (i.e. *ptr) to a value if that value had a memory address? i.e. like doing:
int k = 7;
int *ptr = k;
and then:
*ptr = 2;
The problem here is that ptr is not pointing to allocated space. See the following:
#include <stdio.h>
#include <stdlib.h>
int main(void){
// Create a pointer to an integer.
// This pointer will point to some random (likely unallocated) memory address.
// Trying set the value at this memory address will almost certainly cause a segfault.
int *ptr;
// Create a new integer on the heap, and assign its address to ptr.
// Don't forget to call free() on it later!
ptr = malloc(sizeof(*ptr));
// Alternatively, we could create a new integer on the stack, and have
// ptr point to this.
int value;
ptr = &value;
// Set the value of our new integer to 2.
*ptr = 2;
// Print out the value at our now properly set integer.
printf("%d\n", *ptr);
return 0;
}
It's not 'illegal', it's simply implementation defined. In fact, on some platforms (such as DOS), specific memory addresses were necessary, for example to write text to the video buffer which started at 0xB8000, or memory mapped controller I/O on the SNES.
On most current OS's, a feature called ASLR is used, for security reasons, which makes ancient modes of dedicated addresses a thing of the past, in favor of going through driver and kernel layers, which is what makes it 'illegal' for most places you would run it.
The most basic issue here is that you are not assigning ptr to a valid memory address, there are some cases where 0 is a valid memory address but usually not. Since ptr is global variable in your first case, it will be initialized to 0. remyabal asked a great follow-up question and best answer made me realize that this is a redeclaration here:
*ptr = 2;
and you are then setting ptr to have a value of 2 which is except by chance unlikely to point to a valid memory address.
If ptr was a local or automatic variable then it would be uninitialized and it's value would be indeterminate. Using a pointer with an indeterminate value is undefined behavior in both C and C++. It is in most case undefined behavior to use a NULL pointer as well although implementations are allowed to define the behavior.
On most modern system attempting to access memory your process does not own will result in a segmentation fault.
You can assign a valid memory address to ptr in a few ways, for example:
int k = 7;
int *ptr = &k;
^
note the use of of & to take the address of k or you could use malloc to allocate memory dynamically for it.
Your code is invalid, though some C compilers may permit it for compatibility with older versions of the language.
Statements, including assignment statements, are illegal (a syntax error) if they appear outside the body of a function.
You have:
int *ptr;
*ptr = 2;
at file scope. The first line is a valid declaration of an int* object called ptr, implicitly initialized to a null pointer value. The second line looks like an assignment statement, but since it's outside a function, the compiler most likely won't even try to interpret it that way. gcc treats it as a declaration. Old versions of C permitted you to omit the type name in a declaration; C99 dropped the "implicit int" rule. So gcc treats
*ptr = 2;
as equivalent to
int *ptr = 2;
and produces the following warnings:
c.c:4:1: warning: data definition has no type or storage class [enabled by default]
c.c:4:8: warning: initialization makes pointer from integer without a cast [enabled by default]
The first warning is because you omitted the int (or other type name) from the declaration. The second is because 2 is a value of type int, and you're using it to initialize an object of type int*; there is no implicit conversion from int to int* (other than the special case of a null pointer constant).
Once you get past that, you have two declarations of the same object -- but they're compatible, so that's permitted. And the pointer variable is initialized to (int*)2, which is a garbage pointer value (there's not likely to be anything useful at memory address 0x00000002).
In your main function, you do:
printf("%d\n", *ptr);
which attempts to print the value of an int object at that memory address. Since that address is not likely to be one that your program has permission to access, a segmentation fault is not a surprising result. (More generally, the behavior is undefined.)
(This is a fairly common problem in C: minor errors in a program can result in something that still compiles, but is completely different from what you intended. The way I think of it is that C's grammar is relatively"dense"; small random tweaks to a valid program often produce different but syntactically valid programs rather than creating syntax errors.)
So that's what your program actually does; I'm sure it's not what you intended it to do.
Take a deep breath and read on.
Here's something that's probably closer to what you intended:
#include <stdio.h>
int *ptr;
int main(void) {
*ptr = 2;
printf("%d\n", *ptr);
return 0;
}
Since there's now no initializer for ptr, it's implicitly initialized to a null pointer value. (And if ptr were defined inside main, its initial value would be garbage.) The assignment statement attempts to dereference that null pointer, causing a segmentation fault (again, the behavior is undefined; a segmentation fault is a likely result). Execution never reaches the printf call.
I thought that *ptr=2 would set the rvalue that the pointer ptr is point to to 2. Is that not the case?
Not quite. Pointers don't point to rvalues; an "rvalue" is merely the value of an expression. Pointers point to objects (if they point to anything). The assignment
*ptr = 2;
would assign the value 2 to the object that ptr points to -- but ptr doesn't point to an object!
Now let's see a version of your program that actually works:
#include <stdio.h>
int *ptr;
int variable;
int main(void) {
ptr = &variable;
*ptr = 2;
printf("*ptr = %d\n", *ptr);
printf("variable = %d\n", variable);
return 0;
}
Now ptr points to an object, and *ptr = 2 assigns a value to that object. The output is:
*ptr = 2
variable = 2

Allocating a value to a pointer in c

I am trying to give a value to a pointer.
this code works fine
int i =5;
int *ptr ;
ptr = &i;
printf(" %d\n",*ptr);
but this code shows an error
int i =5;
int *ptr ;
*ptr = 5;
printf(" %d\n",*ptr);
can someone explain this to me?
int *ptr gets initialized to a random location, which possibly points to invalid memory, and you try to write 5 to that location.
When you declare int *ptr, you're creating a variable called ptr which can hold the address of an int. When you dereference it in *ptr = 5, you're saying "store 5 in the address that ptr points to" - but as ptr is uninitialised, where it points to is undefined. So you're invoking undefined behaviour.
An int * does not store an int, but just an address that points to one. There still has to be a real int at that address.
If you want to allocate an int that exists outside of the local scope, you can use malloc. A simple conversion of your example without error checking:
int *ptr = malloc(sizeof(int));
*ptr = 5;
printf(" %d\n",*ptr);
If you do use malloc, just remember to free the allocated memory when you're finished:
free(ptr);
The second version assigns 5 to the memory pointed to by ptr, but you haven't yet initialized ptr to point to a known location.
So it writes the value to the memory at whatever address happens to be in ptr, which is whatever was in memory where ptr was declared.
You can't deterrence a pointer that doesn't point to somewhere you own.
int* ptr;
That allocates space for the pointer, but it doesn't allocate space for the chunk the pointer points to.
Also, since it's not initialized, ptr has an indeterminate value. This means that not only does it likely point to something you don't own, it also would be rather difficult to check if it's a valid pointer. It's usually a good idea to initialize pointers to either NULL (or nullptr if available) or an actual location (like you did in the first example).

Putting an int in an int pointer gives runtime error in C .

The following code works fine in ideone but it gives a runtime error in codeblocks IDE . Is my IDE broken or is there any programming language specific issues .
#include<stdio.h>
int main(){
int *pointer;
int num = 45;
*pointer = num;
printf("pointer points to value %d", *pointer);
return 0;
}
replace this
*pointer = num;
by
pointer = &num;
Your pointer should be pointed to a memory space before assignment of value to it.
When you define pointer in this way:
int *pointer;
This meas that you have defined pointer but the pointer is not yet pointing to a memory space. And if you use the pointer directly without pointing it to a memory space (like you did in your code) then you will get undefined behaviour.
pointing the pointer to amemory space could be done by one of the following way:
1) pointing to a static memory
int num;
int *pointer = &num;
num is an int defined as a static. So the pointer could be pointed to the num memory
2) pointing to a dynamic memory
int *pointer = malloc(sizeof(int));
the pointer could be pointed to a dynamic memory. the dynamic memory could be allocated with malloc() and when the memory became useless we can free memory with free(pointer)
Assign address of num to pointer as pointer is supposed to hold address not value. You can read more about pointers here
pointer = &num;
Change value of variable through pointer
*pointer = 11;
First,you have defined a pointer by "int *pointer".
Then, you try to use "*pointer = num" to realize indirect access โ€”โ€” assign num's value to the memory space which the pointer "pointer" has pointed to.
OK, here is the problem! From your codes, you only have defined a pointer, but you have not made it pointed to a memory space. Making indirect access without doing it is very dangerous. So, you see the runtime error.
Now, you should add "int value;pointer = &value;" to your codes. It will make the pointer "pointer" point to "value". And you can assign "num" to "value" through indirect access "*pointer = num".
In my opinion, you should distinguish definition and indirect access when you study pointer.
I'm a person with poor English. This is my first answer in stack overflow. I hope that my answer can help you. Thank you.
First of all u should initialize the pointer as to which its trying to point then use it to modify the pointed value..as...
pointer=&num;
now use the pointer to change or access the value to which its pointing.

Resources