tags:

views:

573

answers:

8

I have this situation:

{
    float foo[10];
    for (int i = 0; i < 10; i++) {
         foo[i] = 1.0f;
    }
    object.function1(foo); // stores the float pointer to a const void* member of object
}
object.function2(); // uses the stored void pointer

Are the contents of the float pointer unknown in the second function call? It seems that I get weird results when I run my program. But if I declare the float foo[10] to be const and initialize it in the declaration, I get correct results. Why is this happening?

+2  A: 

Yes, foo[] is out of scope when you call function2. It is an automatic variable, stored on the stack. When the code exits the block it was defined in, it is deallocated. You may have stored a reference (pointer) to it elsewhere, but that is meaningless.

Rob Jones
A: 

It's undefined behavior in both cases. You should consider the stack based variable deallocated when control leaves the block.

Mehrdad Afshari
Is it undefined in the first function call also?
juvenis
(`object.function1(foo)` is correct) By both cases I mean whether or not it's `const`. It might work or crash or blow up your whole computer. Don't rely on the fact that it had worked on your implementation
Mehrdad Afshari
+1  A: 

The memory associated with foo goes out of scope and is reclaimed. Outside the {}, the pointer is invalid.

It is a good idea to make objects manage their own memory rather than refer to an external pointer. In this specific case your object could allocate its own foo internally and copy the data into it. However it really depends on what you are trying to achieve.

Timothy Pratley
+8  A: 

For the first question, yes using foo once it goes out of scope is incorrect. I'm not sure if it's defined behavior in the spec or not but it's definitely incorrect to do so. Best case scenario is that your program will immediately crash.

As for the second question, why does making it const work? This is an artifact of implementation. Likely what's happenning is the data is being written out to the data section of the DLL and hence is valid for the life of the program. The original sample instead puts the data on the stack where it has a much shorter lifetime. The code is still wrong, it just happens to work.

JaredPar
A: 

What's happening is currently you're probably just setting a pointer (can't see the code, so I can't be sure). This pointer will point to the object foo, which is in scope at that point. But when it goes out of scope, all hell can break loose, and the C standard can make no guarantees about what happens to that data once it goes out of scope. It can be overwritten by anything. It works for a const array because you're lucky. Don't do that.

If you want the code to work correctly as it is, function1() is going to need to copy the data into the object member. Which means you'll also have to know the length of the array, which means you'll have to pass it in or have some nice termination method.

Chris Lutz
+1  A: 

In both cases you are getting undefined behaviour. Anything might happen.

You are storing a pointer to the locally declared array, but once the scope containing the array definition is exited the array - and all its members are destroyed.

The pointer that you have stored now no longer points to a float or even a valid memory address that could be used for a float. It might be an address that is reused for something else or it might continue to contain the original data unchanged. Either way, it is still not valid to attempt to dereference the pointer, either for reading or writing a float value.

Charles Bailey
+2  A: 

For any declaration like this:

{
   type_1 variable_name_1;
   type_2 variable_name_2;
   type_3 variable_name_3;
}

declaration, the variables are allocated on the stack.

You can print out the address of each variable: printf("%p\n", variable_name ) and you'll see that addresses increase by small amount roughly (but not always exactly equal to), the amount of space each variable needs to store its data. The memory used by stack variables is recycled when the '}' is reached and the variables go out of scope. This is done nice an efficiently just by subtracting some number from a special pointer called the 'stack pointer', which says where the data for new stack variables will have their data allocated. By incrementing and decrementing the stack pointer, programs have an extremely fast way of working out were the memory for variables will live. Its such and important concept that every major processor maintains a special piece of memory just for the stack pointer.

The memory for your array is also pushed and popped from the program's data stack and your array pointer is a pointer into the program's stack memory. While the language specification says accessing the data owned by out-of-scope variables has undefined consequences, the result is typically easy to predict. Usually, your array pointer will continue to hold its original data until new stack variables are allocated and assigned data (i.e. the memory is reused for other purposes).

So don't do it. Copy the array.

I'm less clear about what the standard says about constant arrays (probably the same thing -- the memory is invalid when the original declaration goes out of scope). However, your different behavior is explainable if your compiler allocated a chunk of memory for constants that is initialized when your program starts, and later, foo is made to point to that data when it comes into scope. At least, if I were writing a compiler, that's probably what I'd do as its both very fast and leads to using the smallest amount of memory. This theory is easily testable in the following way:

void f()
{
   const float foo[2] = {99, 101};
   fprintf( "-- %f\n", foo[0] );
   const_cast<foo*>(foo)[0]  = 666; 
}

Call foo() twice. If the printed value changed between calls (or an invalid memory access exception is thrown), its a fair bet that the data for foo is allocated in special area for constants that the above code wrote over.

Allocating the memory in a special area doesn't work for non-const data because recursive functions may cause many separate copies of a variable to exist on the stack at the same time, each of which may hold different data.

A: 

For simple problems like this it is better to give a simple answer, not 3 paragraphs about stacks and memory addresses.

There are 2 pairs of braces {}, one is inside the other. The array was declared after the first left brace { so it stops existing before the last brace }

The end

When answering a question you must answer it at the level of the person asking regardless of how well you yourself comprehend the issue or you may confuse the student.

-experienced ESL teacher

Ray G