gardner@prls.UUCP (Robert Gardner) (11/14/87)
Is there any danger of stack overflow with the following construct:
for(;;) {
int k;
etc. etc.
}
(assume the loop eventually exits, of course).
In other words, is k allocated only once at the beginning of the
procedure containing this construct, or is it allocated each time
the {} block is entered? Is the answer implementation dependent?
Robert Gardner
chris@mimsy.UUCP (Chris Torek) (11/15/87)
In article <7401@prls.UUCP> gardner@prls.UUCP (Robert Gardner) writes: >Is there any danger of stack overflow with the following construct: >for(;;) { > int k; > etc. etc. >} No. >In other words, is k allocated only once at the beginning of the >procedure containing this construct, or is it allocated each time >the {} block is entered? Is the answer implementation dependent? The answer is implementation dependent, but if a loop like for (;;) { int k; } runs for several minutes, then produces a stack overflow, the implementation is horrible. For example, a very straightforward compiler might generate this: proc() { int a, b; for (;;) { int k; if (g()) break; } a = 1; } -------- _proc: sub $8,sp | create local variables a, b L1: sub $4,sp | create local variable k call _g | run g tst r0 | what was g's return value? bne L3 | nonzero, break loop L2: add $4,sp | destroy local variable k bra L1 | back around the loop L3: add $4,sp | destroy local variable k mov $1,-4(sp) | a = 1 L0: add $8,sp | destroy locals a, b ret Label `L2' above is for a `continue' that might appear inside the for loop. It is not used, but so what? Likewise, L0 is allocated at the beginning of code generation for proc(), in case there are any `return' statements in it. I optimised away the sequence a truly dumb compiler would probably emit for `if (g()) break'. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
new@udel.EDU (Darren New) (11/15/87)
In article <7401@prls.UUCP> gardner@prls.UUCP (Robert Gardner) writes: >Is there any danger of stack overflow with the following construct: >[code with allocation of local inside a for-loop block here] >In other words, is k allocated only once at the beginning of the >procedure containing this construct, or is it allocated each time >the {} block is entered? Is the answer implementation dependent? > >Robert Gardner Most compilers I've seen allocate it once at the beginning of the function. If there are multiple blocks with local variables, these definitions overlap. EG: {int k; k = 3;} {int w; w = 77;} would put K and W in the same place. However, even if the variable is allocated on each entry, it will be deallocated at the end of each loop. Thus, no stack problems. (Of course, recursive functions are something else.) Darren New @ UDel
daveb@laidbak.UUCP (Dave Burton) (11/18/87)
In article <7401@prls.UUCP> gardner@prls.UUCP (Robert Gardner) writes: >Is there any danger of stack overflow with the following construct: > >for(;;) { > int k; > etc. etc. >} >(assume the loop eventually exits, of course). >In other words, is k allocated only once at the beginning of the >procedure containing this construct, or is it allocated each time >the {} block is entered? Is the answer implementation dependent? In response to your more general question (subject line), the answer is: Local (auto) variables in C are placed on a stack dynamically at function invocation time. This is the reason you cannot rely on the initial value of such a variable without an explicit initializer. These locals are destroyed upon return from the function. The usual implementation in a stack oriented computer is to point some register to the current stack location, then subtract a constant to the stack pointer to create an empty region which the locals will inhabit. Upon return, the stack pointer will be reset to the saved location, and the arguments 'popped' off (quite often, the popping action is accomplished by adding a value equivalent to the amount of storage required by the argument). Blocks '{statement;...}' define a new scope, but not a new invocation. Most compilers will create room for variables defined in a block when space is allocated for the function's local variables. Even if some implementation behaves this way, you are guaranteed the auto variable will be destroyed upon exit from the block, in the same manner as that described above. Your for loop is an example of a block. As a further example, the following program: main() { int a; a = 2; { int a; a = 1; printf("%d\n", a); } printf("%d\n", a); } produces the (unsurprising) output: 1 2 The variable 'a' in the inner block has a different scope than the variable in the outer block (main()). It is in a different 'name space'. My compiler (pcc based 68k) reserves room on the stack upon entry to the function main() via the link instruction, and destroys this room upon return via unlk. Machines without such instructions typically add and subtract compile-time calculated values from the stack pointer. This is correct for a K&R and ANSI conforming compiler. Be warned that not all compilers behave properly with name spaces, however, and the above program may not work due to symbol redefinition. (BTW, if you name your variables in like manner, you get what you deserve.) -- --------------------"Well, it looked good when I wrote it"--------------------- Verbal: Dave Burton Net: ...!ihnp4!laidbak!daveb V-MAIL: (312) 505-9100 x325 USSnail: 1901 N. Naper Blvd. #include <disclaimer.h> Naperville, IL 60540
djones@megatest.UUCP (11/19/87)
in article <9367@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) says: > > In article <7401@prls.UUCP> gardner@prls.UUCP (Robert Gardner) writes: >>Is there any danger of stack overflow with the following construct: >>for(;;) { >> int k; >> etc. etc. >>} > > No. > >>In other words, is k allocated only once at the beginning of the >>procedure containing this construct, or is it allocated each time >>the {} block is entered? Is the answer implementation dependent? > > The answer is implementation dependent, but if a loop like > > for (;;) { > int k; > } > > runs for several minutes, then produces a stack overflow, the > implementation is horrible. For example, a very straightforward > compiler might generate this: > > proc() > { > int a, b; > > for (;;) { > int k; > if (g()) > break; > } > a = 1; > } > -------- > _proc: > sub $8,sp | create local variables a, b > L1: > sub $4,sp | create local variable k > call _g | run g > tst r0 | what was g's return value? > bne L3 | nonzero, break loop > L2: > add $4,sp | destroy local variable k > bra L1 | back around the loop > L3: > add $4,sp | destroy local variable k > mov $1,-4(sp) | a = 1 > L0: > add $8,sp | destroy locals a, b > ret > > Label `L2' above is for a `continue' that might appear inside > the for loop. It is not used, but so what? Likewise, L0 is > allocated at the beginning of code generation for proc(), in > case there are any `return' statements in it. I optimised away > the sequence a truly dumb compiler would probably emit for > `if (g()) break'. > -- > In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) > Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris I wasn't going to answer, but this is just TOO silly. (Don't feel bad. The world needs some silliness from time to time.) 8-] The answer is, "No the stack will not overflow." If an explanation is required, it might be, "The variable k retains its value on each iteration, so no copy of it is needed. (Procedure local variables, unlike in FORTRAN for example, are not retained between procedure activations, and so new memory for them must typically be allocated on the stack.)" By the way, some compilers will produce code to use only one memory cell for all the variables k (one per procedure activation) implied by the execution of the following procedure, even when k > 0. It's called "removing tail-recursion." Wow. :-) foo(k) { if(k==0) return; else { /* etc. */ foo(k-1); } }
msb@sq.UUCP (11/22/87)
There have been several followups regarding the code:
for (;;) {
int k;
...
}
but I think they have each either left out an important point, or wandered
at length into side issues. So let me try.
The above requests that a new variable "k" be created, and destroyed
again, on each iteration of the loop. Since it is destroyed each time,
obviously it can't cause a stack overflow after many iterations.
Any sensible compiler will implement the creation and destruction
virtually, which is to say, any actual stack operations will take place
either when the loop is entered for the first time and exited for the last
time, or when the function is entered and exited. Or perhaps "k" will be
put in a register and there will be no stack operations at all.
But the value of "k" is NOT guaranteed to be retained from one iteration
to the next, and you must not assume it will be. If you want that, you
have to declare "k" in a larger scope including the for-header.
Mark Brader, SoftQuad Inc., Toronto, utzoo!sq!msb, msb@sq.com
"Have you ever heard [my honesty] questioned?"
"I never even heard it mentioned." -- Every Day's a Holiday
reggie@pdnbah.UUCP (George Leach) (11/24/87)
In article <1987Nov22.085210.20641@sq.uucp> msb@sq.UUCP (Mark Brader) writes: >There have been several followups regarding the code: > for (;;) { > int k; > ... > } >but I think they have each either left out an important point, or wandered >at length into side issues. So let me try. [stuff deleted concerning allocation and deallocation of the variable k and what might actually be implemented by a compiler....... >But the value of "k" is NOT guaranteed to be retained from one iteration >to the next, and you must not assume it will be. If you want that, you >have to declare "k" in a larger scope including the for-header. Or you can declare it static: for (;;) { static int k; ^^^^^^ ... } George W. Leach Paradyne Corporation {gatech,rutgers,attmail}!codas!pdn!reggie Mail stop LF-207 Phone: (813) 530-2376 P.O. Box 2826 Largo, FL 34649-2826
richardh@killer.UUCP (11/26/87)
In article <1987Nov22.085210.20641@sq.uucp>, msb@sq.uucp (Mark Brader) writes: > There have been several followups regarding the code: > > for (;;) { > int k; > ... > } > ... > The above requests that a new variable "k" be created, and destroyed > again, on each iteration of the loop. Since it is destroyed each time, No it doesn't! It requests that a variable "k" be allocated and be visible for the duration of execution of the block defined by the "{}" pair. To quote H&S: "An object is said to have _local extent_ when (in the case of C) it is created upon entry to a block or function and is destroyed upon exit from the block or function." p. 57, sec. 4.2.7, third paragraph, ed. 1. Even if it is allocated on entry to the block, it will only be allocated once. As has been pointed out, many compilers actually allocate the storage upon entry to the function and limit the object's visibility to the defining block. > > But the value of "k" is NOT guaranteed to be retained from one iteration > to the next, and you must not assume it will be. If you want that, you Yes it is! The variable's scope is the block in which it is defined. richard hargrove ...!killer!richardh ------------------- Found on a Post-It attached to the front of a terminal in a deserted office in downtown Wichita: "Aunty Em, Hate you. Hate Kansas. Took the dog. Dorothy" ---------------------------------------------
chris@mimsy.UUCP (Chris Torek) (11/27/87)
In article <2218@killer.UUCP> richardh@killer.UUCP (Richard Hargrove) writes a bunch of stuff that quotes H&S correctly, but misinterpets something somewhere: >> for (;;) { >> int k; >> ... >... requests that a variable "k" be allocated and be visible >for the duration of execution of the block defined by the "{}" pair. Good so far. >Even if it is allocated on entry to the block, it will only be allocated >once. It will be `allocated' each time you enter the block. When do you enter the block? Answer: once for each trip around the loop: this is probably more than once. >As has been pointed out, many compilers actually allocate the storage upon >entry to the function and limit the object's visibility to the defining >block. Right again. >>But the value of "k" is NOT guaranteed to be retained from one iteration >>to the next, and you must not assume it will be. (The >> text is by Mark Brader, msb@sq.UUCP, and is correct.) >Yes it is! The variable's scope is the block in which it is defined. The scope is indeed the block in which it it is defined. Here is an example of *incorrect* usage: int i; for (i = 0; i < 10; i++) { int j; if (i == 0) j = 1; else j *= i; ... } This uses the undefined value of `j' for all but the first trip around the loop. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
gwyn@brl-smoke.ARPA (Doug Gwyn ) (11/27/87)
In article <2218@killer.UUCP> richardh@killer.UUCP (Richard Hargrove) writes: >> But the value of "k" is NOT guaranteed to be retained from one iteration >> to the next, and you must not assume it will be. If you want that, you >Yes it is! The variable's scope is the block in which it is defined. No, execution leaves (and re-enters) the block statement each iteration of the loop. The variable goes out of scope each time the block is left.
msb@sq.UUCP (11/27/87)
> >But the value of "k" is NOT guaranteed to be retained from one iteration > >to the next, and you must not assume it will be. If you want that, you > >have to declare "k" in a larger scope including the for-header. > > Or you can declare it static: > for (;;) { > static int k; > ... > } True, but then there's only one copy of k even if the function is re-called recursively(). If that isn't a problem, static is fine. I prefer not to limit potential recursions unnecessarily. Mark Brader "Male got pregnant -- on the first try." utzoo!sq!msb Newsweek article on high-tech conception msb@sq.com November 30, 1987
richardh@killer.UUCP (Richard Hargrove) (11/27/87)
In article <2218@killer.UUCP>, richardh@killer.UUCP (Richard Hargrove) writes: > In article <1987Nov22.085210.20641@sq.uucp>, msb@sq.uucp (Mark Brader) writes: > > There have been several followups regarding the code: > > (deleted) > > The above requests that a new variable "k" be created, and destroyed > > again, on each iteration of the loop. Since it is destroyed each time, > > No it doesn't! It requests that a variable "k" be allocated and be visible > > > > > But the value of "k" is NOT guaranteed to be retained from one iteration > > to the next, and you must not assume it will be. If you want that, you > > Yes it is! The variable's scope is the block in which it is defined. > Time for some humble pie along with all the other Thanksgiving deserts. I am obviously in error. If my claim above were true then objects declared local to the block would be visible to the loop control code, which they obviously aren't. Hope I don't need too much flame retardant. richard hargrove ...!killer!richardh ------------------- Found on a Post-It attached to the front of a terminal in a deserted office in downtown Wichita: "Aunty Em, Hate you. Hate Kansas. Took the dog. Dorothy" ---------------------------------------------
platt@emory.uucp (Dan Platt) (11/28/87)
=================================================================== Actually, it might be worthwhile to point out that in several compilers, locally defined variables are placed on the stack as needed, and popped when you've left the loop. Since there may be other stuff being put on the stack and being popped in between your accesses once you've left the {} where a variable was assigned, you can't assume the 'first use' in the {} will be the same as the value it had the last time you were in the {}. This is also how re-entrant and recursive code is handled: the arguments AND the local variables are allocated space on the stack, so that when the routine returns a value to the older version of itself, all its information is still sitting there waiting for it on the stack. Dan Platt
I1090801%DBSTU1.BITNET@CUNYVM.CUNY.EDU (11/29/87)
I think there is a good reason for a C-compiler to allocate space for all local variables at the beginning of a function. Look at the following example: int f() { if (...) { int a; ... goto label2; label1: ... /* do someting with a */ } else { int b; ... goto label1; label2: ... /* do something with b */ } } In this case it is not possible to allocate the same space for the variables a and b. If you allocate space for a at the start of the then-block there is no space allocated for variable b after you do the jump into the else-part (at least not until the compiler does some very tricky code generation). Another reason for allocating space at the beginning of a function is the gain in code-space and runtime (but perhaps with a loss of data-space, which may only become a problem in recursive calls). Ulf Gruene I1090801@DBSTU1.BITNET Technische Universitaet Braunschweig West Germany
wendt@arizona.edu (Alan Lee Wendt) (11/30/87)
Regarding whether k is allocated and deallocated on each iteration of the loop: > > for (;;) { > > int k; > > ... > > } As Richard H. states, the variable's scope is the block in which it is created. But this block does not include any expressions outside of the curly braces, hence does not include the for initialization, test and increment expression. Therefore control is exiting this block and re-entering, and the variable is being allocated and deallocated repeatedly, at least as far as the language semantics go. (A compiler may legally choose to re-use k's location while evaluating the for-expressions). To convince yourself that, the "block defined" can't possibly include the for-expressions, you can look at the definition of a block. Or, consider the situation of the poor compiler writer if someone actually mentioned the variable k inside one of the for-expressions. The compiler writer would be in a position of having to compile a reference to k before k has been declared. Alan W.
tainter@ihlpg.ATT.COM (Tainter) (11/30/87)
>But the value of "k" is NOT guaranteed to be retained from one iteration >to the next, and you must not assume it will be. If you want that, you >have to declare "k" in a larger scope including the for-header. And you can do this without getting k too far away, as so: { int k; /* a variable that lives for the life of this loop */ for (;;) { ... } } Why is this so unpopular? --j.a.tainter
woerz@iaoobelix.UUCP (12/01/87)
> /***** iaoobelix:comp.lang.c / brl-adm!I1090801%DBSTU1.BITNET@CU / 1:41 pm Nov 29, 1987*/ > I think there is a good reason for a C-compiler to allocate > space for all local variables at the beginning of a function. > Look at the following example: > int f() > { > if (...) > { int a; > ... > goto label2; > label1: ... /* do someting with a */ > } > else > { int b; > ... > goto label1; > label2: ... /* do something with b */ > } > } > In this case it is not possible to allocate the same space > for the variables a and b. If you allocate space for a at the > start of the then-block there is no space allocated for variable > b after you do the jump into the else-part (at least not until > the compiler does some very tricky code generation). I think that it is possible in this case, if you use a label the same way you use the normal entry to a block. That is you have to allocate space if you hit a label and there are local variables for that block. You will have to jump around the code, if you entered the block at another entry. I'm not saying that this is worth to do, but I think that it's doable without some tricky code generation. > ... > > Ulf Gruene > I1090801@DBSTU1.BITNET > Technische Universitaet Braunschweig > West Germany > /* ---------- */ ------------------------------------------------------------------------------ Dieter Woerz Fraunhofer Institut fuer Arbeitswirtschaft und Organisation Abt. 453 Holzgartenstrasse 17 D-7000 Stuttgart 1 W-Germany BITNET: iaoobel.uucp!woerz@unido.bitnet UUCP: ...{uunet!unido, pyramid}!iaoobel!woerz
cdwf@root.co.uk (Clive D.W. Feather) (12/01/87)
In article <10572@brl-adm.ARPA> I1090801%DBSTU1.BITNET@CUNYVM.CUNY.EDU writes: >I think there is a good reason for a C-compiler to allocate >space for all local variables at the beginning of a function. >Look at the following example: [Example involving jumping out of one block with 'a' declared, and into another block with 'b' declared] >In this case it is not possible to allocate the same space >for the variables a and b. If you allocate space for a at the >start of the then-block there is no space allocated for variable >b after you do the jump into the else-part (at least not until >the compiler does some very tricky code generation). On the contrary, a only exists in the then-block, and b in the else-block. When you jump out of a block, all variables declared in it cease to exist. When you jump into a block, all variables declared in it come into existence (though they may not have been initialised correctly). For example, the code: main () { /* Some code, #1 */ goto label; /* Some code, #2 */ { int x = 72; /* Some code, #3 */ label: /* Some code, #4 */ } } has the same semantics as: main () { int j_label; /* Some code, #1 */ { j_label = 1; goto label; } /* Some code, #2 */ j_label = 0; label: { int x; if (j_label) goto label_2; x = 72; /* Some code, #3 */ label_2: j_label = 0; /* In case we ever come back again */ /* Some code, #4 */ } } [Yes, I know this looks terrible. Serves you right for using goto.]