About regaining the memory associated with a block-specific variableand reusing it further.

M

Myth__Buster

NOTE : I am using the terms "reuse" or "reusable" with respect to
the object code generated at the compilation time.

/*
* Program to analyze whether it is meaningful to reuse the
* stack memory once the respective variable goes out of
* scope.
*/

#include<stdio.h>

int main()
{
int a; /* Let's say this has the address : A. */
printf("\n &a = %p ", &a);

{
int b; /* Let's say this has the address : B. */
printf("\n &b = %p", &b);
}

int c; /* Let's say this has the address : C. */
printf("\n &c = %p", &c);

return 0;
}

Win32 O/P:
&a = 0022FF74
&b = 0022FF70
&c = 0022FF6C

Fedora Core Linux O/P:
&a = 0xbfaec044
&b = 0xbfaec040
&c = 0xbfaec03c

Here, I think that C could be same as B since the respective memory
becomes reusable once the variable 'b' goes out of scope, But, the
behavior of this program differs from what I think as above when
tested
on platforms - Windows XP and Fedora Core Linux with GCC versions
3.4.2 (Thread model - win32) and 3.4.6 (Thread model - posix)
respectively.

This behavior looks like being closely related to the implementation
of the compiler in deciding the memory utilization for the local
variables. However, it would be interesting to know whether are
there any restrictions imposed by Standard C(C99) in doing so(reuse)
as
far as the language "C" is concerned.

Cheers.
 
B

Ben Bacarisse

Myth__Buster said:
NOTE : I am using the terms "reuse" or "reusable" with respect to
the object code generated at the compilation time.

/*
* Program to analyze whether it is meaningful to reuse the
* stack memory once the respective variable goes out of
* scope.
*/

#include<stdio.h>

int main()
{
int a; /* Let's say this has the address : A. */
printf("\n &a = %p ", &a);

Technically, you should say printf("\n &a = %p ", (void *)&a); here.
{
int b; /* Let's say this has the address : B. */
printf("\n &b = %p", &b);
}

int c; /* Let's say this has the address : C. */
printf("\n &c = %p", &c);

return 0;
}

Win32 O/P:
&a = 0022FF74
&b = 0022FF70
&c = 0022FF6C

Fedora Core Linux O/P:
&a = 0xbfaec044
&b = 0xbfaec040
&c = 0xbfaec03c

Here, I think that C could be same as B since the respective memory
becomes reusable once the variable 'b' goes out of scope, But, the
behavior of this program differs from what I think as above when
tested
on platforms - Windows XP and Fedora Core Linux with GCC versions
3.4.2 (Thread model - win32) and 3.4.6 (Thread model - posix)
respectively.

This behavior looks like being closely related to the implementation
of the compiler in deciding the memory utilization for the local
variables.

Yes, it is.
However, it would be interesting to know whether are
there any restrictions imposed by Standard C(C99) in doing so(reuse)
as
far as the language "C" is concerned.

The main one is that an object has a constant address throughout its
lifetime. Combine this with the fact that addresses compare equal when
they refer to the same object and you will have most of the rules you
need.

The compiler can always break C's rules if the program can't tell the
difference. For example, a, b and c could be allocated at the same
location (or even not at all) if you did not take the address of any of
them and the usage permitted such re-use. I don't think gcc spends much
time trying to reduce the space used by local objects so you are
unlikely to see this in practise.
 
M

Myth__Buster

Technically, you should say printf("\n &a = %p ", (void *)&a); here.
















Yes, it is.


The main one is that an object has a constant address throughout its
lifetime.  Combine this with the fact that addresses compare equal when
they refer to the same object and you will have most of the rules you
need.

The compiler can always break C's rules if the program can't tell the
difference.  For example, a, b and c could be allocated at the same
location (or even not at all) if you did not take the address of any of
them and the usage permitted such re-use.  I don't think gcc spends much
time trying to reduce the space used by local objects so you are
unlikely to see this in practise.

Okay. Actually, my idea was to optimize the stack usage by the local
arrays
of reasonable sizes specific to blocks.
 
B

Ben Bacarisse

