Memory-Maps: Die Landkarte durch den Speicherschungel

Als Besitzer einer Compiler-Sprache (hier C) ist man bei der Verwaltung des vorhandenen Speichers auf die Betriebssystemroutinen angewiesen. Diese sind zwar nicht unbedingt komfortabel, aber es läßt sich doch so manche gestellte Hürde überwinden. Das Vergessen eines wichtigen Befehls, z.B. das Freigeben des Speichers, wird meist sofort durch den obligatorischen Griff zum Reset-Knopf bestraft, da keine anderen Programme mehr gestartet werden können.

Das Problem der Speicherverwaltung auf dem ATARI ST/TT ist sicherlich hinreichend bekannt, denn das GEMDOS unterstützt nur eine begrenzte Anzahl von Speicherblöcken. In Belegt- und Frei-Listen, die in ihrer Größe fixiert sind, werden die Speicherallozierungsaufrufe registriert. Bei einem Rechner der älteren Generation (TOS 1.0) fassen die Listen ca. 280 bis 290 Einträge, während die neueren mit ca. 800 möglichen Malloc-Aufrufen eigentlich schon ganz gut bestückt sind; aber für viele Anwendungen kann dies noch zu wenig sein. Daher sollte man immer versuchen, hauptsächlich große Speicherblöcke zu reservieren, um ein allzu häufiges Aufrufen der Speicherverwaltungsroutinen zu vermeiden. (Besonders kleine Speicherblöcke sollte man gar nicht erst allozieren.) Wenn man dann noch die Reihenfolge der Speicherfreigabe mißachtet, ist nicht nur eine häufige Segmentierung des Speichers die Folge, sondern auch einige wundersame Abstürze des Systems sind zu beobachten. Atari empfiehlt sogar für alle die Fälle, in denen man einen Speicherblock nur kurzfristig benötigt, ganz einfach den gesamten freien Speicher (minus 8 KByte) zu allozieren, um eine Fragmentierung zu verhindern (Stand 1989).

Im Zeitalter eines Multitasking-Betriebssystems (MultiGEM, Multi-TOS), ist dies aber auch nicht mehr so sinnvoll, da dann nur ein einziges Programm „gleichzeitig“ laufen könnte. Man sollte sich grundsätzlich als Programmierer vorher überlegen, wieviel Speicher das Programm benötigen wird und entsprechend handeln. Damit ermöglicht man einen Betrieb von mehreren Programmen und beschert dem ATA-RI-Rechner einen zufriedenen Benutzer. Die hier vorzustellenden neuen Routinen gehören zu einer neuen internen Speicherverwaltung, die das Betriebssystem fast vollständig entlastet. Es werden von ihr genau die gewohnten Funktionen inkl. Aufrufe zur Verfügung gestellt, die das Betriebssystem auch bietet - und noch mehr: einen variablen Zwischenspeicher.

Das Feature des Zwischenspeichers besteht darin, daß eine Speicherbelegung an zwei Positionen durchgeführt werden kann. Dabei gibt ein zusätzlicher Parameter an, ob der Speicher „vorne“ oder „hinten“ reserviert werden soll. Mit den neuen Speicherroutinen ist es möglich, anzugeben, ob der Speicherblock im vorderen oder im hinteren Teil des Speichers alloziert werden soll. Dies macht genau dann Sinn, wenn man nur vorübergehend einen Teil des Speichers benötigt. Man erreicht dadurch eine kompaktere Reservierung und effektivere Nutzung des Speichers.

Beim Grundaufbau der Speicherroutinen handelt es sich um zwei Arrays, die zum einen die Speichergrößenverteilung und zum anderen die Zeigeradreßvergabe speichern. Zwei Zähler geben zusätzlich an, wieviele Speichersegmente schon verteilt sind; wobei die größtmögliche Segmentanzahl durch ein Makro vorher bestimmt werden kann. Die Zeiger und Zähler sind zwar global definiert, sollten aber nicht verändert werden! Die weitere Beschreibung und die Schilderung der Aufgabe der einzelnen Routinen steht im Quelltext oder in jedem besseren ATAR1-ST-Systembuch.

