Board index » cppbuilder » Re: Dynamic allocation of arrays

Re: Dynamic allocation of arrays


2005-01-21 05:08:14 AM
cppbuilder49
"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >writes:
Quote
"Oscar Fuentes" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...

>Any self-proclaimed C/C++ programmer knows this. The fact is
>that the documentation (writen as per the compiler developers
>guidelines) advices against alloca inside try blocks...

The help file only warns that values placed in the stack space allocated by
alloca() will be invalid when an exception is thrown. The same can be said
for any values placed on the stack in general, not just via alloca().
Exception handlers free any stack space that is allocated inside the try
block when an exception is thrown.
First: No, exception handlers doesn't free stack space. Object
destructors are called, which is a different thing altogether.
Second: alloca'ted space is freed when the function exits, as per
Borland's documentation. Not when the scope block ends. So this code
should be ok for Borland's compiler if no exception is thrown:
void foo() {
void *data(0);
try {
if(something) {
data = alloca(100);
}
}
catch(...) {}
if( data != 0 ) {
do_something_with_data();
}
}
Quote
>>1) you must have at least 1 local variable that the function actually
>>makes use of. This forces the compiler to produce a stack frame
>>for the function.
>
>An now you understand why people advices against alloca, don't you?

For that one reason only, no. Most of the time, alloca() is used in
functions that already satisfy the local variable requirement
anyway.
Oh well.
Quote
It is
only for functions that don't already use local variables that a dummy
variable would be needed. That should not be a deterrant from using
alloca() altogether, though. Anyone wanting to use alloca() should
understand its requirements (which are few) before using it.
And where it is documented the requirement about needing a local
variable you mention?
Quote
When used properly, it works just fine.
Just as dynamite. <g>
--
Oscar
 
 

Re:Re: Dynamic allocation of arrays

"Oscar Fuentes" < XXXX@XXXXX.COM >wrote in message
Quote
But an old-fashioned C/C++ array destroys its elements
when the array goes out of scope.
C has no concepts of objects, constructors, or destructors. Arrays in C are
raw memory only. Arrays of objects in C++, on the other hand, call
constructors and destructors when appropriate. But C++ arrays are not
allocated by malloc() in the first place. malloc() is still a
raw-memory-only function, even in C++, as is alloca(). You can't really
compare malloc/alloca() to object arrays. If you need a dynamic array of
objects, std::vector is the way to go. That is not to say that you can't
make object arrays with alloca(), though, because you can. You just have to
do a little more manual work, though, ie:
template <typename T>
class AllocaInit
{
private:
T* arr;
int elements;
public:
AllocaInit(T* array, int count)
: arr(array), elements(0)
{
for(int i = 0; i < count; ++i)
{
new(&array[i]) T;
++elements;
}
}
~AllocaInit()
{
for(int i = 0; i < elements; ++i)
arr->~T();
}
}
{
int count = 5;
int num_elements_ok = 0;
AnsiString *array = (AnsiString*) alloca(sizeof(AnsiString) *
count);
if( array )
{
AllocaInit<AnsiString>init(array, count);
// use array as needed...
}
}
Quote
The OP wanted a variable-length version of a C array, and
`alloca' only does part of the work, that is, providing the
memory space.
The OP posted an example that was using ints, not objects. alloca() can
satisfy the OP's original example.
Gambit
 

Re:Re: Dynamic allocation of arrays

"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >writes:
Quote
"Oscar Fuentes" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...

>But an old-fashioned C/C++ array destroys its elements
>when the array goes out of scope.

C has no concepts of objects, constructors, or destructors. Arrays in C are
raw memory only. Arrays of objects in C++, on the other hand, call
constructors and destructors when appropriate. But C++ arrays are not
allocated by malloc() in the first place. malloc() is still a
raw-memory-only function, even in C++, as is alloca(). You can't really
compare malloc/alloca() to object arrays.
Agreed, but somebody adviced `alloca' for achieving the functionality
of local variable-length arrays.
[snip]
Quote
>The OP wanted a variable-length version of a C array, and
>`alloca' only does part of the work, that is, providing the
>memory space.

The OP posted an example that was using ints, not objects. alloca() can
satisfy the OP's original example.
If you read the OP's example as if it were exactly what he wants,
yes. I tend to assume that an example is just there to give a general
idea of what the asker wants, so a general solution is required, or at
least a partial solution with explicit mentions of its limitations.
--
Oscar
 

{smallsort}

Re:Re: Dynamic allocation of arrays

"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >writes:
[snip]
Quote
>>1) you must have at least 1 local variable that the function actually
>>makes use of. This forces the compiler to produce a stack frame
>>for the function.
>
>An now you understand why people advices against alloca, don't you?

For that one reason only, no. Most of the time, alloca() is used in
functions that already satisfy the local variable requirement anyway. It is
only for functions that don't already use local variables that a dummy
variable would be needed.
How can be you sure that the dummy local variable is not removed by
current or future compiler optimizations?
[snip]
--
Oscar
 

Re:Re: Dynamic allocation of arrays

"Oscar Fuentes" < XXXX@XXXXX.COM >wrote in message
Quote
Agreed, but somebody adviced `alloca' for achieving
the functionality of local variable-length arrays.
Yes. It can be used for that.
Quote
If you read the OP's example as if it were exactly what he wants,
yes. I tend to assume that an example is just there to give a general
idea of what the asker wants, so a general solution is required, or
at least a partial solution with explicit mentions of its limitations.
If you look at my other replies, I also posted an example of using alloca()
for an array of objects. In fact, the code I used is similar to what the
runtime code generated by the compiler does for fixed-length arrays -
allocate a raw block of memory, then loop through it calling the
constructors on the memory for each element of the array, then later looping
through the array again calling individual destructors before then freeing
the array memory itself.
Gambit
 

