Warum sind die C-gelieferten Integer-Typen für praktisch jedes Projekt nicht gut genug?

7

Ich bin viel mehr ein Systemadministrator als ein Programmierer. Aber ich verbringe übermäßig viel Zeit damit, den Programmierercode zu durchsuchen, um herauszufinden, was schief gelaufen ist. Und eine beunruhigende Menge von dieser Zeit wird mit Problemen verbracht, wenn der Programmierer eine Definition von __u_ll_int32_t oder was auch immer erwartete (ja, ich weiß, dass das nicht real ist), aber entweder die Datei erwartet, die diesen Typ definiert anders als es ist, oder (und das ist viel schlimmer, aber glücklicherweise selten) erwartete die Semantik dieser Definition etwas anderes als es ist.

Wie ich C verstehe, macht es absichtlich keine Breiten-Definitionen für Integer-Typen (und das ist eine gute Sache), sondern gibt dem Programmierer char , short , int , long , und long long , in allen ihren signierten und nicht signierten Ruhm, mit definierten Minima, die die Implementierung (hoffentlich) erfüllt. Darüber hinaus gibt es dem Programmierer verschiedene Makros, die die Implementierung bereitstellen muss, um Ihnen Dinge wie die Breite eines Zeichens, das größte unsignierte Lang usw. zu erklären. Und doch scheint das erste, was ein nicht-triviales C-Projekt zu tun scheint, entweder Importieren oder Erfinden eine andere Reihe von Typen, die ihnen explizit 8, 16, 32 und 64-Bit-Ganzzahlen geben. Das bedeutet, dass ich als Sysadmin diese Definitionsdateien an einem Ort haben muss, den der Programmierer erwartet (das ist schließlich mein Job), aber dann sind nicht alle Semantiken all dieser Definitionen gleich (dieses Rad ist es gewesen) oft neu erfunden) und es gibt keine nicht-ad-hoc-Art, die ich kenne, um alle Bedürfnisse meiner Benutzer hier zu befriedigen. (Ich habe manchmal darauf zurückgegriffen, ein & lt; bits / types_for_ralph.h & gt; zu machen, von dem ich weiß, dass Welpen jedes Mal weinen, wenn ich es tue.)

Was bedeutet der Versuch, die Bitbreite von Zahlen explizit zu definieren (in einer Sprache, die das nicht will), den Programmierer zu gewinnen, der all diese Probleme des Konfigurationsmanagements wert ist? Warum kennen die definierten Minima und die von der Plattform bereitgestellten MAX / MIN-Makros nicht genug, um das zu tun, was C-Programmierer tun wollen? Warum sollten Sie eine Sprache verwenden, deren Hauptvorteil darin besteht, dass sie über willkürlich gestochene Plattformen portierbar ist und sich dann in bestimmte Bitbreiten eingibt?

    
Bandrami 27.06.2014, 05:10
quelle

5 Antworten

11

Wenn ein C- oder C ++ - Programmierer (nachfolgend in der zweiten Person angesprochen) die Größe einer Integer-Variablen auswählt, ist dies normalerweise in einer der folgenden Situationen der Fall:

  • Sie kennen (zumindest grob) den gültigen Bereich für die Variable, basierend auf dem realen Wert, den sie repräsentiert. Beispielsweise,
    • numPassengersOnPlane in einem Reservierungssystem sollte das größte unterstützte Flugzeug aufnehmen, also mindestens 10 Bits. (Runde bis 16.)
    • numPeopleInState in einem US-Zensustabellen-Programm muss den bevölkerungsreichsten Staat aufnehmen (derzeit etwa 38 Millionen), also braucht es mindestens 26 Bits. (Runde bis 32.)

In diesem Fall möchten Sie die Semantik von int_leastN_t von <stdint.h> . Es ist üblich, dass Programmierer hier die exact-width intN_t verwenden, wenn technisch nicht; Allerdings sind 8/16/32/64-Bit-Maschinen heute so überwältigend dominant, dass die Unterscheidung lediglich akademischer Natur ist.

