Bartels :: Bartels AutoEngineer :: BAE Dokumentation :: User Language Programmierhandbuch :: Sprachbeschreibung :: Datentypen und Definitionen

Bartels User Language - Programmierhandbuch

2.3 Datentypen und Definitionen

Bartels AutoEngineer® Dokumentation

2.3.1 Datentypen

Die Bartels User Language stellt die folgenden elementaren Datentypen zur Verfügung:

char Zeichen aus dem Zeichensatz der Maschine
int ganzzahliger numerischer Wert
doubledoppelt genauer numerischer Gleitkommawert
stringZeichenkette (char-Vektor)
index Index auf definierte DDB-Struktur des Bartels AutoEngineer

Daneben ist die Verwendung folgender zusammengesetzter Datentypen möglich:

VektorZusammenfassung von Elementen gleichen Datentyps
structZusammenfassung von Elementen unterschiedlichen Datentyps

Datentyp-Konvertierung

Verschiedene Operatoren können implizite Datentypumwandlungen verursachen. So setzen eine Reihe arithmetischer Operationen definierte Datentypen bzw. Datentyppaare für ihre Operanden voraus. Ebenso wird bei der Zuweisung eines Wertes an eine Variable oder der Übergabe von Funktionsparametern eine entsprechende Datentypkompatibilität verlangt. Sofern an irgendeiner Stelle im Programm die geforderte Kompatibilität nicht vorliegt, wird versucht, den Wert des betroffenen Operanden in den gewünschten Datentyp zu überführen (man spricht von einem "type cast"). Diese Typkonvertierung läuft nach folgenden Regeln ab: Zulässige Umwandlungen ohne Informationsverlust sind char in int oder char in string, sowie int in double; ebenfalls zulässige Umwandlungen - jedoch mit möglichem Informationsverlust - sind int in char, sowie double in int. Der Compiler gibt Fehlermeldungen aus, wenn auch unter Ausnutzung der Typkonvertierungsregeln keine Typkompatibilität erreicht werden kann.


2.3.2 Variablen

Alle globalen und lokalen Variablen müssen vor ihrem Gebrauch deklariert werden, damit sowohl ihr Datentyp als auch ihr Name definiert sind. Durch derartige Vereinbarungen wird festgelegt, wie einzelne, vom Benutzer eingeführte Namen durch die User Language zu interpretieren sind. Jede Vereinbarung besteht aus einer Datentypspezifikation sowie einer Liste von Deklaratoren; die Deklaratoren wiederum bestehen aus dem Variablennamen sowie wahlweise einer Initialisierung der Variablen.

Elementare Datentypen

Beispiel für die Deklaration von char-Variablen:

char c;
char TAB = '\t', NEWLINE = '\n';

In obigem Beispiel werden die char-Variablen c (nicht initialisiert) sowie TAB und NEWLINE (initialisiert mit dem Tabulator- bzw. dem Zeilenvorschub-Zeichen) deklariert.

Beispiel für die Deklaration von int-Variablen:

int i, MAXLINELEN = 80;
int pincount = 0;

In obigem Beispiel werden die int-Variablen i (nicht initialisiert) und MAXLINELEN (initialisiert mit dem Wert 80), sowie pincount (initialisiert mit 0) vereinbart.

Beispiel für die Deklaration von double-Variablen:

double x_coord, y_coord;
double MMTOINCH = 1.0/25.4;
double starttime = clock();

In obigem Beispiel werden die double-Variablen x_coord und y_coord (nicht initialisiert), MMTOINCH (initialisiert mit einem numerischen Ausdruck), sowie starttime deklariert; die Variable starttime wird initialisiert mit dem Resultatwert der (System-)Funktion clock, welche die Prozessor-Zeit zurückgibt.

Beispiel für die Deklaration von string-Variablen:

string s1;
string ProgName = "TESTPROGRAM", ProgVer = "V1.0";
string ProgHeader = ProgName+"\t"+ProgVer;

