Variable scope inside while loop - c

This is perhaps one of the most odd things I've ever encountered. I don't program much in C but from what I know to be true plus checking with different sources online, variables macroName and macroBody are only defined in scope of the while loop. So every time the loop runs, I'm expecting marcoName and macroBody to get new addresses and be completely new variables. However that is not true.
What I'm finding is that even though the loop is running again, both variables share the same address and this is causing me serious headache for a linked list where I need to check for uniqueness of elements. I don't know why this is. Shouldn't macroName and macroBody get completely new addresses each time the while loop runs?
I know this is the problem because I'm printing the addresses and they are the same.
while(fgets(line, sizeof(line), fp) != NULL) // Get new line
{
char macroName[MAXLINE];
char macroBody[MAXLINE];
// ... more code
switch (command_type)
{
case hake_macro_definition:
// ... more code
printf("**********%p | %p\n", &macroName, &macroBody);
break;
// .... more cases
}
}
Code that is part of my linked-list code.
struct macro {
struct macro *next;
struct macro *previous;
char *name;
char *body;
};
Function that checks if element already exists inside linked-list. But since *name has the same address, I always end up inside the if condition.
static struct macro *macro_lookup(char *name)
{
struct macro *temp = macro_list_head;
while (temp != NULL)
{
if (are_strings_equal(name, temp->name))
{
break;
}
temp = temp->next;
}
return temp;
}

These arrays are allocated on the stack:
char macroName[MAXLINE];
char macroBody[MAXLINE];
The compiler has pre-allocated space for you that exists at the start of your function. In other words, from the computer's viewpoint, the location of these arrays would the same as if you had defined them outside the loop body at the top of your function body.
The scope in C merely indicates where an identifier is visible. So the compiler (but not the computer) enforces the semantics that macroName and macroBody cannot be referenced before or after the loop body. But from the computer's viewpoint, the actual data for these arrays exists once the function starts and only goes away when the function ends.
If you were to look at the assembly dump of your code, you'd likely see that your machine's frame pointer is decremented by a big enough amount for your function's call stack to have space for all of your local variables, including these arrays.

What I need to mention in addition to chrisaycock's answer: you should never use pointers to local variables outside function these variables were defined in. Consider this example:
int * f()
{
int local_var = 0;
return &local_var;
}
int g(int x)
{
return (x > 0) ? x : 0;
}
int main()
{
int * from_f = f(); //
*from_f = 100; //Undefined behavior
g(15); //some function call to change stack
printf("%d", *from_f); //Will print some random value
return 0;
}
The same, actually, applies to a block. Technically, block-local variables can be cleaned out after the block ends. So, on each iteration of a loop old addresses can be invalid. It will not be true since C compiler indeed puts these vars to the same address for perfomance reasons, but you can not rely on it.
What you need to understand is how memory is allocated. If you want to implement a list, it is a structure that grows. Where does the memory come from? You can not allocate much memory from the stack, plus the memory is invalidated once you return from a function. So, you will need to allocate it from the heap (using malloc).

Related

Preventing GCC from merging variables in braced groups

