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 cast

    3. "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 ‘&’ operand

    4. 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 type

    Also, 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 assignment

    Weiter, 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 size

    8. 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
    🙂


Anmelden zum Antworten