Re:Re: Dynamic allocation of arrays

"Oscar Fuentes" < XXXX@XXXXX.COM >wrote in message
Quote
How can be you sure that the dummy local variable is not
removed by current or future compiler optimizations?
The requirement is that the variable actually be used by the function, so
that it does not get optimized out for non-use.
Gambit
 

Re:Re: Dynamic allocation of arrays

"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >writes:
Quote
>How can be you sure that the dummy local variable is not
>removed by current or future compiler optimizations?

The requirement is that the variable actually be used by the function, so
that it does not get optimized out for non-use.
I'll re-quote what you wrote:
Quote
It is only for functions that don't already use local variables that
a dummy variable would be needed.
If it is a *dummy* variable whose existence is only justified due the
presence of alloca, how can you ensure that it is used by the
function? Modern optimizers are quite smart detecting dead code.
Furthermore, do you realize that there is no commitment by the
compiler vendor about keeping those empirical tricks working among
releases?
--
Oscar
 

Re:Re: Dynamic allocation of arrays

"Oscar Fuentes" < XXXX@XXXXX.COM >wrote in message
Quote
If it is a *dummy* variable whose existence is only justified due
the presence of alloca, how can you ensure that it is used by the
function? Modern optimizers are quite smart detecting dead code.
The function needs to actually use the variable, not just declare it. I
already said that earlier.
Gambit
 

Re:Re: Dynamic allocation of arrays

"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >writes:
Quote
"Oscar Fuentes" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...

>If it is a *dummy* variable whose existence is only justified due
>the presence of alloca, how can you ensure that it is used by the
>function? Modern optimizers are quite smart detecting dead code.

The function needs to actually use the variable, not just declare it. I
already said that earlier.
Yes, and what I asked earlier is: how do you ensure that a dummy
variable is actually used?
(Hint: you can reasonably expect to achieve that using certain
techniques on certain compiler version, but there is no guarantee that
it will keep working on future versions)
--
Oscar
 

Re:Re: Dynamic allocation of arrays