In obigem Beispiel werden die string-Variablen s1 (nicht initialisiert), ProgName und ProgVer (initialisiert mit TESTPROGRAM und V1.0), sowie ProgHeader deklariert; ProgHeader wird dabei mit einem Ausdruck initialisiert, der sich durch Aneinanderfügen der string-Variablen ProgName, des Tabulatorzeichens, sowie der string-Variablen ProgVer ergibt.

Beispiel für die Deklaration von index-Variablen:

index L_MACRO macro;
index L_CNET net1, net2;

In obigem Beispiel werden die index-Variablen macro (vom index-Variablentyp L_MACRO) sowie net1 und net2 (vom index-Variablentyp L_CNET) vereinbart. Bei der Deklaration von index-Variablen besteht die Spezifikation des Datentyps aus dem Schlüsselwort index und zusätzlich dem Namen des index-Variablentyps (hier L_MACRO bzw. L_CNET). Die Namen für die index-Variablentypen sind vordefiniert (siehe Anhang B). Es muss sichergestellt sein, dass in einem Programm nur zueinander kompatible index-Variablentypen verwendet werden. Dies beruht auf der Tatsache, dass über index-Datentypen der Zugriff auf entsprechende Einträge aus der Design-Datenbank (DDB) des Bartels AutoEngineer definiert wird; die Verfügbarkeit dieser DDB-Einträge unterscheidet sich je nach Interpreterumgebung (im Schaltplaneditor sind andere Datentypen definiert als im Layout). Bei der Verwendung zueinander nicht kompatibler index-Variablentypen im selben Programm gibt der User Language Compiler eine entsprechende Fehlermeldung aus und erzeugt keinen Code. Ähnlich verhält sich der User Language Interpreter; beim Versuch ein User Language-Programm aufzurufen, das zur Interpreterumgebung inkompatible index-Datentyp-Referenzen enthält, gibt das System eine entsprechende Fehlermeldung aus und führt das betreffende Programm nicht aus. Die Information über die Kompatibilität der index-Datentypen ist dem Anhang A bzw. dem Anhang B zu entnehmen.

Vektoren

Unter einem Vektor, auch bezeichnet als Feld oder Array, versteht man die Zusammenfassung einzelner Elemente gleichen Datentyps. Bei der Deklaration von Vektorvariablen wird neben der Spezifikation des Datentyps und der Definition des Variablennamens zusätzlich die Angabe der Vektordimension benötigt. Diese Dimensions-Angabe erfolgt durch Anfügen eckiger Klammern an den Variablennamen, wobei jeweils ein Klammernpaar einer Dimension entspricht. Bei der Initialisierung von Vektor-Elementen sind die Initialisierungswerte durch Kommata zu trennen, und jede Vektordimension ist in geschweifte Klammern einzuschließen.

Beispiel für die Deklaration von Vektor-Variablen:

int intary[], intfield[][][];
double valtab[][] = {
      { 1.0, 2.54, 3.14 },
      { 1.0/valtab[0][1], clock() }
      };
string TECHNOLOGIES[] = {
      "TTL", "AC", "ACT", "ALS", "AS", "F",
      "H", "HC", "HCT", "HCU", "L", "LS", "S"
      };

Obiges Beispiel enthält die Deklarationen der int-Vektoren intary und intfield (ein- bzw. dreidimensional), des zweidimensionalen double-Vektors valtab, sowie des eindimensionalen string-Vektors TECHNOLOGIES. In den Deklarationen für valtab und TECHNOLOGIES sind Initialisierungen angegeben, die den folgenden Zuweisungen entsprechen:

valtab[0][0] = 1.0;
valtab[0][1] = 2.54;
valtab[0][2] = 3.14;
valtab[1][0] = 1.0/valtab[0][1];
valtab[1][1] = clock();
TECHNOLOGIES[0] = "TTL";
TECHNOLOGIES[1] = "AC";
TECHNOLOGIES[2] = "ACT";
:
TECHNOLOGIES[11] = "LS";
TECHNOLOGIES[12] = "S";