Myth__Buster said:
Okay. Actually, my idea was to optimize the stack usage by the local
arrays
of reasonable sizes specific to blocks.

In that case, if the arrays are not variably modified, you could put
those that can be reused into a union. If they are of the same element
type, you could even dispense with the union, but it helps to keep the
naming clear.

Your example with single ints would then be:

int a;
/* ... */
union { int b, c; } b_or_c;
{
/* use b_or_c.b here */
}
/* use b_or_c.c here */

If the arrays are variably modified and of different types, then I am
not sure what the best strategy might be. I think it would depend on
exactly what you are doing.
 
K

Keith Thompson

Myth__Buster said:
NOTE : I am using the terms "reuse" or "reusable" with respect to
the object code generated at the compilation time.

/*
* Program to analyze whether it is meaningful to reuse the
* stack memory once the respective variable goes out of
* scope.
*/

#include<stdio.h>

int main()
{
int a; /* Let's say this has the address : A. */
printf("\n &a = %p ", &a);

{
int b; /* Let's say this has the address : B. */
printf("\n &b = %p", &b);
}

int c; /* Let's say this has the address : C. */
printf("\n &c = %p", &c);

return 0;
}

The lifetimes of both a and c cover the entire block that encloses
their declarations. The lifetime of b covers the inner block in
which it's declared. Thus b and c have overlapping lifetimes,
and cannot (in the abstract machine) share the same address.

You might think that the lifetime of c starts at its declaration,
but C99 6.2.4p5 says otherwise; it exists starting on entry to
the block, but its value is indeterminate until its declaration
is reached. It's possible to contrive a program that stores the
address of c in an int* variable, then uses goto to branch to
a point before its declaration; that code can access the object
"before" its declaration.

If you hadn't displayed the addresses of the objects, the compiler
could store them in the same location, as long as the program's
visible behavior isn't affected by the optimization.
 
M

Myth__Buster

In that case, if the arrays are not variably modified, you could put
those that can be reused into a union.  If they are of the same element
type, you could even dispense with the union, but it helps to keep the
naming clear.

Your example with single ints would then be:

  int a;
  /* ... */
  union { int b, c; } b_or_c;
  {
      /* use b_or_c.b here */
  }
  /* use b_or_c.c here */

If the arrays are variably modified and of different types, then I am
not sure what the best strategy might be.  I think it would depend on
exactly what you are doing.

Yes. Nice to see the other possibility. But, I just tried my hands on
the approach where the compiler does the job for you instead as I
said
earlier. And I agree that if it were VLAs it might need some more
tweaking
before one can zero in on the suitable strategy herein.
 
S

Seebs

However, it would be interesting to know whether are
there any restrictions imposed by Standard C(C99) in doing so(reuse)
as
far as the language "C" is concerned.

None, it's all undefined behavior.

-s
 
S

Seebs

Okay. Actually, my idea was to optimize the stack usage by the local
arrays
of reasonable sizes specific to blocks.

This is unlikely to yield a good return on your time.

-s
 
M

Myth__Buster

The lifetimes of both a and c cover the entire block that encloses
their declarations.  The lifetime of b covers the inner block in
which it's declared.  Thus b and c have overlapping lifetimes,
and cannot (in the abstract machine) share the same address.

If this turns out to be the case, I could see that some stack space
being eaten up by the variables yet unused in the outermost block.
Well, this could be an inefficient object code generation as well
right? The inefficiency here I am referring is in terms of memory
space consumption.

Having said that, I could see no other sort of manipulations to avoid
this compile-time inefficiency in case the control doesn't reach the
yet
unused variables as mentioned above. If there are, then I would be
eager to know them.
You might think that the lifetime of c starts at its declaration,
but C99 6.2.4p5 says otherwise; it exists starting on entry to
the block, but its value is indeterminate until its declaration
is reached.  It's possible to contrive a program that stores the
address of c in an int* variable, then uses goto to branch to
a point before its declaration; that code can access the object
"before" its declaration.

Yes, I agree on this - bad coding practice but interesting
possibility
as well..
If you hadn't displayed the addresses of the objects, the compiler
could store them in the same location, as long as the program's
visible behavior isn't affected by the optimization.