Als Demonstration dieses Speichermodells soll das kleine Programm C-REM-Kill dienen, daß die Remarks (Bemerkungen, REM) eines C-Quelltextes eliminiert. Es arbeitet nach einem einfachen Prinzip, bei dem ein Zähler (Schachtelungstiefenanzeiger) um Eins erhöht wird, wenn eine Bemerkung eingeleitet, und um Eins erniedrigt wird, wenn eine Bemerkung beendet wird. Nur wenn dieser Null ist, d.h. wenn keine Bemerkung eingeleitet worden ist, wird der Text in einen anderen Speicherbereich kopiert. Über Sinn und Unsinn dieses Programms läßt sich wahrscheinlich vortrefflich streiten, jedoch kann man hier die Arbeitsweise der Routinen sehr gut ablesen. Zuerst wird ein Speicher reserviert, in den der ganze C-Quelltext geladen wird. Da dieser eine Art Festplatten-Cache darstellt, wird er nur vorübergehend benötigt und somit „hinten“ alloziert. Der Speicherbereich, in dem sich dann das von REMs entfernte C-Listing befindet, wird „vorne“ alloziert. Nachdem die Datei in den hinteren Speicherbereich geladen worden ist, wird es Byte für Byte unter der Voraussetzung nach vorne kopiert, daß es sich nicht um ein REM handelt. Danach wird der „Cache“ gelöscht und der vordere Speicherbereich auf die benötigte Größe verkleinert. Somit könnte jetzt eine weitere Bearbeitung folgen, oder (wie es hier gemacht wurde) diese Datei mit der Extension „.REM“ abgespeichert werden.

