[Back to MEMORY SWAG index]  [Back to Main SWAG index]  [Original]

{
From: bobs@dragons.nest.nl (Bob Swart)

> I need some advice on managing the stack.
Read The Pascal Magazine issue #4 (Borland Pascal Efficiency on speed vs.
size) - a long excerpt will follow to make up for this plug:

> I was taught that it is good form to use locally declared variables
> in a procedure rather than manipulating global variables from
> within a procedure. Well, this seems to be causing some problems for
> me.

Yes, this eats up stack. Your string handling routine will probably eat up
stack also, as I'll point out a little further down...

>   What are some good strategies to use to manage the stack? How can
>   prevent these stack overflow errors without having to increase the
>   size of the stack...

The stack is often a soft spot of many applications. Generally, it's best to
specify a large stack size (like 32Kbytes) unless you have specific reasons not
to do so. The most annoying thing about the stack it that it's often hard to
see how much is actually used by a specific "run" of your program (and hence,
how much was still free). The following unit can be used to check for the
minimum value of the Stack Pointer:
}

{$A+,B-,D-,E-,F-,G-,I-,L-,N-,O-,P-,Q-,R-,S-,T-,V-,X-}
unit stack;
interface
Const
  MinStack: Word = $FFFF;
  NumProbe: Word = $0000;

  procedure StackProbe;

implementation
uses Dos;

  procedure StackProbe; Assembler;
  ASM
        inc   NumProbe { only count REAL probes }
        cmp   SP,MinStack
        jnb   @Exit
        mov   MinStack,SP
 @Exit:
  end {StackProbe};

Const
  OurOwnSS: Word = 0;
  SaveExit: pointer = nil;
  OldInt08: pointer = nil;

  procedure NewInt08; interrupt; Assembler;
  ASM
        mov   AX,SS
        cmp   OurOwnSS,AX
        jne   @Exit
        inc   NumProbe { only count REAL probes }
        cmp   SP,MinStack
        jnb   @Exit
        mov   MinStack,SP
 @Exit:
        pushF
        call  DWORD PTR [OldInt08] { goto original int $08 handler }
  end {NewInt8};

  procedure NewExit; far;
  begin
    ExitProc := SaveExit;
    SetIntVec($08, OldInt08) {get old int from code segment}
  end {NewExit};

begin
  ASM
        mov   AX,SS
        mov   OurOwnSS,AX
  end { TurboSS := SSeg };
  SaveExit := ExitProc;
  GetIntVec($08, OldInt08);  {store old int in code segment}
  SetIntVec($08, @NewInt08); {set int $08 to our ISR}
  ExitProc := @NewExit
end.

{
>   what can I do that would allow me to use locally declared variables
>   from within a procedure without having to worry about stack overflow??
Stack space is used by calls to routines and their local variables. Stack space
is also used for the variables to procedures and functions. If you want to
preserve stack space, you should take care when using recursive calls.
Recursion may be a very elegant way to implement a solution, it is also
dangerous as it eats your stack. Make sure you always know the maximum depth of
a recursive call!

Furthermore, you should try to avoid passing large structures (like Strings) as
Value parameters to procedures and functions. This will use a lot of stack
space, and will slow down your program as the variable must be copied onto the
stack (and removed from the stack again). Since version 7.0, we can pass
arguments not only by value (which uses a lot of stack space) and by reference
(var parameter) but also by 'constant value', which actually is by reference
but means that you get an error if you try to modify the contents.

Functions returning strings are especially space wasters. For example, suppose
you have a

  Function UpCaseStr(Str: String): String;
  begin
    UpCaseStr := ...
  end;

If you're implementing it in plain Pascal, you'll need 1024 bytes of data at a
minimum:
- 256 bytes are allocated for "Str", the formal parameter
- 256 bytes for a local copy of "Str" since it was passed as a value parameter
- 256 bytes for a local variable of the type String, working storage to build
the function result
- 256 bytes for assigning the result to the function result (as in: "UpCaseStr
:= Result").

You can cut this figure by 50% by taking changing the parameter header into
"Function UpCaseStr(Const Str: String): String". Provided you don't change
"Str", no local copy of the string will be created. An alternative could be to
implement the routine as a procedure (no result) or in BASM, as BASM routines
always pass String arguments by reference only.

For more information I suggest my regular Pascal Efficiency column in The
Pascal Magazine...
}

[Back to MEMORY SWAG index]  [Back to Main SWAG index]  [Original]