"Oscar Fuentes" < XXXX@XXXXX.COM >wrote in message
Quote
First: No, exception handlers doesn't free stack space.
Yes, actually they do. During the setup of an exception frame, the current
stack pointer is saved before the try block is entered. When the exception
frame is torn down after the exception handler is called, the previous stack
pointer is restored.
I traced through my earlier example code one line at a time watching the ESP
register, and it did change exactly as described above. Here is the trace
for you:
#include <malloc.h>
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
int Results[3] = {0};
// ESP = 0x0066FD9C
// initial value after local variables and exception structures have
// been allocated. Note - as stack space is allocated, ESP will
// decrement, not increment
try
{
int *arr1;
arr1 = (int*)_alloca(sizeof(int) * 4);
// ESP = 0x0066FD8C
// 16 bytes allocated
if( arr1 )
{
arr1[0] = 10;
arr1[1] = 20;
arr1[2] = 30;
arr1[3] = 40;
throw (int)1;
// ESP = 0x0066FC84
// 264 bytes allocated
}
else
Results[0] = 0;
}
catch(const int&)
{
// ESP = 0x0066FD9C
// memory allocated by alloca() and throw has been freed
Results[0] = 1;
}
catch(...)
{
Results[0] = -1;
}
// ESP = still 0x0066FD9C
try
{
char *arr2;
arr2 = (char*)_alloca(6);
// ESP = 0x0066FD94
// 8 bytes allocated
// rounded to nearest dword boundary
if( arr2 )
{
memcpy(arr2, "Hello", 6);
MessageBox(NULL, arr2, "Value of arr2", MB_OK);
Results[1] = 1;
}
else
Results[1] = 0;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
Results[1] = -11;
}
// ESP = still 0x0066FD94
// which shows that Borland's try..except SEH implementation
// does not work the same as its try..catch implementation,
// or MS's try..except SEH implementation, which does reset
// the ESP as expected
try
{
try
{
bool *b = (bool*)_alloca(sizeof(bool));
// ESP = 0x0066FD90
// 4 bytes allocated
// rounded to nearest dword boundary
if( b )
{
*b = true;
throw (int)1;
// ESP = 0x0066FC88
// 264 bytes allocated
}
}
__finally
{
// ESP = 0x0066F7A0
// 1256 bytes allocated
Results[2] = 2;
}
}
catch(const int&)
{
// ESP = 0x0066FD9C
// memory allocated by alloca(), throw, finally, and
// previous try..except have all been freed
Results[2] = 1;
}
catch(...)
{
Results[2] = -1;
}
// ESP = still 0x0066FD9C
MessageBox(NULL, AnsiString(Results[0]).c_str(), "Result 1", MB_OK);
MessageBox(NULL, AnsiString(Results[1]).c_str(), "Result 2", MB_OK);
MessageBox(NULL, AnsiString(Results[2]).c_str(), "Result 3", MB_OK);
return 0;
}
Quote
Second: alloca'ted space is freed when the function exits,
as per Borland's documentation.
Borland's documentation does not take the effects of exception handling into
account. I have seen how exception handlers are actually built up in
machine code. Exception frames can have there own stack memory that gets
freed when the exception frame exits.
Quote
Not when the scope block ends.
I never said stack memory allocated with alloca() is freed when it goes out
of scope. I know it is not, because I've seen code that allocates stack
memory in local scopes but still accesses the memory after going out of
scope. For instance:
void SomeFunc(BYTE *ptr)
{
//...
if( ptr == NULL )
{
// calculate needed size...
ptr = (BYTE*)_alloca(...);
}
// access ptr here...
// safe even when _alloca() is in a different scope
//...
}
Quote
So this code should be ok for Borland's compiler if no exception
is thrown:
No. I never said that, and neither does Borland's docs. Stack memory
allocated while inside the try block is not valid outside of the try block.
When the try block exits, any memory allocated inside the try block is
freed.
Quote
And where it is documented the requirement about needing a local
variable you mention?
In the source code for _alloca(), which is available in the
$(BCB)\source\rtl\memory folder, if you have chosen to install Borland's RTL
and VCL sources while installing BCB. Also, the example of alloca() in the
help file shows a dummy variable being used in order to set up a stack
frame.
Gambit
 

