PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C++] Spaß mit Enums ("Spaß" == "Problem")



Jesus_666
19.01.2004, 23:03
Ich habe ein kleines Problem. Ich habe Code, bei dem ich eine Kombination von Werten angeben muß. Soweit kein Problem, läßt sich mit Zweierpotenzen und Addition wunderbar erledigen.
Damit das Ganze übersichtlicher aussieht habe ich mich entschlossen, enums zu verwenden.
Ein paar Deklarationen aus meinem Code:

enum { S_None = 0, S_Barrel = 1, S_Top = 2, S_Under = 4, S_Grip = 8, S_Stock = 16 }; //Spaces
int enmSpaces;

Etwas weiter im Code setze ich dann einen Wert und gebe ihn aus:

enmSpaces = S_Barrel|S_Top|S_Grip;

char * acharTempChar;
acharTempChar = (char*) malloc(256);
if(acharTempChar!=NULL)
{
sprintf(acharTempChar, "%i", enmSpaces);
outG_Debug->value(acharTempChar);

free(acharTempChar);
}

Ich sollte 1|2|8 = 11 zurückbekommen.
Was ich zurückbekomme ist irgendein zehnstelliger Wert, der mit nichts irgendwas zu tun hat.
Interessanterweise bekomme ich, wenn ich mehrere Variablen gleich definiere, unterschiedliche Werte zurück. Das riecht für mich stark nach uninitialisiertem Speicher, allerdings ändert sich nichts am Ergebnis, wenn ich enmSpaces malloc()e.

Ich weiß mit 100%iger Sicherheit, daß es an dem Code ab char * nicht liegen kann. Das ist normaler Standardcode, wie ich ihn bei FLTK in bestimmten Situationen brauche und ich bekomme bei anderen Werten eine korrekte Ausgabe.

Hat irgendwer eine Ahnung, warum meine Enums Amok laufen? Sollte ich den ganzen Ramsch einfach als einzelne const-Variablen definieren?

Oh, noch was... Ich habe gerade gemerkt, daß der Fehler bei ALLEN enumerierten Werten auftritt, nicht nur bei denen, die ich mit | verbinde.

MuadDib
20.01.2004, 00:46
#include <stdio.h>

enum { S_None = 0, S_Barrel = 1, S_Top = 2, S_Under = 4, S_Grip = 8, S_Stock = 16 }; //Spaces
int enmSpaces;

int main(void)
{
enmSpaces = S_Barrel|S_Top|S_Grip;
printf("%i", enmSpaces);
}


Wenn ich dieses Codestück übersetze und ausführe kommt 11 raus. Deine Enums laufen also. Wenn du deine Zahl in einen String umwandeln möchtest (oder was hast du mit sprintf vor?), würde ich itoa(char* dest, int src, int radix) verwenden. Oder versuch einfach, &acharTempChar als Parameter zu verwenden. Ansonsten, schreib welchen Compiler du verwendest und gib mir den Konsolenoutput, viell. kann ich da mehr rauslesen

Jesus_666
20.01.2004, 01:48
itoa() wäre eine Idee. Nicht per enum definierte Integerwerte werden übrigens mit dem derzeitigen Code korrekt angezeigt.

Oh, und exit(enmSpaces); liefert den korrekten Wert.



Der Compiler ist gcc version 3.3.1 (cygming special).

Output:
gcc -I/code/fltk-1.1.4/bin/include -mwindows -DWIN32 -mno-cygwin -o ShadowStuff3 ShadowStuff3.cxx -mwindows /code/fltk-1.1.4/bin/lib/libfltk.a -lole32 -luuid -lcomctl32 -lwsock32 -lsupc++

ShadowStuff3.cxx: In function `char* damageEffects(int, int)':
ShadowStuff3.cxx:129: warning: address of local variable `acharBuffer' returned

damageEffects() hat nichts mit dem Programmteil zu tun, in dem der Fehler auftritt und funktioniert super.


Ich probier's mal mit itoa().

PS: Klappt. Stellt sich nur noch die Frage, warum sprintf() nicht mit enums klarkommt...

