| Reader's Clinic - Functions and Subroutines |
| Reader's Letters - Jim Owen |
| Playing with Scan Codes |
| Argument passing - Subroutines and Functions - Mike Pope |
| Reader's Clinic - Line Length > 256 Characters |
| QTIPS - String Space |
| QTIPS - String Space Format Errors |
| Reader's Forum - Numeric Precision in R/Basic - Hal Wyman |
| QTIPS - Using @Upper.Case and @Lower.Case with Foreign Languages |
| @ATTACK - @Lower.Case |
| @ATTACK - @Upper.Case |
| Sorting out Collation Sequences by Mike Pope |
| File Variables |
| Argument passing - Subroutines and Functions - Mike Pope |
| RevTech Replies - Mike Pope (RevTech UK Ltd) |
| Symbol Table Structure |
| VERBatim - V85 |
| A RevTechie Replies - And Miscellaneous Jottings - Mike Pope - Revelation Technologies (UK) Ltd |
| QTIPS - Inconsistent @Variable Behaviour |
| What's New (and un(der)documented!) In 2.12 |
| Form.List.S |
| QTIPS - Aesthetically Improving RLIST Reports |
| QTIPS - Form Processor |
| QTIPS - Suppressing Initial Form Feed |
| QTIPS - Using RTP29 In Place of V6 |
| Reader's Clinic - Functions and Subroutines |
| Argument passing - Subroutines and Functions - Mike Pope |
| Argument passing - Subroutines and Functions - Mike Pope |
| Caching in on the Frames Array - Mike Pope |
| QTIPS - Fast Dynamic Array Building |
| SecureUser |
| VERBatim - V25 |
| @ATTACK - @Files.System |
| Advanced Revelation Initialisation Sequence (Overview) by Mike Pope |
| REVMEDIA Revisted |
RevMedia FKB
| Document | V3I1A7 |
| Title | Argument passing - Subroutines and Functions - Mike Pope |
| Keywords | ARGUMENT SUBROUTINE FUNCTION COMMON VARIABLE DESCRIPTER ARRAY DESCRIPTOR TRANSFER UNASSIGNED |
| Text | Jim Owens notes that "the C esque use of local vs global VARIABLES doesn't apply to R[/]BASIC" True but misleading; there is in fact a notion of local shared and global VARIABLES it's just that it doesn't work as it does WITH C For beginners look at these two versions of essentially the same user DEFINED subroutine: Version 1 FUNCTION SPC(STRING LENGTH JUST) JUST = JUST : '#' : LENGTH STRING = FMT(STRING JUST) RETURN STRING Version 2 FUNCTION SPC(STRING LENGTH JUST) TEMP JUST = JUST : '#' : LENGTH RETURN STRING = FMT(STRING TEMP JUST) RETURN RETURN STRING What's the difference? Both return a formatted string But the second one does so without altering any of the data passed to it The first one modifies two variables: STRING (by reformatting its contents) and JUST (by reassigning it WITH a full justification specification) This explains the results Mr Owen experienced WITH his problem In the former case the values of these VARIABLES will be changed in the program that called this one This is true because the VARIABLES are shared with the CALLING program by virtue of being passed as ARGUMENTS In the latter EXAMPLE by contrast the contents of the variable TEMP JUST is "invisible" to the CALLING program because it is a local variable it is neither one of the VARIABLES used in the argument list nor is it the explicit return value for the function LOCAL SHARED COMMON GLOBAL AND GLOBAL VARIABLES There are four types of shared data areas in R/BASIC summarized as follows: Global @Variables These may be globally readable (@USERNAME) globally settable (@USER0 @SENTENCE) or readable/settable for the CURRENT recursive (TCL) level (@LEVEL @RECUR) Common Global Common areas established WITH a label i e LABELLED common Information in LABELLED common is available to all programs (main or subroutine/function) that declare the same label Information is maintained for the entirety of the logon SESSION unless explicitly nulled (although this does not de allocate the LABELLED area) Shared Argument lists and explicit COMMON VARIABLES This is a limited form of inheritance by which one program can share data WITH programs "below" it VARIABLES of these types can be shared between one main program and any subroutines or functions called by that main program including subroutines that call further subroutines A main program cannot however share ARGUMENTS or common VARIABLES WITH another main program Any change made to a shared variable in any of the interrelated programs is reflected for all subsequent access to that variable There are differences in the way ARGUMENTS and common VARIABLES are handled (See below) Local VARIABLES in a program that are not one of the above i e that are not @ VARIABLES LABELLED common common or part of an argument list PASSING BY REFERENCE PASSING BY VALUE R/BASIC passes information in argument lists common LABELLED common and @variables by reference This means that the variable in the CALLING and called program are referencing the same physical data (more accurately the same area of memory) even if the VARIABLES in the two programs happened to have DIFFERENT NAMES By extension this means that if a subroutine changes the value of a variable passed to it in an argument list or shared via a COMMON declaration this change is reflected in the main program because both programs are actually referring to the same information It is possible to pass by value which means that the CALLING and called routines have separate copies of the data This is done in R/BASIC by passing an expression (rather than a variable) in an argument list or common area (this doesn't work for @Variables) For example: CALL MYSUB(A:B 2 + 3 "TEST") All three ARGUMENTS in the EXAMPLE are expressions and are therefore passed by value In other words the subroutine will receive the values represented by the expressions However no change can be made to the ARGUMENTS as this calling syntax does not allow for a value to be returned USING the argument list (Functions can be called successfully in this manner as they return a value "in place") DESCRIPTERS Underlying R/BASIC's use of VARIABLES is a fairly common operating system technology known as a descripter (or descriptor) table Simply put the descripter table is an array of all the VARIABLES currently in use anywhere in the system including @variables (mostly) local VARIABLES labelled common VARIABLES and a couple of other lower level types If you like you can think of a descripter table as a large bank of pigeonholes or postboxes into which information about the data in a variable can be stored Each variable in each program has one entry (usually) in the descripter table Each entry in the descripter table is 10 bytes long and the table as a whole can be thought of as a single huge variable (more accurately a single segment) and is therefore limited to 64k By deduction the maximum number of VARIABLES that can be allocated in toto for one SESSION is 64k/10 or 6 535 The system itself has grabbed about one thousand of these by the time the USER takes CONTROL however so there are somewhat closer to 5 000 available at level 1 TCL ALLOCATION OF DESCRIPTERS In the simplest scenario when a program is loaded the system allocates a descripter for every variable in the program One of the data in a program is the number of VARIABLES it requires so this is known as soon as the program OBJECT CODE is READ For EXAMPLE if a program uses 5 local variables the system allocates 5 descripter table entries when the program is loaded When the program terminates the descripters are de allocated and those slots in the table again become available When you call a subroutine or function the system loads the code for the routine and allocates descripters for its VARIABLES as well (but see below) When the subroutine or function executes a RETURN STATEMENT however its descripters are not de allocated They remain in the table (as in fact its code remains in the program stack) until the main program that called them terminates WITH a STOP STATEMENT DIMENSIONED ARRAYS One relatively common problem arises out of the attempt to dimension a particularly large array (for EXAMPLE DIM ARRAY(100 100)) This results in the ERROR "maximum number of errors exceded [sic]" As it happens R/BASIC attempts to allocate one DESCRIPTOR table entry for each element in the dimensioned array (plus 2 extra) For EXAMPLE the statement DIM ARRAY (100 100) attempts to allocate 10 002 descripters considerably beyond the SIZE of the table The result is as noted (This is not a problem for DYNAMIC ARRAYS because dynamics ARRAYS are treated as a single variable that just happens to have delimiters in it As a note of interest this explains why access to the 5 000th element of a dimensioned array is faster than access to the 5 000th element of a dynamic array In the former case the system can EXTRACT the data as if it were its own variable which in effect it is In the latter case the system must SCAN through a single large string looking for the 4 999th and 5 000th delimiters) ARGUMENTS Shared VARIABLES passed as ARGUMENTS are handled a bit differently In the main program they are allocated as individual entries in the descripter table In the called routines however a variable that is in the argument list is not simply allocated a new independent descripter table entry Shared VARIABLES in a subroutine do in fact get entries in the table but the entries are not "normal" entries Indeed they are pointers to the descripters that do contain the data for that variable Thus a reference to an argument in a subroutine is looked up in the descripter table which points it to another entry The effect as noted earlier is that there is only one "real" descripter for a shared variable and only one physical location for the data All other references to it via argument lists all point to this one entry Given the explanation of "passing by reference" it should ALSO be clear how passing an expression in an argument list is DIFFERENT In that case the argument in the argument list has no relationship WITH an existing descripter since it isn't a variable that is being passed but an actual value there is nothing to point to except a data value DIRECTLY If a subroutine or function is called recursively each new iteration sets up a new descripter variable (local or shared) in the subroutine or function If the variable is part of an argument list the descripter for that variable points to the descripter referenced by the CALLING routine so that the descripter for an argument to a subroutine that has called itself twice contains a POINTER to a pointer to a pointer to a variable Fortunately if you pass dimensioned ARRAYS the system is intelligent enough not to reallocate new descripters for each element Instead the argument contains a pointer to one of those extra array descripters mentioned earlier one that contains information about the dimension of the array and other such salient data The fact that a new descripter is allocated in a recursively called subroutine for each argument explains why an endless LOOP of recursive calls will sometimes abort WITH "out of memory" but other times WITH "too many variables" In the latter case the first limit that was reached was the number of possible entries in the descripter table for the VARIABLES used by that routine COMMON AND LABELLED COMMON VARIABLES Although the common VARIABLES are often thought to be interchangeable with argument lists they are handled differently in the descripter table The declaration of a common area (labelled or "normal") alerts the COMPILER to tag the VARIABLES in question in the OBJECT code and likewise alerts the program loader to allocate new descripters for those VARIABLES only if they are not already allocated Instead of USING a STACK of indirect pointers as is done WITH ARGUMENTS the system resolves all references to a common variable by ACCESSING the same descripter each TIME This results in a slight improvement in efficiency for lookup and obviously saves the extra descripter entries @VARIABLES @Variables are handled differently yet again These are pre DEFINED in the compiler so the system already knows something about their locations in the descripter table A specific reference is therefore compiled right into the program so that a new descripter is not allocated when the program that references them is loaded FUN WITH DESCRIPTERS There isn't a great deal you can do WITH descripters DIRECTLY FROM R/BASIC but there are a few tricks Three R/BASIC accessible statements and functions deal specifically WITH descripters: Transfer When you assign a value to a variable you allocate a descripter and place information into the descripter If you assign the value of one variable to another (e g A = B) you allocate a second descripter look up the value represented by the first one COPY it and then assign it to the second descripter The end result is two descripters and more importantly two copies of the data The TRANSFER command short circuits this PROCESS Instead of copying the data and assigning it anew the COMMAND simply copies the first descripter wholesale to the second one This literally transfers one descripter to another including information about the data it represents This is a much faster operation than copying the data and assigning it a new descripter However because two descripters cannot point to the same data the first one is reassigned a null value Unassigned An EXTERNAL function (DECLARE it) available somewhere around release 1 1 the UNASSIGNED function tells you whether a variable has been assigned a value You might guess how it works It reads the descripter table entry for the variable you pass it and REPORTS back WITH true if the descripter contains the special value placed in there at allocation time (before any value is assigned to the variable) Descripter The best for last The internal function DESCRIPTER hands back to you the actual descripter table entry for the variable you pass it If you are enterprising (and are reasonably familiar WITH the ways of bit level data handling) you can probably use this function to figure how descripters are built and what they actually contain Beyond that it has limited use although it can be useful on odd occasions when DEBUGGING (Volume 3 Issue 1 Pages 11 15) |
Page last modified: 30/01/03