Re:Re: Dynamic allocation of arrays

"Alisdair Meredith" <alisdair.meredith@ XXXX@XXXXX.COM >
wrote in message news:41f01db6$ XXXX@XXXXX.COM ...
Quote
i/ assume there is no try.
ii/ assume an exception is thrown by a function you call.

As we are not in a try block, is our alloca any safer?
Or is the compiler building all that stack-frame protection anyway?
A local variable is required in order to force the compiler to produce a
stack frame. If there is no stack frame, alloca() cannot be used at all, as
the stack will not be managed properly. Only with a stack frame present is
alloca() then safe to use, even if an exception is thrown.
Gambit
 

Re:Re: Dynamic allocation of arrays

"Oscar Fuentes" < XXXX@XXXXX.COM >wrote in message
Quote
Yes, and what I asked earlier is: how do you ensure that
a dummy variable is actually used?
By writing code that accesses and assigns the variable.
Gambit
 

Re:Re: Dynamic allocation of arrays

"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >writes:
Quote
>First: No, exception handlers doesn't free stack space.

Yes, actually they do. During the setup of an exception frame, the current
stack pointer is saved before the try block is entered. When the exception
frame is torn down after the exception handler is called, the previous stack
pointer is restored.
And do you call this "freeing stack space"? Have you considered the
possibility that the exception handling mechanism of BCB is totally
unaware of the presence of alloca inside a try-block? Well, I've
actually compiled some code and looked at the assembly dump and this
is what I've seen:
If you alloca-te stack space inside a try block and an exception
occurs, the stack pointer is restored (you call this "freeing the
stack") BUT if no exception is thrown, the alloca-ted space will
remain there until function exit.
At the ligth of this, let's read again what the documentation says:
"The use of alloca is not encouraged. In the try-block of a C++
program the alloca function should never be used. If an exception is
thrown, any values placed on the stack by alloca will be corrupted."
You call it "freeing the stack space allocated by alloca", Borland
calls it "corrupting the values placed on the stack allocated by
alloca". I hope that now you see Borland's documentation as more
correct.
[snipped now irrelevant code example]
Quote
>Not when the scope block ends.

I never said stack memory allocated with alloca() is freed when it goes out
of scope.
You were comparing auto variables with alloca'ted memory several
times, altough I can see that your wording can be taken on a
different sense, which is correct.
[snip]
Quote
>So this code should be ok for Borland's compiler if no exception
>is thrown:

No. I never said that, and neither does Borland's docs.
As per Borland docs, the code example you snipped will execute
correctly if no exception is thrown: the documentation warns about
alloca'ted memory corruption if an exception is thrown (it isn't) and
documentation says alloca'ted memory is freed at function exit (unless
it became corrupt at a previous point, I'll add).
[snip]
Quote
>And where it is documented the requirement about needing a local
>variable you mention?

