Seite 2 von 2

Verfasst: 09.04.2008 22:12
von Scarabol
Hi Leute,

kann sich den auch nach einem AllocateMemory() noch was in dem Speicherbereich finden?

Noch ne Frage:
Wenn man Netzwerkfunktionen verwendet, brauch man ja zwangsläufig einen Empfangsbuffer, nur wie leert man den am Besten oder muss man den vielleicht gar nicht leeren?

Gruß
Scarabol

Verfasst: 09.04.2008 22:46
von NicTheQuick
'AllocateMemory()' füllt den allokierten Speicher automatisch mit Nullen. Man
kann aber mittels 'globalalloc_()' auch einen nicht nullgesetzten
Speicherbereich allokieren und darin evtl. noch Informationen von anderen
Programmen finden. Und ich denke, darum geht es hier gerade. Dass man
den Speicher auf Null setzt, bevor man wieder 'FreeMemory()' aufruft.

Kann mich aber auch irren.

Verfasst: 09.04.2008 22:51
von AND51
> oder muss man den vielleicht gar nicht leeren
Es ist nicht notwendig, den Speicher zu leeren. Dazu muss man wissen, dass PureBasic das Ende von Strings im Speicher mit einer 0 markiert (Chr(0)).

Experiment:
Allokiere 10 Bytes, Poke 9 Zeichen hinein. PokeS() von PureBasic hängt automatisch eine 0 an, die 10 allokierte Bytes werden also vollständig ausgenutzt. Dann Poke 5 Zeichen. Das Poken von 5 Zeichen bewirkt, dass 6 Zeichen geschrieben werden (weil...? Ja genau! PB's null!)

Das sieht so aus:

Code: Alles auswählen

*buffer=AllocateMemory(10)
PokeS(*buffer, "ZYXWVUTSR")
PokeS(*bufer, "abcef")

Debug PeekS(*buffer)
Debug PeekS(*buffer+6)
Soll also nun ein gepoketer Text gelesen werden, geschieht das mit PeekS(). Der Knackpunkt: Es werden nur alle Daten bis zum ersten auftretenden 0-Byte gelesen!
Zur Erinnerung: In unserem *buffer befinden sich 2 nullen. Eine an Stelle 10 von dem ersten PokeS. Die zweite null befindet sich ja an Stelle 6.
PeekS() liest also korrekt nur bis zur Stelle 6, und das, obwohl noch "Müll" von einem älteren PokeS im Speicher steckt.

Diesen Müll kann man nachweisen, indem man zur Speicheradresse den Offset von 6 dazu addiert. Das bewirkt, dass PeekS() erst an der 7. Stelle anfängt zu lesen und zwar wie gehabt bis zum nächsten Null-Byte. In diesem Fall allerdings wäre das nächste Null-Byte an 10. Stelle, denn es stammt ja noch vom 1. Poke-Versuch.

Fazit: Auch bei Netzwerkbuffern benötigt man kein ClearMemory. Denn ReceiveNetworkData() tut nichts anderes, als Poken. Es benutzt (vermute ich) auch PokeS() intern, was ein automatisches Anhängen eines 0-Bytes bewirkt. Liest du dann die Daten später noch mit PeekS(), läuft alles korrekt ab (siehe oben).
Und selbst wenn es Probleme gibt: ReceiveNetworkData() gibt die Anzahl an empfangenen Bytes zurück, diese Angabe kann man im Bedarfsfall auch noch benutzen.

Fazit: Auch beim normalen Poken+Peeken benötigt man nicht unbedingt ein ClearMemory.

Verfasst: 09.04.2008 23:05
von ZeHa
Denn ReceiveNetworkData() tut nichts anderes, als Poken. Es benutzt (vermute ich) auch PokeS() intern, was ein automatisches Anhängen eines 0-Bytes bewirkt.
Ähem... also ich will jetzt nicht behaupten, daß ich alles zu 100% weiß, aber ich weiß wenigstens zu 100%, daß das, was Du da gerade geschrieben hast, völliger Quatsch ist :mrgreen:

Wie würdest Du sonst jemals eine 0 übers Netzwerk schicken können? Das läuft anders, es werden sog. Netzwerk-Pakete empfangen, die ihrerseits im Header eine Information darüber besitzen, wie lang das ist. D.h. es wird erstmal diese Längenangabe ausgelesen, und danach wird einfach genau so viel aus dem Speicher gelesen, wie dort drinsteht. Auch wenn zwischendrin mal eine 0 vorkommt, das ist in dem Fall egal, Du kannst übers Netzwerk jedes beliebige Byte schicken, die Daten werden einfach immer entsprechend in einen sog. Data Frame verpackt (bzw. in mehrere, aber das ist wieder ein anderes Thema und auch nicht so wichtig, wichtig ist nur daß da nicht mit Null-Bytes gearbeitet wird. Das wird nur bei Strings so gemacht weil dort die 0 nicht benötigt wird bzw. eben genau als solches "Trennzeichen" definiert wurde (siehe ASCII-Tabelle)).

Verfasst: 09.04.2008 23:17
von NicTheQuick
PureBasic nutzt ja auch nur deswegen die Nullterminierung, weil Windows es
nicht anders macht. Turbo Pascal und alle Nachfolger bis Delphi 2007 kennen
zwar auch nullterminierte String, machen es aber üblicherweise so, dass vor
dem String steht wie lange er ist. Dadurch kann man auch Nullbytes in den
String schreiben. Aber zur Kommunikation mit der Win-API muss auch Delphi
mit nullterminierten Strings arbeiten.

Verfasst: 09.04.2008 23:21
von AND51
ZeHa hat geschrieben:
Denn ReceiveNetworkData() tut nichts anderes, als Poken. Es benutzt (vermute ich) auch PokeS() intern, was ein automatisches Anhängen eines 0-Bytes bewirkt.
Ähem... also ich will jetzt nicht behaupten, daß ich alles zu 100% weiß, aber ich weiß wenigstens zu 100%, daß das, was Du da gerade geschrieben hast, völliger Quatsch ist :mrgreen:

Wie würdest Du sonst jemals eine 0 übers Netzwerk schicken können?
Das ist wahr. Das habe ich mich beim Posten auch gefragt, dann aber vergessen, darauf einzugehen.

Deshalb habe ich ja auch geschrieben, dass man im Notfall immernoch die Längenangabe in Bytes zur Hilfe nehmen kann, die ReceiveNetworkData() zurückgibt.

Verfasst: 09.04.2008 23:22
von ZeHa
weil Windows es nicht anders macht.
Ich würd mal sagen, das liegt nicht an Windows, sondern eher an C ;)

