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 ,N
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
; 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
; (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