Edit:
Apparently accessing variables inside braced groups after they end is undefined behaviour. Since I don't want to use dynamic allocation for nodes (as suggested by #dbush, #ikegami) I assume the next best way to keep hidden variables (within a function) is generating unique variable names for the nodes (with __LINE__) and 'declaring' without the use of a braced group. The code now reads something like
#define PASTE_(x, y) x ## y
#define PASTE(x, y) PASTE_(x, y)
#define LT_mark_(LABEL, NAME, DELETE)\
struct LifeTime LABEL ={\
.delete=DELETE,\
.prev=lt_head,\
.ref=NAME\
};\
\
lt_head = &LABEL;\
#define LT_mark(NAME, DELETE) LT_mark_(PASTE(lt_, __LINE__), NAME, DELETE)
/Edit
I'm trying to keep records for memory allocated within a function's scope.
Records are kept by a LifeTime structure, which form a linked list. This list is later traversed when returning from said function, in order to automatically free the memory. The lt_head variable is used to keep track of the current head of the list.
struct LifeTime {
void (*delete)(void*);
struct LifeTime *prev;
void *ref;
};
#define LT_mark(NAME, DELETE)\
{\
struct LifeTime _ ={\
.delete=DELETE,\
.prev=lt_head,\
.ref=NAME\
};\
\
lt_head = &_;\
}
int example (){
struct LifeTime *lt_head = NULL;
char *s = malloc(64); LT_mark(s, free);
char *s2 = malloc(64); LT_mark(s2, free);
...
}
Using this code, the temporary variables (named _) within the braced groups created by the LT_mark macro, are created with the same memory address.
I assume the reason for this is, as stated in the answer to this question: In C, do braces act as a stack frame?
that variables with non-overlapping usage lifetimes may be merged if the compiler deems it appropriate.
Is there any way to override this behaviour? I acknowledge it may be impossible (I am using GCC without any optimization flags, so I can't simply remove them), but the actual code I am working with requires that the variables inside these groups are kept afterwards, though hidden from visibility (as braced groups do usually). I considered using __attribute__((used)) but apparently this is only valid for functions and global variables.
The lifetime of a variable is that of its enclosing scope, so when that scope ends the variable no longer exits. Saving the address of that variable and attempting to use it when its lifetime has ended causes undefined behavior.
For example:
int *p;
{
int i=4;
p=&i;
printf("*p=%d\n", *p); // prints *p=4
}
printf("*p=%d\n", *p); // undefined behavior, p points to invalid memory
Inside of the braces, p points to valid memory and can be dereferenced. Outside of the braces p cannot be safely defererenced.
You'll need to do some dynamic allocation to create these structures. Also, this isn't a place where you should be using a macro instead of a function:
void LT_mark(void *p, void (*cleanup)(void *))
{
struct LifeTime *l = malloc(sizeof *l);
l->delete = cleanup;
l->prev = lt_head;
l->ref = p;
lt_head = l;
}
And similarly the cleanup function:
void LT_clean()
{
struct LiftTime *p;
while (lt_head) {
lt_head->delete(lt_head->ref);
p = lt_head->prev;
lt_head = lt_head->prev;
free(p);
}
}
Also, the prev field should be renamed to next, as the existing name is misleading.
Under most circumstances, you'll want to use #dbush's dynamic allocation solution. Since you're presumably using this with dynamic memory allocations of some kind anyway, dynamically allocating the descriptor blocks shouldn't be a huge overhead.
However, under some really restricted circumstances which you will have to police yourself, and assuming that you're not using an antediluvian version of the C compiler, it is possible to do this fairly simply with compound literals. Aside from the C compiler version limitation (C99 or better, which shouldn't be a huge burden), this will work in exactly the same circumstances as your edit#1 using token concatenation to generate a unique name: that is, if no use of the LT_mark macro is inside a braced-block subordinate to the function.
The reason for this restriction -- which, as I said, applies also to your solution with token concatenation -- is that the lifetime of automatic allocations terminates when control exits from the block in which they were declared. This is an essential aspect of C (and many other programming languages), so it's important to be clear about how it works.
Here's a simple example:
int example (){
struct LifeTime *lt_head = NULL;
char *s = malloc(64); LT_mark(s, free);
for (int i = 0; i < 4; ++i) {
/* InnerBlock */
char *s2 = malloc(64); LT_mark(s2, free);
....
}
/* Lifetime of all variables declared in InnerBlock expires */
....
/* If lt_head points to a struct automatically allocated inside
* InnerBlock, it is now a dangling pointer and cannot be used.
* The next statement is Undefined Behaviour.
*/
freeTheMallocs(lt_head);
}
Note that the problem is not that the inner block is executed more than once (although that will probably guarantee that you notice the problem). The same thing would happen had I written it as a conditional:
int example (int flag){
struct LifeTime *lt_head = NULL;
char *s = malloc(64); LT_mark(s, free);
if (flag) {
/* InnerBlock */
char *s2 = malloc(64); LT_mark(s2, free);
....
}
/* Lifetime of all variables declared in InnerBlock expires */
....
freeTheMallocs(lt_head); /* Dangling pointer */
}
The above cannot work with automatic allocation of descriptor blocks (but it will work fine with dynamic allocation).
OK, so what happens if you absolutely promise to only use LT_mark in the outermost block of your function, as with your original example:
int example (){
struct LifeTime *lt_head = NULL;
char *s = malloc(64); LT_mark(s, free);
char *s2 = malloc(64); LT_mark(s2, free);
freeTheMallocs(lt_head);
}
That will work. Your only problem is how to enforce the restriction, including on all the maintenance programmers who will modify the code after you leave the project, and may not have the foggiest idea of why they're not allowed to nest LT_mark inside a block (or even know that they're not allowed to do that).
But if you like playing with fire, you can do it like this:
#define LT_mark(NAME, DELETE) \
lt_head = &(struct LifeTime){ \
.delete=DELETE, \
.prev=lt_head, \
.ref=NAME \
}
This will work, in the limited set of cases in which it does work, because the the compound literal created by the macro "has automatic storage duration associated with the enclosing block." (§6.5.2.5/5).
Honestly, I sincerely hope you don't use the above code. I contribute this answer mostly in the hopes that it provides some kind of explanation of the importance of understanding lifetimes.