Yes, that seems feasible. I tried with the same win32 GCC and printed
the addresses of b and C through GDB. Unless I try to print their
addresses,
they are having the same addresses - tried assigning a value to it,
etc.
So, that's a good finding for me.. Thanks.
 
K

Keith Thompson

Ben Bacarisse said:
In that case, if the arrays are not variably modified, you could put
those that can be reused into a union. If they are of the same element
type, you could even dispense with the union, but it helps to keep the
naming clear.

Your example with single ints would then be:

int a;
/* ... */
union { int b, c; } b_or_c;
{
/* use b_or_c.b here */
}
/* use b_or_c.c here */

If the arrays are variably modified and of different types, then I am
not sure what the best strategy might be. I think it would depend on
exactly what you are doing.

Hmm. That strikes me as excessive micro-optimization, especially
since (in this case) all it saves you is the storage for a single
int. And b_or_c might as well be an int rather than a union.

Crank up the optimization level on your compiler, *don't* take
the address of the variables in your code, and take a look at the
generated assembly to see if it manages to reuse the same memory
(or, perhaps more likely, just keep the values in registers).

Or, if you haven't demonstrated a real need to save storage,
consider not worrying about it.
 
K

Keith Thompson

Seebs said:
None, it's all undefined behavior.

What behavior is undefined?

It's up to the implementation to decide whether to use the same
storage for different objects (but only if doing so doesn't change
the behavior of the program). But I see nothing undefined about
the behavior of the original program. Plenty of unspecified and/or
implementation-defined behavior (such as the output produced by
"%p"), but nothing undefined.
 
S

Seebs

What behavior is undefined?
It's up to the implementation to decide whether to use the same
storage for different objects (but only if doing so doesn't change
the behavior of the program). But I see nothing undefined about
the behavior of the original program. Plenty of unspecified and/or
implementation-defined behavior (such as the output produced by
"%p"), but nothing undefined.

Right you are. I misinterpreted the OP to be asking about reusing objects
after they'd been deallocated -- say, returning pointers to local variables
and using them after they've gone out of scope.

-s
 
B

Ben Bacarisse

Keith Thompson said:
Hmm. That strikes me as excessive micro-optimization, especially
since (in this case) all it saves you is the storage for a single
int.

The OP said it was for arrays. I am not sure if it is worth it even
then, but I didn't want to make up a new example so I just illustrated
the idea with the OP's example which had stand-alone ints.

<snip>
 
R

robertwessel2

If this turns out to be the case, I could see that some stack space
being eaten up by the variables yet unused in the outermost block.
Well, this could be an inefficient object code generation as well
right? The inefficiency here I am referring is in terms of memory
space consumption.

Having said that, I could see no other sort of manipulations to avoid
this compile-time inefficiency in case the control doesn't reach the
yet
unused variables as mentioned above. If there are, then I would be
eager to know them.


Why not enclose the parts that use a and c in their own blocks?
 
M

Myth__Buster

Why not enclose the parts that use a and c in their own blocks?

Yes, that sounds good. So, I tried it with the below example on both
win32
and Fedora core Linux platforms with the same gcc and gdb versions
used earlier.

int main()
{
{
int a = 1;
}

{
int b = 1;
}

{
int c = 1;
}

return 0;
}

The addresses seen through GDB was same for all the variables under
both win32
and Linux platforms.

Based on these observations and the inputs from the people here, I am
getting
the feeling that it is really up to the compiler to reuse the memory
specific
to block variables for the subsequent variables. So, this flexibility
doesn't
appear as a portable one.

The only difference though not relevant to the present question was
that when
an attempt was made to print the addresses of all the variables after
putting
the breakpoint at main and running it, under win32 it succeeded in
displaying
the addresses whereas under Linux, it was not the case.

Under Linux, when I tried the same way of debugging, unless the
control reached
the variable's definition, the respective symbol information wasn't
available
and hence was unable to print the variables' addresses in advance at
the very
entry point of main.

I said the above observation is irrelevant here since I think the
above is something
to do with the debugging information populated by the gcc when used
with option 'g'.
Isn't it?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top