Qt C++ QByteArray.constdata() copy

Gast #5964048
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.
1
void MyClass::handlemessage(const QByteArray &ba)
2
{
3
  
4
    MyProtocolclass protocolclass;
5
    protocolclass.Init();
6
    void *databytes;
7
    // set id and size
8
    int id = 10;
9
    int size = 2; 
10
    
11
    databytes = protocolclass.Adddata(id,size);
12
    MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
13
    wdata->value0 = 1;
14
    wdata->value1 = 1;
15
    // ....
16
    // Das klappt wunderbar, aber es wird auch nicht die Daten aus dem QByteArray genutzt
17
  SendData(protocolclass)
18
}
19

20
void MyClass::SendData(MyProtocolclass &myprotolclass)
21
{
22
  // ... 
23

24
}


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

15
    // Werte sind hier gesetzt wie gewollt
16

17
    SendData(protocolclass)
18
}
19

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

Wie kann ich die Daten sauber an die Klasse MyProtocolclass übergeben, 
so das die Daten noch gültig sind?
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());
Gast
In der Zeile:
1
writedata = (MyDataClass*) ba.constData();

speicherst du einen Pointer auf deine Daten irgendwo hin und erwartest 
dann hier
1
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.
Gast
Hallo, ja ganz glücklich bin ich auch noch nicht, aber dazu muss erst 
mehr Erfahrung sammeln.
1
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
Gast
>das deine Daten in einem bis jetzt komplett unbeteiligten Objekt stehen?


1
void *databytes;
2
// set id and size
3
int id = 10;
4
int size = 2; 
5
databytes = protocolclass.Adddata(id,size);
6
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?
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 ;)
Gast
>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:
1
void MyClass::handlemessage(const QByteArray &ba)
2
{
3
  
4
    MyProtocolclass protocolclass;
5
    protocolclass.Init();
6
    void *databytes;
7
    // set id and size
8
    int id = 10;
9
    int size = 2; 
10
    
11
    databytes = protocolclass.Adddata(id,size);
12
    MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
13
    wdata->value0 = 1;
14
    wdata->value1 = 1;
15
   SendData(protocolclass)
16
}
17

18
void MyClass::SendData(MyProtocolclass &myprotolclass)
19
{
20
  // ... 
21

22
}

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:
1
void MyClass::handlemessage(const QByteArray &ba)
2
{
3
  
4
    MyProtocolclass protocolclass;
5
    protocolclass.Init();
6
    void *databytes;
7
    // set id and size
8
    int id = 10;
9
    int size = 2; 
10
    
11
    databytes = protocolclass.Adddata(id,size);
12
    MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
13
    wdata->value0 = ba[0];
14
    wdata->value1 = ba[1];
15
   SendData(protocolclass)
16
}
17

18
void MyClass::SendData(MyProtocolclass &myprotolclass)
19
{
20
  // ... 
21

22
}

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.
1
    MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
2
    wdata->value0 = ba[0];
3
    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.
Gast
Habs nun hinbekommen.
1
void MyClass::handlemessage(const QByteArray &ba)
2
{
3
  
4
    MyProtocolclass protocolclass;
5
    protocolclass.Init();
6
    void *databytes;
7
    // set id and size
8
    int id = 10;
9
    int size = 2; 
10
    
11
    databytes = protocolclass.Adddata(id,size);
12
    MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
13
    memcpy(wdata,ba.data(),size);
14

15

16
   SendData(protocolclass)
17
}
Gast
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.
Gast
>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.
1
databytes = protocolclass.Adddata(id,size);
2
MyDataclass* wdata = reinterpret_cast<MyDataclass*>(databytes);
3
memcpy(wdata,ba.data(),size);

Das Memcpy kopiert das ByteArray auf den Speicherbereich wdata.
Gast
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.
Gast
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.
Gast
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.
Gast
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.
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...
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.
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
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.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 7 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.