Variablenfelder und Pointer

Von Heinz - Volker Viehof

 

1.Theorie

Variablenfelder (Arrays) und Pointer (Zeiger) zählen zu den festen und unverzichtbaren Bestandteilen

aller modernen Programmiersprachen. Sie bieten eine exzellente Möglichkeit Daten im Speicher organisiert abzulegen, und wahlfrei darauf zugreifen zu können. Außerdem ermöglichen sie auf einfache Art und Weise Tabellen von Daten zu erstellen.:

 

Speicherzelle   00       01        02        03        04        u.s.w.

Inhalt                „H“        „a“        „l“         „l“         „o“

Zeiger n=“03“:                                                          liefert „l“

Zeiger n=“04“:                                                          liefert „o“

 

Ein „klassisches“ Beispiel hierfür wäre die Druckerzeichensatztabelle, der PC benutzt das darzustellende Zeichen als Zeiger und nimmt dann damit aus dem Variablenfeld „Zeichensatz“ den Code für das zu druckende Zeichen

 

 

 

2.Beispiele für eindimensionale Felder:

Im einfachsten Fall ist das Variablenfeld eindimensional (StringVariable) und wird über nur einen Zeiger adressiert:

 

#Python Beispiel : Iteration durch ein Variablenfeld

#d.h. jedes Zeichen wird einzel geholt und mit „print

#ausgegeben.

 

Variablenfeld=["H","a","l","l","o"," ","W","e","l","t"," ","!"]

for n in range(0,12):

   print Variablenfeld[n],

 

Erklärung:

Jeder Buchstabe aus dem Variablenfeld wird über den Zeiger [n] adressiert und einzeln ausgegeben.

Hier ist dasselbe Beispiel in 16 Bit Assembler:

 

stack               segment stack word 'stapel'

                        db 10 dup (?)

stack               ends

 

;________________________________________________

;

; Das Variablenfeld *string* im Datensegment

; (wo sonst ?)

;________________________________________________

data                segment public word 'daten'

string               db 'Hallo Welt !'

data                ends

 

 

code               segment public word 'prog'

main:              mov ax,seg string

                        mov ds,ax

;________________________________________________

;

; Der Zeiger *bx*

;________________________________________________

                        mov bx,offset string

                        mov cx,12

X1:                   mov dl,ds:[bx]

                        mov ah,2

                        int 021

                        inc bx

                        loop X1

                        mov ax,04c00

                        int 021

code               ends

                        end

 

Das macht haargenau das gleiche wie das Python Programm. (sowohl in Assembler als auch in Python gibt es natürlich simplere Möglichkeiten einen Text anzeigen zu lassen). Die einzelnen Zeichen werden mit mov dl,ds:[bx] aus dem Variablenfeld geladen, hierbei ist bx der Zeiger. Register cx wurde mit der Länge des Feldes geladen und dient als Zähler, d.h. bei jedem Laden eines Zeichens aus dem Variablenfeld wird cx um eins vermindert und bx (Zeichenzeiger) wird um eins erhöht.

 

; Demoprogramm "string"

; <C> 2004 H.V.Viehof

;________________________________________________

stack               segment stack word 'stapel'

                        db 10 dup (?)

stack               ends

 

;________________________________________________

;

; Das Variablenfeld *string* im Datensegment

; (wo sonst ?) ,das at (@) Zeichen markiert das

; Ende des Variablenfeldes.Die Laenge des Strings

; muss nicht mehr explizit angegeben werden !

;________________________________________________

data                segment public word 'daten'

string               db 'Hallo Welt !','@'

data                ends

 

 

code               segment public word 'prog'

main:              mov ax,seg string

                        mov ds,ax

;________________________________________________

;

; Der Zeiger *bx*

;________________________________________________

                        mov bx,offset string

X1:                   mov dl,ds:[bx]

                        cmp dl,'@'

                        jz X2

                        mov ah,2

                        int 021

                        inc bx

                        jmp X1