Ineluki
20.01.2004, 06:01
wich ich Jeez schon per ICQ mitteile hab ich eine vermutung, woran das liegen koennte ... vielleicht interessierts den einen oder anderen ja cuh noch

Die enums belegen wahrscheinlich nur so viel speicher, wie sie grade brauchen ... als in diesem codebeispiel 8 bit

sprintf erwartet aber einen integer ... mit int in der regel als 32 bit ...

somit ist dein enum fuer einen int wert 3 byte zu klein und der rest zeigt in der tat auf uninitialisierten speicher .. in dem fall bringt aber das malloc()en auch nixm, da du ja nur das eine byte mallocst

Gruss Ineluki

Jesus_666
20.01.2004, 20:10
Und das ist es auch.
Ich habe noch mal in den Stroustrup gesehen und dabei die Erklärung für das Verhalten gefunden (und wie ich mir dachte hat uninitialisierter Speicher eine Rolle gespielt). Ich zitiere:

Der Bereich einer Aufzählung umfaßt alle seine Enumeratoren aufgerundet auf die nächstgrößere Zweierpotenz minus eins. Der Bereich geht herunter bis null, falls der kleinste Enumerator nicht negativ ist, und bis zur nächstkleineren Zweierpotenz plus eins sonst. Dadurch wird das kleinste Bitfeld, das die Werte der Enumeratoren speichern kann, definiert.
Aus: "Die C++-Programmiersprache" von Bjarne Stroustrup, 3. AuflageDas wirkt erst mal ganz harmlos, bis man merkt: Dadurch wird auch definiert, wie viel Speicher für den Wert reserviert wird. Für die enums wurde bei mir grundsätzlich je 1 Byte reserviert, der Rest des Speichers, auf den sprintf() zugriff, war uninitialisiert.
Wenn enmSpaces gemalloc()t gewesen wäre, hätte ich auch eine korrekte Ausgabe bekommen müssen.

Merke: Wenn man mit enums arbeitet vorher alle Variablen, die mit ihnen in Kontakt kommen, initialisieren oder Funktionen verwenden, die sie in korrekte Integer umwandeln (oder wie solche behandeln).

MuadDib
21.01.2004, 00:33
Das ergibt für mich aber keinen Sinn. Immerhin legst du ja mit 'int enmSpaces' ja genug Speicher für eine int-Variable an, und mit 'enmSpaces = S_Barrel|S_Top|S_Grip;' machst du einen impliziten Typecast auf Integer

Ausserdem ergibt dieser Code


#include <stdio.h>

enum { S_None = 0, S_Barrel = 1, S_Top = 2, S_Under = 4, S_Grip = 8, S_Stock = 16 }; //Spaces
int enmSpaces;

int main(void)
{
char * acharTempChar;
acharTempChar = (char*) malloc(256);
if(acharTempChar!=NULL)
{
sprintf(acharTempChar, "%i", enmSpaces);
printf("%s",acharTempChar);
free(acharTempChar);
}
}
ebenfalls 11. Zumindest, wenn ich es mit mingw übersetze.

Jesus_666
21.01.2004, 02:19
Daß ich einen int deklariere bedeutet nicht, daß auch der Speicher an der Stelle vorbereitet wird; sonst wäre malloc() überflüssig. Ich lege einfach fest, in welchen Speicherbereich ich was schreiben will.
Wenn ich jetzt etwas wie int i = 5; mache führt C++ automatisch die richtige Operation (zero-fill) durch, damit aus dem Speicherbereich später auch ein Wert der Länge sizeof(int) mit dem Inhalt 5 gelesen werden kann.
Wenn ich aber einen int mit dem Inhalt einer Enumeration fülle scheinen aber aus irgendeinem Grund nur so viele Bits geschrieben zu werden wie die Enumeration erfordert. itoa() und printf() scheinen das zu verstehen und den richtigen Wert zu extrahieren; sprintf() hingegen nicht.
Anders kann ich es mir nicht vorstellen; FLTK ist eigentlich in der Lage, einen String zu handhaben.

MuadDib
21.01.2004, 05:21
Original geschrieben von Jesus_666
Daß ich einen int deklariere bedeutet nicht, daß auch der Speicher an der Stelle vorbereitet wird; sonst wäre malloc() überflüssig. Ich lege einfach fest, in welchen Speicherbereich ich was schreiben will.