Es sei an dieser Stelle nochmals darauf hingewiesen, dass in der User Language der elementare Datentyp string einem eindimensionalen char-Vektor entspricht. Die Deklarationen

string s;

und

char s[];

sind also äquivalent.

Strukturen

Unter einer Struktur versteht man die Zusammenfassung mehrerer, jeweils durch Name und Datentyp definierter Elemente, die in einem bestimmten verarbeitungstechnischen oder auch nur logischen Zusammenhang stehen. Bei der Vereinbarung von Strukturen unterscheidet man die Strukturdefinition und die Strukturdeklaration. Die Strukturdefinition besteht aus dem Schlüsselwort struct, dem Namen der Strukturdefinition, und - in geschweiften Klammern - den Definitionen der Strukturelemente. Die Strukturdeklaration besteht aus dem Schlüsselwort struct, dem Namen einer gültigen Strukturdefinition, sowie dem Namen der Variablen, die der Strukturdefinition zugeordnet wird. Strukturdefinition und Strukturdeklaration können zusammengefasst werden, wobei dann auch der Name für die Strukturdefinition entfallen kann. Initialisierungen innerhalb von Strukturdeklarationen sind in der aus den Vektordeklarationen bekannten Nomenklatur zulässig.

Beispiel für die Vereinbarung von Strukturen:

// Structure declarations

struct coordpair {
      double x
      double y;
      };

struct coordpair elementsize = {
      bae_planwsux()-bae_planwslx(),
      bae_planwsuy()-bae_planwsly()
      };

struct elementdes {
      string fname, ename;
      int class;
      struct coordpair origin, size;
      } element = {
              bae_planfname(),
              bae_planename(),
              bae_planddbclass(),
              {
                      bae_planwsnx(),
                      bae_planwsny()
                      },
              elementsize
              };

struct {
      string id, version;
      struct {
              int day;
              string month;
              int year;
              } reldate;
      } program = {
              "UL PROGRAM",
              "Version 1.1",
              { 4, "July", 1992 }
              };

Obiges Beispiel enthält die Definition der Struktur coordpair, die Deklaration der Variablen elementsize (Struktur vom Typ coordpair), die Definition der Struktur elementdes, die Deklaration der Variablen element (Struktur vom Typ elementdes), sowie die Deklaration der Strukturvariablen program. Die in den Deklarationen für elementsize, element und program enthaltenen Initialisierungen entsprechen den folgenden Zuweisungen:

elementsize.x=bae_planwsux()-bae_planwslx();
elementsize.y=bae_planwsuy()-bae_planwsly();
element.fname=bae_planfname();
element.ename=bae_planename();
element.class=bae_planddbclass();
element.origin.x=bae_planwsnx();
element.origin.y=bae_planwsny();
element.size=plansize;
program.id="UL PROG";
program.version="Version 1.1";
program.reldate.day=4;
program.reldate.month="July";
program.reldate.year=1992;

Auch die Definition von Vektoren aus Strukturen bzw. die Verwendung von Vektordatentypen innerhalb von Strukturen ist möglich, wie das folgende Beispiel demonstriert:

struct drilldef {
      index L_DRILL drilltool;
      struct { double x, y; } drillcoords[];
      } drilltable[];

Datentypumbenennung

Bartels User Language verfügt über einen Mechanismus zur Umbenennung von Datentypen. Dabei handelt es sich nicht um die Schaffung eines neuen Datentyps, sondern lediglich um die Vergabe eines zusätzlichen Namens für einen bereits bekannten Typ. Eine derartige Typumbennung erfolgt durch die Angabe des Schlüsselwortes typedef gefolgt von der Spezifikation des Datentyps sowie dem zur Bezeichnung dieses Datentyps zusätzlich einzuführenden Namen. Ein mit typedef eingeführter Name kann anschließend zur Typspezifikation bei der Deklaration von Variablen, Funktionen und Funktionsparametern verwendet werden.