X2:                   mov ax,04c00

                        int 021

code               ends

                        end

 

Hier hab Ich das noch mal ein wenig verbessert, nachdem ein Zeichen aus dem Feld geladen wurde,

wird geprüft ob es sich um das „@“ Zeichen handelt (cmp dl,’@’) wenn ja, wird das Programm beendet, wenn nein, wird das nächste Zeichen geholt und dargestellt. 

So, genug „Hallo Welt !“ Beispiele, jetzt mal etwas anspruchsvolleres:

 

.486                                         ; create 32 bit code

.model flat, stdcall                            ; 32 bit memory model

option casemap :none           ; case sensitive

 

include e:\masm32\include\windows.inc

include e:\masm32\include\user32.inc

include e:\masm32\include\kernel32.inc

include e:\masm32\include\advapi32.inc

 

includelib e:\masm32\lib\user32.lib

includelib e:\masm32\lib\kernel32.lib

includelib e:\masm32\lib\advapi32.lib

 

;---------------------------------------------------------------------------

;  

;   2004 H.V.Viehof

;   spielt melodie mit noten und pausen

;   tonhoehe = frequenz in Hertz

;

;---------------------------------------------------------------------------

 

.data

noten   dd 262,330,392,1,1,1,1,262,330,392,1,1,1,1

        dd 262,330,392,1,1,1,1,262,330,392,440,466

        dd 0

 

.code

start:

    mov esi,offset noten

X1: lodsd

    cmp eax,0

    je TheEnd

    cmp eax,1

    je X2      

    invoke Beep,eax,100

X2: invoke Sleep,50

    jmp X1

TheEnd:

    invoke ExitProcess ,NULL

 

end start

 

Hier werden aus dem Variablenfeld „Noten“ die einzelnen Frequenzwerte geholt und mit der Windows API Funktion „Beep“ wiedergegeben. Der Zeiger zur addressierung ist <esi>, mit lodsd werden die einzelnen Feldelemente Adressiert, der Zeiger <esi> wird beim Feldzugriff mit lodsd (oder auch lodsb oder lodsw) automatisch inkrementiert.

 

3.Speichern von Daten im Variablenfeld:

In den bisherigen Beispielen wurden Daten aus dem Variablenfeld geholt, in dem folgenden und vorläufig letzten Beispiel zum Thema eindimensionale Variablenfelder werden Daten mit STOSB

Im Feld gespeichert und mit CMPSB mit dem Inhalt eines zweiten Variablenfeldes verglichen:

 

; Ein gutes Beispiel zur Verwendung von Feld- bzw 'String-' Variablen,

; und ne' Demonstration der Arbeitsweise eines "Parsers".

; <C> 2004 H.V.Viehof

;

; Stapelsegment:

; Wer's nicht weiss : Hier werden bei 'Push'-Operationen die Registerinhalte

; gespeichert, die mit der 'Pop-'Anweisung wieder zurueckgeholt werden.

; ist der Stapel (Stack) zu klein, kommt es zum 'StackOverflowError',

; und das Programm bricht ab !

;______________________________________________________________________

 

stack               segment stack word 'stack'

                        dw ?

Top_of_stack:

stack               ends

 

;______________________________________________________________________

;

; Ach ,uebrigens eifriges kommentieren ist zugleich eine gute

; Dokumentation und erhoeht die Uebersichtlichkeit !

; Die Kommentare werden ja nicht mit kompiliert ;-)

; Datensegment:

;______________________________________________________________________

 

data                segment public word 'daten'

meldung:        db 0a,0d,'Your Keycode Please - Bitte Passwort eingeben : $'

passw:             db 'gurkensaft@'

true:                db 0a,0d,'======================'

                        db 0a,0d,'=   Access Granted!  ='

                        db 0a,0d,'= Zugang gestattet!  ='

                        db 0a,0d,'======================',0a,0d,024