In the source code for _alloca(), which is available in the
$(BCB)\source\rtl\memory folder, if you have chosen to install Borland's RTL
and VCL sources while installing BCB.
Oh my! Don't you think that 'alloca' requires a lot of effort and
care to be used safely *on* *a* *single* *implementation*? That's
the reason it is not well regarded among C++ developers.
Quote
Also, the example of alloca() in the help file shows a dummy
variable being used in order to set up a stack frame.
I can't see what dummy[0] = 0; does there, nor what the `dummy'
variable does at all, as there are two more variables, but a decent
optimizer will delete `dummy' from the function. That's a really bad
example.
The truth about requiring at least one variable on the function is
that the compiler doesn't build a stack frame if it is not needed. If
the compiler is not smart enough to notice the presence of `alloca' on
the function body and does not provide a stack frame for restoring the
stack pointer at function exit, problems may arise.
Really, it should be clear by now that `alloca' is more or less like
embedded assembly: not for the faint of heart. The problem is that
embedded assembly, due to its alien nature, is the first suspect when
a bug is reported on a piece of code, but `alloca' looks just like
another function call. Morale: never advise `alloca' except to very
knowleadgeable, brave, performance-obsessed programmers.
--
Oscar
 

Re:Re: Dynamic allocation of arrays

"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >writes:
Quote
>Yes, and what I asked earlier is: how do you ensure that
>a dummy variable is actually used?

By writing code that accesses and assigns the variable.
If the compiler can prove that that code has no influence on the
program's observable behavior, it can remove the accesses and
assignments. Actually, there are compilers that do that on a whole
program basis, not even by translation unit.
--
Oscar
 

Re:Re: Dynamic allocation of arrays

"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >writes:
Quote
>First: No, exception handlers doesn't free stack space.

Yes, actually they do. During the setup of an exception frame, the current
stack pointer is saved before the try block is entered. When the exception
frame is torn down after the exception handler is called, the previous stack
pointer is restored.
And do you call this "freeing stack space"? Have you considered the
possibility that the exception handling mechanism of BCB is totally
unaware of the presence of alloca inside a try-block? Well, I've
actually compiled some code and looked at the assembly dump and this
is what I've seen:
If you alloca-te stack space inside a try block and an exception
occurs, the stack pointer is restored (you call this "freeing the
stack") BUT if no exception is thrown, the alloca-ted space will
remain there until function exit.
At the ligth of this, let's read again what the documentation says:
"The use of alloca is not encouraged. In the try-block of a C++
program the alloca function should never be used. If an exception is
thrown, any values placed on the stack by alloca will be corrupted."
You call it "freeing the stack space allocated by alloca", Borland
calls it "corrupting the values placed on the stack allocated by
alloca". I hope that now you see Borland's documentation as more
correct.
[snipped now irrelevant code example]
Quote
>Not when the scope block ends.

I never said stack memory allocated with alloca() is freed when it goes out
of scope.
You were comparing auto variables with alloca'ted memory several
times, altough I can see that your wording can be taken on a
different sense, which is correct.
[snip]
Quote
>So this code should be ok for Borland's compiler if no exception
>is thrown:

No. I never said that, and neither does Borland's docs.
As per Borland docs, the code example you snipped will execute
correctly if no exception is thrown: the documentation warns about
alloca'ted memory corruption if an exception is thrown (it isn't) and
documentation says alloca'ted memory is freed at function exit (unless
it became corrupt at a previous point, I'll add).
[snip]
Quote
>And where it is documented the requirement about needing a local
>variable you mention?

In the source code for _alloca(), which is available in the
$(BCB)\source\rtl\memory folder, if you have chosen to install Borland's RTL
and VCL sources while installing BCB.
Oh my! Don't you think that 'alloca' requires a lot of effort and
care to be used safely *on* *a* *single* *implementation*? That's
the reason it is not well regarded among C++ developers.
Quote
Also, the example of alloca() in the help file shows a dummy
variable being used in order to set up a stack frame.
I can't see what dummy[0] = 0; does there, nor what the `dummy'
variable does at all, as there are two more variables, but a decent
optimizer will delete `dummy' from the function. That's a really bad
example.
The truth about requiring at least one variable on the function is
that the compiler doesn't build a stack frame if it is not needed. If
the compiler is not smart enough to notice the presence of `alloca' on
the function body and does not provide a stack frame for restoring the
stack pointer at function exit, problems may arise.
Really, it should be clear by now that `alloca' is more or less like
embedded assembly: not for the faint of heart. What makes `alloca
worse' is that embedded assembly, due to its alien nature, is the
first suspect when a bug is reported on a piece of code, but `alloca'
looks just like another function call. Morale: never advise `alloca'
except to very knowleadgeable, brave, performance-obsessed
programmers.
--
Oscar