malloc wird auch nur verwendet, wenn du Speicher für ein struct oder ein Array anlegen willst (z.B: string = char-Array), wenn du einfach einen int deklarierst, wird genau ein int auch reserviert (angelegt natürlich bei initialisierung), oder hast du jemals malloc für i = 5 verwendet?
Der Fehler liegt imo beim Compiler, denn dieser Code:


#include <stdio.h>
#include <stdlib.h>

enum { S_None = 0, S_Barrel = 1, S_Top = 2, S_Under = 4, S_Grip = 8, S_Stock = 16 }; //Spaces
int enmSpaces;

int main(void)
{
enmSpaces = S_Barrel | S_Grip | S_Top;
char * acharTempChar;
acharTempChar = (char*) malloc(256);
if(acharTempChar!=NULL)
{
sprintf(acharTempChar, "%i", enmSpaces);
printf("%s\n",acharTempChar);
free(acharTempChar);
}

}

gibt bei mir 11 aus.

Jesus_666
21.01.2004, 20:48
Welche Version der GCC benutzt du? Cygwin und MinGW teilen sich einen Branch; es könnte also höchstens an der Version liegen.

Ich weise auch wieder darauf hin, daß ich nicht printf() benutze und printf() sich nicht notwendig so verhält wie FL_Output::value(const char*). Ich weiß auch nicht, wie C++ sich verhält, wenn ich einen 8 Bit langen Wert in einen 64 Bit langen Speicherbereich schreibe.
Ich weiß, daß ich per exit() den korrekten Wert zurückbekommen habe und daß ein per sprintf() übersetzter und an FL_Output::value(const char*) übergebener Wert zufällige Zahlen beinhaltete, die nach uninitialisiertem Speicher aussahen.

MuadDib
21.01.2004, 23:07
Original geschrieben von Jesus_666
[B]Welche Version der GCC benutzt du? Cygwin und MinGW teilen sich einen Branch; es könnte also höchstens an der Version liegen.

3.1.0

Master of Disaster
21.01.2004, 23:11
g++ --version
g++ (GCC) 3.3.2 20031218 (Gentoo Linux 3.3.2-r5, propolice-3.3-7)

cat ${MuabDibs Codestück} >> test.cpp

$ g++ -o tester test.cpp
$ ./tester
11


$ cat test.cpp
#include <stdio.h>
#include <stdlib.h>

enum { S_None = 0, S_Barrel = 1, S_Top = 2, S_Under = 4, S_Grip = 8, S_Stock = 16 };
int enmSpaces;

int main(void) {
enmSpaces = S_Barrel|S_Top|S_Grip;

char *acharTempChar;
acharTempChar = (char*) malloc(256);
if(acharTempChar!=NULL)
{
sprintf(acharTempChar, "%i", enmSpaces);
//outG_Debug->value(acharTempChar);
printf("%s\n%i\n", acharTempChar, enmSpaces);
free(acharTempChar);
}
}

cat $ABOVE > test.cpp

$ g++ -o tester test.cpp
$ ./tester
11
11

Jesus_666
21.01.2004, 23:42
Wenn Version 3.1.0, 3.3.1 (cygming special) und 3.3.2 das gleiche Ergebnis liefern halte ich einen Compilerfehler für unwahrscheinlich.
Es ist einfach irgendein Verhalten von Fl_Output::value(const char*), basta.

Ineluki
22.01.2004, 22:09
hm .. gibt denn ein printf("%s\n",dein_string) das richtige ergebnis aus ? ... und vielleicht solltest du es ja auch nochmal mit einem
for(int i=0; i<size_deines_strings_in_byte; i++) printf("%c",dein_string[i]); printf("/n");
versuchen. Ist beides korrekt, kannst du davon ausgehen, das die umwandlung in einen string korrekt war und dann kann der fehler ja nicht in Fl_Output::value(const char*) liegen, den der string ist vom ersten bis letzten zeichen ja fest dimensioniert und auch gemalloc()t

also bei mir geht ein print("%s\n",acharTempChar); ohne probleme und gibt mir 11 aus ... gcc version 2.95.3-6 (mingw special)