false:               db 0a,0d,'______________________'

                        db 0a,0d,'|   Access Denied !  |'

                        db 0a,0d,'| Zugang Verweigert! |'

                        db 0a,0d,'|____________________|',0a,0d,024

puffer:             db 20 dup(020)

data                ends

 

;______________________________________________________________________

; Hier beginnt das Hauptprogramm (ProgrammEntryPoint),

; zweckmaessigerweise findet man hier fast nur Unterprogrammaufrufe

; (sogenannte ProcedureCall's):

; Programmsegment:

;______________________________________________________________________

 

code               segment public word 'programm'

main:              call init

                        mov dx,offset meldung

                        call output

                        call input

                        call compare

                        cmp al,0ff

                        jne Z4

;______________________________________________________________________

; Das Passwort war falsch ! Zugriff verweigert anzeigen, und nochmal bitte!

;______________________________________________________________________

                        mov dx,offset false

                        call output

                        jmp main

;______________________________________________________________________

; Das Passwort wurde als richtig erkannt, die positive Meldung wird

; Angezeigt und das Programm beendet :

;______________________________________________________________________

Z4:                   mov dx,offset true

                        call output

                        mov ax,04c00

                        int 021

 

 

;______________________________________________________________________

; Die Subroutinen (heutzutage : Prozeduren),

; aber die bekommen in einem so kleinen Programm keine eigenen Segmente !

;______________________________________________________________________

; Daten- und Extrasegment initialisieren:

;______________________________________________________________________

init:                  mov ax,seg meldung

                        mov ds,ax

                        mov es,ax

                        ret

;______________________________________________________________________

; Die Textausgabe ,die Funktion 09 des DOS Interrupt 021h gibt die

; bei DS:DX beginnende und mit einen Dollarzeichen "$" abgeschlossene

; Zeichenkette als ASCII Text auf der Konsole aus:

;______________________________________________________________________

output:            mov ah,9

                        ds:int 021

                        ret

 

;______________________________________________________________________

; Tastatureingabe :

; Funktion 07 des DOS Interrupt 021h wartet auf Tastatureingabe.

;______________________________________________________________________

input:              mov di,offset puffer

R1:                   mov ah,07

                        int 021

;______________________________________________________________________

; War das die 'Enter'-Taste (0d) ? wenn ja, dann: fertig

;______________________________________________________________________

                        cmp al,0d

                        jz ret

;______________________________________________________________________

; Das Zeichen in AX auf dem Stapel sichern (push ax), dann stattdesen

; mit Funktion 02 des DOS Interrupt 021h einen Stern '*' darstellen,

; danach AX wiederherstellen (pop ax).

;______________________________________________________________________

                        push ax

                        mov dl,'*'

                        mov ah,2

                        int 021

                        pop ax

;______________________________________________________________________

; Jetzt das Zeichen aus AX mit STOSB (StoreStringByte) in dem durch den

; Zeiger ES:DI addresssierten Variablenfeld speichern.

;______________________________________________________________________

                        es:stosb

                        jmp R1

 

;______________________________________________________________________

; Das Passwort mit dem Pufferinhalt Vergleichen,CMPSB (CompareStringByte)

; vergleicht zwei Variablenfelder Byte fuer Byte. das @ - Symbol signalisiert

; das Ende des Passworts, war der Vergleich negativ, wird das al - Register

; auf 0ff bzw. dezimal 255 gesetzt !

;______________________________________________________________________

compare:        mov si,offset passw

                        mov di,offset puffer

D1:                   es:cmpsb

                        je D1

                        dec si

                        es:lodsb

                        cmp al,'@'

                        jne bad

                        ret

bad:                mov al,0ff

                        ret

 

code               ends

                        end

 

So, das waren vorläufig genug Beispiele zur Verwendung eindimensionaler Variablenfelder, weiter geht’s dann in Teil II :“mehrdimensionale Variablenfelder“.

 

 

Variablenfelder/druckversion/hvviehof/30.09.2004

Ergänzt und geändert: 02.10.2004