Beispiel für Typ-Umbennungen:

typedef index L_CNET NETLIST[];
typedef int IARY[];
typedef IARY MAT_2[];
typedef struct {
      int pointcount;
      struct {
              int t;
              double x,y;
              } pointlist[];
      } POLYLIST[];
MAT_2 routmatrix;
NETLIST netlist;
POLYLIST polygonlist;

In obigem Beispiel werden die drei Variablen routmatrix (zweidimensionaler int-Vektor), netlist (eindimensionaler index-Vektor vom Typ L_CNET), sowie polygonlist (eindimensionaler Vektor aus Strukturen, die ihrerseits ein int-Element und einen struct-Vektor enthalten) deklariert.


2.3.3 Funktionen

Bei komplexen Operationen und Berechnungen ist es meist nicht notwendig, zu wissen, wie das Ergebnis zustande kommt; interessant ist lediglich das Ergebnis selbst. Auch ist es wünschenswert, bestimmte Bearbeitungsfolgen immer wieder verwenden zu können. Mit Hilfe von Funktionen ist die Zerlegung großer Problemstellungen in kleinere, d.h. die Modularisierung und Vereinfachung von Programmen möglich. In der Bartels User Language wird unterschieden zwischen den Systemfunktionen und den durch den Anwender definierten Funktionen.

Funktionsdefinition

Die Definitionen der Systemfunktionen sind dem Compiler bekannt, die Funktionen selbst sind in den Interpreter eingebunden. Anhang C enthält die Beschreibung dieser Systemfunktionen. Der Anwender kann also bei der Implementierung seiner Programme die Systemfunktionen verwenden und hat darüber hinaus die Möglichkeit, eigene Funktionen zu definieren.

Eine Funktionsdefinition besteht aus dem Funktionskopf (Header) und dem Funktionsrumpf (Block). Der Header enthält eine Typspezifikation und den Namen der Funktion, sowie die Definition und Deklaration der Funktionsparameter. Die Typspezifikation definiert den Datentyp des Wertes, den die Funktion an den Aufrufer zurückliefert (Rückgabewert). Hierbei steht zusätzlich der Datentyp void zur Verfügung, der dem Compiler angibt, dass die Funktion keinen Rückgabewert liefert. Wird die Datentypspezifikation weggelassen, dann wird der Funktion automatisch der Datentyp int zugeordnet. Nach dem Funktionsnamen folgt die Parameterdefinition. Diese besteht aus einer in runde Klammern eingeschlossenen Liste von durch Kommata getrennten Parameternamen bzw. Parameterdeklarationen. Für den Fall, dass die Funktion gar keine Parameter enthält, ist lediglich das Klammernpaar anzugeben. Alle definierten Parameter (außer den bereits in der Liste der Parameterdefinitionen deklarierten sowie den int-Typen) müssen nach der Liste der Parameterdefinitionen explizit vereinbart werden. Diese Parameterdeklarationen sind wie Variablendeklarationen vorzunehmen. Nach dem Funktionskopf muss der Funktionsrumpf definiert werden. Dieser ist ein durch geschweifte Klammern umschlossener Block von Anweisungen.

Beispiele für Funktionsdefinitionen:

double netroutwidth(index L_CNET net)
// Get the routing width of a given net
// Returns : width or 0.0 if two pins with different width
{
      index L_CPIN pin;       // Pin index
      int pincnt=0;           // Pin count
      double rw=0.0;          // Rout width
      // Loop thru all pins
      forall (pin of net) {
              // Test if the pin introduces a new rout width
              if (pin.RWIDTH!=rw && pincnt++>0)
                      return(0.0);
              // Set the rout width
              rw=pin.RWIDTH;
              }
      // Return the rout width
      return(rw);
}