Sie könnten die Standardtypen verwenden und sich auf Einschränkungen wie " int muss mindestens 16 Bit" verlassen, aber ein Nachteil davon ist, dass es kein Standard maximum Größe für die Integer-Typen. Wenn int aus 32 Bits besteht, wenn Sie nur 16 benötigen, haben Sie die Größe Ihrer Daten unnötig verdoppelt. In vielen Fällen (siehe unten) ist dies kein Problem, aber wenn Sie ein Array von Millionen Zahlen haben, erhalten Sie viele Seitenfehler.

  • Ihre Zahlen müssen nicht so groß sein, aber aus Gründen der Effizienz möchten Sie einen schnellen, "nativen" Datentyp anstelle eines kleinen Datentyps, der bei Bitmaskierung oder Null- / Zeichenerweiterung Zeit verschwendet.

Dies sind die int_fastN_t -Typen in <stdint.h> . Es ist jedoch üblich, einfach das eingebaute int zu verwenden, das in den 16/32-Bit-Tagen die Semantik von int_fast16_t hatte. Es ist nicht der native Typ auf 64-Bit-Systemen, aber es ist normalerweise gut genug.

  • Die Variable ist eine Menge an Speicher, Array-Index oder Casted Pointer und benötigt daher eine Größe, die von der Größe des adressierbaren Speichers abhängt.

Dies entspricht den typedefs size_t , ptrdiff_t , intptr_t usw. Sie haben hier typedefs zu verwenden, da es no eingebauten Typ gibt das ist garantiert speichergroß.

  • Die Variable ist Teil einer Struktur, die mit fread / fwrite in eine Datei serialisiert oder aus einer Nicht-C-Sprache (Java, COBOL usw.) aufgerufen wird, die ihre eigenen Datentypen mit fester Breite hat.

In diesen Fällen benötigen Sie wirklich einen genauen Breitentyp.

  • Sie haben einfach nicht über den passenden Typ nachgedacht und verwenden int aus Gewohnheit.

Oft funktioniert das gut genug.

Zusammenfassend haben alle Typdefinitionen von <stdint.h> ihre Anwendungsfälle. Der Nutzen der eingebauten Typen ist jedoch begrenzt durch:

  • Fehlende maximale Größen für diese Typen.
  • Fehlen eines nativen Memsize-Typs.
  • Die willkürliche Wahl zwischen LP64 (auf Unix-ähnlichen Systemen) und LLP64 (unter Windows) Datenmodellen auf 64-Bit-Systemen.

Warum gibt es so viele redundante Typdefinitionen fester Breite ( WORD , DWORD , __int64 , gint64 , FINT64 usw.) und memsize ( INT_PTR , LPARAM , VPTRDIFF , etc.) Integer-Typen, hauptsächlich deshalb, weil <stdint.h> in Cs Entwicklung zu spät kam und die Leute immer noch ältere Compiler benutzen, die sie nicht unterstützen, also müssen Bibliotheken ihre definieren besitzen. Derselbe Grund, warum C ++ so viele String-Klassen hat.

    
dan04 27.06.2014, 06:15
quelle
4

Manchmal ist es wichtig. Zum Beispiel erfordern die meisten Bilddateiformate eine genaue Anzahl von Bits / Bytes, die verwendet (oder zumindest spezifiziert) werden müssen.

Wenn Sie nur eine Datei teilen möchten, die von demselben Compiler auf derselben Computerarchitektur erstellt wurde, wären Sie richtig (oder zumindest würden die Dinge funktionieren). Aber im wirklichen Leben werden Dinge wie Dateispezifikationen und Netzwerkpakete von einer Vielzahl von Computerarchitekturen und Compilern erzeugt, so dass wir uns in diesen Fällen (zumindest) um die Details kümmern müssen.

    
Dwayne Towell 27.06.2014 05:17
quelle
3