BOOLEAN allocate_items(struct item * items, size_t howmany) function for allocate an array of struct item

Recently, I'm learning C. I found a question on the internet. The question is:
What is the problem with this function in terms of memory allocation?
What is a good solution? You may assume that a struct item type has
been declared. The purpose of this function is to allocate an array of
struct item, which you may assume has been declared prior to this
function.
BOOLEAN allocate_items(struct item * items, size_t howmany)
{
size_t count;
items = malloc(sizeof(struct item) * howmany);
if(!items) {
perror("failed to allocate memory");
return FALSE;
}
return TRUE;
}
So, I think that the 4th line is wrong. It should be like:
items = malloc(sizeof(struct item));
And also the 6th line is wrong. It should be like:
if(items == NULL){
Is it correct?
First of all, both line 4 and 6, as you mentioned seems to be OK.
That said, the basic problem with this function is, you're allocating memory to a local scope of variable. This way
as you don't return the pointer to allocated memory, after the function returns, there would be no way to access the allocated memory.
by not freeing up the allocated memory, you will face memory leak.
If you have to allocate memory to a pointer, you need to pass the address of that pointer to the function and allocate memory. You can also return the pointer but then you need to change the function signature.
Finally, arrays are not pointers and vice-versa. They may appear or beahave similar sometimes, but they are not the same.
The 4th line is not wrong as they are trying to declare an array of the structs.
You should add a line inside the function that declares a new pointer, temp, to hold the current value of items, then after allocating the memory,
the 6th line should be
if(items == temp)
to check whether the value has changed(because that is the closest we can get to checking whether malloc worked)
this is because the ! operator is used to check if a condition is true or not(at least at a basic level in most languages) and as a pointer isn't a condition or an int that can be used as true or false, the operator won't work.
Here a fixed version, as it would probably be written in the "industry".
bool allocate_items(struct item ** pitems, size_t howmany)
{
// argument validation
assert(NULL != pitems); // some also add release version checks...
if(NULL == pitems ) return false;
// We can also spot memory leak sources here.
// If *pItems != NULL - does that mean we have to free first to prevent
// a leak? What if it is just some random value and not something we can
// free? So contract usually is: *pitems has to be NULL...
assert(NULL == *pitems);
if(NULL != *pitems) return false;
// implementation
*pitems = malloc(sizeof(struct item) * howmany);
if(NULL == *pitems) {
perror("failed to allocate memory");
}
return NULL != *pitems;
}
While the bool defined in stdbool.h sometimes causes trouble with C++ interop (same symbols on both sides, but sometimes sizeof(bool) differs), it is still the better option compared to inventing yet another bool type.
The pitems is a pointer to the location where the pointer to the new chunk of memory shall be written to. A caller of this function might have written:
int main(int argc, const char*[] argv) {
struct item *myBunchOfStuff = NULL;
if(false != allocate_items( &myBunchOfStuff, 20) ) {
// ...
free(myBunchOfStuff);
myBunchOfStuff = NULL;
}
return 0;
}
Defensive programming states: Your function cannot claim "Heh - my function only crashed because I was given a bad value!". Instead, it has to verify itself. It is responsible not to crash. The pointer could still be != NULL but otherwise bad. That is impossible for the function to catch, usually.
In C, everyone is proud of not requiring the cast of malloc()'s return value. You can be proud of that until you compile your code with a C++ compiler. Then you have to change your code and fix that. Well, I guess it is a matter of preference...
While parameter checking is often seen as a separate part of the functions implementation, after that, you should try to stick to "single point of exit". Main reason for that is maintainability. With multiple exit points, if the function gets bigger later on, it gets harder to spot if some early exit forgets to free some memory or cleanup other forms of state.

Smart Pointers in a language that compiles to C

I'm writing a simple language that compiles to C, and I want to implement smart pointers. I need a bit of help with that though, as I can't seem to think of how I would go around it, or if it's even possible. My current idea is to free the pointer when it goes out of scope, the compiler would handle inserting the frees. This leads to my questions:
How would I tell when a pointer has gone out of scope?
Is this even possible?
The compiler is written in C, and compiles to C. I thought that I could check when the pointer goes out of scope at compile-time, and insert a free into the generated code for the pointer, i.e:
// generated C code.
int main() {
int *x = malloc(sizeof(*x));
*x = 5;
free(x); // inserted by the compiler
}
The scoping rules (in my language) are exactly the same as C.
My current setup is your standard compiler, first it lexes the file contents, then it parses the token stream, semantically analyzes it, and then generates code to C. The parser is a recursive descent parser. I would like to avoid something that happens on execution, i.e. I want it to be a compile-time check that has little to no overhead, and isn't full blown garbage collection.
For functions, each { starts a new scope, and each } closes the corresponding scope. When a } is reached, the variables inside that block go out-of-scope. Members of structs go out of scope when the struct instance goes out of scope. There's a couple exceptions, such as temporary objects go out-of-scope at the next ;, and compilers silently put for loops inside their own block scope.
struct thing {
int member;
};
int foo;
int main() {
thing a;
{
int b = 3;
for(int c=0; c<b; ++c) {
int d = rand(); //the return value of rand goes out of scope after assignment
} //d and c go out of scope here
} //b goes out of scope here
}//a and its members go out of scope here
//globals like foo go out-of-scope after main ends
C++ tries really hard to destroy objects in the opposite order they're constructed, you should probably do that in your language too.
(This is all from my knowledge of C++, so it might be slightly different from C, but I don't think it is)
As for memory, you'll probably want to do a little magic behind the scenes. Whenever the user mallocs memory, you replace it with something that allocates more memory, and "hide" a reference count in the extra space. It's easiest to do that at the beginning of the allocation, and to keep alignment guarantees, you use something akin to this:
typedef union {
long double f;
void* v;
char* c;
unsigned long long l;
} bad_alignment;
void* ref_count_malloc(int bytes)
{
void* p = malloc(bytes + sizeof(bad_alignment)); //does C have sizeof?
int* ref_count = p;
*ref_count = 1; //now is 1 pointer pointing at this block
return p + sizeof(bad_alignment);
}
When they copy a pointer, you silently add something akin to this before the copy
void copy_pointer(void* from, void* to) {
if (from != NULL)
ref_count_free(free); //no longer points at previous block
bad_alignment* ref_count = to-sizeof(bad_alignment);
++*ref_count; //one additional pointing at this block
}
And when they free or a pointer goes out of scope, you add/replace the call with something like this:
void ref_count_free(void* ptr) {
if(ptr) {
bad_alignment* ref_count = ptr-sizeof(bad_alignment);
if (--*ref_count == 0) //if no more pointing at this block
free(ptr);
}
}
If you have threads, you'll have to add locks to all that. My C is rusty and the code is untested, so do a lot of research on these concepts.
The problem is slightly more difficult, since your code is straightforward, but... what if another pointer is made to point to the same place as x?
// generated C code.
int main() {
int *x = malloc(sizeof(*x));
int *y = x;
*x = 5;
free(x); // inserted by the compiler, now wrong
}
You doubtlessly will have a heap structure, in which each block has a header that tells a) whether the block is in use, and b) the size of the block. This can be achieved with a small structure, or by using the highest bit for a) in the integer value for b) [is this a 64bit compiler or 32bit?]. For simplicity, lets consider:
typedef struct {
bool allocated: 1;
size_t size;
} BlockHeader;
You would have to add another field to that small structure, which would be a reference count. Each time a pointer points to that block in the heap, you increment the reference count. When a pointer stops pointing to a block, then its reference count is decremented. If it reaches 0, then it can be compacted or whatever. The use of the allocated field has now gone.
typedef struct {
size_t size;
size_t referenceCount;
} BlockHeader;
Reference counting is quite simple to implement, but comes with a down side: it means there is overhead each time the value of a pointer changes. Still, is the simplest scheme to work, and that's why some programming languages still use it, such as Python.

