Actually there are no pointers in Basic like in C, but maybe they can be imitated. I've tried this now with VarPtr, Peek and Poke, and I didn't find any error in my exercise to achieve this. I don't see any at the moment either. Access to the memory address is basically like in C, and I can also change the content.
Pointers are a powerful, but also dangerous, tool in C! One could definitely good use them in Basic too. I think so.
I would be grateful to anyone who is interested and takes a look at the program if they could point out whether and if so, where I made a mistake in my thinking.
The explanations/comments are of course in German, so I can understand what's going on.
Code: (Select All)
'Zeigerbeispiel in Basic - 16. Juli 2023
'Mit VarPtr und Peek und Poke ist es moeglich Zeiger in
'Basic nachzuahmen.
$Console:Only
Option _Explicit
Dim As Long zahl1, zahl2, wert, wert2
Dim As Long speicherAdresse, speicherAdresse2
Locate 2, 3
Input "Zahl 1: ", zahl1
Locate 3, 3
Input "Zahl 2: ", zahl2
Locate 5, 3
Print Using "Zeige Zahl 1: ### -- Zahl 2: ### "; zahl1, zahl2
'Adresse der Zahl im Speicher ermitteln
speicherAdresse = VarPtr(zahl1)
'Inhalt der Speicheradresse
Locate 11, 3
Print "Inhalt der 2ten Speicheradresse: ", wert2
Locate 12, 3
Print "Jetzt auf die Adresse von Zahl 2 zugreifen, um den Inhalt zu aendern."
'Der 2ten Variablen den Wert von wert2 von der
'ersten Speicheradresse zuweisen
Locate 14, 3
wert2 = Peek(speicherAdresse)
Print "2te Variable hat jetzt den selben Wert wie Zahl 1: ", wert2
07-17-2023, 12:05 AM (This post was last modified: 07-17-2023, 12:07 AM by mnrvovrfc.)
As bplus already elucidated, what I call the _MEM gang. But it's somewhat clunky to some people used to how code looks in C. They prefer Freebasic because those developers desired as close association to C/C++ as possible. It explains the "ZString" type and the folly with using fixed-length strings in UDT's and working with files.
QB64 allows the programmer to do pointer-heavy stuff in C if he/she wanted to. Just include stuff in "dot-H" files and have a matching "DECLARE LIBRARY... END DECLARE" to have subprogram and function prototypes. This is actually done extensively in QB64 itself to expand the language.
Show Content
SpoilerDo not look at a lot of Purebasic code, especially that targeted for Win32 API because it's confusing. They talk sometimes about using an integer type (declared as "something.i") to sometimes substitute for pointer. This was because the language used to be not sophisticated enough to handle pointer variables per se and there needed to be a way to give an address to point to a variable or function or something. The thing wrong with that is that most "pointers" in Win32 API were 32-bit for a 32-bit operating system, and therefore it was easy to assume the "dot-i" which is for 32-bit integer. Pointers should never have types in any language, it depends always on the CPU architecture.
Another thing to get confused about is the _OFFSET type in QB64. But that is actually a go-between so the programmer could call stuff from Win API and other places. _OFFSET is actually the "void" pointer type in C.
Here is very close to what is called "PEEK" and "POKE" in Purebasic: just simple memory buffers to store stuff into. But this could be done about as easily with the _MEM statements.
' 32Bit / 64Bit Speicher (Der von Windows)
' Reine 32/64 Bit Adressierung
' 32/64 Bit Pointer
' Funktioniert fuer Werte mit bis zu 64Bit (Integer64)
DIM SEGBlock AS _MEM
DIM wert AS _UNSIGNED LONG
DIM wert2 AS _UNSIGNED LONG
wert = 4294967295
' Speicherblock der Variable ermitteln
SEGBlock = _MEM(wert)
' Aendern des Wertes im Speicher um -5
_MEMPUT SEGBlock, SEGBlock.OFFSET, _MEMGET(SEGBlock, SEGBlock.OFFSET, LONG) - 5 AS LONG
' Wert aus dem Speicher holen
wert2 = _MEMGET(SEGBlock, SEGBlock.OFFSET, LONG)
PRINT "Speicher Start Adresse :"; SEGBlock.OFFSET
PRINT "Groesse des Blocks :"; SEGBlock.SIZE
PRINT "Typ des Blocks :"; SEGBlock.TYPE
PRINT "Datentyp des Blocks :"; SEGBlock.ELEMENTSIZE
PRINT "Wert der Speicher Adresse:"; wert2
' Speicherblock freigeben
_MEMFREE SEGBlock
Old Method:
Code: (Select All)
' 16 Bit Speicher (Wird emuliert von QB64)
' Es handelt sich hierbei um DOS Adressierungen
' 16 Bit Pointer (Segment Adressierung im Speicher)
' Funktioniert nur fuer 1 Byte Werte 0 - 255
DIM SpeicherAdresse AS LONG
DIM wert AS LONG
wert = 65535
DEF SEG = VARSEG(SpeicherAdresse)
' Bei groesseren Variablen muss die Zahl zerlegt werden.
' Zerlegung der Variable in Bytes, die dann in den Speicher geschrieben werden
POKE SpeicherAdresse + 0, (wert AND &HFF00&) / &H100&
POKE SpeicherAdresse + 1, (wert AND &HFF&)
' Aendern des Wertes im Speicher um -5
POKE SpeicherAdresse + 1, PEEK(SpeicherAdresse + 1) - 5
' Zusammensetzung des Integer Wertes aus dem Speicher
wert2 = PEEK(SpeicherAdresse + 0) * &H100& + PEEK(SpeicherAdresse + 1)
I concur with what everyone else has stated here. Use the _MEM set of commands. They operate very similar to binary files and are a better protected (i.e. less dangerous) way of doing PEEKs & POKEs. There's essentially no chance of "going off the reservation". They can be a bit fussy to use, but worth the effort. Check out Steve's video tutorials on the subject, if you haven't already, that's where I learned them.
@SagraS - Thanks for the example and the description in German.
I tried a few things with the example to understand the whole thing. Well, it comes so slowly, understanding. The thing about _MEM is new for me in Basic.
The number in SEGBlock.TYPE obviously depends on the variable declaration, because it changes depending on the variable type. Is there a table showing what each number means?
What still surprises me is that with a 2nd number, the memory address increases by 8 bytes, but SEGBlock.SIZE and SEGBlock.ELEMENTSIZE only show 4 bytes (4 + 4?).
Code: (Select All)
'SagraS, Beispiel fuer die Nutzung von _MEM - 16. Juli 2023
' 32Bit / 64Bit Speicher (Der von Windows)
' Reine 32/64 Bit Adressierung
' 32/64 Bit Pointer
' Funktioniert fuer Werte mit bis zu 64Bit (Integer64)
Option _Explicit
Dim SEGBlock As _MEM
Dim As _Unsigned Long wert, wert1, wert2
Here a new Code Example:
variable wert2_I on line 62 is a test value for a example in the text (line 8 - 46)
Code: (Select All)
'SagraS, Beispiel fuer die Nutzung von _MEM - 16. Juli 2023
' 32Bit / 64Bit Speicher (Der von Windows)
' Reine 32/64 Bit Adressierung
' 32/64 Bit Pointer
' Funktioniert fuer Werte mit bis zu 64Bit (Integer64)
'*****************************************
' Das hier ist der Datentyp den _MEM erzeugt
' IMAGE und SOUND wird erst mit dem jeweiligen _MEMIMAGE oder _MEMSOUND Befehl
' aufgerufen. So wuerde sich der Datentyp sogar vergroessern
' Also hast du die ersten 4 Datentypen des Typs memory_type fuer _MEM
'
' TYPE memory_type
' OFFSET AS _OFFSET 'start location of block(changes with byte position)
' SIZE AS _OFFSET 'size of block remaining at offset(changes with position)
' TYPE AS _OFFSET 'type description of variable used(never changes)
' ELEMENTSIZE AS _OFFSET 'byte size of values inside the block(never changes)
' -----------------------
' IMAGE AS LONG 'the image handle used when _MEMIMAGE(handle) is used
' SOUND AS LONG 'the sound handle used when _MEMSOUND(handle) is used
' END TYPE
'
' Jede Position im Speicher egal um welchen Datentyp es sich handelt betraegt
' 8 Bytes, da die Funktion von _MEM fuer 64Bit ausgelegt ist.
' Du kannst dich auch nur innerhalb des jeweiligen Speicher Segmentes bewegen
' In diesem Falle die 8 Bytes
' Bedeutet:
' Um Fehler vorzubeugen, weil du einen Wert mit Integer reingeschrieben hast,
' kannst du diesen Wert an selber Adresse mit einem Integer64 Datentyp auslesen
' lassen, ohne den Wert darin zu gefaehrden.
'
' Darum ist _MEM auch sehr sicher und man kann nix ausserhalb seines Programmes
' kaputt machen. z.B. Windows eigene Speicher Adressen, so das dein Rechner z.B.
' einfach runter faehrt.
'
' Gibst du eine INTEGER64 Variable in den Speicher mit _MEM, so kannst
' du die ersten 4, 2 oder 1 Byte mit long, integer oder einem Byte Datentyp
' von dieser Zahl auslesen
' z.B. &FEDC BA98 7654 3210 <- Integer64
' Jetzt kannst du mit _MEMGET einen LONG Wert daraus auslesen
' und die Offset Position darin verschieben.
' z.B. willst du BA98 7654 auslesen
' Dann geht das so:
' wert2_O = _MEMGET(SEGBlock2, SEGBlock2.OFFSET + 2, LONG)
OPTION _EXPLICIT
DIM SEGBlock1 AS _MEM
DIM SEGBlock2 AS _MEM
DIM AS _UNSIGNED _INTEGER64 wert1_I, wert2_I
DIM AS _UNSIGNED LONG wert1_O, wert2_O
DIM AS _OFFSET position1, position2
'Entspricht der Startadresse
' *** Dies ist unnoetig fuer _MEM ***
position1 = _OFFSET(wert1_I)
position2 = _OFFSET(wert2_I)
' Aendern des Wertes im Speicher um +5
' *** Mit _MEMPUT kannst du an einer Offsetadresse ein Wert eintragen ***
' Um den Wert zu aendern der bereits drin steht, holt man ihn mit _MEMGET raus und manupuliert ihn
'_MEMPUT SEGBlock1, SEGBlock1.OFFSET, _MEMGET(SEGBlock1, SEGBlock1.OFFSET, _OFFSET) + 5 AS _OFFSET