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


von Thorben (Gast)


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.
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?

von Sven B. (scummos)


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());

von derMosphet (Gast)


Lesenswert?

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.

von Thorben (Gast)


Lesenswert?

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

von Thorben (Gast)


Lesenswert?

>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?

von Sven B. (scummos)


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 ;)

von Thorben (Gast)


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:
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.

von Thorben (Gast)


Lesenswert?

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
}

von mh (Gast)


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.

von Sven B. (scummos)


Lesenswert?

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

von Thorben (Gast)


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

von mh (Gast)


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.

von Thorben (Gast)


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.

von mh (Gast)


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.

von Rolf M. (rmagnus)


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.

von PittyJ (Gast)


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.

von Vincent H. (vinci)


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

von Rolf M. (rmagnus)


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.

von Dirk K. (merciless)


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

von Rolf M. (rmagnus)


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.

von Thorben (Gast)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.