Verfasst: 10.04.2008 19:41
von Thorium
LCD hat geschrieben:Habe ein Benchmark durchgeführt, und es gibt so gut wie keinen Unterschied zu der WinAPI Funktion ZeroMemory_(*Memory,Length), was mich etwas irritiert. Normalerweise müsste Assembler etwas schneller sein.
Wieso muss Assembler etwas schneller sein?
Im Grunde sind die Win-API auch nur Assemblercode.
Der Vorteil ist halt das man mit dem Code nicht auf die API angewiesen ist.

Das ist der Assemblercode der RtlZeroMemory Funktion von Windows XP:

Code: Alles auswählen

7C91311B   PUSH EDI
7C91311C   MOV EDI,DWORD PTR SS:[ESP+8]
7C913120   MOV ECX,DWORD PTR SS:[ESP+C]
7C913124   XOR EAX,EAX
7C913126   CLD
7C913127   MOV EDX,ECX
7C913129   AND EDX,3
7C91312C   SHR ECX,2
7C91312F   REP STOS DWORD PTR ES:[EDI]
7C913131   OR ECX,EDX
7C913133   JNZ SHORT ntdll.7C913139
7C913135   POP EDI
7C913136   RETN 8
7C913139   REP STOS BYTE PTR ES:[EDI]
7C91313B   POP EDI
7C91313C   RETN 8
Wie zu sehen ist, ist der zeitkritische Teil der gleiche:

Code: Alles auswählen

7C91312F   REP STOS DWORD PTR ES:[EDI]
Also ist klar das es keinen großen Unterschied in der Geschwindigkeit gibt.