In C ++ , sizeof('a') == sizeof(char) == 1
. Dies ist intuitiv sinnvoll, da 'a'
es sich um ein Zeichenliteral handelt und sizeof(char) == 1
wie im Standard definiert.
In C jedoch sizeof('a') == sizeof(int)
. Das heißt, es scheint, dass C-Zeichenliterale tatsächlich ganze Zahlen sind. Weiß jemand warum? Ich kann viele Erwähnungen dieser C-Eigenart finden, aber keine Erklärung dafür, warum sie existiert.
Antworten:
Diskussion zum gleichen Thema
quelle
char
Variable kein int ist, also ist es ein Sonderfall, ein Zeichen konstant zu machen. Und es ist einfach, einen Charakterwert zu verwenden, ohne ihn zu fördern :c1 = c2;
. OTOHc1 = 'x'
ist eine Abwärtsumwandlung . Am wichtigsten istsizeof(char) != sizeof('x')
, was ein ernsthafter Sprachpfusch ist. Multibyte-Zeichenkonstanten: Sie sind der Grund, aber veraltet.Die ursprüngliche Frage lautet "Warum?"
Der Grund dafür ist, dass sich die Definition eines Literalzeichens weiterentwickelt und geändert hat, während versucht wurde, abwärtskompatibel mit vorhandenem Code zu bleiben.
In den dunklen Tagen des frühen C gab es überhaupt keine Typen. Als ich das Programmieren in C zum ersten Mal lernte, wurden Typen eingeführt, aber Funktionen hatten keine Prototypen, um dem Aufrufer die Argumenttypen mitzuteilen. Stattdessen wurde standardisiert, dass alles, was als Parameter übergeben wird, entweder die Größe eines Int hat (dies schließt alle Zeiger ein) oder ein Double.
Dies bedeutete, dass beim Schreiben der Funktion alle Parameter, die nicht doppelt waren, als Ints auf dem Stapel gespeichert wurden, unabhängig davon, wie Sie sie deklariert haben, und der Compiler Code in die Funktion einfügte, um dies für Sie zu erledigen.
Dies machte die Dinge etwas inkonsistent. Als K & R ihr berühmtes Buch schrieb, setzten sie die Regel ein, dass ein Zeichenliteral in jedem Ausdruck immer zu einem int heraufgestuft wird, nicht nur zu einem Funktionsparameter.
Als das ANSI-Komitee C zum ersten Mal standardisierte, änderten sie diese Regel so, dass ein Zeichenliteral einfach ein int war, da dies ein einfacherer Weg war, dasselbe zu erreichen.
Bei der Entwicklung von C ++ mussten alle Funktionen über vollständige Prototypen verfügen (dies ist in C immer noch nicht erforderlich, obwohl es allgemein als bewährte Methode akzeptiert wird). Aus diesem Grund wurde entschieden, dass ein Zeichenliteral in einem Zeichen gespeichert werden kann. Dies hat in C ++ den Vorteil, dass eine Funktion mit einem char-Parameter und eine Funktion mit einem int-Parameter unterschiedliche Signaturen haben. Dieser Vorteil ist bei C nicht der Fall.
Deshalb sind sie unterschiedlich. Evolution...
quelle
void f(unsigned char)
Vsvoid f(signed char)
.f('a')
, dass Sie wahrscheinlich möchten, dass die Überlastungsauflösungf(char)
für diesen Aufruf gewählt wird und nichtf(int)
. Die relativen Größen vonint
undchar
sind nicht relevant, wie Sie sagen.Ich kenne die spezifischen Gründe nicht, warum ein Zeichenliteral in C vom Typ int ist. Aber in C ++ gibt es einen guten Grund, diesen Weg nicht zu gehen. Bedenken Sie:
Sie würden erwarten, dass der Aufruf zum Drucken die zweite Version mit einem Zeichen auswählt. Ein Charakter-Literal als Int zu haben, würde dies unmöglich machen. Beachten Sie, dass in C ++ - Literalen mit mehr als einem Zeichen immer noch der Typ int vorhanden ist, obwohl ihr Wert durch die Implementierung definiert ist. Also,
'ab'
hat Typint
, während'a'
hat Typchar
.quelle
Mit gcc auf meinem MacBook versuche ich:
was beim Ausführen gibt:
was darauf hindeutet, dass ein Zeichen 8 Bit hat, wie Sie vermuten, aber ein Zeichenliteral ist ein int.
quelle
Als C geschrieben wurde, hatte die MACRO-11-Assemblersprache des PDP-11:
Diese Art von Dingen ist in der Assemblersprache weit verbreitet - die niedrigen 8 Bits enthalten den Zeichencode, andere Bits werden auf 0 gelöscht. PDP-11 hatte sogar:
Dies bot eine bequeme Möglichkeit, zwei Zeichen in die niedrigen und hohen Bytes des 16-Bit-Registers zu laden. Sie können diese dann an eine andere Stelle schreiben und einige Textdaten oder den Bildschirmspeicher aktualisieren.
Die Idee, Zeichen zur Registergröße zu befördern, ist also ganz normal und wünschenswert. Angenommen, Sie müssen 'A' nicht als Teil des fest codierten Opcodes in ein Register eintragen, sondern von einem Ort im Hauptspeicher, der Folgendes enthält:
Wenn Sie nur ein 'A' aus diesem Hauptspeicher in ein Register einlesen möchten, welches würden Sie lesen?
Einige CPUs unterstützen möglicherweise nur das direkte Einlesen eines 16-Bit-Werts in ein 16-Bit-Register, was bedeuten würde, dass beim Lesen bei 20 oder 22 die Bits von 'X' gelöscht werden müssen, und dies hängt von der Endigkeit der CPU ab müsste in das Byte niedriger Ordnung verschoben werden.
Einige CPUs erfordern möglicherweise einen speicherausgerichteten Lesevorgang. Dies bedeutet, dass die niedrigste betroffene Adresse ein Vielfaches der Datengröße sein muss: Möglicherweise können Sie von den Adressen 24 und 25 lesen, nicht jedoch von den Adressen 27 und 28.
Ein Compiler, der Code generiert, um ein 'A' in das Register zu bringen, kann es daher vorziehen, ein wenig zusätzlichen Speicher zu verschwenden und den Wert als 0 'A' oder 'A' 0 zu codieren - abhängig von der Endianität und auch sicherzustellen, dass er richtig ausgerichtet ist ( dh nicht an einer ungeraden Speicheradresse).
Ich vermute, dass Cs diese Ebene des CPU-zentrierten Verhaltens einfach übernommen haben, indem sie an Zeichenkonstanten gedacht haben, die Registergrößen des Speichers belegen, was die allgemeine Einschätzung von C als "High-Level-Assembler" bestätigt.
(Siehe 6.3.3 auf Seite 6-25 von http://www.dmv.net/dec/pdf/macro.pdf )
quelle
Ich erinnere mich, wie ich K & R gelesen und ein Code-Snippet gesehen habe, das jeweils ein Zeichen las, bis es EOF traf. Da alle Zeichen gültige Zeichen für einen Datei- / Eingabestream sind, bedeutet dies, dass EOF kein Zeichenwert sein kann. Der Code hat das gelesene Zeichen in ein int eingefügt, dann auf EOF getestet und dann in ein Zeichen konvertiert, wenn dies nicht der Fall war.
Mir ist klar, dass dies Ihre Frage nicht genau beantwortet, aber es wäre sinnvoll, wenn der Rest der Zeichenliterale sizeof (int) wäre, wenn das EOF-Literal wäre.
quelle
Ich habe keine Begründung dafür gesehen (C-Zeichen-Literale sind int-Typen), aber hier ist etwas, was Stroustrup dazu zu sagen hatte (aus Design und Evolution 11.2.1 - Feinkornauflösung):
Zum größten Teil sollte es also keine Probleme verursachen.
quelle
Der historische Grund dafür ist, dass C und sein Vorgänger B ursprünglich auf verschiedenen Modellen von DEC PDP-Minicomputern mit verschiedenen Wortgrößen entwickelt wurden, die 8-Bit-ASCII unterstützten, aber nur Arithmetik für Register ausführen konnten. (Nicht der PDP-11, der später kam.) Frühere Versionen von C wurden
int
als native Wortgröße der Maschine definiert, und jeder Wert, der kleiner als a istint
, musste erweitertint
werden, um an oder von einer Funktion übergeben zu werden oder in einem bitweisen, logischen oder arithmetischen Ausdruck verwendet, weil die zugrunde liegende Hardware so funktionierte.Das ist auch der Grund, warum die Regeln für die Ganzzahl-Heraufstufung immer noch besagen, dass jeder Datentyp, der kleiner als ein
int
ist, heraufgestuft wirdint
. C-Implementierungen dürfen aus ähnlichen historischen Gründen auch die Eins-Komplement-Mathematik anstelle der Zwei-Komplement-Mathematik verwenden. Der Grund dafür, dass Oktalzeichen entkommen und Oktalkonstanten im Vergleich zu Hex erstklassige Bürger sind, liegt ebenfalls darin, dass diese frühen DEC-Minicomputer Wortgrößen hatten, die in Drei-Byte-Blöcke, aber nicht in Vier-Byte-Halbbytes unterteilt werden konnten.quelle
char
war genau 3 Oktalstellen langDies ist das richtige Verhalten, das als "integrale Werbung" bezeichnet wird. Es kann auch in anderen Fällen passieren (hauptsächlich binäre Operatoren, wenn ich mich richtig erinnere).
BEARBEITEN: Nur um sicherzugehen, habe ich meine Kopie von Expert C Programming: Deep Secrets überprüft und bestätigt, dass ein Zeichenliteral nicht mit einem Typ int beginnt . Es ist zunächst vom Typ char , aber wenn es in einem verwendet wird , Ausdruck wird gefördert zu einem int . Folgendes wird aus dem Buch zitiert:
quelle
Ich weiß es nicht, aber ich denke, es war einfacher, es so zu implementieren, und es war nicht wirklich wichtig. Erst in C ++, als der Typ bestimmen konnte, welche Funktion aufgerufen werden würde, musste sie behoben werden.
quelle
Das wusste ich wirklich nicht. Bevor es Prototypen gab, wurde alles, was schmaler als ein int war, in ein int konvertiert, wenn es als Funktionsargument verwendet wurde. Das kann Teil der Erklärung sein.
quelle
char
inint
würde es ziemlich unnötig machen , dass Zeichenkonstanten Ints sind. Relevant ist, dass die Sprache Zeichenkonstanten anders behandelt (indem sie ihnen einen anderen Typ gibt) alschar
Variablen, und was benötigt wird, ist eine Erklärung dieses Unterschieds.Dies ist nur tangential zur Sprachspezifikation, aber in der Hardware hat die CPU normalerweise nur eine Registergröße - sagen wir 32 Bit - und wann immer es tatsächlich auf einem Zeichen funktioniert (durch Addieren, Subtrahieren oder Vergleichen), gibt es eine implizite Konvertierung in int, wenn es in das Register geladen wird. Der Compiler sorgt dafür, dass die Zahl nach jeder Operation richtig maskiert und verschoben wird. Wenn Sie beispielsweise 2 zu (vorzeichenloses Zeichen) 254 hinzufügen, wird sie auf 0 anstatt auf 256 umgebrochen, aber im Silizium ist es wirklich ein Int bis Sie es wieder im Speicher speichern.
Es ist eine Art akademischer Punkt, da die Sprache ohnehin einen 8-Bit-Literaltyp hätte angeben können, aber in diesem Fall spiegelt die Sprachspezifikation genauer wider, was die CPU wirklich tut.
(x86-Wonks stellen möglicherweise fest, dass es z. B. ein natives Add-Op gibt, das die Short-Wide-Register in einem Schritt hinzufügt. Innerhalb des RISC-Kerns bedeutet dies jedoch zwei Schritte: Addieren Sie die Zahlen und erweitern Sie das Vorzeichen wie ein Add / Extsh-Paar der PowerPC)
quelle
char
Variablen unterschiedliche Typen haben. Automatische Heraufstufungen, die die Hardware widerspiegeln, sind nicht relevant - sie sind tatsächlich nicht relevant, dachar
Variablen automatisch heraufgestuft werden, sodass Zeichenliterale nicht vom Typ sindchar
. Der wahre Grund sind Multibyte-Literale, die mittlerweile veraltet sind.