int allpartsplaced()
// Test if all netlist-defined parts are placed
// Returns : 1 if all parts are placed or zero otherwise
{
      index L_CPART cpart;    // Connection part index
      // Loop thru the connection part list
      forall (cpart where !cpart.USED)
              // Unplaced part matched
              return(0);
      // All parts are placed
      return(1);
}

double getdistance(xs,ys,xe,ye)
// Get the distance between two points
// Returns : the distance length value
double xs, ys;                // Start point coordinate
double xe, ye;                // End point coordinate
{
      double xd=xe-xs;        // X distance
      double yd=ye-ys;        // Y distance
      // Calculate and return the distance (Pythagoras)
      return(sqrt(xd*xd+yd*yd));
}

double arclength(r,a1,a2)
// Get arc segment length by radius and start-/end-point angle
// Returns : the arc segment length value
double r;                     // Radius
double a1;                    // Start point angle (in radians)
double a2;                    // End point angle (in radians)
{
      // Arc; "absolute" angle between start and end point
      double arc = a1<a2 ? a2-a1 : 2*PI()+a2-a1;
      // Get and return the arc segment length
      return(arc*r);
}

double getangle(cx,cy,x,y)
// Get the angle of a circle arc point
// Returns : the angle (in radians; range [0,2*PI])
double cx, cy;                // Circle center coordinate
double x, y;                  // Circle arc point coordinate
{
      double res;             // Result value
      // Get arc tangent of angle defined by circle point
      res=atan2(y-cy,x-cx);
      // Test the result
      if (res<0.0)
              // Get the "absolute" angle value
              res=PI()-res;
      // Return the result value
      return(res);
}

double PI()
// Returns the value of PI in radians
{
      // Convert 180 degree and return the result
      return(cvtangle(180.0,1,2));
}

void cputimeuse(rn,st)
// Report CPU time usage (in seconds)
string rn;                    // Routine name
double st;                    // Start time
{
      // Print CPU time elapsed since start time
      printf("(%s) Elapsed CPU Time = %6.1f [Sec]\n",rn,clock()-st);
}

Funktionsaufruf und Wertübergabe

Jede in einem User Language-Programm (bzw. Programmtext) per Definition bekannte Funktion kann innerhalb dieses Programmes (bzw. in dem entsprechenden Programm-Modul) auch aufgerufen werden. Bei der Verwendung von User Language Systemfunktionen besteht jedoch die Einschränkung, dass in einem Programm nur zueinander kompatible Systemfunktionen verwendet werden können. Dies beruht auf der Tatsache, dass über derartige Funktionsaufrufe ggf. Aktionen ausgelöst werden, die nur in einer bestimmten Interpreterumgebung ausgeführt werden können (die Funktion zur Festlegung von Plotparametern im CAM-Prozessor des AutoEngineers kann selbstverständlich nicht im Schaltplaneditor aufgerufen werden). Bei der Verwendung zueinander nicht kompatibler Systemfunktionen gibt der User Language Compiler eine Fehlermeldung aus und erzeugt keinen Programm-Code. Ähnlich verhält sich der User Language Interpreter; beim Versuch ein User Language-Programm aufzurufen, das zur Interpreterumgebung inkompatible Systemfunktions-Referenzen enthält, erzeugt das System eine entsprechende Fehlermeldung und führt das betreffende Programm nicht aus. Die Information über die Kompatibilität der User Language-Systemfunktionen ist dem Anhang A bzw. dem Anhang C zu entnehmen.

Der Funktionsaufruf setzt sich zusammen aus dem Funktionsnamen und der in runden Klammern eingeschlossenen Liste der aktuellen Parameter, die der Funktion übergeben werden soll.