Der Hauptgrund, warum die grundlegenden Typen nicht behoben werden können, besteht darin, dass einige Maschinen keine 8-Bit-Bytes verwenden. Genug Programmierern ist es egal, oder sie wollen nicht, dass sie sich mit der Unterstützung solcher Biester beschäftigen, dass der Großteil des gut geschriebenen Codes eine bestimmte Anzahl von Bits erfordert, wo auch immer Überlauf ein Problem sein könnte. p>

Es ist besser, einen erforderlichen Bereich anzugeben, als int oder long direkt zu verwenden, da die Frage nach "relativ groß" oder "relativ klein" ziemlich bedeutungslos ist. Der Punkt ist zu wissen, mit welchen Eingaben das Programm arbeiten kann.

Übrigens gibt es normalerweise ein Compiler-Flag, das die eingebauten Typen anpasst. Siehe INT_TYPE_SIZE für GCC. Es könnte sauberer sein, das in das Makefile zu stecken, als die gesamte Systemumgebung mit neuen Headern zu spezialisieren.

    
Potatoswatter 27.06.2014 05:29
quelle
1

Wenn Sie portablen Code möchten, möchten Sie, dass der Code, den Sie schreiben, auf allen Plattformen identisch funktioniert. Wenn Sie

haben %Vor%

Sie können nicht sicher sagen, was i+1 Ihnen auf allen Plattformen geben wird.

Dies ist nicht tragbar. Einige Compiler (auf der gleichen CPU-Architektur!) Geben Ihnen -32768 und einige geben Ihnen 32768. Einige perverse werden Ihnen 0 geben. Das ist ein ziemlich großer Unterschied. Zugegeben, wenn es überläuft, ist dies Undefined Behavior, aber Sie wissen nicht, dass es UB ist, wenn Sie nicht genau wissen, wie groß int ist.

Wenn Sie die Standard-Integer-Definitionen verwenden (also <stdint.h> , ISO / IEC 9899: 1999), wissen Sie, dass die Antwort von +1 eine genaue Antwort liefert.

%Vor%     
Mark Lakata 27.06.2014 05:52
quelle
0

Hier spielen zwei gegnerische Kräfte eine Rolle:

  • Die Notwendigkeit, dass sich C auf jede CPU-Architektur auf natürliche Weise anpasst.
  • Die Notwendigkeit von Daten, die zu / von einem Programm (Netzwerk, Festplatte, Datei usw.) übertragen werden, damit ein Programm, das auf einer beliebigen Architektur ausgeführt wird, es richtig interpretieren kann.

Das Bedürfnis nach "CPU-Anpassung" hat mit der Effizienz zu tun. Es gibt eine CPU-Menge, die am einfachsten als eine einzige Einheit gehandhabt wird, an der alle arithmetischen Operationen einfach und effizient ausgeführt werden, und die dazu führt, daß die wenigsten Bits der Befehlscodierung benötigt werden. Dieser Typ ist int . Es könnte 16 Bits, 18 Bits *, 32 Bits, 36 Bits *, 64 Bits oder sogar 128 Bits auf einigen Maschinen sein. (* Das waren einige nicht bekannte Maschinen aus den 1960er und 1970er Jahren, die vielleicht nie einen C-Compiler hatten.)

Die Datenübertragungsanforderungen bei der Übertragung von Binärdaten erfordern, dass Datensatzfelder die gleiche Größe und Ausrichtung haben. Aus diesem Grund ist es wichtig, die Größe der Daten zu kontrollieren. Es gibt auch Endianness und vielleicht binäre Datendarstellungen, wie Gleitkommadarstellungen.

Ein Programm, das alle Integer-Operationen im Interesse der Größenkompatibilität zu 32 Bit zwingt, funktioniert auf einigen CPU-Architekturen gut, aber nicht auf anderen (besonders 16 Bit, aber vielleicht auch 64 Bit).

Die Verwendung der nativen CPU-Registergröße ist vorzuziehen, wenn der gesamte Datenaustausch in einem nicht binären Format wie XML oder SQL (oder einer anderen ASCII-Codierung) erfolgt.

    
wallyk 27.06.2014 05:32
quelle

Tags und Links