mikrocontroller.net

Forum: PC-Programmierung Qt C++ QByteArray.constdata() copy


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Thorben (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo, ich bin immer noch dabei C++ zulernen und hab definitiv immer 
noch Probleme mit Pointern.


In der Qt Dokumenation zum QByteArray constdata steht, dass es ein 
Zeiger auf die Daten ist und die Gültigkeit solange besteht wie das 
QByteArray nicht zerstört wurde.

Mit statischen Daten klappt alles wunderbar, nun bekomme ich woanders 
her die Daten und würde Sie gerne übergeben.
void MyClass::handlemessage(const QByteArray &ba)
{
  
    MyProtocolclass protocolclass;
    protocolclass.Init();
    void *databytes;
    // set id and size
    int id = 10;
    int size = 2; 
    
    databytes = protocolclass.Adddata(id,size);
    MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
    wdata->value0 = 1;
    wdata->value1 = 1;
    // ....
    // Das klappt wunderbar, aber es wird auch nicht die Daten aus dem QByteArray genutzt
  SendData(protocolclass)
}

void MyClass::SendData(MyProtocolclass &myprotolclass)
{
  // ... 

}


Wenn ich nun die Daten aus dem QByteArray nehme, dann wird das 
QByteArray natürlich zerstört und die Daten sind weg.
void MyClass::handlemessage(const QByteArray &ba)
{
  
    MyProtocolclass protocolclass;
    protocolclass.Init();
    void *databytes;
    // set id and size
    int id = 10;
    int size = 2; 
    
    databytes = protocolclass.Adddata(id,size);
    MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
    writedata = (MyDataClass*) ba.constData();

    // Werte sind hier gesetzt wie gewollt

    SendData(protocolclass)
}

void MyClass::SendData(MyProtocolclass &myprotolclass)
{
  // ... hier sind die Werte 0, weil der Pointer natürlich nicht mehr Gültig ist vom QByteArray.constdata()
}

Wie kann ich die Daten sauber an die Klasse MyProtocolclass übergeben, 
so das die Daten noch gültig sind?

Autor: Sven B. (scummos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein Memory-Management ist generell ziemlich wirr. Wo soll das Ergebnis 
des Casts denn leben?

Ich glaube ich würde in diesem Fall das Ergebnis einfach nach dem Cast 
kopieren:

const MyClass d = *reinterpret_cast<MyClass*>(byteArr.data());

Autor: derMosphet (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In der Zeile:
writedata = (MyDataClass*) ba.constData();

speicherst du einen Pointer auf deine Daten irgendwo hin und erwartest 
dann hier
SendData(protocolclass)

das deine Daten in einem bis jetzt komplett unbeteiligten Objekt stehen?

Versuch noch einmal deinen Code etwas aufgeräumter zu gestalten, so wird 
dir wahrscheinlich keiner helfen können. Generell gilt auch, dass casts 
praktisch nie eine gute Idee sind.

Autor: Thorben (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo, ja ganz glücklich bin ich auch noch nicht, aber dazu muss erst 
mehr Erfahrung sammeln.
const MyClass d = *reinterpret_cast<MyClass*>(byteArr.data());


Ähnlich hatte ich es auch am Anfang gedacht, aber das gibt die 
Fehlermeldung

Semantic Issue
reinterpret_cast from type 'const char *' to MyClass* casts away 
qualifiers

Autor: Thorben (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>das deine Daten in einem bis jetzt komplett unbeteiligten Objekt stehen?


void *databytes;
// set id and size
int id = 10;
int size = 2; 
databytes = protocolclass.Adddata(id,size);
MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);

Unbeteiligt ist es doch nicht nach meinem Verständnis. Mein Verständnis 
ist, dass der void* databytes Pointer auf den Speicherbereich der 
MyProtocolclass zeigt, welches wiederum ein Buffer ist. Der 
Speicherbereich wird auf die Struktur (MyDataclass) gecastest und ich 
kann die Werte dementsprechend ändern.

Oder ist mein Verständnis falsch?

Autor: Sven B. (scummos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht erklärst du erstmal was du eigentlich erreichen willst, weil 
das ist alles irgendwie ein bisschen wirr was du da machst. Bei dem Code 
oben fehlt irgendwo ein const oder es ist wo eins zu viel, du kannst dir 
ziemlich aussuchen welches du weg oder hin machst ;)

Autor: Thorben (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Vielleicht erklärst du erstmal was du eigentlich erreichen willst, weil
>das ist alles irgendwie ein bisschen wirr was du da machst. Bei dem Code
>oben fehlt irgendwo ein const oder es ist wo eins zu viel, du kannst dir
>ziemlich aussuchen welches du weg oder hin machst ;)


Ok, zweiter Versuch:
void MyClass::handlemessage(const QByteArray &ba)
{
  
    MyProtocolclass protocolclass;
    protocolclass.Init();
    void *databytes;
    // set id and size
    int id = 10;
    int size = 2; 
    
    databytes = protocolclass.Adddata(id,size);
    MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
    wdata->value0 = 1;
    wdata->value1 = 1;
   SendData(protocolclass)
}

void MyClass::SendData(MyProtocolclass &myprotolclass)
{
  // ... 

}

In dem Beispiel setze ich Werte im Struct MyDataclass manuell und sende 
Sie per Ethernet an ein Gerät. Die Klasse MyProtoclass ist gegeben und 
beinhaltet den kompletten Protokolloverhead und die Nutzdaten für das 
Gerät.

Das Beispiel orientiert sich am Hersteller des Gerätes und Protokolls 
zum Daten senden, klappt auch wunderbar.

Ich bekomme nun von anderen Applikationen nur die Nutzdaten als 
QByteArray und möchte die einfach übergeben und an das Gerät senden.

Das hier klappt auch wunderbar und es werden die Werte aus dem 
QByteArray an das Gerät gesendet:
void MyClass::handlemessage(const QByteArray &ba)
{
  
    MyProtocolclass protocolclass;
    protocolclass.Init();
    void *databytes;
    // set id and size
    int id = 10;
    int size = 2; 
    
    databytes = protocolclass.Adddata(id,size);
    MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
    wdata->value0 = ba[0];
    wdata->value1 = ba[1];
   SendData(protocolclass)
}

void MyClass::SendData(MyProtocolclass &myprotolclass)
{
  // ... 

}

Diesen Teil würde ich gerne ändern und die Daten im QByteArary direkt 
auf die Struktur kopieren und senden, ohne jeden einzeln Index 
anzugeben.
Hier ist auch der Nachteil, wenn die Struktur z.B. ein uint16 beinhaltet 
würde es mit dem Index gar nicht gehen.
    MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
    wdata->value0 = ba[0];
    wdata->value1 = ba[1];

Ist es jetzt nicht möglich einfach die Daten im QByteArray auf die 
Struktur MyDataClass zu kopieren? Ich würde kein Index benötigen und ich 
könnte auch unterschiedliche Typen z.B. uint16 übergeben.

Autor: Thorben (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habs nun hinbekommen.

void MyClass::handlemessage(const QByteArray &ba)
{
  
    MyProtocolclass protocolclass;
    protocolclass.Init();
    void *databytes;
    // set id and size
    int id = 10;
    int size = 2; 
    
    databytes = protocolclass.Adddata(id,size);
    MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
    memcpy(wdata,ba.data(),size);


   SendData(protocolclass)
}


Autor: mh (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Thorben schrieb:
> databytes = protocolclass.Adddata(id,size);
> myDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
> memcpy(wdata,ba.data(),size);

Gut, dass du den wdata Pointer zwischen der 2. und 3. Zeile nicht 
dereferenzierst. Das macht die 2. Zeile allerdings auch überflüssig.

Autor: Sven B. (scummos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ne das ist kaputt, das geht so nicht. Überleg doch mal wo das memcpy 
eigentlich hin kopiert.

Autor: Thorben (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Ne das ist kaputt, das geht so nicht. Überleg doch mal wo das memcpy
>eigentlich hin kopiert.


Läuft seit 18:00 Uhr im Dauerlauftest mit Werteänderungen im QByteArray. 
Die erste Stunde gab es keine Probleme und mehr weiss ich erst am 
Montag.
databytes = protocolclass.Adddata(id,size);
MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
memcpy(wdata,ba.data(),size);

Das Memcpy kopiert das ByteArray auf den Speicherbereich wdata.

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thorben schrieb:
> Das Memcpy kopiert das ByteArray auf den Speicherbereich wdata.

wdata ist ein Pointer kein Speicherbereich. Dieser Pointer zeigt 
hoffentlich auf einen zulässigen Speicherbereich. Was genau dieses 
Addddata zurück gibt, weißt nur du.

Wenn die Addata Funtkion einen void* zurück gibt, ist der 
reinterpret_cast unnötigt. Liefert sie etwas anderes zurück, hast du 
vermutlich undefined behaviour.

Autor: Thorben (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du hast natürlich recht wdata ist ein Pointer und der Pointer zeigt auf 
den Speicherbereich.

>Wenn die Addata Funtkion einen void* zurück gibt, ist der
>reinterpret_cast unnötigt.

Addata gibt ein void* zurück, welcher auf den Speicherbereich des 
dynamischen Buffers in der Protokolklasse zeigt.

Ich glaube hier hast du ebenfalls vollkommen recht. Ich denke der 
reinterpret_cast wird nur benötigt, wenn man gerne über die Struktur 
darauf zugreifen möchte.

Ich werde es  am Montag probieren direkt die Daten im QByteArray.data() 
mit memcopy zukopieren.

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thorben schrieb:
> Addata gibt ein void* zurück, welcher auf den Speicherbereich des
> dynamischen Buffers in der Protokolklasse zeigt.

Du solltest bei der Gelegenheit auch nochmal über deine Namen 
nachdenken. Ich habe mit Absicht einmal Addata und einmal Addddata 
geschrieben ;-) Bei SendData kommt man nicht so leicht durcheinander.

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thorben schrieb:
> Wenn ich nun die Daten aus dem QByteArray nehme, dann wird das
> QByteArray natürlich zerstört und die Daten sind weg.

Nein, das QByteArray wird zerstört, wenn die Funktion beendet ist. 
SendData() wird vor dem Beenden der Funktion aufgerufen.

Autor: PittyJ (Gast)
Datum:

Bewertung
-2 lesenswert
nicht lesenswert
Früher war das einfacher. Da hat man ein char Array genommen und fertig.

Heute wird das in
const QByteArray &ba
gepackt. Und mit
MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
gecastet.
Intern passiert wahrscheinlich das gleiche, als hätte man ein char Array 
genommen hätte. Nur durchblicken tut das keiner mehr.
Am besten programmiert man noch einen Iterator anstelle des memcpy.

Mein Fazit: Nehmt die Komplexität raus, dann versteht ihr euren eigenen 
Code wieder.

Autor: Vincent H. (vinci)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thorben schrieb:
> Ich glaube hier hast du ebenfalls vollkommen recht. Ich denke der
> reinterpret_cast wird nur benötigt, wenn man gerne über die Struktur
> darauf zugreifen möchte.

Nein, das Dereferenzieren von wdata wäre sofort UB (undefined 
behaviour).

Ansonsten liest sich dein Code ein wenig wie diese 
Gehirn-Trainings-Spielchen wo man den Namen einer Farbe in einem 
farbigen Text lesen soll. Also quasi "rot-blau-grün", der Text ist aber 
blau-grün-rot geschrieben...

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
PittyJ schrieb:
> Früher war das einfacher. Da hat man ein char Array genommen und fertig.

Da müsste man aber von Hand malloc und free machen und sich überlegen, 
wer dafür verantwortich ist. Wäre also umständlicher.

> Heute wird das in
> const QByteArray &ba
> gepackt. Und mit
> MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
> gecastet.

Ein Cast wäre so oder so nötig. Der hier ist klarer als ein klassischer 
C-Style-Cast, weil er genauer aussagt, was hier passiert.

> Intern passiert wahrscheinlich das gleiche, als hätte man ein char Array
> genommen hätte. Nur durchblicken tut das keiner mehr.

Du solltest nicht von dir auf andere schließen.

> Am besten programmiert man noch einen Iterator anstelle des memcpy.

Das wäre auch nicht sonderlich dramatisch und hätte mit std::copy 
gegenüber memcpy den Vorteil, dass es Typsicherheit bietet. Bei memcpy 
muss ich für die Benutzung ja erstmal den Typ komplett über Bord werfen.

Autor: Dirk K. (merciless)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Thorben schrieb:
>> Wenn ich nun die Daten aus dem QByteArray nehme, dann wird das
>> QByteArray natürlich zerstört und die Daten sind weg.
>
> Nein, das QByteArray wird zerstört, wenn die Funktion beendet ist.
> SendData() wird vor dem Beenden der Funktion aufgerufen.

Wieso wird das QByteArry zerstört?
Das wird als const Referenz an die Funktion
handlemessage() übergeben.

Da der TO keine Infos zum Kontext des Aufrufes
liefert, kann man darüber keine Aussage treffen.

merciless

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dirk K. schrieb:
>> Nein, das QByteArray wird zerstört, wenn die Funktion beendet ist.
>> SendData() wird vor dem Beenden der Funktion aufgerufen.
>
> Wieso wird das QByteArry zerstört?
> Das wird als const Referenz an die Funktion
> handlemessage() übergeben.

Da hast du natürlich recht. Das QByteArray wird in der Funktion gar 
nicht zersört.

> Da der TO keine Infos zum Kontext des Aufrufes
> liefert, kann man darüber keine Aussage treffen.

Naja, so oder so ist es beim Aufruf von SendData noch existent.

Autor: Thorben (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, anscheinend war mein Fehler wo anders. Der Test über das Wochenende 
ergab keine Probleme.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.