Ist es möglich jeden Typen in ein char Array zu kopieren?



  • wie drakon schon sagte kann das probleme mit dem padding geben, aber meist
    sind die padbytes bei z.b. einer struktur in der mitte oder am ende. außerdem
    richten die meisten allokationsfunktionen (new malloc) den speicher nach
    16, 8 oder 4 byte grenzen aus. wenn du statt in eine referenz in einen pointer
    zurückcastst, sollte das aber funktionieren (weiß grade nicht wos im standart
    steht aber sinngemäß kommt bei reinterpret_cast das selbe wieder raus, wenn
    mans zurückcastet.)

    da eine referenz aber bei fast jedem compiler intern ein pointer ist, ist es
    zumindestens sehr wahrscheinlich das das geht. möglicherweise erkennt der
    kompilier den cast und verhindert das padding. ich hatte mit ähnlichen
    konstrukten noch nie probleme


  • Administrator

    drakon schrieb:

    Hmm. Ich würde mal sagen, dass es da Probleme mit padding geben kann. (Kommt natürlich drauf an, was du dort innerhalb machst..).

    Wieso sollte das Probleme mit dem Padding geben? Das verstehe ich jetzt nicht so recht. Das sizeof sollte die Padbytes bereits beinhalten und somit sollte auch das char Array bereits genügend gross sein, um diese zusätzlichen Bytes aufzunehmen.

    Und ich habe ja geschrieben, dass man möglichst nicht auf den Speicher des Arrays schreiben sollte. Das wäre mir persönlich viel zu gefährlich, da man dann genau wissen müsste, wo der Kompiler welche Bytes hinschaufelt. Der Bereich zwischen Kopie und Rückwandlung kann also ignoriert werden, da ich mal definiere, dass das Array nicht verändert wird.

    Grüssli



  • Ich habe zwar keine Ahnung was der Standard dazu sagt, doch ich habe sowas in der Art auch schon gemacht (ebenfalls nur zu Testzwecken rein aus Neugierde) und das hat keine Probleme bereitet.

    Denn wenn man mal bedenkt, dass ein Zeiger unabhängig vom Typ immmer die gleiche Größe hat und schlicht und einfach eine Speicheradresse speichert, welche ebenfalls vom Typ unabhängig ist, fällt mir jetzt auch kein Grund ein warum das nicht klappen sollte.
    In diesem Zusammenhang stelle ich jetzt einfach mal die vorsichtige Vermutung, dass ein solches Handeln auch nie zu unvorhersehbaren Fehlern führen wird (wenn man alles richtig macht), egal ob das jetzt irgendwo definiert ist oder nicht, schlicht und einfach auf Grund der Natur der Zeiger, einfach nur Zeiger mit typunabhängiger Größe zu sein, welcher auf Speicher zeigt der ebenfalls vom Typ unabhängig ist (abgesehen von der größe)

    Der Typ den man angibt ist halt einfach eine Info die aussagt, wie weit man ab dieser Speicheradresse lesen soll.

    z.B. geht ja auch sowas hier:

    double double_var; // double = 8 byte
    char pchar_var[8] = "tagchen"; // <-- + \0  Ein char ist ja 1 byte und speicher reserviert wird für 8 davon
    
    memcpy(&double_var, pchar_var, 8);
    
    std::cout << (char*)&double_var << std::endl;
    

    Ob das jezt nun Sinn macht oder nicht interessiert bei einem Test ja nicht 🙂

    EDIT:
    Hier auch ein interesanntes Ergebnis

    int int_var;
    float float_var = 12.33;
    
    memcpy(&int_var, &float_var, 4);
    
    std::cout << *((float*)&int_var) << std::endl;
    

    Tatsächlich wird hier die 12.33 erhalten, obwohl dieser Wert in einem Sepeicherbereich lag der für einen Int reserviert war.



  • Achso.. Ich habe das hier:

    // Am besten wohl gar nicht erst beschreiben 🙂

    eher bezogen auf das hier gelesen (auch wegen dem Smiley)

    // Mach was damit, was auch immer ...

    Nich beshreiben was man macht.. 😉

    Aber ich hatte tatsächlich das beschreiben im Kopf, was sehr wahrscheinlich schief gehen würde..



  • Dravere schrieb:

    drakon schrieb:

    Hmm. Ich würde mal sagen, dass es da Probleme mit padding geben kann. (Kommt natürlich drauf an, was du dort innerhalb machst..).

    Wieso sollte das Probleme mit dem Padding geben? Das verstehe ich jetzt nicht so recht. Das sizeof sollte die Padbytes bereits beinhalten und somit sollte auch das char Array bereits genügend gross sein, um diese zusätzlichen Bytes aufzunehmen.

    Ja, Probleme gibts, wenn du das gleiche auf nem anderen System mit anderem Compiler machst, dann kann das Padding anders sein und die Daten passen nicht mehr zusammen. Aber padding kann man auch ausschalten.


  • Administrator

    drakon schrieb:

    ...

    Hmmm, klingt wirklich verwirrend. Ich habe es mal umgeschrieben, für neue Leser.

    @Kahino,
    Beispiele kann ich auch schreiben und sehen das es geht. Nur beweisen Beispiele überhaupt gar nichts. Erst recht nicht bei fundamentalen Typen. Mein gezeigter Code lässt auch nicht PODs zu.
    Vor allem kann man bei solchen Beispielen auch nicht wissen, ob es nicht einfach am Kompiler liegt, da er die Sachen gerade richtig hinschaufelt.
    Ich bin dir zwar dankbar für deine Bemühungen, aber viel bringen tut es leider nichts.

    Vielleicht für die Allgemeinheit, nicht nur Kahino:
    Ich hoffe eigentlich darauf, dass sich irgendjemand hier gut im Thema auskennt und mir das relativ schnell und einfach beantworten kann. Falls jemand Lust hat, sich im Standard dazu schlau zu machen, will ich das natürlich nicht verbieten.
    Vermutungen sind zwar schön und gut, nur leider kann man mit ihnen keine Vermutung beweisen oder widerlegen. 🙂

    @systemator (grad noch in der Vorschau gesehen),
    Das gilt aber nur, wenn die zwei Programme solche Daten miteinander austauschen. Davon bin ich zwar nicht ausgegangen, aber es ist ein guter Vermerk. Wieder mal das Problem vom nicht standardisierten Binarycode bei C++.

    Grüssli



  • Die "Hinwandlung" ist so wie ich den Standard auslege erlaubt.
    Die "Rückwandlung" ganz sicher nicht.

    Grund: du hast an der stelle kein "ValueT" konstruiert, also darfst du es auch nicht als "ValueT" ansprechen.
    Praktisch gesehen kann natürlich auch ganz furchtbar viel in die Hose gehen, nämlich wenn die Klasse keinen trivialen Copy-Ctor hat (oder u.U. garkeinen Copy-Ctor).

    Alignment sollte passen, da new grundsätzlich immer Speicher zurückgibt der für alle Objekte die <= N sind (N = Grösse des angeforderten Blocks) passend aligned ist.

    Kurz: ja, du darfst aus einem T mittels memcpy einen char-Haufen machen, aber aus dem char-Haufen wird auf legale Weise kein T mehr.

    p.S.: Mag sein dass es eine Ausnahme für PODs gibt.

    p.p.S.: für T = int, short, ... gibt es *glaube ich* eine Ausnahme, obwohl ich mir nichtmal da 100% sicher bin.



  • padding kann z.b. probleme bei ungraden größen machen:

    struct test
    {
        char c[5];
    };
    test *t = new test[2];
    char *c = reinterpret_cast<char *>(t);
    
    test &t2 = reinterpret_cast<test &>(*c);
    
    delete[] t;
    

    es kann sein dass die refernez auf ein / drei bytes vor c zeigt. ist aber
    unwahrscheilich


  • Administrator

    hustbaer schrieb:

    Grund: du hast an der stelle kein "ValueT" konstruiert, also darfst du es auch nicht als "ValueT" ansprechen.

    Gutes Argument. Was ist, wenn ich auf den char Haufen zuerst ein "Placement New" für den entsprechenden Typen durchführe? Dann hätte ich ja grundsätzlich ein ValueT konstruiert. Das kopieren sollte funktionieren, und das ansprechen dann auch .. oder?

    hustbaer schrieb:

    Praktisch gesehen kann natürlich auch ganz furchtbar viel in die Hose gehen, nämlich wenn die Klasse keinen trivialen Copy-Ctor hat (oder u.U. garkeinen Copy-Ctor).

    Sehr guter Vermerk. Das sollte man sich aber einfach im Hinterkopf behalten. Also schreiben wir dazu:
    static_assert("has trivial copy-ctor");

    Danke.

    @paddi,
    Das ist aber ein wenig ein anderer Code als meiner. Du machst hier ein Array von Typen und erst dann ein Cast auf char (und kopierst nicht einmal).
    Zudem weiss ich nicht, ob der Kompiler das Padding vor oder nach dem erstellen des Arrays macht. Meiner Meinung ist es nämlich vor. Weiss das einer genauer?

    Grüssli



  • @Dravere:
    Was ist deine Frage genau? Ob es effektiv mit "den üblichen Compilern" geht, oder ob es laut Standard OK ist? Laut Standard ist es nicht OK, effekti gehen tut's aber.

    Darüber warum es der Standard nicht erlaubt kann man nur mutmassen, aber die übliche Erklärung die kommt, wenn man solche oder ähnliche Fragen stellst ist weil
    a) man auch gut ohne diese Erlaubnis auskommt und
    b) man "die Implementierung" (den Compiler) nicht unnötig einschränken will

    Ein Compiler könnte z.B. relative Adressen verwenden, z.B. um den VTable zu finden. Kopierst du den gesamten "Inhalt" eines Objekts jetzt Byte für Byte über ein anderes drüber, überschreibst du auch den VTable Pointer. Wenn dieser relativ ist, stimmt er an der neuen Adresse nichtmehr.

    Aber wozu soll das ganze überhaupt gut sein? Wenn man voraussetzt dass die Klasse einen trivialen Copy-Ctor und einen trivialen Assignment-Operator hat, dann kann man gleich Placement-New bzw. den Assignment-Operator verwenden, denn wesentlich schneller wird ein memcpy normalerweise auch nicht sein.


  • Administrator

    hustbaer schrieb:

    Was ist deine Frage genau? Ob es effektiv mit "den üblichen Compilern" geht, oder ob es laut Standard OK ist?

    Zweiteres. Also ob es nach Standard OK ist. Daher ist meine Frage wohl mehr oder weniger beantwortet. Muss mir das dann morgen, wenn ich wieder vollständig ausgeschlafen bin, nochmals durch den Kopf gehen lassen.
    Danke für die Erklärung.

    hustbaer schrieb:

    Aber wozu soll das ganze überhaupt gut sein?

    Für nichts ausser einem Selbsttest, bzw. dem Füllen von C++ Wissenslücken. Ich möchte mein Wissen über C++ anfangen abzurunden, den letzten Schliff geben, zumindest was den Standard angeht. Damit der neue Standard das schöne Konstrukt dann hoffentlich nicht zu schwer wieder beschädigt 😃
    Es sind halt einfach ein paar Fragen, welche ich gerne über C++ noch wissen möchte. Wie ich mich auch dringend nochmals genauer mit std::locale, facet und codecvt auseinander setzen muss oder die Allokatoren nochmals durchgehen. Vielleicht auch die Streams noch einmal genau ansehen, wie sie intern funktionieren, bzw. wie man sie erweitert.
    Es ist halt reiner Wissensdurst. Wenn man mich schon mehreren Orten als C++ Nerd beschimpft, dann will ich dem ja auch gerecht werden 😃

    Grüssli


Anmelden zum Antworten