Warum sind C-Zeichenliterale Ints statt Chars?

Lesezeit: 9 Minuten

Warum sind C Zeichenliterale Ints statt Chars
Josef Garwin

In C++, sizeof('a') == sizeof(char) == 1. Dies macht intuitiv Sinn, da 'a' ist ein Zeichenliteral, und sizeof(char) == 1 wie von der Norm definiert.

In C hingegen 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.

  • sizeof würde nur die Größe eines Bytes zurückgeben, oder? Sind ein char und ein int nicht gleich groß?

    – Josh Smeaton

    11. Januar 2009 um 23:13 Uhr

  • Dies ist wahrscheinlich abhängig vom Compiler (und der Architektur). Möchten Sie sagen, was Sie verwenden? Der Standard (zumindest bis ’89) war sehr locker.

    – dmckee — Ex-Moderator-Kätzchen

    11. Januar 2009 um 23:14 Uhr

  • Nein. ein char ist immer 1 byte groß, also sizeof(‘a’) == 1 immer (in c++), während ein int das kann theoretisch sizeof 1 sein, aber das würde ein Byte mit mindestens 16 Bit erfordern, was ist sehr unwahrscheinlich 🙂 also sizeof(‘a’) != sizeof(int) ist sehr wahrscheinlich in C++ in den meisten Implementierungen

    – Johannes Schaub – litb

    11. Januar 2009 um 23:30 Uhr

  • … während es in C immer falsch ist.

    – Johannes Schaub – litb

    11. Januar 2009 um 23:31 Uhr

  • ‘a’ ist ein int in C – Punkt. C war zuerst dort – C machte die Regeln. C++ hat die Regeln geändert. Sie können argumentieren, dass die C++-Regeln sinnvoller sind, aber eine Änderung der C-Regeln würde mehr schaden als nützen, also hat das C-Standardkomitee dies klugerweise nicht berührt.

    – Jonathan Leffler

    12. Januar 2009 um 1:12 Uhr

Diskussion an gleiches Thema

“Genauer gesagt die integralen Beförderungen. In K&R C war es praktisch (?) unmöglich, einen Zeichenwert zu verwenden, ohne dass er zuerst int hochgestuft wurde, also eliminierte die Zeichenkonstante von vornherein diesen Schritt. Es gab und gibt mehrere Zeichen Konstanten wie ‘abcd’ oder wie viele auch immer in ein int passen.”

  • Konstanten mit mehreren Zeichen sind nicht portierbar, nicht einmal zwischen Compilern auf einem einzelnen Rechner (obwohl GCC plattformübergreifend selbstkonsistent zu sein scheint). Siehe: stackoverflow.com/questions/328215

    – Jonathan Leffler

    12. Januar 2009 um 1:10 Uhr

  • Ich möchte anmerken, dass a) dieses Zitat nicht zugeschrieben ist; Das Zitat sagt lediglich: “Würden Sie dieser Meinung nicht zustimmen, die in einem früheren Thread gepostet wurde, in dem das fragliche Thema diskutiert wurde?” … und b) Es ist lächerlichweil ein char variable ist kein int, also ist es ein Sonderfall, eine Zeichenkonstante zu einem zu machen. Und es ist einfach, einen Zeichenwert zu verwenden, ohne ihn zu bewerben: c1 = c2;. OTOH, c1 = 'x' ist eine Abwärtskonvertierung. Am wichtigsten, sizeof(char) != sizeof('x'), was ernsthafter Sprachpfusch ist. Was Multibyte-Zeichenkonstanten betrifft: Sie sind der Grund, aber sie sind veraltet.

    – Jim Balter

    16. März 2011 um 13:02 Uhr

Die ursprüngliche Frage ist “warum?”

Der Grund dafür ist, dass sich die Definition eines Literalzeichens weiterentwickelt und geändert hat, während versucht wurde, rückwärtskompatibel mit bestehendem Code zu bleiben.

