Dakuan
Anmeldungsdatum: 2. November 2004
Beiträge: 6506
Wohnort: Hamburg
|
Ich habe da ein Problem mit einem C Programm, weshalb ich mich gerade mit gdb zergnüge, aber nicht so recht weiterkomme. Vorgeschichte: Ich experimentiere seit über einem Jahr, immer in sporadischen Schüben, an einem Morsedecoder. Ja, ich weiss, das es sowas schon gibt, aber ich möchte eine neue Vorgehensweise ausprobieren. Für das grundlegende Problem der Tonerkennung, habe ich mich allerdings von anderen Programmen inspirieren lassen (siehe Goertzel algorithm ). Um nicht vom Zufall abhängig zu sein und um mit reproduzierbaren Ausgangsdaten arbeiten zu können, arbeite ich momentan noch mit .wav Dateien als Input. Dabei habe ich jetzt das Phänomen, das allein das ändern eines printf() Befehls meine gesamten Statistikdaten verändert. Die beiden Varianten sind:
printf( " %d\n", pr_val );
und
printf( "\n" );
Das klingt natürlich völlig abwegig, weshalb ich nun doch nach langer Zeit wieder mal gdb installiert habe. Aber so richtig komme ich damit nicht weiter. Ich bräuchte etwas, womit ich nach jedem Schritt im der Hauptschleife (next) etwa 10 bis 20 Variablen überwachen kann. Unter Windows konnte ich in meiner Borland IDE fast beliebig viele zu überwachende Variablen angeben, die dann in einem gesonderten Fenster angezeigt wurden. Sowas hätte ich unter Linux auch gerne. Aus den Beschreibungen im Wiki konnte ich leider nicht erkennen ob irgendein Frontend sowas bietet und was ich dafür ggf. alles installiern muss. Kann mir da jemand hilfreiche Tips geben, wie ich da am besten rangehe?
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13213
|
Dakuan schrieb: Dabei habe ich jetzt das Phänomen, das allein das ändern eines printf() Befehls meine gesamten Statistikdaten verändert. Die beiden Varianten sind:
printf( " %d\n", pr_val );
und
printf( "\n" );
Das klingt natürlich völlig abwegig, weshalb ich nun doch nach langer Zeit wieder mal gdb installiert habe.
Das klingt für mich sehr danach, dass Du irgendwo Zeiger auf uninitialisierten Speicher verwendest. Möglicherweise helfen da auch schon die Compileroptionen "-ansi -Wall". Es soll auch Optimierungen geben, die unter bestimmten Umständen die Semantik von Programmen ändern...
Unter Windows konnte ich in meiner Borland IDE fast beliebig viele zu überwachende Variablen angeben, die dann in einem gesonderten Fenster angezeigt wurden. Sowas hätte ich unter Linux auch gerne. Aus den Beschreibungen im Wiki konnte ich leider nicht erkennen ob irgendein Frontend sowas bietet und was ich dafür ggf. alles installiern muss. Kann mir da jemand hilfreiche Tips geben, wie ich da am besten rangehe?
Ist schon ewig her, dass ich mit gdb arbeiten musste (meine Programme haben heutzutage keine Fehler mehr - im Ernst: ich programmiere so gut wie kein C/C++). Ich könnte mir vorstellen, dass das entweder mit emacs oder mit Eclipse geht. Ciao robert
|
Vain
Anmeldungsdatum: 12. April 2008
Beiträge: 2510
|
Servus, so völlig abwegig ist das nicht. Vielleicht hast du Glück und es ist ein einfacher Fehler: Wenn sich Daten verändern aufgrund eines Funktionsaufrufs, dann lagen die Daten in einem Bereich auf dem Stack, der durch den Funktionsaufruf an sich überschrieben wurde. Generell: Wenn solche „mysteriösen“ Sachen auftreten, dann benutzt du nicht mehr gültige oder nie gültig gewesene Speicherbereiche. Ein einfaches Beispiel, um das zu konstruieren: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 | #include <stdio.h>
char *somePointer(void)
{
/* (2) */
/* buf liegt auf dem Stackframe dieser Funktion. Nachdem die
* Funktion zuende ist, ist dieser Speicherbereich nicht mehr
* "sicher" und kann von weiteren Funktionsaufrufen überschrieben
* werden. Wir geben den Pointer auf diese Speicherstelle trotzdem
* zurück, um einen Fehler zu provozieren. */
char buf[] = "Hallo Welt!";
return buf;
}
int main(int argc, char *argv[])
{
char one = 0;
/* (1) */
/* Rufe die Funktion auf und kopiere dann das erste Zeichen am
* zurückgegebenen Pointer in eine weitere Variable. Das findet
* statt, ohne dass der Stack berührt wird, also klappt das noch. */
char *ptr = somePointer();
one = ptr[0];
/* Rufe jetzt printf auf, um one auszugeben. Dabei wird der Stack
* verändert! one ist nicht betroffen, also wird der ehemalige Wert
* noch richtig ausgegeben. Aber ... */
printf("%c\n", one); /* <-- innerhalb liegt (3) */
/* ... wenn wir jetzt nochmal nachschauen wollen, was an der
* Speicherstelle ptr[0] steht, dann kommt Murks raus. */
one = ptr[0];
printf("%c\n", one);
return 0;
}
|
Noch ein Bildchen dazu, dann erklärender Text:
: :
+-----+ | pri |
| buf | | ntf |
+-----+ +-----+ +-----+
| ptr | | ptr | | ptr |
| one | | one | | one |
+-----+ +-----+ +-----+
(1) (2) (3) Innerhalb von main (1) hast du ptr und one auf dem Stackframe der main-Funktion liegen. Dann betrittst du somePointer(), dabei entsteht ja wieder ein funktionslokaler Stackframe und auf dem liegt dann buf (2). Den Pointer darauf gibst du zurück. Bis jetzt ist alles gut. Für printf wird aber eben wieder ein Stackframe erzeugt und dadurch dein buf überschrieben (3). Ich würde zunächst das Programm unbedingt mit
gcc -Wall -Wextra ...
übersetzen, denn das zeigt dir sowas an.
Falls es das nicht ist, dann probieren wir’s mal mit gdb (übersetz’ dein Programm mit „-g “ als gcc-Flag). Gestehen muss ich, dass ich den gdb auch vergleichsweise selten zum Debuggen von C-Code verwende (sonst eher Assembler). Aber ein bisschen sollte das schon weiterhelfen:
break n setzt einen Breakpoint auf eine bestimmte Zeile. Im Beispiel unten habe ich bei Zeile 23 angehalten, also kurz vor der Zuweisung von ptr.
run startet das Ding und dann kannst du mit watch ptr Variablen beobachten.
s geht einen Schritt in deinem Quelltext weiter.
Ein einfaches Return wiederholt den vorigen Befehl.
Man sieht in dem Beispiel, dass bei Aufruf von printf der Speicher, auf den ptr zeigt, einfach mit „Müll” (eben dem Stackframe der printf-Funktion(en)) überschrieben wird. (gdb) break 23
Breakpoint 1 at 0x804842a: file bla.c, line 23.
(gdb) run
Starting program: /tmp/tmp/bla
Breakpoint 1, main (argc=1, argv=0xbffff1d4) at bla.c:23
23 char *ptr = somePointer();
(gdb) watch ptr
Hardware watchpoint 2: ptr
(gdb) watch *ptr
Hardware watchpoint 3: *ptr
(gdb) s
somePointer () at bla.c:11
11 char buf[] = "Hallo Welt!";
(gdb)
12 return buf;
(gdb)
13 }
(gdb)
Hardware watchpoint 2: ptr
Old value = 0x804848b "\201Å\022"
New value = 0xbffff0fc "Hallo Welt!"
Hardware watchpoint 3: *ptr
Old value = -127 '\201'
New value = 72 'H'
main (argc=1, argv=0xbffff1d4) at bla.c:24
24 one = ptr[0];
(gdb)
29 printf("%c\n", one); /* <-- innerhalb liegt (3) */
(gdb)
Hardware watchpoint 3: *ptr
Old value = 72 'H'
New value = -44 '\324'
0xb7ff28f2 in _dl_runtime_resolve () from /lib/ld-linux.so.2
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6506
Wohnort: Hamburg
|
Das klingt für mich sehr danach, dass Du irgendwo Zeiger auf uninitialisierten Speicher verwendest.
Das währen ja die üblichen Verdächtigen, die ich natürlich schon untersucht habe. Aber entdecken konnte ich dabei noch nichts. Nicht initialisierter Speicher ist es jedenfalls nicht, da der erste Wert des ersten Schleifendurchlaufts höchstwahrscheinlich richtig ist, da er bei allen Konsellationen immer gleich bleibt. Erst beim zweiten Durchlauf treten Differenzen auf.
so völlig abwegig ist das nicht. Vielleicht hast du Glück und es ist ein einfacher Fehler: Wenn sich Daten verändern aufgrund eines Funktionsaufrufs, dann lagen die Daten in einem Bereich auf dem Stack, der durch den Funktionsaufruf an sich überschrieben wurde.
Genau das ist meine Befürchtung, aber wie ich da rankomme, weiss ich noch nicht. Dein Beispiel werde ich aber erst morgen früh analysieren, wenn ich wieder ausgeschlafen bin.
Man sieht in dem Beispiel, dass bei Aufruf von printf der Speicher, auf den ptr zeigt, einfach mit „Müll” (eben dem Stackframe der printf-Funktion(en)) überschrieben wird.
Ob das in meinem Fall auch so ist, kann ich noch nicht erkennen. Mal sehen was mein ausgeschlafenes Auge morgen früh erkennt. Bis dann.
|
chilidude
Anmeldungsdatum: 18. Februar 2010
Beiträge: 867
|
Das Problem ist, dass es unter Linux nur Debugger gibt, die auf Grundlage von Symboltabellen arbeiten. Hat man keine oder bewegt sich abseits wird das Debuggen zu einer echten Qual. (Verglichen mit Windows-Debuggern ist es das aber auch so schon.) Wenn du keine Symboltabelle für die lokalen Variablen hast, dann kannst du sie dir mit dem GDB über "x/40xb $esp" anzeigen lassen. Ein weiterer symbolischer Debugger wäre der "Nemiver".
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13213
|
Dakuan schrieb: Das klingt für mich sehr danach, dass Du irgendwo Zeiger auf uninitialisierten Speicher verwendest.
Das währen ja die üblichen Verdächtigen, die ich natürlich schon untersucht habe. Aber entdecken konnte ich dabei noch nichts.
Kannst / willst Du den Code veröffentlichen? Manchmal hat man Glück und wenn ein zweiter oder dritter drauf schaut, findet er sofort etwas.
Nicht initialisierter Speicher ist es jedenfalls nicht, da der erste Wert des ersten Schleifendurchlaufts höchstwahrscheinlich richtig ist, da er bei allen Konsellationen immer gleich bleibt. Erst beim zweiten Durchlauf treten Differenzen auf.
Das hängt vom Datentyp ab: je kürzer er ist, desto größer ist die Wahrscheinlichkeit, dass das Bitmuster zufällig passt. ☺ Viel Erfolg! robert
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6506
Wohnort: Hamburg
|
So langsam drehe ich durch, aber der Reihe nach. Also "-Wextra" hat nicht viel gebracht. Hat nur meine WAV Funktionen angemeckert, weil eine Funktion einen Wert bekommt, den sie nicht braucht und weil einmal ein "int" mit "unsigned long" verglichen wird. Der Wertebereich ist 40 bis 250 (gelesene Samples). Aber einen anderen Fehler habe ich mit gdb entdeckt. Die gelesenen Sampledaten müssen "short int" statt "int" sein, sonst gehen bei der Umwandlung in "double" die Vorzeiche verloren. Ich wundere mich jetzt nur, wieso die Frequenzanalyse trotzdem brauchbare Werte geliefert hatte.
Kannst / willst Du den Code veröffentlichen?
Im Prinzip ja, aber nicht so wie er jetzt ist. Ich habe da einfach die die Funktionen für die WAV Bearbeitung aus einem anderen, ebenfalls noch nicht fertigen, Projekt hineinkopiert. Das Ergebnis sind 1500 Zeilen in mehreren Blöcken, die noch nicht zusammengewachsen sind. Ich habe dann angefangen etwas aufzuräumen, also hauptsächlich nur zusammengehörige Funktionen zusammenzubringen und auskommentierte Zeilen zu löchen. Funktionell hätte das eigentlich keine Auswirkungen haben sollen, aber der Ursprüngliche Fehler tritt nicht mehr auf. Davor hatte ich eigentlich die meiste Angst, weil nach meiner bisherigen Erfahrung solche schlafenden Fehler immer im unpassendsten Moment wieder erwachen. @rklm Falls du dennoch was zum lesen haben möchtest, kann ich dir mal die Sourcen und eine Beispieldatei zukommen lassen. Ich möchte nur nicht, dass Google noch nach Jahren den Leuten meine Fehler zeigt und das auch noch lustig findet.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13213
|
Dakuan schrieb: So langsam drehe ich durch, aber der Reihe nach.
Gut, dass Du wenigstens kontrolliert durchdrehst - immer schön der Reihe nach. 😉
Aber einen anderen Fehler habe ich mit gdb entdeckt. Die gelesenen Sampledaten müssen "short int" statt "int" sein, sonst gehen bei der Umwandlung in "double" die Vorzeiche verloren. Ich wundere mich jetzt nur, wieso die Frequenzanalyse trotzdem brauchbare Werte geliefert hatte.
Wat? 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | $ g++ -Wall x.cc && ./a.exe
s -5 double -5
s -4 double -4
s -3 double -3
s -2 double -2
s -1 double -1
s 0 double 0
s 1 double 1
s 2 double 2
s 3 double 3
s 4 double 4
i -5 double -5
i -4 double -4
i -3 double -3
i -2 double -2
i -1 double -1
i 0 double 0
i 1 double 1
i 2 double 2
i 3 double 3
i 4 double 4
|
(Test angehängt.)
Kannst / willst Du den Code veröffentlichen?
Ich habe dann angefangen etwas aufzuräumen, also hauptsächlich nur zusammengehörige Funktionen zusammenzubringen und auskommentierte Zeilen zu löchen. Funktionell hätte das eigentlich keine Auswirkungen haben sollen, aber der Ursprüngliche Fehler tritt nicht mehr auf. Davor hatte ich eigentlich die meiste Angst, weil nach meiner bisherigen Erfahrung solche schlafenden Fehler immer im unpassendsten Moment wieder erwachen.
Oha. Das klingt nicht gut.
@rklm Falls du dennoch was zum lesen haben möchtest, kann ich dir mal die Sourcen und eine Beispieldatei zukommen lassen. Ich möchte nur nicht, dass Google noch nach Jahren den Leuten meine Fehler zeigt und das auch noch lustig findet.
Du könntest es als verschlüsseltes Zip anhängen und dann das Passwort im Beitrag erwähnen. Das findet Google nie. Ciao robert
- x.cc (440 Bytes)
- Download x.cc
|
Vain
Anmeldungsdatum: 12. April 2008
Beiträge: 2510
|
rklm schrieb: Du könntest es als verschlüsseltes Zip anhängen und dann das Passwort im Beitrag erwähnen. Das findet Google nie.
Oder im Zweifelsfall das Passwort per PM verschicken. 😉
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6506
Wohnort: Hamburg
|
Oder im Zweifelsfall das Passwort per PM verschicken.
Dann kann ich aber auch die Dateien direkt verschicken oder noch besser, ich verschicke links auf eine Unterseite meiner Homepage. Mal sehen. Noch etwas zur Vorzeichenproblematik: Ich habe meine eigenen Funktionen zur Behandlung von WAV Dateien geschrieben, da kein mir bekanntes Linux Programm Broadcast-Wave Extensions und Cue-Points verarbeiten kann. Auch einige Windows Programme benötigen für die Cue Points oft spezielle Plugins, da die Hersteller von Hardware Recordern die RIFF Spezifikationen oft abenteuerlich interpretieren. Aber den Export eines einzelen Kanals hatte ich ursprünglich nicht vorgesehen. Wenn man das richtig machen will, müste man auch die Besonderheiten von 8, 16 und 24 Bit Formaten berücksichtigen. Deshalb habe ich es "auf die Schnelle" mit folgendem Hack realisiert:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | int
read_wav( int num_of_frames )
{
//int i_val;
short i_val;
int i = 0;
double * dvp = dval;
int chn = ((fmtCHUNK *)fmtbuf)->Channels; /* !!???!! */
int j;
while ( i++ < num_of_frames ) {
if ( fread( &i_val, 1, 2, WaveFile ) == 0 ){ /* chan 1 */
return 0;
}
*dvp++ = (double)i_val;
j = 1;
while ( j++ < chn ) {
fread( &i_val, 1, 2, WaveFile ); /* dummy read */
}
}
return i - 1;
}
|
dval ist das globale Array für die Eingangswerte des Goertzel Algotithmus. Aufgefallen ist mir der Fehler, weil dort statt kleiner negativer Werte plötzlich 67xxx Werte waren. Das es trotzdem funktioniert hat, hat möglicherweise mit irgendwelchen Aliaseffekten und der großen Amplitude zu tun. Da meine Testdateien meist mit Aufnahmepegeln von -9 bis -12 dBFS (plus Hintergrundrauschen), dürften kaum Samplewerte größer als +/-9000 auftreten. Ich habe übrigens gerade ddd installiert. Das ist zwar schon etwas näher an meiner alten Windows Welt, aber trotzdem noch gewöhnungsbedürftig. Schon nach 3 Sekunden fiel mir auf, das ddd kein UTF-8 beherrscht und das Mausrad "verkehrt herum" interpretiert wird. Ansonsten scheint das Programm OK zu sein.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13213
|
Dakuan schrieb: Oder im Zweifelsfall das Passwort per PM verschicken.
Noch etwas zur Vorzeichenproblematik: Ich habe meine eigenen Funktionen zur Behandlung von WAV Dateien geschrieben, da kein mir bekanntes Linux Programm Broadcast-Wave Extensions und Cue-Points verarbeiten kann. Auch einige Windows Programme benötigen für die Cue Points oft spezielle Plugins, da die Hersteller von Hardware Recordern die RIFF Spezifikationen oft abenteuerlich interpretieren. Aber den Export eines einzelen Kanals hatte ich ursprünglich nicht vorgesehen. Wenn man das richtig machen will, müste man auch die Besonderheiten von 8, 16 und 24 Bit Formaten berücksichtigen. Deshalb habe ich es "auf die Schnelle" mit folgendem Hack realisiert:
<snip> M.E. wäre die Leseoperation z.B. so deutlich korrekter und robuster: | fread( &i_val, sizeof(i_val), 1, WaveFile )
|
Das erklärt auch, warum Du Probleme mit i_val als int gehabt haben könntest: Du liest ja immer nur zwei Bytes des int - der "Rest" könnte durchaus merkwürdige Werte annehmen (obwohl das wg. der Initialisierung mit 0 eigentlich nicht zu erwarten wäre). Ich würde mich auch nicht auf die Endianness der Plattform verlassen, sondern die Bytes in unsigned char[] lesen und dann explizit an die passende Stelle kopieren. | fread( &bytes, 1, 2, WaveFile );
*dvp++ = (double) ((((unsiged int) bytes[1]) << 8) | ((unsigned int) bytes[0]));
|
Bzw. 1 und 0 vertauscht - je nach Definition des Formats. Ciao robert
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6506
Wohnort: Hamburg
|
Du liest ja immer nur zwei Bytes ...
Das ist kein Zufall , sondern ergibt sich aus dem Dateiformat (16 Bit LE). Ich erwähnte das ja bereits kurz. Und weil ich mögliche andere Formate in diesem Fall nicht abteste, hatte ich das ja auch als Hack bezeichnet. Ich verwende eigentlich nur Formate mit 16 und 24 Bit mit 1, 2 oder 4 Kanälen und Abtastraten bis 192 kBit/s (eigentlich nur bis 48kBit/s, der Rest ist Theorie). Ich benutze die WAV Dateien für dieses Projekt ja auch nur als Übergangslösung. Sobald ich die, für mich, optimale Decoderstrategie gefunden habe, möchte ich natürlich direkt mit der Soundkarte arbeiten und dann kann der ganze WAV Overhead raus.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13213
|
Dakuan schrieb: Du liest ja immer nur zwei Bytes ...
Das ist kein Zufall ,
Das hatte ich auch nicht angenommen. Aber zwei Bytes ist halt weniger als sizeof(int).
Ich benutze die WAV Dateien für dieses Projekt ja auch nur als Übergangslösung. Sobald ich die, für mich, optimale Decoderstrategie gefunden habe, möchte ich natürlich direkt mit der Soundkarte arbeiten und dann kann der ganze WAV Overhead raus.
Ja, aber bis dahin rätselst Du, was falsch ist. 😈 Ciao robert
|
Vain
Anmeldungsdatum: 12. April 2008
Beiträge: 2510
|
Dakuan schrieb: Aufgefallen ist mir der Fehler, weil dort statt kleiner negativer Werte plötzlich 67xxx Werte waren.
Ah! Okay, das liegt aber nicht am Cast nach Double. Du liest halt nur zwei Bytes ein und ein Integer hat vier Bytes – das ist dir bestimmt auch klar. Aber machen kann man das in der Form, wie du es da hast, nicht einfach so. Dem Integer „fehlen“ dann zwei Bytes an Information, um den negativen Wert korrekt abzubilden. Anders ausgedrückt: -480 als Short Integer ist im Speicher 0x20 0xFE. -480 als Integer ist im Speicher aber 0x20 0xFE 0xFF 0xFF. Da du deinen Integer mit 0 initialisiert hast, haben die zwei fetten Bytes den falschen Wert. Demo: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 | #include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
/* siWert ist bei dir das, was in der Datei steht. */
short int siWert = -480;
/* iWert fängt mit 0 an, wie bei dir. */
int iWert = 0;
/* "Brauchen" wir später. Naja, nicht zwingend notwendig, macht den
* Code aber besser lesbar. */
char *ptr;
/* Kopiere die zwei Byte von siWert nach iWert. Das entspricht
* deiner fread()-Operation. */
memmove(&iWert, &siWert, 2);
/* Zu erwarten: Wie bei dir, siWert korrekt, aber iWert hat
* Vorzeichen "verloren". */
printf("siWert: %hd\n", siWert);
printf("iWert : %d\n", iWert);
/* Hexdumps der beiden Variablen. */
ptr = (char*)&siWert;
printf("hexdump siWert: %hhX %hhX\n", ptr[0], ptr[1]);
ptr = (char*)&iWert;
printf("hexdump iWert : %hhX %hhX %hhX %hhX\n",
ptr[0], ptr[1], ptr[2], ptr[3]);
printf("\n");
/* Wie müsste der Hexdump von iWert korrekterweise aussehen? */
iWert = -480;
printf("hexdump iWert, zu erwarten: %hhX %hhX %hhX %hhX\n",
ptr[0], ptr[1], ptr[2], ptr[3]);
return 0;
}
|
Dein fread() bzw. mein memmove() fasst wirklich nur die beiden Bytes an. Es liest nicht den Wert als Short Integer ein und führt dann einen Cast in einen Integer aus.
Bei rklms Vorschlag fehlt übrigens außen noch ein Cast, sonst interpretiert er einfach nur die beiden Bytes als Double, was du ja auch nicht willst:
| double d = (double) ((int)((((unsigned int) ptr[1]) << 8) | ((unsigned int) ptr[0])));
|
Ich bin mir gerade nicht sicher, ob es da nicht irgendwo Hilfsfunktionen à la „host to network“ gibt.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13213
|
Vain schrieb: Dakuan schrieb: Aufgefallen ist mir der Fehler, weil dort statt kleiner negativer Werte plötzlich 67xxx Werte waren.
Okay, das liegt aber nicht am Cast nach Double. Du liest halt nur zwei Bytes ein und ein Integer hat vier Bytes – das ist dir bestimmt auch klar. Aber machen kann man das in der Form, wie du es da hast, nicht einfach so. Dem Integer „fehlen“ dann zwei Bytes an Information, um den negativen Wert korrekt abzubilden.
Genau mein Punkt.
Bei rklms Vorschlag fehlt übrigens außen noch ein Cast, sonst interpretiert er einfach nur die beiden Bytes als Double, was du ja auch nicht willst:
| double d = (double) ((int)((((unsigned int) ptr[1]) << 8) | ((unsigned int) ptr[0])));
|
Ich bin mir gerade nicht sicher, ob es da nicht irgendwo Hilfsfunktionen à la „host to network“ gibt.
Ja, ich hatte das falsch herum, als ich das schrieb: das sollten natürlich nicht "unsigned int" sondern "int" sein. Dein Cast löst es aber m.E. nicht, sondern man muss dann die oberen Bits auf 1 setzen. Also ungefähr so: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | #include <iostream>
#include <iomanip>
using namespace std;
int main(int argc, char* argv[]) {
// simulate two bytes input
for ( unsigned int input = 0x0000; input < 0x10000; ++input ) {
cout << setfill('0') << hex << "byte 0: 0x" << setw(2) << (input & 0xFF) << " byte 1: 0x" << setw(2) << ((input >> 8) & 0xFF);
int v = input;
if ((v & 0x80) != 0) {
// high bit set => set all upper bits
v |= -1 ^ 0xFF;
}
cout << setfill(' ') << dec << " value: " << setw(7) << input << " regular converted value: " << setw(7) << v << endl;
}
return 0;
}
|
Ciao robert
|