Ich habe versucht, die strengen Aliasing-Regeln zu verstehen, die für den Zeichenzeiger gelten.
Hier das ist angegeben:
Es wird immer davon ausgegangen, dass sich ein char* auf einen Alias eines beliebigen Objekts beziehen kann.
Ok, im Kontext des Socket-Codes kann ich Folgendes tun:
struct SocketMsg
{
int a;
int b;
};
int main(int argc, char** argv)
{
// Some code...
SocketMsg msgToSend;
msgToSend.a = 0;
msgToSend.b = 1;
send(socket, (char*)(&msgToSend), sizeof(msgToSend);
};
Aber dann gibt es diese Aussage
Die Umkehrung ist nicht wahr. Das Umwandeln eines char* in einen Zeiger eines anderen Typs als char* und dessen Dereferenzierung verstößt normalerweise gegen die strikte Aliasing-Regel.
Bedeutet dies, dass ich beim Empfangen eines Char-Arrays die Umwandlung in eine Struktur nicht neu interpretieren kann, wenn ich die Struktur der Nachricht kenne:
struct SocketMsgToRecv
{
int a;
int b;
};
int main()
{
SocketMsgToRecv* pointerToMsg;
char msgBuff[100];
...
recv(socket, msgBuff, 100);
// Ommiting make sure we have a complete message from the stream
// but lets assume msgBuff[0] has a complete msg, and lets interpret the msg
// SAFE!?!?!?
pointerToMsg = &msgBuff[0];
printf("Got Msg: a: %i, b: %i", pointerToMsg->a, pointerToMsg->b);
}
Wird dieses zweite Beispiel nicht funktionieren, weil der Basistyp ein Char-Array ist und ich es in eine Struktur umwandle? Wie gehen Sie mit dieser Situation in einer streng aliasierten Welt um?
Zu @Adam Rosenfield: Die Gewerkschaft wird eine Angleichung erreichen, solange der Lieferant des Char* damit begonnen hat, etwas Ähnliches zu tun.
Es kann nützlich sein, einen Schritt zurückzutreten und herauszufinden, worum es hier geht.
Die Grundlage für die Aliasing-Regel ist die Tatsache, dass Compiler Werte verschiedener einfacher Typen an verschiedenen Speichergrenzen platzieren können, um den Zugriff zu verbessern, und dass Hardware in einigen Fällen eine solche Ausrichtung erfordern kann, um den Zeiger überhaupt verwenden zu können. Dies kann auch in Strukturen auftreten, in denen es eine Vielzahl unterschiedlich großer Elemente gibt. Die Struktur kann an einer guten Grenze begonnen werden. Darüber hinaus kann der Compiler immer noch Slack Bites in das Innere der Struktur einführen, um eine ordnungsgemäße Ausrichtung der Strukturelemente zu erreichen, die dies erfordern.
Wenn man bedenkt, dass Compiler oft Optionen haben, um zu steuern, wie all dies gehandhabt wird oder nicht, können Sie sehen, dass es viele Möglichkeiten gibt, wie Überraschungen auftreten können. Dies ist besonders wichtig, wenn Zeiger auf Strukturen (als char* umgewandelt oder nicht) an Bibliotheken übergeben werden, die kompiliert wurden, um unterschiedliche Ausrichtungskonventionen zu erwarten.
Was ist mit Char*?
Die Vermutung über char* ist, dass sizeof(char) == 1 (relativ zu den Größen aller anderen Daten mit Größenangabe) und dass char*-Zeiger keine Ausrichtungsanforderung haben. Ein echtes Zeichen* kann also immer sicher herumgereicht und erfolgreich verwendet werden, ohne sich Gedanken über die Ausrichtung machen zu müssen, und das gilt für jedes Element eines Zeichens[] Array, Ausführen von ++ und — auf den Zeigern und so weiter. (Seltsamerweise ist void* nicht ganz dasselbe.)
Jetzt sollten Sie in der Lage sein zu sehen, wie Sie eine Art Strukturdaten in ein Zeichen übertragen[] Array, das selbst nicht richtig ausgerichtet war, kann der Versuch, auf einen Zeiger zurückzusetzen, der Ausrichtung(en) erfordert, ein ernsthaftes Problem darstellen.
Wenn Sie eine Vereinigung von char machen[] Array und einer Struktur, wird die anspruchsvollste Ausrichtung (dh die der Struktur) vom Compiler berücksichtigt. Dies funktioniert, wenn der Lieferant und der Verbraucher effektiv übereinstimmende Verbindungen verwenden, so dass das Casting der Struktur * in char * und zurück einwandfrei funktioniert.
In diesem Fall würde ich hoffen, dass die Daten in einer ähnlichen Union erstellt wurden, bevor der Zeiger darauf in char* umgewandelt oder auf andere Weise als Array von sizeof(char) Bytes übertragen wurde. Es ist auch wichtig sicherzustellen, dass alle Compileroptionen zwischen den verwendeten Bibliotheken und Ihrem eigenen Code kompatibel sind.
Richtig, das zweite Beispiel verstößt gegen die strengen Aliasing-Regeln, also wenn man mit dem kompiliert -fstrict-aliasing
Flag, besteht die Möglichkeit, dass Sie einen falschen Objektcode erhalten. Die vollständig korrekte Lösung wäre, hier eine Union zu verwenden:
union
{
SocketMsgToRecv msg;
char msgBuff[100];
};
recv(socket, msgBuff, 100);
printf("Got Msg: a: %i, b: %i", msg.a, msg.b);
Erfordert das zweite Stück Code nicht, dass Sie explizit casr ausführen? Hast du alle Warnungen aktiviert?
– Ajay Brahmakshatriya
13. November 2017 um 2:40 Uhr