In den dunklen Tagen des frühen C gab es überhaupt keine Typen. Als ich zum ersten Mal lernte, in C zu programmieren, waren Typen eingeführt worden, aber Funktionen hatten keine Prototypen, um dem Aufrufer mitzuteilen, was die Argumenttypen waren. Stattdessen wurde standardisiert, dass alles, was als Parameter übergeben wird, entweder die Größe eines int (dies schließt alle Zeiger ein) oder ein Double hat.

Das bedeutete, dass beim Schreiben der Funktion alle Parameter, die nicht doppelt waren, als ints auf dem Stack gespeichert wurden, egal wie Sie sie deklariert haben, und der Compiler Code in die Funktion eingefügt hat, um dies für Sie zu handhaben.

Dies machte die Dinge etwas inkonsistent, und als K&R ihr berühmtes Buch schrieb, stellten sie die Regel auf, dass ein Zeichenliteral in jedem Ausdruck immer zu einem Int hochgestuft 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 zu sein schien, dasselbe zu erreichen.

Als C++ entworfen wurde, 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 in C nicht gegeben.

Deshalb sind sie unterschiedlich. Evolution…

  • +1 von mir, weil ich tatsächlich auf „Warum?“ geantwortet habe. Aber ich bin mit der letzten Aussage nicht einverstanden – “Der Vorteil davon in C++ ist, dass eine Funktion mit einem char-Parameter und eine Funktion mit einem int-Parameter unterschiedliche Signaturen haben” – in C++ ist es immer noch möglich, dass 2 Funktionen Parameter von haben gleiche Größe und unterschiedliche Signaturen, z void f(unsigned char) Vs void f(signed char).

    – PeterK

    7. Februar 2017 um 17:37 Uhr


  • @ PeterK John hätte es besser ausdrücken können, aber was er sagt, ist im Wesentlichen korrekt. Die Motivation für den Wechsel in C++ war, wenn Sie schreiben f('a')möchten Sie wahrscheinlich die Überladungsauflösung auswählen f(char) für diesen Anruf eher als f(int). Die relativen Größen von int und char nicht relevant, wie Sie sagen.

    – zol

    9. Februar 2017 um 18:53 Uhr


Warum sind C Zeichenliterale Ints statt Chars
Johannes Schaub – litb

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:

void print(int);
void print(char);

print('a');

Sie würden erwarten, dass der Aufruf zum Drucken die zweite Version auswählt, die ein Zeichen nimmt. Ein Zeichenliteral als int zu haben, würde das unmöglich machen. Beachten Sie, dass in C++ Literale mit mehr als einem Zeichen immer noch den Typ int haben, obwohl ihr Wert implementierungsdefiniert ist. Damit, 'ab' Typ hat intwährend 'a' Typ hat char.

  • Ja, “Design and Evolution of C++” sagt, dass überladene Ein-/Ausgaberoutinen der Hauptgrund waren, warum C++ die Regeln geändert hat.

    – Max Lybbert

    12. Januar 2009 um 8:25 Uhr

  • Max, ja, ich habe geschummelt. Ich habe im Standard im Kompatibilitätsbereich nachgesehen 🙂

    – Johannes Schaub – litb

    12. Januar 2009 um 10:55 Uhr

1647171013 21 Warum sind C Zeichenliterale Ints statt Chars
dmckee — Ex-Moderator-Kätzchen

Mit gcc auf meinem MacBook versuche ich:

#include <stdio.h>
#define test(A) do{printf(#A":\t%i\n",sizeof(A));}while(0)
int main(void){
  test('a');
  test("a");
  test("");
  test(char);
  test(short);
  test(int);
  test(long);
  test((char)0x0);
  test((short)0x0);
  test((int)0x0);
  test((long)0x0);
  return 0;
};

was beim Ausführen ergibt:

'a':    4
"a":    2
"":     1
char:   1
short:  2
int:    4
long:   4
(char)0x0:      1
(short)0x0:     2
(int)0x0:       4
(long)0x0:      4

was darauf hindeutet, dass ein Zeichen 8 Bit ist, wie Sie vermuten, aber ein Zeichenliteral ist ein Int.

1647171014 479 Warum sind C Zeichenliterale Ints statt Chars
Toni Delroy