Die Inhalte der global definierten Variablen eines Programms stehen grundsätzlich in jeder Funktion desselben Geltungsbereichs zur Verfügung, d.h. globale Variablen können zur Übergabe von Werten an Funktionen benutzt werden. Darüber hinaus besteht die Möglichkeit der Wertübergabe über Funktionsparameter. Die Übergabe per Parameter kann einfach kontrolliert werden und ist daher i.d.R. der Methode der Übergabe über globale Variablen vorzuziehen. Die Liste der aktuellen Parameter, also der Ausdrücke, die man bei einem Funktionsaufruf übergibt, muss mit der formalen Parameterdefinition (also Anzahl und Datentypen) der aufzurufenden Funktion übereinstimmen. Beim Funktionsaufruf werden die Werte der aktuellen Parameter in die entsprechenden formalen Parameter kopiert. Nach erfolgreicher Beendigung der Funktion wird jeder durch die Funktion geänderte Parameterwert wieder auf den aktuellen Parameter zurückgespeichert, sofern dieser eine Variablenreferenz darstellt. Schließlich besteht noch die Möglichkeit der Wertübergabe über den Rückgabewert der Funktion. Dabei wird innerhalb der Funktion mit Hilfe der return-Anweisung ein Funktionsergebnis gesetzt, das anschließend vom Aufrufer innerhalb des Ausdrucks, der den Funktionsaufruf enthält, ausgewertet werden kann.

Beispiel für Funktionsaufruf und Wertübergabe:

// Date structure
struct date { int day, month, year; };
// Global program variables
string globalstr="Global string";
int fctcallcount=0;

// Main program
main()
{
      // Local variables of main
      string resultstr="function not yet called";
      struct date today = { 0, 0, 0 };
      double p=0.0, b=2.0, e=10.0;
      // Print the global variables
      printf("fctcallcount=%d, %s\n",fctcallcount,globalstr);
      // Print the local variables
      printf("resultstr=\"%s\"\n",resultstr);
      printf("today : %d,%d,%d",today.day,today.month,today.year);
      printf("\t\tb=%.1f, e=%.1f, p=%.1f\n",b,e,p);
      // Call function
      resultstr=function(today,b,e,p);
      // Print the global variables
      printf("fctcallcount=%d, %s\n",fctcallcount,globalstr);
      // Print the local variables
      printf("resultstr=\"%s\"\n",resultstr);
      printf("today : %d,%d,%d",today.day,today.month,today.year);
      printf("\t\tb=%.1f, e=%.1f, p=%.1f\n",b,e,p);
}

string function(curdate,base,exponent,power)
struct date curdate;          // Current date parameter
double base;                  // Base parameter
double exponent;              // Exponent parameter
double power;                 // Power parameter
{
      // Increment the function call count
      fctcallcount++;
      // Set the global string
      globalstr="Global string changed by function";
      // Get the current date
      get_date(curdate.day,curdate.month,curdate.year);
      // Calculate the power
      power=pow(base,exponent);
      // Return with a result string
      return("function result string");
}

Obiges Beispiel-Programm erzeugt folgende Ausgabe:

fctcallcount=0, Global string
resultstr="function not yet called"
today : 0,0,0         b=2.0, e=10.0, p=0.0
fctcallcount=1, Global string changed by function
resultstr="function result string"
today : 4,6,92                b=2.0, e=10.0, p=1024.0

Kontrollfluss und Programmstruktur

Jede Funktion der User Language behält nach ihrem Aufruf solange die Kontrolle, bis sie auf einen weiteren Funktionsaufruf, auf eine return-Anweisung, oder nach der Abarbeitung der letzten Anweisung der Funktion auf das Funktionsende trifft. Bei einem Funktionsaufruf wird die Kontrolle an die aufgerufene Funktion weitergegeben. Beim Erreichen einer return-Anweisung oder am Funktionsende wird die Kontrolle an den Aufrufer der Funktion zurückgegeben. Ist in einem Programm eine Anwenderfunktion mit dem Namen main definiert, dann erzeugt der User Language Compiler Programm-Code, der dafür sorgt, dass - unmittelbar nach der Initialisierung der globalen Variablen - diese Funktion aufgerufen wird. Der Kontrollfluss eines User Language-Programms beginnt also üblicherweise bei der Funktion main. Da bei rekursionsfreier Programmierung jede Funktion irgendwann ihre Kontrolle wieder an den Aufrufer zurückgibt, fällt die Kontrolle schließlich wieder an die main-Funktion. Wird darin dann das Funktionsende oder eine return-Anweisung erreicht, dann ist auch das Programmende erreicht, und der User Language Interpreter kann den Kontrollfluss beenden.

