void* nach void* zuweisen
-
Hallo,
Ich habe eine aehnliche struct wie folgende:
typedef struct variable_s{ void* data; enum datatype_e type; int size; char name[32]; ... }variable_t;
...und eine Funktion:
void init_variable( variable_t var, void* data, enum datatype_e type, char name[32], ...) { // ... var allokieren, namen setzen, etc switch(type){ case CHARTYPE: // ... size ermitteln, Speicher allokieren, etc var->data = data; // Problem: Initialisierung des Wertes!!! break; case INTTYPE: // ... wie oben, mit int break; case DOUBLETYPE: // ... wie oben, double break; } }
Ich rufe die Funktion dann in etwa so auf:
init_variable( pvar, 'C', CHARTYPE, "foobarvar", ...);
Als Compiler nehme ich den gcc 4.4.1 mit den flags -g -Wall. Mein Problem ist die Zuweisung. Wie
angedeutet habe ich zB folgende Typen char, int, double. sizeof( char ) ist 1 Byte, sizeof( int )
ist 8, sizeof( double ) ist 8 und sizeof( void* ) ist ebenfalls 8 Byte.Bei der initialisierung des Wertes bekomme ich nun folgende Probleme:
1. Ansatz memcpy() mit Size, also soetwas wie:
memcpy( var->data, data, size);
Leider klappt das fuer Typen kleiner als 8 nicht. Bspw hat char nur 1 Byte. Da "data" aber void* ist,
wird zwar ein Byte ausgelesen, aber das welches am weitesten "links" steht von den 8 Bytes die der
"void* data" theoretisch lang ist. Das gibt es aber nicht und somit bekomme ich hier Probleme (11).
Ausserdem, da der Code auf embedded laufen soll, ist mir das zu riskant mit den little und big endian
Unterschieden.2. Ich versuche nochmal memcpy() mit cast, damit ich das big / little endian Problem und das
erstes-Byte-links-aussen-Problem loswerde, und ende mit zwei Warnungen, die mir etwas sinnfrei erscheinen.memcpy( var->data, (char) data, size);
warning: cast from pointer to integer of different size
warning: passing argument 2 of ‘memcpy’ makes pointer from integer without a cast3. "cast" - ja das will ich; "pointer from integer", ok, klar, also...
memcpy( var->data, &((char) data), size);
warning: cast from pointer to integer of different size
error: lvalue required as unary ‘&’ operand4. Ok, anderer Ansatz: mache eine einfache Zuweisung. In var->data habe ich ein char allokiert, in data
ebenfalls nur ein Byte, trotzdem weise ich void* auf void* zu:var->data = data;
Hier bekomme ich folgende Warnung:
warning: assignment discards qualifiers from pointer target typeAlso, 5., typedef-e ich:
var->data = (char) data;
Nun bekomme ich gleich zwei Warnungen:
warning: cast from pointer to integer of different size
warning: assignment makes pointer from integer without a cast
...die ich beide ziemlich seltsam finde, arbeite ich doch erstens mit void* und zweitens will ich das
EXPLIZIT ge-typedef-t, haben, also keine implizite Umwandlung!?!6. Also typedef-e ich weiter um die Warnungen zu umgehen:
((char) var->data) = ((char) data);
Das geht gar nicht:
warning: cast from pointer to integer of different size
warning: cast from pointer to integer of different size
error: lvalue required as left operand of assignmentWeiter, 7.:
*((char*) var->data) = ((char) data);
Das kompiliert, warnt mich aber davor, dass ich den 8-Byte-Typ ( void* ) auf einen 1-Byte-Typ (char) caste.
warning: cast from pointer to integer of different size8. Ich kotze!
Gut und schoen, nur warum werde ich immer noch gewarnt, wenn ich das explizit tun will? Wie schaffe ich es
den Wert eines void* auf einen anderen ohne Warnungen zuzuweisen, wenn der Typ einfach kleiner ist, als void*?
Ich habe das sogar mit Zwischenvariable - char tmp - versucht, und nicht einmal das scheint ohne Warnung zu
gehn?!!! Ich fange mehr und mehr an herumzuprobieren, nur...Wie macht man es richtig?
-
Hi,
tu mal deine Variable als Zeiger rüber tun:
init_variable( variable_t***** var, ...
denn sonst kommt bei der von außen übergebenen Strukturvariable
nicht viel an Daten an, weil nur eine innerhalb der Funktion sichtbare
Kopie erstellt wird. Guckst du:enum Type { DOUBLE, INT, PCHAR }; struct test { void* data; enum Type type; }; int init ( struct test* pt, void* data, int size, enum Type type ) { if ( NULL == ( pt->data = malloc ( size ))) return 1; memcpy ( pt->data, data, size ); pt->type = type; return 0; } void tfree ( struct test* pt ) { free ( pt->data ); } void kuggimachen ( struct test* pt ) { switch ( pt->type ) { case DOUBLE: printf ( "%lf\n", *((double*)pt->data) ); break; case INT: printf ( "%d\n", *((int*)pt->data) ); break; case PCHAR: printf ( "%s\n", (char*)pt->data ); break; default: puts("kuggi nix"); } } int main() { struct test t = {0}; struct test* pt = &t; double dnum = 1.1; int inum = 1; char* s = "test"; if ( init ( pt, &dnum, sizeof(double), DOUBLE )) return 1; // No RAM. kuggimachen ( pt, DOUBLE ); tfree ( pt ); if ( init ( pt, &inum, sizeof(int), INT )) return 1; // No RAM. kuggimachen ( pt, INT ); tfree ( pt ); if ( init ( pt, s, strlen(s)+1, PCHAR )) return 1; // No RAM. kuggimachen ( pt, PCHAR ); tfree ( pt ); return 0; }
Gruß,
B.B.
-
Hier noch einmal, feingeschliffen:
enum Type { DOUBLE, INT, PCHAR }; struct test { void* data; enum Type type; }; int init ( struct test* pt, void* data, int size, enum Type type ) { if ( NULL == ( pt->data = malloc ( size ))) return 1; memcpy ( pt->data, data, size ); pt->type = type; return 0; } void tfree ( struct test* pt ) { free ( pt->data ); } void kuggimachen ( struct test* pt ) { switch ( pt->type ) { case DOUBLE: printf ( "%lf\n", *((double*)pt->data) ); break; case INT: printf ( "%d\n", *((int*)pt->data) ); break; case PCHAR: printf ( "%s\n", pt->data ); break; default: puts("kuggi nix"); } } int main() { struct test t = {0}; struct test* pt = &t; double dnum = 1.1; int inum = 1; char* s = "test"; if ( init ( pt, &dnum, sizeof(double), DOUBLE )) return 1; // No RAM. kuggimachen ( pt ); tfree ( pt ); if ( init ( pt, &inum, sizeof(int), INT )) return 1; // No RAM. kuggimachen ( pt ); tfree ( pt ); if ( init ( pt, s, strlen(s)+1, PCHAR )) return 1; // No RAM. kuggimachen ( pt ); tfree ( pt ); return 0; }
-
Hallo,
Ich hatte bei meinem zusammengeklebten "fiktiven" Beispiel vergessen, ich hab noch folgende Zeile:
typedef variable_t* variable_p;
..und nehme natuerlich immer nur variable_p her, klar. Also das Problem sollte eig auch nicht die Zeigerarithmetik sein. Danke trotzdem fuer den Hinweis.
Meine Hauptfrage ist einfach immer noch v.a., wie caste ich einen void* auf char, ohne dass ich dabei Warnungen - dass dabei auf eine kleinere Groesse gecastet wird - mit obigem gcc und -g -Wall Flags, bekomme?
-
kpl. ob das was hilft aber einen versuch ist es wert
void *test = 'x'; char back = (char)((int)test); printf("%c",back);
lg lolo
-
noobLolo schrieb:
kpl. ob das was hilft aber einen versuch ist es wert
void *test = 'x'; char back = (char)((int)test); printf("%c",back);
oder so vielleicht
void *test = (void*)'x'; char back = (char)(ptrdiff_t)test;
-
@;fricky
"ptrdiff_t" wo hast denn das ausgegraben, das hab ich ja bisher noch nie gesehen, gut zu wissen das es sowas gibt
-
noobLolo schrieb:
@;fricky
"ptrdiff_t" wo hast denn das ausgegraben, das hab ich ja bisher noch nie gesehen, gut zu wissen das es sowas gibt
hier: http://www.cplusplus.com/reference/clibrary/cstddef/ptrdiff_t/
ist sowas wie ein 'int' mit adressbusbreite, so dass ein pointer da reingeht.
-
Danke, aber - ohne jetzt wieder Spielverderber zu sein - nur noch eine Frage: wie ist das mit der Portabilitaet, v.a. little endian / big endian. Ja, ich weiss, ein paar der Vorschlaege oben (memcpy) rennen da genau in diese Falle rein, momentan experimentiere ich sogar etwas mit "write" und "read" und den direkten Pointeradressen rum.
Ein fundamentales Problem, was mir einfach auch etwas Kopfschmerzen macht ist, dass ich zB. fuer einen char (1 Byte) speicher allokiert habe, dann einen void* (8 Byte) drauf zeigen lasse. Es herrscht einfach irgendwie auch dauernde Gefahr, dass das ganze zwar kompiliert, aber dann wird das "falsche" Byte herausgelesen. Also, d.h. 7 Byte des void* zeigen auf nicht allokierten Speicher, den ich gar nicht nutzen darf.
Es scheint sehr leicht zu sein, sich damit ins Bein zu schiessen, v.a. manches meiner weiteren Experimente, klappt auf x86, aber auf der Embedded Arch, dann wieder nicht...
Vllt. hilft mir ein Link auf "portierbare" Funktionen in C weiter? Hat da jemand vllt zufaellig ein paar Tipps? LInks? (Danke, ansonsten, caste ich momentan zweimal auf void* bei einer Zuweisung, das geht momentan ohne Warning).
-
voidberg schrieb:
Es scheint sehr leicht zu sein, sich damit ins Bein zu schiessen, v.a. manches meiner weiteren Experimente, klappt auf x86, aber auf der Embedded Arch, dann wieder nicht...
wichtig ist, dass du die daten immer als das behandelst was sie sind, auch wenn der zieltyp falsch ist (was manchmal ja leider so ist), z.b:
void *p = (void*)'x'; // falscher typ, aber breit genug char c = (char)(ptrdiff_t)p; // char wieder rausholen, sollte immer gehen
was aber nicht geht, ist z.b.
char a[sizeof(int*)]; // speicher fuer... a[0] = ...; // ...einen selbstgemachten int-pointer a[1] = ...; // fuellen... ... // ... *(int*)a = irgendwas; // *peng*, bus-error wegen falscher ausrichtung
auch auf byte-arrays über struct-pointer zuzugreifen wird gern mal gemacht, geht aber fast immer schief. oder bitfields bzw. unions zu nehmen, um teile eines breiteren typs zu extrahieren, ist sträflicher leichtsinn. sowas geht portabel nur mit shifts und bitoperationen.
aber schau mal hier: http://www.psgd.org/paul/docs/cstyle/cstyle16.htm