Damals, als C geschrieben wurde, hatte die MACRO-11-Assemblersprache des PDP-11:

MOV #'A, R0      // 8-bit character encoding for 'A' into 16 bit register

So etwas ist in der Assemblersprache ziemlich üblich – die niedrigen 8 Bits enthalten den Zeichencode, andere Bits sind auf 0 gelöscht. PDP-11 hatte sogar:

MOV #"AB, R0     // 16-bit character encoding for 'A' (low byte) and 'B'

Dies bot eine bequeme Möglichkeit, zwei Zeichen in das Low- und High-Byte des 16-Bit-Registers zu laden. Sie könnten diese dann woanders schreiben und einige Textdaten oder den Bildschirmspeicher aktualisieren.

Daher ist die Idee, dass Charaktere auf Registergröße befördert werden, ganz normal und wünschenswert. Aber nehmen wir an, Sie müssen ‘A’ in ein Register bringen, nicht als Teil des fest codierten Opcodes, sondern von irgendwo im Hauptspeicher, der Folgendes enthält:

address: value
20: 'X'
21: 'A'
22: 'A'
23: 'X'
24: 0
25: 'A'
26: 'A'
27: 0
28: 'A'

Wenn Sie nur ein ‘A’ aus diesem Hauptspeicher in ein Register lesen möchten, welches würden Sie lesen?

  • Einige CPUs unterstützen möglicherweise nur das direkte Lesen eines 16-Bit-Werts in ein 16-Bit-Register, was bedeuten würde, dass ein Lesen bei 20 oder 22 dann erfordern würde, dass die Bits von ‘X’ gelöscht werden, und abhängig von der Endianness der CPU das eine oder andere müsste in das niederwertige Byte verschoben werden.

  • Einige CPUs erfordern möglicherweise einen speicherausgerichteten Lesevorgang, was bedeutet, dass die niedrigste betroffene Adresse ein Vielfaches der Datengröße sein muss: Sie können möglicherweise von den Adressen 24 und 25 lesen, aber nicht von 27 und 28.

Ein Compiler, der Code generiert, um ein ‘A’ in das Register zu bringen, verschwendet möglicherweise lieber etwas zusätzlichen Speicher und codiert den Wert als 0 ‘A’ oder ‘A’ 0 – je nach Endianness und stellt außerdem sicher, dass er richtig ausgerichtet ist ( dh nicht an einer ungeraden Speicheradresse).

Meine Vermutung ist, dass C’s diese Ebene des CPU-zentrierten Verhaltens einfach übernommen hat, indem es an Zeichenkonstanten dachte, 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)

Ich erinnere mich, dass ich K&R gelesen und einen Codeausschnitt gesehen habe, der ein Zeichen nach dem anderen gelesen hat, bis es EOF erreichte. Da alle Zeichen gültige Zeichen in einer Datei/einem Eingabestrom 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 es dann in ein char 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.

int r;
char buffer[1024], *p; // don't use in production - buffer overflow likely
p = buffer;

while ((r = getc(file)) != EOF)
{
  *(p++) = (char) r;
}

1647171014 741 Warum sind C Zeichenliterale Ints statt Chars
Michael Burr

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 and Evolution 11.2.1 – Fine-Grain Resolution):

In C ist der Typ eines Zeichenliterals wie z 'a' ist int. Überraschenderweise geben 'a' Art char in C++ verursacht keine Kompatibilitätsprobleme. Bis auf das pathologische Beispiel sizeof('a')liefert jedes Konstrukt, das sowohl in C als auch in C++ ausgedrückt werden kann, dasselbe Ergebnis.

In den meisten Fällen sollte es also keine Probleme geben.

  • Interessant! Widerspricht irgendwie dem, was andere darüber gesagt haben, wie das C-Standards-Komitee “weise” entschieden hat, diese Eigenart nicht aus C zu entfernen.

    – j_random_hacker

    12. Januar 2009 um 8:42 Uhr

997400cookie-checkWarum sind C-Zeichenliterale Ints statt Chars?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy