Diese Frage wird von mir motiviert, kryptografische Algorithmen (z. B. SHA-1) in C / C ++ zu implementieren, tragbaren plattformunabhängigen Code zu schreiben und undefiniertes Verhalten gründlich zu vermeiden .
Angenommen, ein standardisierter Krypto-Algorithmus fordert Sie auf, Folgendes zu implementieren:
b = (a << 31) & 0xFFFFFFFF
wo aund bsind vorzeichenlose 32-Bit-Ganzzahlen. Beachten Sie, dass wir im Ergebnis alle Bits verwerfen, die über den niedrigstwertigen 32 Bits liegen.
Als erste naive Annäherung könnten wir annehmen, dass diese intauf den meisten Plattformen 32 Bit breit ist, also würden wir schreiben:
unsigned int a = (...);
unsigned int b = a << 31;
Wir wissen, dass dieser Code nicht überall funktioniert, da er intauf einigen Systemen 16 Bit, auf anderen 64 Bit und möglicherweise sogar 36 Bit breit ist. Mit stdint.hkönnen wir diesen Code jedoch mit dem folgenden uint32_tTyp verbessern :
uint32_t a = (...);
uint32_t b = a << 31;
Also sind wir fertig, oder? Das habe ich mir jahrelang gedacht. ... Nicht ganz. Angenommen, auf einer bestimmten Plattform haben wir:
// stdint.h
typedef unsigned short uint32_t;
Die Regel für die Ausführung von arithmetischen Operationen in C / C ++ lautet: Wenn der Typ (z. B. short) schmaler als ist int, wird er erweitert, intwenn alle Werte passen können oder auf unsigned intandere Weise.
Angenommen, der Compiler definiert shortals 32 Bit (signiert) und intals 48 Bit (signiert). Dann diese Codezeilen:
uint32_t a = (...);
uint32_t b = a << 31;
wird effektiv bedeuten:
unsigned short a = (...);
unsigned short b = (unsigned short)((int)a << 31);
Beachten Sie, dass zu abefördert wird, intweil alle ushort(dh uint32) in int(dh int48) passen .
Aber jetzt haben wir ein Problem: Das Verschieben von Nicht-Null-Bits in das Vorzeichenbit eines vorzeichenbehafteten Integer-Typs ist ein undefiniertes Verhalten . Dieses Problem trat auf, weil wir uint32befördert wurden int48- anstatt befördert zu werden uint48(wo Linksverschiebung in Ordnung wäre).
Hier sind meine Fragen:
Ist meine Argumentation richtig und ist dies theoretisch ein legitimes Problem?
Ist dieses Problem sicher zu ignorieren, da auf jeder Plattform der nächste Ganzzahltyp doppelt so breit ist?
Ist es eine gute Idee, sich gegen diese pathologische Situation richtig zu verteidigen, indem Sie die Eingabe wie folgt vormaskieren?:
b = (a & 1) << 31;. (Dies muss auf jeder Plattform korrekt sein. Dies kann jedoch dazu führen, dass ein geschwindigkeitskritischer Krypto-Algorithmus langsamer als erforderlich ist.)
Erläuterungen / Änderungen:
Ich akzeptiere Antworten für C oder C ++ oder beides. Ich möchte die Antwort für mindestens eine der Sprachen wissen.
Die Vormaskierungslogik kann die Bitrotation beeinträchtigen. Beispielsweise wird GCC
b = (a << 31) | (a >> 1);zu einem 32-Bit-Bitrotationsbefehl in Assemblersprache kompiliert . Wenn wir jedoch die Linksverschiebung vormaskieren, ist es möglich, dass die neue Logik nicht in Bitrotation übersetzt wird, was bedeutet, dass jetzt 4 Operationen anstelle von 1 ausgeführt werden.