I've been told at my university that everything is put on the top of the "stack" when working with a function. So, when returning from one, the top of the stack is removed, until we reach the bottom - main(). Suggesting that everything is lost we have made in the local scope of the previous functions.
Most books tell me the same thing.
However, i have come across a number of occurrences, where i used this exact feature.
For instance:
void address (bool** xpp)
{
bool* y = (bool*) malloc(10*sizeof(bool));
y[2] = false;
**xpp = &y;
}
int main(void)
{
bool* x;
bool** xp = &x;
address(&xp);
xp[0] = false;
xp[2] = false;
xp[7] = false;
printf("%d", xp[0]);
printf("%d", xp[2]);
printf("%d", xp[7]);
return 0;
}
In this case, to my understanding, i should not be able to refer to the xp[] array in the main() after address(), because, indeed i have set it's pointer to an arrays first elements pointer, but after returning to main() the array i had created in address() is supposed to be gone. So it should be pointing to nowhere and should pop up an exception.
However, all of the bool xp elements print '0', implying it's success in working.
Well, there are two different answers here. Your question boils down to, "I heard that local variables are lost when they're popped from the stack, but I tried it, and they were still there", but your actual code does not demonstrate the use of local variables on the stack that would be lost; it demonstrates the use of malloc, which is completely different.
And then the second answer is, any time you ask anything like "I heard that X doesn't work, but I tried it, and it worked", you're playing with fire: it might have seemed to have worked, but it wasn't guaranteed to.
Let's look at a slightly different version of your program. Instead of playing with pointers to pointers, I'm just going to have the address function return a pointer to the first element of an array. And instead of an array of bools, I'm going to use an array of int, so I can more easily show interesting numbers. Here's the first version of the code:
#include <stdio.h>
#include <stdlib.h>
int *address()
{
int *p = malloc(5 * sizeof(int));
if(p == NULL) abort();
p[0] = 1; p[1] = 2; p[2] = 3; p[3] = 4; p[4] = 5;
return p;
}
int main()
{
int *a = address();
int i, sum = 0;
for(i = 0; i < 5; i++) sum += a[i];
printf("sum = %d\n", sum);
for(i = 0; i < 5; i++) printf("a[%d] = %d\n", i, a[i]);
}
Function address returns a pointer, but it's a pointer to dynamically-allocated memory, which is guaranteed to stick around even after address returns. The program prints
sum = 15
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
which is just what you would expect.
But now let's change function address to not call malloc, but instead, use a local array:
int *address()
{
int la[5];
la[0] = 1; la[1] = 2; la[2] = 3; la[3] = 4; la[4] = 5;
return la;
}
This function is broken. It returns a pointer to the first element of a local array. By the time the function returns to its caller, that array will no longer exist. The pointer can never be useful to its caller; it is virtually guaranteed not to work. Indeed, when I compile this second version, my compiler warns me about it:
warning: address of stack memory associated with local variable 'a' returned
But if Ignore that warning and run the resulting program anyway, here's what I get:
sum = 15
a[0] = 507402241
a[1] = -16764064
a[2] = 0
a[3] = 0
a[4] = 0
This is a very interesting result. The second thing to notice is that the contents of the array are all wrong. We don't see 1, 2, 3, 4, 5; we see some numbers that are clearly garbage. But this was to be expected, because that array la in function address is gone.
But the first and perhaps more interesting thing to notice is that the sum is correct! How did that happen? And the sum doesn't match the contents of the array as printed! It's as if the "broken" pointer returned by the address function worked for a little while, just long enough to compute the "correct" sum, but got trashed later. And, in fact, that's exactly what happened.
After function address returns, the array la is "gone", but the memory on the stack that it was using hasn't been reused or erased yet, so the bit patterns are still there. So the calling function, main, can try to access those bits, and it even seems to get the right answer -- it computes the same sum, 15. It's important to note that this is absolutely not guaranteed to work; you would never want to depend on it in a real program -- it just happens to work.
But then, having computed the sum, main calls printf to print it out. And printf is a function that gets called -- a big, complicated function -- and it does all sorts of stuff, and allocates all sorts of its own variables on the stack. So that's when the stack memory that had been allocated to the la array actually gets overwritten. So that's why, when the last half of the main function tries to print out the array, it's garbage.
There's another point to make and that has to do with addresses. When we worry about the local array la getting lost or not, there are two questions to ask: do we lose the contents of the array, and, do we lose the pointer to the array? And in answering those questions, we encounter a significant fact: a function can still, perfectly well, return a value, even though the function's local (stack) storage has gone away.
To see this, consider the function
int five()
{
int r = 5;
return r;
}
When function five returns, its local variable r goes away. But the caller who says
int x = five();
has no problem, because as function five returns, and even as function fives local variable r is being allocated, the return value 5 is being copied into the caller's own variable x, so it's not lost.
But with all of that said -- and besides the question of whether the array is a local (stack) array or allocated with malloc -- there's also something wrong, sort of differently wrong, with he way you're taking y's address. y is a local variable, so after the function exits, &y is bogus, and not guaranteed to work, no matter where you've stashed it. (But, again, it might seem to work, for a little while, until something else overwrites the stack.)
How is it possible to access a functions variable outside the function?
Assuming it's not declared static you simply cannot, for the reason you mentioned: It's gone the moment the function had been left.
after returning to main() the array i had created in address() is supposed to be gone.
The call to malloc did not create a "variable", but it allocated memory from the heap. All that is stored locally inside the function's variable y is the address of this very block of memory. This address indeed is lost when y goes away by leaving the function.
To not lose the address of the memory allocated fix the code for example like this:
#include <stdlib.h> /* for malloc() */
#include <stdio.h> /* for printf() */
void address (bool** xpp)
{
bool* y = malloc(10*sizeof(bool));
y[2] = false;
*xpp = y;
}
int main(void)
{
bool* xp;
address(&xp);
xp[0] = false;
xp[2] = false;
xp[7] = false;
printf("%d", xp[0]);
printf("%d", xp[2]);
printf("%d", xp[7]);
return 0;
}
A major flaw still with this code is, that the caller (main) and the callee (address) do not "talk" about how much memory has to be/was allocate.
Almost everything about this code is misleading.
The question is, "How can the xp pointer be valid after function address returns? But it turns out that the call to address does not modify the xp pointer.
xp is set to point to x. The call to address doesn't change that. (The call to address does end up changing what xp points to, that is, it changes x.
Although xp is not a pointer to bool, it's used as if it were, in the three assignments
xp[0] = false;
xp[2] = false;
xp[7] = false;
Although xp doesn't actually point to memory that's been allocated to hold 10 bools, wherever it does point (that is, somewhere on the stack surrounding x), there's evidently enough memory to accidentally store 10 bools without causing too much damage.
And then, having stored a few bools at wherever-it-is, trying to print those three bools back out (from wherever-it-was) happens to work.
For answers to the question of how the local storage in a function can still be accessed (though incorrectly) after the function returns -- and although this code doesn't actually end up doing that -- see my other answer.
For the record, there are other problems. The call
address(&xp);
is a type mismatch (&xpp is a pointer-to-pointer-to-pointer-to-bool, but address() accepts a pointer-to-pointer-to-bool). The line
**xpp = &y;
is a type mismatch (&y is a pointer-to-pointer-to=-bool, but **xpp is a bool). Lines like
xp[0] = false;
are a type mismatch (false is a bool, but xp[0] is a pointer-to-bool). Finally, lines like
printf("%d", xp[0]);
are a type mismatch (xp[0] is a pointer-tobool, but %d expects an int, or maybe a bool).
Related
I've written a function that returns an array whilst I know that I should return a dynamically allocated pointer instead, but still I wanted to know what happens when I am returning an array declared locally inside a function (without declaring it as static), and I got surprised when I noticed that the memory of the internal array in my function wasn't deallocated, and I got my array back to main.
The main:
int main()
{
int* arr_p;
arr_p = demo(10);
return 0;
}
And the function:
int* demo(int i)
{
int arr[10] = { 0 };
for (int i = 0; i < 10; i++)
{
arr[i] = i;
}
return arr;
}
When I dereference arr_p I can see the 0-9 integers set in the demo function.
Two questions:
How come when I examined arr_p I saw that its address is the same as arr which is in the demo function?
How come demo_p is pointing to data which is not deallocated (the 0-9 numbers) already in demo? I expected that arr inside demo will be deallocated as we got out of demo scope.
One of the things you have to be careful of when programming is to pay good attention to what the rules say, and not just to what seems to work. The rules say you're not supposed to return a pointer to a locally-allocated array, and that's a real, true rule.
If you don't get an error when you write a program that returns a pointer to a locally-allocated array, that doesn't mean it was okay. (Although, it means you really ought to get a newer compiler, because any decent, modern compiler will warn about this.)
If you write a program that returns a pointer to a locally-allocated array and it seems to work, that doesn't mean it was okay, either. Be really careful about this: In general, in programming, but especially in C, seeming to work is not proof that your program is okay. What you really want is for your program to work for the right reasons.
Suppose you rent an apartment. Suppose, when your lease is up, and you move out, your landlord does not collect your key from you, but does not change the lock, either. Suppose, a few days later, you realize you forgot something in the back of one closet. Suppose, without asking, you sneak back to try to collect it. What happens next?
As it happens, your key still works in the lock. Is this a total surprise, or mildly unexpected, or guaranteed to work?
As it happens, your forgotten item still is in the closet. It has not yet been cleared out. Is this a total surprise, or mildly unexpected, or guaranteed to happen?
In the end, neither your old landlord, nor the police, accost you for this act of trespass. Once more, is this a total surprise, or mildly unexpected, or just about completely expected?
What you need to know is that, in C, reusing memory you're no longer allowed to use is just about exactly analogous to sneaking back in to an apartment you're no longer renting. It might work, or it might not. Your stuff might still be there, or it might not. You might get in trouble, or you might not. There's no way to predict what will happen, and there's no (valid) conclusion you can draw from whatever does or doesn't happen.
Returning to your program: local variables like arr are usually stored on the call stack, meaning they're still there even after the function returns, and probably won't be overwritten until the next function gets called and uses that zone on the stack for its own purposes (and maybe not even then). So if you return a pointer to locally-allocated memory, and dereference that pointer right away (before calling any other function), it's at least somewhat likely to "work". This is, again, analogous to the apartment situation: if no one else has moved in yet, it's likely that your forgotten item will still be there. But it's obviously not something you can ever depend on.
arr is a local variable in demo that will get destroyed when you return from the function. Since you return a pointer to that variable, the pointer is said to be dangling. Dereferencing the pointer makes your program have undefined behavior.
One way to fix it is to malloc (memory allocate) the memory you need.
Example:
#include <stdio.h>
#include <stdlib.h>
int* demo(int n) {
int* arr = malloc(sizeof(*arr) * n); // allocate
for (int i = 0; i < n; i++) {
arr[i] = i;
}
return arr;
}
int main() {
int* arr_p;
arr_p = demo(10);
printf("%d\n", arr_p[9]);
free(arr_p) // free the allocated memory
}
Output:
9
How come demo_p is pointing to data which is not deallocated (the 0-9 numbers) already in demo? I expected that arr inside demo will be deallocated as we got out of demo scope.
The life of the arr object has ended and reading the memory addresses previously occupied by arr makes your program have undefined behavior. You may be able to see the old data or the program may crash - or do something completely different. Anything can happen.
… I noticed that the memory of the internal array in my function wasn't deallocated…
Deallocation of memory is not something you can notice or observe, except by looking at the data that records memory reservations (in this case, the stack pointer). When memory is reserved or released, that is just a bookkeeping process about what memory is available or not available. Releasing memory does not necessarily erase memory or immediately reuse it for another purpose. Looking at the memory does not necessarily tell you whether it is in use or not.
When int arr[10] = { 0 }; appears inside a function, it defines an array that is allocated automatically when the function starts executing (or at certain times within the function execution if the definition is in some nested scope). This is commonly done by adjusting the stack pointer. In common systems, programs have a region of memory called the stack, and a stack pointer contains an address that marks the end of the portion of the stack that is currently reserved for use. When a function starts executing, the stack pointer is changed to reserve more memory for that function’s data. When execution of the function ends, the stack pointer is changed to release that memory.
If you keep a pointer to that memory (how you can do that is another matter, discussed below), you will not “notice” or “observe” any change to that memory immediately after the function returns. That is why you see the value of arr_p is the address that arr had, and it is why you see the old data in that memory.
If you call some other function, the stack pointer will be adjusted for the new function, that function will generally use the memory for its own purposes, and then the contents of that memory will have changed. The data you had in arr will be gone. A common example of this that beginners happen across is:
int main(void)
{
int *p = demo(10);
// p points to where arr started, and arr’s data is still there.
printf("arr[3] = %d.\n", p[3]);
// To execute this call, the program loads data from p[3]. Since it has
// not changed, 3 is loaded. This is passed to printf.
// Then printf prints “arr[3] = 3.\n”. In doing this, it uses memory
// on the stack. This changes the data in the memory that p points to.
printf("arr[3] = %d.\n", p[3]);
// When we try the same call again, the program loads data from p[3],
// but it has been changed, so something different is printed. Two
// different things are printed by the same printf statement even
// though there is no visible code changing p[3].
}
Going back to how you can have a copy of a pointer to memory, compilers follow rules that are specified abstractly in the C standard. The C standard defines an abstract lifetime of the array arr in demo and says that lifetime ends when the function returns. It further says the value of a pointer becomes indeterminate when the lifetime of the object it points to ends.
If your compiler is simplistically generating code, as it does when you compile using GCC with -O0 to turn off optimization, it typically keeps the address in p and you will see the behaviors described above. But, if you turn optimization on and compile more complicated programs, the compiler seeks to optimize the code it generates. Instead of mechanically generating assembly code, it tries to find the “best” code that performs the defined behavior of your program. If you use a pointer with indeterminate value or try to access an object whose lifetime has ended, there is no defined behavior of your program, so optimization by the compiler can produce results that are unexpected by new programmers.
As you know dear, the existence of a variable declared in the local function is within that local scope only. Once the required task is done the function terminates and the local variable is destroyed afterwards. As you are trying to return a pointer from demo() function ,but the thing is the array to which the pointer points to will get destroyed once we come out of demo(). So indeed you are trying to return a dangling pointer which is pointing to de-allocated memory. But our rule suggests us to avoid dangling pointer at any cost.
So you can avoid it by re-initializing it after freeing memory using free(). Either you can also allocate some contiguous block of memory using malloc() or you can declare your array in demo() as static array. This will store the allocated memory constant also when the local function exits successfully.
Thank You Dear..
#include<stdio.h>
#define N 10
int demo();
int main()
{
int* arr_p;
arr_p = demo();
printf("%d\n", *(arr_p+3));
}
int* demo()
{
static int arr[N];
for(i=0;i<N;i++)
{
arr[i] = i;
}
return arr;
}
OUTPUT : 3
Or you can also write as......
#include <stdio.h>
#include <stdlib.h>
#define N 10
int* demo() {
int* arr = (int*)malloc(sizeof(arr) * N);
for(int i = 0; i < N; i++)
{
arr[i]=i;
}
return arr;
}
int main()
{
int* arr_p;
arr_p = demo();
printf("%d\n", *(arr_p+3));
free(arr_p);
return 0;
}
OUTPUT : 3
Had the similar situation when i have been trying to return char array from the function. But i always needed an array of a fixed size.
Solved this by declaring a struct with a fixed size char array in it and returning that struct from the function:
#include <time.h>
typedef struct TimeStamp
{
char Char[9];
} TimeStamp;
TimeStamp GetTimeStamp()
{
time_t CurrentCalendarTime;
time(&CurrentCalendarTime);
struct tm* LocalTime = localtime(&CurrentCalendarTime);
TimeStamp Time = { 0 };
strftime(Time.Char, 9, "%H:%M:%S", LocalTime);
return Time;
}
I don't understand why this first version of my code is working and the second isn't.
First version :
#include <stdio.h>
#include <stdlib.h>
void procedure(int *t){
t = (int*)realloc(t, 4*sizeof(int));
t[3] = 4;
}
int main()
{
int *var;
var = (int*)malloc(sizeof(int));
procedure(var);
printf("%d\n\n", var[3]);
}
Second version:
#include <stdio.h>
#include <stdlib.h>
void procedure(int *t){
t = (int*)malloc(4*sizeof(int));
t[3] = 4;
}
int main()
{
int *var = NULL;
procedure(var);
printf("%d\n\n", var[3]);
}
In the second version, var is still a NULL pointer after the procedure execution. Why?
In the second version of your code, you are simply passing the address var points to. In procedure(...), you have, in essence, declared an int-pointer and on the first line (the malloc line), you are assigning that pointer a value. var is not updated with this value, because var is a separate entity entirely.
Essentially, you might as well be doing the following:
int *var = NULL;
int *t = malloc(4*sizeof(int));
t[3]=4;
printf("%d\n\n", var[3]);
var is never reassigned to anything, so var will always be null.
This can be corrected, though, like so:
#include <stdio.h>
#include <stdlib.h>
void procedure(int **t){
*t = malloc(4*sizeof(int));
(*t)[3] = 4;
}
int main()
{
int *var = NULL;
procedure(&var);
printf("%d\n\n", var[3]);
}
You can view this working at: https://ideone.com/aTY9Ok
EDIT: Also, it is worth noting that you should be doing the same thing in your first version because realloc(...) might return a different address than var which could lead to a memory leak.
You should remember that everything in C is passed by value, even pointers. So let's start with the second example.
You set var in main to NULL. Then you copy the address in it (NULL) to the varaible t in procedure. You proceed to malloc memory and assign that address to t. But t is just a copy of var, and any change to t is not reflected in var. Outside of procedure, the pointer retains the NULL address and the call to printf with var[3] is undefined behavior.
The second case features the same symptoms, but with a twist. realloc doesn't have to return a different address. It's memory allocator aware, so if it can just "extend" the block of memory being pointed to, it will. That's what you see happening. The call to realloc extends the memory and returns the same address it was given. So by sheer coincidence, t and var end up pointing to the same location, still.
That's why the modification to t[3] is visible using var[3]. This is not a behavior you can rely on.
Let's take it step by step here.
int *t means the "address of t", meaning that it is simply a number
Passing any number into a function means you are COPYING that data into that function (NOT LINKING IT)
Which means that number can change inside the function, but not outside (because it is a copy)
Even though t is a pointer, that pointer is still a number... setting equal to something else (ie. t = (int*)malloc(4*sizeof(int));) does not mean the value of var changed. (Because again, t is a copy of var)
If to read the description of the function realloc in the C Standard then you will know that
Returns 4
The realloc function returns a pointer to the new object
(which may have the same value as a pointer to the old object), or
a null pointer if the new object could not be allocated.
So in the first case it occured such a way that the value of the pointer was not changed. Though to rely on this results in undefined behavior.
In the second case when the standard function malloc was used the new extent with a different address was allocated. So the original pointer does not points to the new extent of memory because it was passed to the function by value.
Thus in the both cases the original pointer was not changed. The difference is that in the first case the system can just enlarge the memory extent keeping its original address unchanged while in the second case the system allocates a new extent with a different address.
I'm studying for a test and I came across something I'm finding hard to understand. We're working with pointers and memory allocation, and I was just fooling around with things, trying to see what changed what. I have this bit of code:
int * arr[10];
for(i=0; i<5;i++)
{
int index = i;
arr[index] = malloc(sizeof(int*));
int i = 2 * index;
*arr[index] = i;
printf("arr [%d] = %d\n", index, *arr[index]); /* should be 0, 2, 4, 6, 8 */
}
But what I've found is that if instead of using *arr[index] = i, I use arr[index] = &i I don't need the malloc. I've always assumed that these two things were essentially the same thing, but there must be some key difference I don't understand to warrant the use of malloc with the one.
I'm actually confused why I need malloc at all here really. I'm fairly new with memory allocation and I don't really understand when it's supposed to be used (obviously) and was wondering if anyone could clear this up for me.
Try this code instead:
int * arr[10];
for(i=0; i<5;i++)
{
int index = i;
int value = 2*i;
arr[index] = malloc(sizeof(int*));
*arr[index] = value;
}
for (i=0; i<5; i++)
{
int index = i;
printf("arr [%d] = %d\n", index, *arr[index]); /* should be 0, 2, 4, 6, 8 */
}
If you make the change you suggest, you would now have undefined behavior. Whereas this code still is valid.
You'd have undefined behavior because *arr[0] now points to a piece of stack memory that has left scope.
Your malloc should actually be malloc(sizeof(int)). You're allocating space for an int, not for a int *.
Written this way:
*arr[index] = i;
Means: Copy the value of i to the memory location pointed to by arr[index] (that was allocated earlier in your code).
arr[index] = &i;
Means: Copy the address of i to arr[index].
In your code i is automatically created inside the for loop and only exists inside that loop. Once you leave the loop (scope) the memory used to store i is then free to part of any newly created variables.
As sharth suggests, try looking at the values outside the original for loop to see some interesting results.
Yeah I think it is hard to understand, because i gets redefined in the middle of the for. I'll rewrite the code right now. I wrote i instead of index and 2*i instead of the redefined i.
int * arr[10];
for(i=0; i<5;i++)
{
arr[i] = malloc(sizeof(int));
*arr[i] = 2*i;
printf("arr [%d] = %d\n", i, *arr[i]); /* should be 0, 2, 4, 6, 8 */
}
You don't acutally need dynamic memory here, you know that array 0-4 will be used. You need dynamic memory, when you don't know how mutch data will you need. This code is written, so that the rest of your code will still work, but there is no malloc.
int array[5];
int **arr=array;
The following code means, that array[index] should point to the memory adress i is stored in. It does not copy the value that is in i, so when you change i, or i gets deleted, this will cause this pointer to be faulty, and cause problems later. You should't do this.
arr[index] = &i
One key difference is that &i will cease to exist once i goes out of scope (or, rather, that piece of memory can be reused for something else... which probably won't contain what you thought it contains).
Edit: I say below you didn't show how i was declared. Actually, you redeclare it, hiding the original value if i used in the loop. Regardless, i is going to go out of scope at the end of the loop or, likely, when the routine ends.
You don't show how i is declared here. However, in most cases, it'd be a local variable or perhaps a parameter passed to a method. In either case, the space for that variable is declared on the stack. You can take the address of that variable with &i, but that variable is going to go away after the method ends and the code pops those values off the stack. You might get lucky and have that value remain untouched for as long as you need it. But the moment another method is called, that value is likely to be overwritten and boom, your program is at best going to behave incorrectly.
You could get away with this if i is declared globally.
Further, you're pointing to the same address even after changing the value of i. If, at the end of your routine, you printed out all of the values of your array, you'd see they were all the same value - the last value you put into the array. That's because each entry in the array points to the same location.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Can a local variable's memory be accessed outside its scope?
returning address of local variable
I have a question, first of all look at the code
#include <stdio.h>
int sum(); /* function declaration */
int main()
{
int *p2;
p2 = sum(); /* Calling function sum and coping its return type to pointer variable p2 */
printf("%d",*p2);
} /* END of main */ `
int sum()
{
int a = 10;
int *p = &a;
return p;
} /* END of sum */
I think the answer is 10 and address of variable a, but my tesacher says that a is local to the function come so a and its
value will be deleted from the memory location when the function returns or is finished executing. I tried this code and the answer is weel of course 10 and address of a, I use the GNU/GCC compiler. Can anyone say what is right and wrong.
Thanks in advance.
Your teacher is absolutely right: even if you fix your program to return int* in place of int, your program still contains undefined behavior. The issue is that the memory in which a used to be placed is ripe for reuse once sum returns. The memory may stay around untouched for you to access, so you might even print ten, but this behavior is still undefined: it may run on one platform and crash on ten others.
You may get the right result but that is just because you are lucky, by the time the sum() is returned, the memory of a is returned to the system, and it can be used by any other variables, so the value may be changed.
For example:
#include <stdio.h>
int* sum(); /* function declaration */
int* sum2(); /* function declaration */
int main()
{
int *p2;
p2 = sum(); /* Calling function sum and coping its return type to pointer variable p2 */
sum2();
printf("%d",*p2);
}
int* sum()
{
int a = 10;
int *p = &a;
return p;
} /* END of sum */
int* sum2()
{
int a = 100;
int *p = &a;
return p;
} /* END of sum */
With this code, the a will be reused by sum2() thus override the memory value with 100.
Here you just return a pointer to int, suppose you are returning an object:
TestClass* sum()
{
TestClass tc;
TestClass *p = &tc;
return p;
}
Then when you dereference tc, weird things would happen because the memory it points to might be totally screwed.
Your pointer still points to a place in memory where your 10 resides. However, from Cs point of view that memory is unallocated and could be reused. Placing more items on the stack, or allocating memory could cause that part of memory to be reused.
The object a in the function sum has automatic lifetime. It's lifetime will be ended as soon as the scope in which it has been declared in (the function body) is left by the program flow (the return statement at the end of the function).
After that, accessing the memory at which a lived will either do what you expect, summon dragons or set your computer on fire. This is called undefined behavior in C. The C standard itself, however, says nothing about deleting something from memory as it has no concept of memory.
Logically speaking, a no longer exists once sum exits; its lifetime is limited to the scope of the function. Physically speaking, the memory that a occupied is still there and still contains the bit pattern for the value 10, but that memory is now available for something else to use, and may be overwritten before you can use it in main. Your output may be 10, or it may be garbage.
Attempting to access the value of a variable outside that variable's lifetime leads to undefined behavior, meaning the compiler is free to handle the situation any way it wants to. It doesn't have to warn you that you're doing anything hinky, it doesn't have to work the way you expect it to, it doesn't have to work at all.
I am currently experimenting with C, and I am having a really hard time understanding how to use pointers in functions. I understand what is happening when I initialize a pointer then dereferencing it, however, I get a little confused when I have to use them in functions as follows:
I have three functions here, but I do not know exactly when to use which. I am still relatively new to C.
int returnSomething(int a, int b)
int returnSomething(int *ptrA, int *ptrB)
int* returnSomething(int *ptrA, int *ptrB);
edit:
Is there a major difference between the three?
You need to adapt your usage to every situation.
The first case, you take two ints by value as parameters and return an int. Because your parameters are by value, any changes applied to them will only have function scope.
For example:
int returnSomething(int a, int b)
{
a = 0;
b = 0;
return 0;
}
//....
int x = 3;
int y = 4;
returnSomething(a,b);
// x will still be 3, y will still be 4
In the second case, because you pass parameters as pointers, you will be able to change the values.
int returnSomething(int* a, int* b)
{
*a = 0;
*b = 0;
return 0;
}
//....
int x = 3;
int y = 4;
returnSomething(&a,&b);
// x and y will be 0 here
The third case, besides passing parameters by their pointer, you return a pointer to an int. This means inside the function you have to allocate memory and free it when you are done. I don't recommend using this, there usually are workarounds to doing it.
int* returnSomething(int* a, int* b)
{
int* x = malloc(sizeof(int));
*x = 1;
return x;
}
//....
int x = 3;
int y = 4;
int* z = returnSomething(&a,&b);
free(z);
The answer is, it depends on what you want to do. If you need to change the parameters value in the method, pass by reference or by pointer. I wouldn't recommend using the last method.
Also, this applies because you're dealing with POD types. If you have your own struct, it will be more efficient passing it by pointer or returning a pointer, since a new copy won't have to be made.
Let's explain passing by reference to you first; it's a lot less complicated to deal with.
Say you have:
int increment(int &a)
{
a = a + 1;
return a;
}
increment(foo); // foo = foo + 1;
(NOTE: To make it easier to understand, I've sacrificed some 'correctness'.)
This does two things:
The third line increments a by 1. But notice - we put &a in the function declaration. This means that the value passed to increment() "by reference" is also incremented by 1. In other words, foo increases by 1, just like a.
The fourth line returns the value of a - a number, such as 1, 2, 42, -21, etc.
One more thing: Passing by reference is C++; you can't use it in C, but it's a good concept to learn before you start messing with pointers.
Passing a pointer is basically just passing by value... except you're passing the location in memory (0x12345678), as opposed to the actual foo.
int pincrement(int *p)
{
*p = *p + 1;
return *p;
}
pincrement(&foo); // foo = foo + 1;
This does the same thing as our first program - it increments the value of foo.
The &foo tells you the address of foo in memory. This information is passed to p. So:
p = &foo;
On the third line, the value pointed to by *p is incremented. In other words, foo is incremented by 1.
The fourth line returns the value of foo - a number, such as 1, 2, 42, -21, etc.
For returning pointers, you could use them to return strings:
char *HelloWorld()
{
return "Hello, World!";
}
The answer to your question has more to do with memory considerations and good code design i.e. whether you'd like to conserve resources and if you are aware of what's going on in your code at any one instance in time.
1) when you pass by value ( int returnSomething(int a, int b) ) every parameter is a copy and any changes made to them doesn't affect the original variable outside of the function (The parameters have function scope), and the function returns a value which you can then use to initialise a variable.
2) When you pass by pointer, you're passing an address to a location in memory so remember that as a matter of good code design you have to insulate that location against modification by another external (lock semantics) process. This especially applies to your provided examples:
int returnSomething(int *ptrA, int *ptrB)
int* returnSomething(int *ptrA, int *ptrB);
wherein changes made to *ptrA and *ptrB within the function persists after the function exits. The only difference between the two is that one of the functions return a value which you can then use to initialise a variable ( int returnSomething(int *ptrA, int *ptrB) ), the other returns another address to a location in memory that maybe subject to change and/or garbage headaches depending on your program design (you create memory inside the function for the return type and assign that address to a pointer variable, the pointer variable itself can be arbitrarily changed to point to another location in memory, e.t.c.). I'll expand on Luchian's last example by adding: imagine some somewhere else in your code you pass the *z variable to another function which then tries to use the memory pointed to by that address, you now have a pointer variable pointing to nothing which you then try to use.
It all boils down to good code design. If you understand the pros and cons of pointers and use them appropriately then you'll have no issues.
Luchian Grigore has written some good description above. I would like to give you a small thought to further simplify your thinking.
When ever you pass a argument to a function in C, try to think what exactly goes on to the stack ( in case 1, actual integer variables gets pushed onto stack and in case 2 & 3 adresses of those integer variables gets pushed ), now to this combine that fact that changes made to variables on stack vanish as soon as the control returns from funciton and stack unwindes.
So in simple terms if you plan to change the varibales being passed inside the function and expect to use those changes later then consider passing the address of those varibles, else simply pass variables.
For int, always pass by value unless it is not possible to do so.
i.e int returnSomething(int a, int b).
When you are passing some custom big struct, pass it and return it as a pointer unless it is not possible to do so.