ein for(int i=0; i<256; i++) printf("%x ",acharTempChar[i]); lieferte mir aber folgendes interessantes ergebnis ...

31 31 0 0 78 1 3d 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Wenn man den String als nullterminiert betrachtet ist ja alles in ordnung. Wird er aber als Array of Char interpretiert (also nicht null terminiert) kommt da noch etwas datenmuell hinterher, obwohl er vorher gemalloc()t war ... vielleicht liegt da dein problem mit Fl_Output::value(const char*) ?

ein explizites ZeroMemory(acharTempChar,256); direkt nach dem malloc()en loeste aber auch dieses problem

MuadDib
22.01.2004, 22:26
#include <stdio.h>
#include <stdlib.h>

enum { S_None = 0, S_Barrel = 1, S_Top = 2, S_Under = 4, S_Grip = 8, S_Stock = 16 }; //Spaces
int enmSpaces;

int main(void)
{
enmSpaces = S_Barrel | S_Grip | S_Top;
char * acharTempChar;
acharTempChar = (char*) malloc(256);
if(acharTempChar!=NULL)
{
sprintf(acharTempChar, "%i", enmSpaces);
for(int i = 0; i < sizeof(acharTempChar); i++)
{
printf("%c",acharTempChar[i]);
}
free(acharTempChar);
}

}


Output:


C:\cmdprogs\cprogs>gcc -o tester.exe enum.cpp

C:\cmdprogs\cprogs>tester
11
C:\cmdprogs\cprogs>

nun...


den der string ist vom ersten bis letzten zeichen ja fest dimensioniert und auch gemalloc()t
Und ausserdem am Schluß der Zeichenkette mit dem obligatorischen 0-Byte versehen, um den String abzuschliessen. Gibt eigentlich Fl_Output::value(const char*) den String in irgendeiner anderen Weise aus bzw. ändert die Funktion etwas am String? Vielleicht kann es ja sein, dass das 0-Byte überschrieben wird.

EDIT: Eine Minute zu spät ^^

Jesus_666
23.01.2004, 00:09
Nun... Das App wird ohne Konsolensupport kompiliert (-mwindows) und weigert sich, ohne -mwindows zu laufen. Mit printf() kriege ich nichts zurück.


Ich habe mal MuadDibs Testprogramm zusammengeschraubt. und ein paar Tests mit verschiedenen Versionen gemacht.

Ich teste in folgender Reihenfolge:
hunzst: for(int i = 0; i < sizeof(acharTempChar); i++) { printf("%c",acharTempChar[i]); }
hunzst2: for(int i = 0; i < sizeof(acharTempChar); i++) { printf("%x ",acharTempChar[i]); }
hunzst3: for(int i = 0; i < 256; i++) { printf("%x ",acharTempChar[i]); }

$gcc -o hunzst hunzst.cxx && ./hunzst
11
$gcc -o hunzst2 hunzst2.cxx && ./hunzst2
31 31 0 0
$gcc -o hunzst3 hunzst3.cxx && ./hunzst3
31 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Ineluki
23.01.2004, 01:41
Original geschrieben von Jesus_666
Nun... Das App wird ohne Konsolensupport kompiliert (-mwindows) und weigert sich, ohne -mwindows zu laufen. Mit printf() kriege ich nichts zurück. Dann lass es in eine datei schreiben, sollte doch kein Problem fuer dich sein. ....

So wie es aussieht, ist der string in ordnung ... aber wenn der string in ordnung ist, was soll dann dein FL_xyz::Value(blaa) denn noch falsch machen ...

du koenntest noch ausprobieren, dass du nen neuen char* machst, den du mit der echten groesse des erhaltenen strings mallocst und dann den inhalt kopierst .... so dass der string wirklich nur als datenblock so viel inhalt hat, wie du brauchst .... wuerde mich aber wundern, wenn das was aendern wuerde

Jesus_666
23.01.2004, 01:52
Es könnte die chronische Müdigkeit sein, die momentan wieder aus mir spricht, aber ich denke, ich werde es eher pragmatisch angehen: Mit itoa() funzt es, also benutze ich itoa(). Fertig. *gähn*