Rekursive Funktionen

Funktionen dürfen rekursiv benutzt werden, d.h. eine Funktion darf sich direkt oder indirekt selbst aufrufen. Dies ist jedoch nur dann sinnvoll, wenn sich bei jedem Aufruf ein Zustand derart verändert, dass sich irgendwann ein eindeutig definierter Endzustand einstellt, mit dessen Hilfe sich die Rekursion abbrechen lässt.

Durch die rekursive Programmierung von Funktionen kann man im Allgemeinen Programm-Code einsparen und eventuell die Lesbarkeit erhöhen. Die Programmlaufzeit und der Speicherplatzbedarf hingegen werden sich durch Rekursionen erhöhen. Daher sollte man jeweils prüfen, ob die Verwendung einer Rekursion tatsächlich nützlich ist. Auch besteht die Gefahr, dass man "versehentlich" eine Endlosrekursion implementiert, d.h. eine Rekursion, die nie den Endzustand erreicht. Der User Language Interpreter wird bei der Abarbeitung einer solchen Endlosrekursion irgendwann einen Speicher- bzw. Stapel-Überlauf feststellen, da für jeden Funktionsaufruf zumindest eine Rücksprung-Adresse gespeichert werden muss.


2.3.4 Regeln zum Geltungsbereich

Bei der Referenzierung der Objekte eines User Language-Programms muss der Compiler jeweils die Gültigkeit der Referenz überprüfen. Hierzu ist jedem Objekt ein bestimmter Geltungsbereich innerhalb des Programms zugeordnet. Innerhalb dieses Geltungsbereiches ist das entsprechende Objekt bekannt und kann referenziert werden. Es wird unterschieden zwischen globalem und lokalem Geltungsbereich. Der globale Geltungsbereich erstreckt sich über das gesamte Programm (also auch auf noch einzubindende, getrennt kompilierte Programmteile bzw. Libraries), während die lokalen Geltungsbereiche des Programms Bezug nehmen auf die Funktionsdefinitionen.

Die Funktionen eines Programms sind global, d.h. im gesamten Programm gültig. Variablen- und Typ-Definitionen, die in einer Funktion vorgenommen werden, gelten nur lokal innerhalb dieser Funktion; außerhalb von Funktionen gelten derartige Definitionen als global. Die Parameterdefinitionen einer Funktione werden wie lokale Variablen behandelt und gelten daher immer nur lokal innerhalb der betreffenden Funktion. Strukturdefinitionen gelten allgemein als global im aktuell zu übersetzenden Programmtext. Durch die Zuweisung der Speicherklasse static kann der Geltungsbereich von Funktionen und globalen Variablen eingeschränkt werden auf den aktuell zu übersetzenden Programmtext. Die Speicherklasse static dient insbesondere der Vermeidung von Namenskonflikten beim Binden bzw. Linken unterschiedlicher Programm-Module bzw. Libraries.

Um Namenskonflikte zu vermeiden, müssen die Elemente jeder Objektklasse innerhalb ihres Geltungsbereichs jeweils unterschiedliche Namen besitzen. Bei der Referenzierung haben die lokalen Objekte Vorrang vor den globalen.


Bartels :: Bartels AutoEngineer :: BAE Dokumentation :: User Language Programmierhandbuch :: Sprachbeschreibung :: Datentypen und Definitionen

Datentypen und Definitionen
© 1985-2024 Oliver Bartels F+E