Pointer to local variable outside the scope of its declaration

Let's say I have a structure representing a PDF document pdf and a structure representing one of its pages pdf_page:
typedef struct pdf_page {
int page_no;
pdf_page *next_page;
char *content;
} pdf_page;
typedef struct {
pdf_page *first_page, *last_page;
} pdf;
From my main(), I call create_pdf_file(pdf *doc):
void main() {
pdf doc;
create_pdf_file(&doc);
// reading the linked list of pages here
}
Assume that create_pdf_file is something along these lines:
void
create_pdf_file(pdf *doc) {
for (int i = 0; i < 10; i++) {
pdf_page p;
p.page_no = i;
p.contents = "Hello, World!";
doc->last_page->next_page = p;
}
}
(This is merely an example source code, so no list processing is shown. Obviously, the first_page and last_page members of pdf need to be set first.)
My question: If I access doc->first_page - as well as the other pages in the linked list - after the create_pdf_file() call in my main(), is it possible that I get segmentation faults because of "taking the local variable p out of its context"?
(I am not sure whether I have guaranteed that the corresponding memory location will not be used for something else.)
If so, how do I avoid this?
yes, p is a local variable stored on the stack, when the lifetime ends (every loop iteration) any pointer to it gets invalid. you need to allocate every page with malloc() and free() it after you are finished.
this would look similar to:
for (int i = 0; i < 10; i++)
{
pdf_page* p = malloc(sizeof(pdf_page));
p->page_no = i;
p->contents = "Hello, World!";
doc->last_page->next_page = p;
}
and when you call your function you have to pass a pointer to doc:
create_pdf_file(&doc);
is it possible that I get segmentation faults because of "taking the local variable p out of its context"?
Once the block in which p is declared terminates, any pointer to p is invalid (a "dangling pointer") and attempting to dereference such a pointer is Undefined Behaviour. In other words, don't do it: you could get segmentation faults, or any other behaviour (including random memory corruption or the use of the wrong data without any error condition.)
(I am not sure whether I have guaranteed that the corresponding memory location will not be used for something else.)
You've guaranteed that the lifetime of p is shorter than a pointer to p.
If so, how do I avoid this?
Use malloc to dynamically allocate a memory region of the correct size to hold the datum. Don't forget to free the memory when you no longer need it.