Ein weiterer wichtiger Stichpunkt ist die Kompatibilität zu den Betriebssystemroutinen, die schon in einem selbstgeschriebenen Programm benutzt worden sind. Dieses muß man nicht neu schreiben, sondern einfach mit der Suche/Ersetze-Funktion eines Editors entsprechend verändern. Dabei läßt man „alloc(„ durch „allocn(0,“, „free(„ durch „freen(0,“ und „Mshrink(„ durch „Mshrink(0,“ ersetzen. Nach dem Linken inkl. der neuen Speicherverwaltung dürften Abstürze der Betriebssystemspeicherroutinen der Vergangenheit angehören.

Literatur:

Jankowski/Rabich/Reschke, ATARI Profibuch ST-STE-TT, Sybex-Verlag Brückmann/English/Gerits, ATARI ST INTERN, Data Becker

/***********************************************/ 
/* Modul:      MEMORY.C                        */
/* ------------------------------------------- */
/* Funktionen:                                 */
/*    Speicherverwaltung für ATARI ST/TT       */
/* Ersteller:                                  */
/*    Hartmut Klindtworth, 14.04.1992          */
/*    Copyright 1992 by MAXON-Verlag, Eschborn */
/***********************************************/

/* = INCLUDES =================================*/
#include <ext.h> 
#include <stdlib.h> 
#include <stdio.h>
#include <string.h>

/* = PROTOTYPEN ===============================*/
int init_malloc( long maxmem ); 
int ende( void );
void *mallocn(int vh, long groesse);
long check( void );
int freen(int vh, void *oldadr);
void *reallocn(int vh,void *oldadr,size_t great); 
int Mshrinkn(int vh, int zero, void *block, long newsize);
void *callocn(int vh, long nitems,long size);

/* = DEFINES ==================================*/
            /* Anzahl des noch z.Vfg. st. Sp. */ 
#define RESERVE_MEMORY 128*1024L
            /* Anzahl der Memory-Speicherblöcke */ 
#define MAXMEMP 100
            /* Nur für LONG definiert */ 
#define GERADE(a)   ((a+1)&-2L)

/* = Globale Variablen ========================*/
char *mempa[MAXMEMP];
/* MEMoryPointerArray
    Im vorderen und hinteren Teil werden 
    Pointer gesichert, die auf die zugeteilten 
    Speicherbereiche zeigen */
long gamem[MAXMEMP];
/* Memorygröße (GrößenArray für MEMory)
    Im vorderen und hinteren Teil befindet sich 
    die Größe eines einzelnen Speicherblockes.*/ 
int va, ha;
/* Anzahl der belegten Spbl. vorne/hinten */

/* Routine:     init_malloc                  */
/* ----------------------------------------- */
/* Funktion: initialisiert Speicher          */
/* Funktionsweise:                           */
/* prüft wieviel Speicher angefordert werden */ 
/* kann und reserviert diesen in Abhänigkeit */ 
/* der von RESERVE-MEMORY und maximalgroesse */ 
/*  Übergabe:   long maxmem          */
/*  maximale Speicheranforderung,falls möglich*/
/*  ==0 => Parameter unwichtig           */
/*  Rückgabe:   int */
/*  -1 bei Fehler, sonst 0  */
int init_malloc( long maxmem )
{ /* Fragt nach, wieviel Speicher frei ist  */
    gamem[0] = (long)coreleft(); 
    if(gamem[0]<=0) return(-1); 
    if(maxmem>0 && gamem[0]>maxmem) 
        gamem[0] =maxmem;
    else
    {   gamem[0]-=RESERVE_MEMORY;
        if<gamem[03<=0) return(-1);
    }
    gamem[0]=GERADE(gamem[0]-1); 
    mempa[0]=(char *)malloc(gamem[0]);

    return(0);
}

/* Routine: ende                              */
/* -------------------------------------------*/
/* Funktion: überprüft evtl. Fehler in der    */
/*  Speicherverwaltung und ruft end auf       */
/* Funktionsweise: Sollte zu viel Speicher    */
/*  reserviert sein, so wird angezeigt, wo    */
/*  noch Speicher zurückgegeben werden sollte.*/
/*  Oder es kann sein, daß zu viele frei ge-  */
/*  geben worden sind, dann wird auch eine    */
/*  Meldung ausgeben. Ganz zum Schluß wird    */
/*  free() aufgerufen, um den Speicher wieder */
/*  an das Betriebssystem zurückzugeben.      */
/* Rückgabe: int                              */
/*  0 => kein Fehler, -1 => Fehler            */
int ende( void )
{   char    text[80];   /* Textspeicherung */
    int     i,          /* Laufvariable */
            fehler=0;   /* Gibt evtl. Fehler an */

    if(va>0)
    { puts("Es sind noch folgende Speicherblöcke"
            " nicht zurückgegeben worden:");
        for(i=1; i<=va; i++)
        {
            memset(text,0,sizeof(text));
            strcpy(text,"Vordere Speicheradresse: "); 
            ltoa((long)mempa[i],text+strlen(text),10); 
            strcat(text," mit Speicher: "); 
            ltoa(gamem[i],text+strlen(text),10); 
            puts(text);
        }
        fehler=-1;
        while(kbhit()) getch(); 
        while(kbhit()==0); 
        getch();
    }
    if(ha>0)
    { 
        if(va<=0)
            puts("Es sind noch folgende Speicher"
                "blocke nicht zurückgegeben worden:");
        for(i=1; i<=ha; i++)
        { 
            memset(text,0,sizeof(text));
            strcpy(text,"Hintere Speicheradresse: "); 
            ltoa((long)mempa[MAXMEMP-i], text+strlen(text),10); 
            strcat(text," mit Speicher: ");
            ltoa(gamem[MAXMEMP-i],text+strlen(text),10);
            puts(text);
        }
        fehler=-1;
        while(kbhit()) getch(); 
        while(kbhit()==0); 
        getch();
    }

    if(va<0 || ha<0)
    { 
        puts("Es sind zuviele Speicherplätze"
            " freigegeben worden:"); 
        memset(text, 0, sizeof(text));
        strcpy(text,"Vorderer Speicherindex: ");
        itoa(va,text+strlen(text),10);
        strcat(text," Hinterer Speicherindex: ");
        itoa(ha,text+strlen(text),10);
        puts(text);
        fehler=-1;
        while(kbhit()) getch(); 
        while(kbhit()==0); 
        getch();
    }
    /* Gesamten Speicherplatz wieder freigeben */
    free( (void *)mempa[0] );

    return(fehler);
}

/* Routine:     mallocn (malloc)              */
/*--------------------------------------------*/
/* Funktion: reserviert Speicher              */
/* Funktionsweise: Wird der Speicher nur      */
/*  vorübergehend genutzt, so wird eine 1 in  */ 
/*  vh übergeben. Damit muß der Speicher am   */ 
/*  Ende des Speicherblocks reserviert werden.*/ 
/*  Dabei wird ein check durchgeführt, ob     */
/*  überhaupt noch soviel Speicher vorhanden  */ 
/*  ist. Dabei wird der Speicherzähler hinten */ 
/*  um einen erhöht und die Adresse des freien*/ 
/*  Speichers wird berechnet. Diese Adresse   */ 
/*  und die Größe werden in die dafür vorge-  */ 
/*  sehenen globalen Arrays eingetragen.      */
/*  Entsprechend wird bei einer vorderen      */
/*  Allozierung des Speichers gehandelt.      */
/* Übergabe: long groesse                     */
/* Größe des gewünschten Speicherplatzes      */
/*           int vh                           */
/* 0=>Speicher vorne, 1=>Speicher hinten      */
/* Rückgabe: void *                           */
/*  typenloser Zeiger, der auf den angeforder-*/ 
/*  ten Speicherblock zeigt, oder Anzahl der  */ 
/*  freien Bytes oder NULL falls Fehler       */
/*  aufgetreten ist (z. B. Speichermangel)    */ 
void *mallocn(int vh, long groesse)
{ 
    void *adr=NULL;
    if(groesse==-1) return( (void *)check() );
    if(groesse<= 0) return(NULL);

    /* Nur gerade Speicherbereichsgrößen */
    groesse=GERADE(groesse+1) ;

    if(vh!= 0)
    { 
        if(groesse<check() && groesse>0)
        { 
            ha++; 
            if(ha<=1)
                mempa[MAXMEMP-ha]=mempa[0]+gamem[0]-groesse;
            else
                mempa[MAXMEMP-ha]=mempa[MAXMEMP-(ha-1)]-groesse; 
            gamem[MAXMEMP-ha]=groesse; 
            adr=mempa[MAXMEMP-ha];
        }
    } else
    { 
        if(groesse<check() && groesse>0)
        { 
            va++; 
            if(va<=1)
                mempa[va]=mempa[va-1]; 
            else
                mempa[va]=mempa[va-1]+gamem[va-1]; 
            gamem[va]=groesse; 
            adr=mempa[va];
        }
    }
    return(adr);
}

/* Routine: check (malloc(-1))                 */
/* ------------------------------------------- */
/* Funktion: ermittelt größtmögl. Speicherbl.  */ 
/* Funktionsweise: vom Hauptspeicher wird der  */ 
/*  belegte Speicherplatz vorne und hinten     */ 
/*  abgezogen.                                 */
/* Übergabe: nichts                            */
/* Rückgabe: long                              */
/*  Größe des verbleibenden Speicherblockes    */
long check( void )
{ 
    long ret;
    ret==gamem[0]; 
    if(va>0)
        ret-=mempa[va]+gamem[va]-mempa[0]; 
    if(ha>0)
        ret-=mempa[0]+gamem[0]-mempa[MAXMEMP-ha]; 
    return(ret);
}

/* Routine: freen (free)                       */
/* ------------------------------------------- */
/* Funktion: gibt belegten Speicherblock frei  */
/* Funktionsweise: setzt die Speichergröße,    */
/*  den Zeiger auf den Speicherplatz auf       */
/*  Null und subrahiert den Speicheranzahl-    */
/*  zeiger um Eins                             */
/* Übergabe: int vh                            */
/*  0 (1) Speicher vorne (hinten) freigeben    */
/*            void *oldadr                     */
/*  NULL dann irrelevant, sonst Zeiger auf     */
/*  Speicheradresse                            */
/* Rückgabe: 0=OK, -1=Fehler                   */
int freen(int vh, void *oldadr)
{ 
    /* Speicher wurde vorrübergehend alloziert */ 
    if(vh!=0)
    { 
        if(ha>0)
        { 
            if(oldadr!=NULL && mempa[MAXMEMP-ha]!=oldadr)
                return(-1); 
            mempa[MAXMEMP-ha] =NULL; 
            gamem[MAXMEMP-ha]=0; ha-- ;
        }
    } else 
        if(va)
        { 
            if(oldadr!=NULL && mempa[va]!=oldadr) 
                return(-1); 
            mempa[va]=NULL; 
            gamem[va]=0; va--;
        };
    return(0);
}

/* Routine: reallocn (realloc)                 */
/*-------------------------------------------- */
/* Funktion: erweitert und grenzt den vor-     */
/*           handenden Speicherblock ein       */
/* Funktionsweise: Wird der Speicherbereich    */
/*  hinten gewählt, so wird der Speicher zu-   */
/*  sätzlich an eine neue Adresse verschoben.  */
/* Übergabe: void *uvh_oldadress               */
/*  Adresse des zuletzt angeforderten Spbl.    */
/*            long groesse                     */
/*  neue absolute Groesse                      */
/*            int vh                           */
/*  0=>Speicher vorne, l=>Speicher hinten      */
/* Rückgabe: void *                            */
/*  wenn alles klappte, wird die neue Adresse  */
/*  des Spbls zurückgegeben. Sie kann (wenn    */
/*  hinten) anders als die vorher übergebene   */
/*  sein! Speicher wurde dann verschoben.      */
/*  NULL, wenn ein Fehler vorlag.              */
void *reallocn(int vh,void *oldadr,size_t great)
{ 
    void *adr=NULL;
    great=GERADE(great+1); 
    if(vh!=0)
    { /* Ist die Adresse auch korrekt?  */
        if(mempa[MAXMEMP-ha]!=oldadr) 
            return(NULL);

        if(great<=gamem[MAXMEMP-ha] && great>0)
        { /* Alte Adresse sichern   */
            adr=mempa[MAXMEMP-ha];
            /* Neuen Pointer errechnen und -setzen */ 
            mempa[MAXMEMP-ha]+=gamem[MAXMEMP-ha]-great;
            /* Speicher vom alten Bereich an den- */
            /* neuen herankopieren */
            memmove((char *)mempa[MAXMEMP-ha],(char *)adr,(size_t)great);
            /* Groesse neusetzen    */
            gamem[MAXMEMP-ha]=great; 
            adr=mempa[MAXMEMP-ha];
        }else
        { /* Noch genug Speicher da?    */
            if(great-gamem[MAXMEMP-ha]> check() && great<=0)
                return(NULL);

            /* Alte Adresse sichern                       */
            adr=mempa[MAXMEMP-ha];
            /* Neuen Pointer errechnen und -setzen        */ 
            mempa[MAXMEMP-ha]-=great-gamem[MAXMEMP-ha]; 
            /* Speicher vom alten Bereich an den          */ 
            /*              neuen herankopieren           */
            memmove((char *)mempa[MAXMEMP-ha],
                (char *)adr,(size_t)gamem[MAXMEMP-ha]); 
            /* Groesse neusetzen */
            gamem[MAXMEMP-ha]=great; 
            adr=mempa[MAXMEMP-ha];
        }
    }else
    { /* Ist die Adresse auch korrekt   */
        if(mempa[va]!=oldadr) 
            return(NULL);

        if(great<=gamem[va] && great>0)
        {   gamem[va]=great;
            adr=oldadr;
        else if(great-gamem[va]<check()&& great>0)
        {   gamem[va]=great; 
            adr=oldadr;
        };
    }
    return(adr);
}

/* Routine: Mshrinkn (Mshrink)                 */
/* ------------------------------------------- */
/* Funktion:   verkleinert Speicherblock       */
/* Funktionsweise:  ruft reallocn mit den      */
/*  entsprechenden Werten auf und liefert      */
/*  0 zurück, falls alles OK war               */
/* Übergabe: int vh                            */
/*  0 (1) Speicherblock vorne (hinten) belegen */
/*            int zero                         */
/*  Standardmäßig auf Null (0)                 */
/*            void *block                      */
/*  Zeiger auf den entsprechenden Speichblock  */ 
/*            long newsize                     */
/*  neue Größe des Speicherblocks              */
int Mshrinkn(int vh, int zero, void *block, long newsize)
{
    void *spbl;

    spbl=reallocn(vh, block, (size_t)newsize); 
    if(spbl==NULL) return(-1);

    block=spbl;
    zero=0; /* Damit entsteht kein Warning */ 
    return(zero);
}

/* Routine:     callocn  (calloc)             */
/* ------------------------------------------ */
/* Funktion: reserviert Speicher, der auf     */
/*           \x00 gesetzt worden ist          */
/* Funktionsweise: reserviert Speicher        */
/*  mittels mallocn und löscht ihn            */
/* Übergabe: long nitems                      */
/*            Anzahl der Speicherschritte     */
/*           long size                        */
/*            Größe der Speicherschritte      */
/*           int vh                           */
/*           0 (1) Speicher vorne (hinten)    */
/* Rückgabe: void *                           */
/*             Zeiger auf geforderten Spei.   */
/*             NULL bei FEHLER                */
void *callocn( int vh, long nitems, long size)
{
    void *adr;

    long ll_size1=GERADE((nitems*size)+1); 
    adr=mallocn(vh, (long)(nitems*size)); 
    if(adr!=NULL) memset(adr,0,ll_size1); 
    return(adr);
}


;»»»> Projektdatei für Pure C ;»»»> REM__KILL PRJ REM_KILL.PRG ; name of executable program .C[-C -K] = ; list of modules follows... PCSTART.O ; startup code MEMORY.C ; Speicherverwaltungsroutinen REM_KILL.C ; eigentliches Programm PCFLTLIB.LIB ; floating point library PCSTDLIB.LIB ; Standard library PCEXTLIB.LIB ; extended library PCTOSLIB.LIB ; TOS library PCGEMLIB.LIB ; AES and VDI library
/**********************************************/
/* Modul:   REM_KILL.C                        */
/* ------------------------------------------ */
/* Funktionen:                                */
/*    Löscht alle C-Remarks in einem File     */
/* Ersteller:                                 */
/*    Hartmut Klindtworth, 26.04.1992         */
/*    Copyright 1992 by MAXON-Verlag, Eschborn*/
/* Extern:                                    */
/*    MEMORY.C                                */
/**********************************************/

/*= INCLUDES =================================*/

#include <ext.h> 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h>

/*= EXTERNE FUNKTIONEN =======================*/

extern int init_malloc( long ul_maxmemory ); 
extern int ende( void );
extern void *mallocn(int ui_vh,long ul_groesse); 
extern int freen(int ui_vh,void *uvp_oldadress); 
extern int Mshrinkn(int ui_vh, int ui_zero, void *uvp_block, long ul_newsize); 
extern void *callocn( int ui_vh, long ul_nitems, long ul_size);

/*= PROTOTYPEN ===============================*/
void exit_ende( void );

int main(int arge, char *argv[])
{
    FILE *daten=NULL; 
    char pfad[MAXPATH]; 
    char *dest,*source; 
    long laenge,sc,de; 
    int remcount;

    if(init_malloc(0)==-1)
        return( 39 ); /* Kein Speicher mehr da! */

    /* Bei Programmabbruch erst Speicher zurück */ 
    atexit(exit_ende);

    /* Information über das Programm ausgeben. */ 
    puts("C-REM - KILLER V 1.0");
    puts("**Autor:**  Hartmut   Klindtworth, 1992\n");

    /* Wurde etwas der Routine übergeben ?  */
    memset(pfad,0,MAXPATH); 
    if(argc>1 && strlen(argv[1])>0) 
        strcpy(pfad,argv[l]); 
    else
    { /* Da nichts übergeben worden ist, ...   */
        puts("Bitte den Pfad und Dateinamen"
                " angeben:");
        gets(pfad);
        if(strlen(pfad)==0) exit(0);
    }
    /* Exist. die Datei, läßt sie sich öffnen? */ 
    daten=fopen(pfad,"rb"); 
    if(daten==NULL) exit(2);

    /* Länge des Files ermitteln und Speicher  */ 
    /* zum Einlesen reservieren.               */
    laenge=filelength(fileno(daten));
    if( (long)mallocn(0,-1) < 2*laenge ) exit(39);

    source=(char *)mallocn(1,laenge); 
    dest  =(char *)callocn(0,1,laenge);

    /* Datei komplett einlesen                 */
    fread(source,laenge,1,daten); 
    fclose(daten);

    /* Funktion:                                */
    /*  Es wird jedes Byte verifiziert, ob es   */
    /*  ein Remark einleitet oder beendet. Ent- */ 
    /*  sprechend wird dann der Remarkcounter   */
    /*  erhöht oder erniedrigt. Nur wenn dieser */ 
    /*  0 ist, ist der Text nicht in ein Remark */
    /*  geschachtelt.                           */

    dc=remcount=0;
    for(sc=0; sc<laenge; sc++)
    { /* Jedes Byte nachsehen, ob es ein REM ist*/ 
        if(*(source+sc)=='/' && *(source+sc+1)=='*') 
            remcount++;
        else
            if(*(source+sc)==’*' && *(source+sc+1)=='/') 
            {   remcount--; sc++; 
                if(remcount<0)
                { puts("FEHLER in REMARKS!"); 
                    freen(0,dest); 
                    freen(1,source); 
                    exit(1);
                }
            }else
                if(remcount==0)
                { *(dest+dc)= *(source+sc); 
                    dc++;
                };
    }
    /* Speicher des kopierten Speicherblocks  */
    /* auf benötigte Größe verkleinern        */
    Mshrinkn(0,0,dest,de);

    /* Source-Speicher freigeben.             */
    freen(1,source);

    strcpy(strrchr(pfad,REM"); 
    if( ( daten=fopen(pfad,"wb") )!=NULL)
    { 
        /* Daten jetzt in REM"-Datei schreiben */ 
        fwrite(dest,1,de,daten); 
        fclose(daten); 
        freen(0,dest);
    }else
    { 
        freen(0,dest); 
        exit(2);
    }
    exit(0); 
    return(0);
}
void exit ende( void )
{
    ende();
}


Hartmut Klindtworth



Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]