Die lieben Pointer ( hab so meine Not mit ihnen



  • <offtopic>
    @HannesW: In deinem eigenen Interesse solltest du imho zudem deine Beiträge (von denen du dir ja Hilfe erhoffst) ein wenig besser strukturieren. Du machst nämlich meines Erachtens manchmal zu große gedankliche Sprünge, welche vom Leser (und potenziellem Helfer) nicht unbedingt leicht zu verstehen sind.

    Caipi
    </offtopic>



  • @ZuK: Umwandeln ja, aber nicht von der Zahlen- in die String-Form, sondern der String soll die (maschinen interne) darstellung der Int haben.

    @Caipi: Tut mir leid, aber ich bin da irtgendwie vernagelt, und würde es doch gern verstehen.

    HannsW



  • Aha. Dnn wars ja nicht so falsch.

    char buffer[5];
    char * cP = buffer;
    int * iP = reinterpret_cast<int*>(buffer);
    *iP = 12345;
    buffer[4]=0; // terminating 0 anhängen für alle fälle
                 // macht zwar wenig sinn denn wahrscheinlich ist schon irgendwo eine 0
    for ( int i = 0; i < 4; ++i ) // int als char ausgeben 
       cout << buffer[i] << endl;
    
    for ( int i = 0; i < 4; ++i ) // int als asciiwerte ausgeben 
       cout << static_cast<int>(buffer[i]) << endl;
    
    // andere Richtung
    strcpy(cP, "1234");
    cout << *iP << endl;
    

    Kurt
    edit: lots of typos



  • Wenn wir von 4Byte ausgehen für nen int, dann schreibst du mit iP = 12345; was in den Speicher das so aussehen könnte 00000000000000000011000000111001, wenn du dir das nun als einen String, oder einzelne Chars ausgeben lässt bekommst du da aber was ganz anderes raus als 12345 oder was das da auch nur entfernt hin kommt.

    Was genau ist denn deine Absicht? Sowas macht man manchmal um Daten zu serialisieren, wobei das auch nicht unbedingt ne gute Möglichkeit ist, da es so sehr Plattformabhängig ist.



  • User--- schrieb:

    Wenn wir von 4Byte ausgehen für nen int, dann schreibst du mit iP = 12345; was in den Speicher das so aussehen könnte 00000000000000000011000000111001, wenn du dir das nun als einen String, oder einzelne Chars ausgeben lässt bekommst du da aber was ganz anderes raus als 12345 oder was das da auch nur entfernt hin kommt.

    Was genau ist denn deine Absicht? Sowas macht man manchmal um Daten zu serialisieren, wobei das auch nicht unbedingt ne gute Möglichkeit ist, da es so sehr Plattformabhängig ist.

    Hat er doch schon gesagt dass die Daten von einer seriellen Schnittstelle kommen und in einem char-Buffer liegen.



  • @HannsW: Bitte bring mal die Formatierung von deinem Post in Ordnung. Da dürften irgendwie die Tags durcheinandergekommen sein. Abgesehen davon ist es aber mal eine wirklich detaillierte Fragestellung. Lob dafür, das kommt nicht häufig vor ;).



  • Danke fürs Beispiel, das auch klappt. ABER 😕 So richtig verstanden hab ich diese Zeilen noch nicht;

    int * iP = reinterpret_cast<int*>(buffer);
    *iP = nWert;
    

    Könnste da noch mal ein paar worte sagen?
    oder soll ichs nochmal versuchen?
    Hanns

    *iP = nWert;
    

    -An die SPeicherstelle, deren Nummer in iP steht, soll nWert eingetragen werden.
    ( Liebling, Dein Essen steht im Kochbuch auf Seite 13 !?)
    -"normal" waäre dann dieses

    int i;
    int * iP = i ;
    

    - da wir iP auf eine Speicherstelle lassen, an der ein char-Array steht, kommt der cast ins SPiel ?

    So etwa?

    War grad mal weg, und sehe eben die anderen Post.
    Was ich machen will?
    Klassen schreiben, um auf ein Datenbanksystem zu greifen zu können.
    Die Api verlangt die Daten in einem char-Array, in dem für int's 4Bytre vorgesehen sind.
    UNd in diees Array müssen eben die richtigen "strings" stehen.
    Für "bool" war das nicht so schwierig:

    bool myClass::Boolean{
          return m_dataBuf[ anDerStelle] == chr(0); 
    }
    

    @User--Genau das möchte ich, und nicht etwa die Zahl als String auf den Drucker geben

    Das mit dem "in Ordnung bringen" klappt leider nicht mehr, da ich diese post als nicht registriert auch nciht mehr editieren kann. Sorry



  • Wenn du eine Adresse auf ein char-array mit reinterpret_cast ab sofort als Adresse auf ein int-array umwandelst, dann ändert sich für dich eigentlich gar nichts, da der Compiler dir ja die Arbeit abnimmt die Adresse je nach Typ richtig zu berechnen.

    Du musst das nur tun um einen Block von Bytes anderst zu behandeln, in deinem Fall eben 4Chars als einen Integer. Du kannst das auch mit ner union machen:

    union konverter
    {
      char alsChars[ 4 ];
      int  alsInt;
    } myKonverter;
    
    myKonverter.alsChars = "1234"; // Zugriff in Form von Char Array auf den Speicher
    myKonverter.alsInt = 12345;    // Zugriff als Integer auf den Speicher
    


  • Da ist mir doch glatt nen Fehler unterlaufen, das "alsChars"-array muss 5 und nicht 4 Byte groß sein.



  • HannsW schrieb:

    Was ich machen will?
    Klassen schreiben, um auf ein Datenbanksystem zu greifen zu können.
    Die Api verlangt die Daten in einem char-Array, in dem für int's 4Bytre vorgesehen sind.
    UNd in diees Array müssen eben die richtigen "strings" stehen.

    Ich versuch einmal zu erklären.
    Also Dein Datenbanksysten hat wahrscheinlich irgendeine Funktion mit der du beliebige Daten Eingeben kannst. Damit das API nicht zu aufwendig wird gibt es wahrscheinlich nur eine ( oder wenige ) funktionen zum schreiben in die datenbank.
    Du Sagst wahrscheinlich nur irgendwas wie Schreibe Daten in den Record soundso Feld sowieso. Die Datenbank weiss ja schon welchen Datentyp das Feld hat. Daher haben sich die Entwickler darauf geeinigt der Funktion die daten als array von chars zu übergeben. Das hat überhaupt nichts mit 0-terminierten strings zu tun. Sie wollen einfach nur mit einer einzigen funktion auskommen.
    Für dich bedeutet das dass du der Funktion einfach einen Pointer auf deine Daten übergeben musst den du immer ( egal welchen DatenTyp das Feld hat ) in einen char * casten musst.

    Ich stelle mir das so vor:

    void set_data( long int ID, const char * feld, const char * data ); // möglicher prototyp der Eingabefunktion
    

    Beispiel:

    char * name = "ein string";
       set_data( 12345, "name", name ); // die datenbank weiss das feld name ist ein string typ passt kein cast
       short int haus_nr = 123;
       set_data( 12345, "Nr", reinterpret_cast<char*>(&hausnummer) ); // die datenbank weiss das feld Nr ist ein short also casten wir halt
       int postleizahl = 45123;
       set_data( 12345, "Plz", reinterpret_cast<char*>(&postleizahl) ); // die datenbank weiss das feld Plz ist ein int also casten wir halt
    

    Umgekehrt wirds wahrscheinlich genauso funktionieren

    void get_data( long int ID, const char * feld, char * data ); // möglicher prototyp der Ausgabefunktion
                                                                   // data kann nicht const sein den die funktion verändert den wert
    

    Beispiel: lesen aus der DB

    char name[255];
       get_data( 12345, "name", name ); // die DB füllt name
       short int haus_nr;
       get_data( 12345, "Nr", reinterpret_cast<char*>(&hausnummer) ); // die DB füllt haus_nr   
       int postleizahl;
       set_data( 12345, "Plz", reinterpret_cast<char*>(&postleizahl) ); // die DB füllt postleitzahl
       // gelesene daten ausgeben
       cout << "Name=" << name << endl;
       cout << "Nr=" << haus_nr << endl;
       cout << "Plz=" << postleizahl << endl;
    

    Langer Rede kurzer Sinn. Die datenbank erwartet die daten immer als char * damit mit einer einzigen Funktion alle möglichen Datentypen abgedeckt werden können.
    Wie die Daten interpretiert weden müssen ist in der DB festgelegt.

    Kurt

    PS:Bitte nicht schlagen wenn das ganze mit deinem Problem nichts zu tun hat.



  • Ja danke, ich glaub, jetzt ist der Groschen ( 10Cent) bei mir gefallen 🙂

    Dann aber noch ne design Frage:
    In meiner TOP-Klasse habe ich nun die Methoden setInteger und getIntger, die ich nun in 2 Varianten ausführen kann.

    void btrieve::setInteger ( int nStart, int  nWert){
            union konverter
            {
                    char alsChars [ APP_INTEGER_LEN + 1 ];   // 4 +1 Byte
                    int  alsInt;
            } myKonverter;
            myKonverter.alsInt = nWert ;    // Zugriff als Integer auf den Speicher
    
            //und in den DatenPuffer schreiben
            for ( int i = 0; i < APP_INTEGER_LEN ; ++i ){
                    m_dataBuf [ i + nStart ] =  myKonverter.alsChars[i];
            }
    }
    

    oder variante 2

    void btrieve::setInteger ( int nStart, int  nWert){
            char buffer[ APP_INTEGER_LEN + 1];
            int * iP = reinterpret_cast<int*>(buffer);
            *iP = nWert;
            for ( int i = 0; i < APP_INTEGER_LEN; ++i ){ 
                    m_dataBuf [ i + nStart ] =  buffer[i];
            }
    }
    

    Was davon ist denn "besserer" Stil, resp schneller?

    Wobei ich bei der Variante 1 ( Union ) die Union bereits als Klassen-Union deklarieren würde? Sie müsste dann nicht bei jedem get-/set- Aufruf erzeugt und danach entsorgt werden. etwa so?

    // btrieve.hh
    //...
            union konverter
            {
                    char alsChars [ APP_INTEGER_LEN + 1 ];   // 4 +1 Byte
                    int  alsInt;
            } myKonverter;
    //...
    //btrieve.cc
    ...
    void btrieve::setInteger ( int nStart, int  nWert){
            myKonverter.alsInt = nWert ;    // Zugriff als Integer auf den Speicher
    
            //und in den DatenPuffer schreiben
            for ( int i = 0; i < APP_INTEGER_LEN ; ++i ){
                    m_dataBuf [ i + nStart ] =  myKonverter.alsChars[i];
            }
    
    int  btrieve::getInteger ( int nStart){
            for ( int i = nStart; i < nStart + APP_INTEGER_LEN ; i++){
                    myKonverter.alsChars [ i -nStart ] = m_dataBuf [i ];
            }
       return  myKonverter.alsInt;
    }
    


  • Während ich weben schrieb, hat ZuK auch geantwortet.
    an ihn:
    Ja das ist es. Es gibt nur einen Api-Aufruf, in dem alles erledigt wird. Und der erwartet ein CHAR-ARRAY asl datenbuffer.

    Ich hab nun ne Klasse btrieve, in der ich u.a die Umwandlung in und zu ints vornehme ( void btrieve::setInteger(int nStart, int nWert) und int btrieve::getInteger ( nstart) )

    davon baue ich nir dann z.B. ne btrieve::aliasPerson() in der ich dann z.B

    int aliasPerson::hausnummer ( ) {
       return getInteger( APP_HAUSNUMMERN_START);
    }
    

    definiere. und in der ANwendung dann eben

    aliasPerson person = aliasPerson(....);
       //open
       //suche und so
       cout << "hausnummer " << person.hausnummer << endl;
    


  • Ok Jetzt verstehe ich was du machen willst.
    Ich würde getInteger und setInteger mit memcpy implementieren. Dann brauchst du keine Konvertierungsunion und auch keinen cast da memcpy mit void * arbeitet.

    void btrieve::setInteger ( int nStart, int  nWert){ // nehme an nStart ist ein offset in byte
        memcpy((m_dataBuf + nStart), &nWert, sizeof(nWert));
    }
    
    int  btrieve::getInteger ( int nStart){
        int ret;
        memcpy( &ret, (m_dataBuf + nStart), sizeof(ret));
        return  ret;
    }
    

    Das ist wahrscheinlich die performanteste lösung. Ist zwar kein schönes C++ aber wenn man C-API's verwenden muss dann ist das meiner meinung nach erlaubt.
    Kurt



  • Jo, auf memcpy hätte ich eigentlich auch kommen können.
    Dazu noch eine weitere Überlegung

    void btrieve::setInteger ( int nStart , int nWert){ // nehme an nStart ist ein offset in byte   == JA 
    
        memcpy((m_dataBuf + nStart), &nWert, sizeof(nWert));
        // neige ich dazu so zu schreiben:
        memcpy((m_dataBuf + nStart), &nWert, APP_INTEGER_LEN) ;
        // oder eindef aus der api.hh nehmen
        memcpy((m_dataBuf + nStart), &nWert, BTI_INT;
    }
    

    Obwohl es dann doch sicherer wäre, den parameter-Typ zu "api-def"fen?

    void btrieve::setInteger ( int nStart, BTI_INT nWert ){ // aus der api.hh
      //...
    }
    

    Deine Lösung könnte ich dann erweitern, und sie gleich für alle Arten von Zahlen
    verwenden:

    void btrieve::setZahl ( int nStart,int nWert,int nBytes ){
    memcpy((m_dataBuf + nStart), &nWert, nBytes );
    }
    
    // und in alisaPerson
    void aliasPerson::hausnummer (int nWert ) {
       setZahl( APP_HAUSNUMMERN_START, nWert, APP_INTEGER_LEN);
    }
    }
    

    Danke für die Hilfe
    Hanns



  • Kann dier da keine Lösung vorschlagen da ich mich mit den Bitgrössen der Zahlen typen nicht allzusehr beschäftigt habe.
    Du solltest dir aber darüber klar sein das du höchstwahrscheinlich Probleme bekommen wirst wenn du auf ein andere Prozessorarchitektur ( 32=>64 Bit ) umsteigst.
    Wahrscheinlich bist du am besten beraten wenn du dir eigene globale typedefs anlegst und mit diesen arbeitest. Dann musst du beim umsteigen die definitionen nur an einer Stelle anpassen.
    z.b

    typedef  __int32 bt_int;  // keine Ahnung ob es __int32 bei deinem compiler gibt
    void btrieve::setInteger ( int nStart , bt_int nWert){ 
      memcpy((m_dataBuf + nStart), &nWert, sizeof(nWert));
    }
    

    Kurt



  • Da war ich leider nicht ganz sauber:
    Die Endvariante sieht so aus

    void btrieve::setInteger ( int nStart , int nWert){ 
        memcpy((m_dataBuf + nStart), &nWert,sizeof( BTI_INT));
    }
    

    Wobei BTI_INT aus den Headern der Api kommt, und dort auch ne Menge #ifdef ARCHITEKTUR UND BETRIEBSYSTEM existieren, die einem diese Arbeit abnehmen.

    #define LINUX
    

    reicht.
    Aber Danke nochmals für den Hinweis
    Hanns


Anmelden zum Antworten