Returning local data from functions in C and C++ via pointer

I have argument with my friend. He says that I can return a pointer to local data from a function. This is not what I have learned but I can't find a counterargument for him to prove my knowledge.
Here is illustrated case:
char *name() {
char n[10] = "bodacydo!";
return n;
}
And it's used as:
int main() {
char *n = name();
printf("%s\n", n);
}
He says this is perfectly OK because after a program calls name, it returns a pointer to n, and right after that it just prints it. Nothing else happens in the program meanwhile, because it's single threaded and execution is serial.
I can't find a counter-argument. I would never write code like that, but he's stubborn and says this is completely ok. If I was his boss, I would fire him for being a stubborn idiot, but I can't find a counter argument.
Another example:
int *number() {
int n = 5;
return &n;
}
int main() {
int *a = number();
int b = 9;
int c = *a * b;
printf("%d\n", c);
}
I will send him this link after I get some good answers, so he at least learns something.
Your friend is wrong.
name is returning a pointer to the call stack. Once you invoke printf, there's no telling how that stack will be overwritten before the data at the pointer is accessed. It may work on his compiler and machine, but it won't work on all of them.
Your friend claims that after name returns, "nothing happens except printing it". printf is itself another function call, with who knows how much complexity inside it. A great deal is happening before the data is printed.
Also, code is never finished, it will be amended and added to. Code the "does nothing" now will do something once it's changed, and your closely-reasoned trick will fall apart.
Returning a pointer to local data is a recipe for disaster.
you will get a problem, when you call another function between name() and printf(), which itself uses the stack
char *fun(char *what) {
char res[10];
strncpy(res, what, 9);
return res;
}
main() {
char *r1 = fun("bla");
char *r2 = fun("blubber");
printf("'%s' is bla and '%s' is blubber", r1, r2);
}
As soon as the scope of the function ends i.e after the closing brace } of function, memory allocated(on stack) for all the local variables will be left. So, returning pointer to some memory which is no longer valid invokes undefined behavior.
Also you can say that local variable lifetime is ended when the function finished execution.
Also more details you can read HERE.
My counter-arguments would be:
it's never OK to write code with undefined behavior,
how long before somebody else uses that function in different context,
the language provides facilities to do the same thing legally (and possibly more efficiently)
It's undefined behavior and the value could easily be destroyed before it is actually printed. printf(), which is just a normal function, could use some local variables or call other functions before the string is actually printed. Since these actions use the stack they could easily corrupt the value.
If the code happens to print the correct value depends on the implementation of printf() and how function calls work on the compiler/platform you are using (which parameters/addresses/variables are put where on the stack,...). Even if the code happens to "work" on your machine with certain compiler settings it's far from sure that it will work anywhere else or under slightly different border conditions.
You are correct - n lives on the stack and so could go away as soon as the function returns.
Your friend's code might work only because the memory location that n is pointing to has not been corrupted (yet!).
As the others have already pointed out it is not illegal to do this, but a bad idea because the returned data resides on the non-used part of the stack and may get overridden at any time by other function calls.
Here is a counter-example that crashes on my system if compiled with optimizations turned on:
char * name ()
{
char n[] = "Hello World";
return n;
}
void test (char * arg)
{
// msg and arg will reside roughly at the same memory location.
// so changing msg will change arg as well:
char msg[100];
// this will override whatever arg points to.
strcpy (msg, "Logging: ");
// here we access the overridden data. A bad idea!
strcat (msg, arg);
strcat (msg, "\n");
printf (msg);
}
int main ()
{
char * n = name();
test (n);
return 0;
}
gcc : main.c: In function ‘name’:
main.c:4: warning: function returns address of local variable
Wherever it could been done like that (but it's not sexy code :p) :
char *name()
{
static char n[10] = "bodacydo!";
return n;
}
int main()
{
char *n = name();
printf("%s\n", n);
}
Warning it's not thread safe.
You're right, your friend is wrong. Here's a simple counterexample:
char *n = name();
printf("(%d): %s\n", 1, n);
Returning pointer to local variable is aways wrong, even if it appears to work in some rare situation.
A local (automatic) variable can be allocated either from stack or from registers.
If it is allocated from stack, it will be overwritten as soon as next function call (such as printf) is executed or if an interrupt occurs.
If the variable is allocated from a register, it is not even possible to have a pointer pointing to it.
Even if the application is "single threaded", the interrupts may use the stack. In order to be relatively safe, you should disable the interrupts. But it is not possible to disable the NMI (Non Maskable Interrupt), so you can never be safe.
While it is true that you cannot return pointers to local stack variables declared inside a function, you can however allocate memory inside a function using malloc and then return a pointer to that block. Maybe this is what your friend meant?
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char* getstr(){
char* ret=malloc(sizeof(char)*15);
strcpy(ret,"Hello World");
return ret;
}
int main(){
char* answer=getstr();
printf("%s\n", answer);
free(answer);
return 0;
}
The way I see it you have three main options because this one is dangerous and utilizes undefined behavior:
replace: char n[10] = "bodacydo!"
with: static char n[10] = "bodacydo!"
This will give undesirable results if you use the same function more than once in row while trying to maintain the values contained therein.
replace:
char n[10] = "bodacydo!"
with:
char *n = new char[10];
*n = "bodacydo!"
With will fix the aforementioned problem, but you will then need to delete the heap memory or start incurring memory leaks.
Or finally:
replace: char n[10] = "bodacydo!";
with: shared_ptr<char> n(new char[10]) = "bodacydo!";
Which relieves you from having to delete the heap memory, but you will then have change the return type and the char *n in main to a shared_prt as well in order to hand off the management of the pointer. If you don't hand it off, the scope of the shared_ptr will end and the value stored in the pointer gets set to NULL.
If we take the code segment u gave....
char *name() {
char n[10] = "bodacydo!";
return n;
}
int main() {
char *n = name();
printf("%s\n", n);
}
Its okay to use that local var in printf() in main 'coz here we are using a string literal which again isn't something local to name().
But now lets look at a slightly different code
class SomeClass {
int *i;
public:
SomeClass() {
i = new int();
*i = 23;
}
~SomeClass() {
delete i;
i = NULL;
}
void print() {
printf("%d", *i);
}
};
SomeClass *name() {
SomeClass s;
return &s;
}
int main() {
SomeClass *n = name();
n->print();
}
In this case when the name() function returns SomeClass destructor would be called and the member var i would have be deallocated and set to NULL.
So when we call print() in main even though since the mem pointed by n isn't overwritten (i am assuming that) the print call will crash when it tried to de-reference a NULL pointer.
So in a way ur code segment will most likely not fail but will most likely fail if the objects deconstructor is doing some resource deinitialization and we are using it afterwards.
Hope it helps

Resources