Ein interessantes Merkmal von C im Vergleich zu einigen anderen Sprachen ist, dass viele seiner Datentypen auf der Wortgröße der Zielarchitektur basieren und nicht absolut angegeben werden. Auf diese Weise kann die Sprache zwar zum Schreiben von Code auf Computern verwendet werden, auf denen bestimmte Typen möglicherweise Schwierigkeiten haben, es ist jedoch sehr schwierig, Code zu entwerfen, der auf verschiedenen Architekturen konsistent ausgeführt wird. Betrachten Sie den Code:
uint16_t ffff16 = 0xFFFF;
int64_t who_knows = ffff16 * ffff16;
Auf einer Architektur mit int
16 Bit (gilt immer noch für viele kleine Mikrocontroller) würde dieser Code unter Verwendung eines genau definierten Verhaltens den Wert 1 zuweisen. Auf Computern mit int
64 Bit wird ein Wert von 4294836225 zugewiesen, wobei wiederum ein genau definiertes Verhalten verwendet wird. Auf Rechnern mit int
32 Bit wird wahrscheinlich ein Wert von -131071 zugewiesen (ich weiß nicht, ob dies ein implementierungsdefiniertes oder ein nicht definiertes Verhalten ist). Obwohl der Code nichts anderes verwendet als nominell "festgelegte" Typen, würde der Standard erfordern, dass zwei verschiedene Arten von Compilern, die heute verwendet werden, zwei unterschiedliche Ergebnisse liefern, und viele populäre Compiler würden heute ein drittes liefern.
Dieses spezielle Beispiel ist insofern etwas ausgeklügelt, als ich nicht erwarten würde, dass im Code der realen Welt das Produkt zweier 16-Bit-Werte direkt einem 64-Bit-Wert zugewiesen wird, sondern es wurde als kurzes Beispiel ausgewählt, um drei Arten von Ganzzahlen zu zeigen Werbeaktionen können mit unsignierten Typen mit angeblich fester Größe interagieren. Es gibt Situationen in der Praxis, in denen Mathematik für vorzeichenlose Typen nach den Regeln der mathematischen Ganzzahlarithmetik ausgeführt werden muss, andere, in denen sie nach den Regeln der modularen Arithmetik ausgeführt werden muss, und solche, in denen dies nicht der Fall ist. t egal. Ein Großteil des realen Codes für Dinge wie Prüfsummen beruht auf dem uint32_t
Arithmetic Wrapping Mod 2³² und der Fähigkeit, eine beliebige Leistung zu erbringenuint16_t
arithmetik und erhalte ergebnisse, die mindestens als genau definiert sind mod 65536 (im gegensatz zum auslösen von undefiniertem verhalten).
Auch wenn diese Situation offensichtlich unerwünscht erscheint (und in zunehmendem Maße dazu führen wird, dass die 64-Bit-Verarbeitung für viele Zwecke zur Norm wird), zieht es das C-Normenkomitee nach meinen Beobachtungen vor, Sprachfunktionen einzuführen, die bereits in einigen bemerkenswerten Produktionen verwendet werden Umgebungen, anstatt sie "von Grund auf neu" zu erfinden. Gibt es bemerkenswerte Erweiterungen der C-Sprache, mit denen der Code nicht nur festlegen kann, wie ein Typ gespeichert wird, sondern auch, wie er sich in Szenarien mit möglichen Werbeaktionen verhalten soll? Ich sehe mindestens drei Möglichkeiten, wie eine Compilererweiterung solche Probleme lösen könnte:
Durch Hinzufügen einer Direktive, die den Compiler anweist, bestimmte "grundlegende" Ganzzahltypen auf bestimmte Größen zu zwingen.
Durch Hinzufügen einer Direktive, die den Compiler anweist, verschiedene Heraufstufungsszenarien zu bewerten, als ob die Maschinentypen bestimmte Größen hätten, unabhängig von den tatsächlichen Größen der Typen in der Zielarchitektur.
Indem Sie zulassen, dass Typen mit bestimmten Merkmalen deklariert werden (z. B. deklarieren, dass ein Typ sich unabhängig von der zugrunde liegenden Wortgröße wie ein Mod-65536-Umbruch-Algebra-Ring verhalten soll und nicht implizit in andere Typen konvertierbar sein soll; Hinzufügen von a
wrap32
zu anint
sollte a ergeben) Das Ergebnis des Typs,wrap32
unabhängig davon, obint
es größer als 16 Bit ist, während daswrap32
direkte Hinzufügen eines zu einemwrap16
ungültig sein sollte (da keines in das andere konvertieren kann).
Meine eigene Präferenz wäre die dritte Alternative, da selbst Maschinen mit ungewöhnlichen Wortgrößen mit viel Code arbeiten könnten, bei dem erwartet wird, dass Variablen wie bei Größen mit Zweierpotenzen "umbrochen" werden. Der Compiler muss möglicherweise Bitmaskierungsanweisungen hinzufügen, damit sich der Typ angemessen verhält. Wenn der Code jedoch einen Typ benötigt, der Mod 65536 umschließt, ist es besser, wenn der Compiler eine solche Maskierung auf Maschinen generiert, die dies benötigen, als den Quellcode damit zu überladen oder einfach einen solchen Code haben, der auf Maschinen unbrauchbar ist, auf denen eine solche Maskierung erforderlich wäre. Ich bin jedoch neugierig, ob es übliche Erweiterungen gibt, die mit einem der oben genannten Mittel oder mit Mitteln, an die ich nicht gedacht habe, tragbares Verhalten erzielen.
Um zu verdeutlichen, wonach ich suche, gibt es ein paar Dinge; vor allem:
Es gibt zwar viele Möglichkeiten, wie Code geschrieben werden kann, um die gewünschte Semantik sicherzustellen (z. B. das Definieren von Makros für die Ausführung von Berechnungen für nicht vorzeichenbehaftete Operanden bestimmter Größe, um ein Ergebnis zu erzielen, das explizit umbrochen wird oder nicht) oder zumindest unerwünschte Ereignisse verhindert Semantik (z. B. bedingte Definition eines Typs
wrap32_t
füruint32_t
Compiler, auf denen auint32_t
nicht heraufgestuft wird, und Angabe, dass es besser für Code ist, bei dem diewrap32_t
Kompilierung auf Computern fehlschlagen muss, auf denen dieser Typ heraufgestuft wird, als dass er ausgeführt wird und ein falsches Verhalten ergibt), Wenn es eine Möglichkeit gibt, den Code zu schreiben, der für zukünftige Spracherweiterungen am besten geeignet ist, ist es besser, diesen Code zu verwenden, als meinen eigenen Ansatz zu entwickeln.Ich habe einige ziemlich solide Ideen, wie die Sprache erweitert werden könnte, um viele ganzzahlige Probleme zu lösen, sodass der Code auf Computern mit unterschiedlichen Wortgrößen die gleiche Semantik ergibt, aber bevor ich sie aufschreibe, möchte ich zu wissen, welche Anstrengungen in diese Richtung bereits unternommen wurden.
Ich möchte in keiner Weise den C-Normenausschuss oder die von ihm geleistete Arbeit herabsetzen. Ich gehe jedoch davon aus, dass es in einigen Jahren erforderlich sein wird, den Code auf Computern, auf denen der "natürliche" Promotion-Typ 32 Bit beträgt, sowie auf Computern, auf denen er 64 Bit beträgt, ordnungsgemäß auszuführen. Ich denke, mit einigen bescheidenen Erweiterungen der Sprache (bescheidener als viele der anderen Änderungen zwischen C99 und C14) wäre es möglich, nicht nur eine saubere Art der effizienten Verwendung von 64-Bit-Architekturen bereitzustellen, sondern auch die Interaktion mit ihnen zu vereinfachen die „Unusual-Wort-size“ Maschinen , die die Standard - historisch umgebogenen rückwärts Träger [eg es möglich , dass ein Maschine mit einem 12-Bit - Herstellung char
zu laufen Code, der eine erwartetuint32_t
um mod 2³² zu wickeln]. Abhängig von der Richtung, in die zukünftige Erweiterungen gehen, würde ich auch erwarten, dass es möglich sein sollte, Makros zu definieren, mit denen heute geschriebener Code auf heutigen Compilern verwendet werden kann, bei denen sich die Standard-Integer-Typen wie "erwartet" verhalten, aber auch auf zukünftigen Compilern, bei denen Integer verwendet werden können Typen würden sich standardmäßig anders verhalten, aber wo können die erforderlichen Verhaltensweisen zur Verfügung gestellt werden.
quelle
int
größer alsuint16_t
würden die Operanden der Multiplikation zu fördernint
und die Multiplikation durchgeführt werden soll , wieint
Multiplikation und der resultierendeint
Wert umgewandelt würdeint64_t
die für die Initialisierungwho_knows
.int
, aber es schleicht sich immer noch hinein. (Wieder unter der Annahme, dass mein Verständnis des C-Standards korrekt ist.)Antworten:
Als die typische Absicht von Code wie folgt
Um die Multiplikation in 64 Bit durchzuführen (die Größe der Variablen, in der das Ergebnis gespeichert wird), müssen Sie normalerweise einen der Operanden verwenden, um eine 64-Bit-Multiplikation zu erzwingen:
Ich habe noch nie C-Erweiterungen gefunden, die diesen Vorgang automatisieren.
quelle
uint32_t
ein Makro umwandeln oder ein Makro verwenden, das als eines#define UMUL1616to16(x,y)((uint16_t)((uint16_t)(x)*(uint16_t)(y)))
oder#define UMUL1616to16(x,y)((uint16_t)((uint32_t)(x)*(uint16_t)(y)))
abhängig von der Größe von definiert istint
), sondern ob es welche gibt neue Standards für den sinnvollen Umgang mit solchen Dingen, anstatt meine eigenen Makros zu definieren.(ushort1*ushort2) & 65535u
es, eine Mod-65536-Arithmetik für alle Operandenwerte durchzuführen. Wenn man die C89-Begründung liest, ist es meines Erachtens ziemlich klar, dass die Autoren zwar erkannten, dass ein solcher Code bei einigen Implementierungen fehlschlagen könnte, wenn das Ergebnis 2147483647 überschreitet, sie jedoch damit rechnen, dass solche Implementierungen zunehmend seltener werden. Ein solcher Code schlägt jedoch manchmal auf modernen gcc fehl.