C und C ++ weisen viele Unterschiede auf, und nicht jeder gültige C-Code ist gültiger C ++ - Code.
(Mit "gültig" meine ich Standardcode mit definiertem Verhalten, dh nicht implementierungsspezifisch / undefiniert / etc.)
Gibt es ein Szenario, in dem ein in C und C ++ gültiger Code beim Kompilieren mit einem Standard-Compiler in jeder Sprache ein anderes Verhalten hervorruft ?
Um einen vernünftigen / nützlichen Vergleich zu ermöglichen (ich versuche, etwas praktisch Nützliches zu lernen, nicht um offensichtliche Lücken in der Frage zu finden), nehmen wir an:
- Nichts Präprozessorbezogenes (was bedeutet, dass es keine Hacks mit
#ifdef __cplusplus
, Pragmas usw. gibt) - Alles, was für die Implementierung definiert ist, ist in beiden Sprachen gleich (z. B. numerische Grenzwerte usw.).
- Wir vergleichen relativ aktuelle Versionen jedes Standards (z. B. C ++ 98 und C90 oder höher).
Wenn die Versionen von Bedeutung sind, geben Sie bitte an, welche Versionen von jedem Standard ein anderes Verhalten erzeugen.
Antworten:
Folgendes, das in C und C ++ gültig ist, wird (höchstwahrscheinlich) zu unterschiedlichen Werten
i
in C und C ++ führen:Siehe Größe des Charakters ( ‚a‘) in C / C ++ für eine Erklärung des Unterschieds.
Ein weiterer aus diesem Artikel :
quelle
struct
vor Strukturnamen benötigt.struct sz { int i[2];};
würde bedeuten , dass C und C ++ haben unterschiedliche Werte zu erzeugen. (Während ein DSP mit sizeof (int) == 1 den gleichen Wert erzeugen könnte ).Hier ist ein Beispiel, das den Unterschied zwischen Funktionsaufrufen und Objektdeklarationen in C und C ++ sowie die Tatsache nutzt, dass C90 das Aufrufen nicht deklarierter Funktionen ermöglicht:
In C ++ wird nichts gedruckt, weil ein temporäres
f
Objekt erstellt und zerstört wird. In C90 wird es gedruckt,hello
weil Funktionen aufgerufen werden können, ohne deklariert worden zu sein.Für den Fall, dass Sie sich gefragt
f
haben,struct f
ob der Name zweimal verwendet werden soll, erlauben die C- und C ++ - Standards dies ausdrücklich. Um ein Objekt zu erstellen, müssen Sie sagen , dass Sie eindeutig sagen müssen , ob Sie die Struktur möchten, oder aufhören,struct
wenn Sie die Funktion möchten.quelle
Für C ++ vs. C90 gibt es mindestens einen Weg, um ein anderes Verhalten zu erhalten, das nicht für die Implementierung definiert ist. C90 hat keine einzeiligen Kommentare. Mit ein wenig Sorgfalt können wir damit einen Ausdruck mit völlig unterschiedlichen Ergebnissen in C90 und in C ++ erstellen.
In C ++ ist alles vom
//
bis zum Ende der Zeile ein Kommentar, daher funktioniert dies wie folgt:Da C90 keine einzeiligen Kommentare enthält, ist nur das
/* comment */
ein Kommentar. Der erste/
und der2
sind beide Teile der Initialisierung, daher ergibt sich Folgendes:Ein korrekter C ++ - Compiler ergibt also 13, aber ein streng korrekter C90-Compiler 8. Natürlich habe ich hier nur beliebige Zahlen ausgewählt - Sie können andere Zahlen verwenden, wie Sie es für richtig halten.
quelle
2
würde es lauten wie10 / + 3
es gültig ist (unary +).C90 vs. C ++ 11 (
int
vs.double
):In C
auto
bedeutet lokale Variable. In C90 ist es in Ordnung, Variablen- oder Funktionstypen wegzulassen. Der Standardwert istint
. In C ++ 11auto
bedeutet dies etwas völlig anderes. Es weist den Compiler an, den Typ der Variablen aus dem Wert abzuleiten, der zum Initialisieren verwendet wurde.quelle
auto
?int
standardmäßig. Das ist klug! +1int
.int
. In der realen Welt, in der es Unmengen von Legacy-Code gibt und der Marktführer C99 noch nicht implementiert hat und dies auch nicht beabsichtigt, ist es absurd, von einer "veralteten Version von C" zu sprechen.Ein weiteres Beispiel, das ich noch nicht erwähnt habe, das einen Präprozessorunterschied hervorhebt:
Dies gibt "false" in C und "true" in C ++ aus. - In C wird jedes undefinierte Makro mit 0 bewertet. In C ++ gibt es eine Ausnahme: "true" wird mit 1 ausgewertet.
quelle
#define true false
ಠ_ಠGemäß C ++ 11 Standard:
ein. Der Kommaoperator führt die Konvertierung von lWert in rWert in C durch, jedoch nicht in C ++:
In C ++ ist der Wert dieses Ausdrucks 100 und in C ist dies
sizeof(char*)
.b. In C ++ ist der Typ des Enumerators seine Aufzählung. In C ist der Typ des Enumerators int.
Dies bedeutet, dass
sizeof(int)
möglicherweise nicht gleich istsizeof(E)
.c. In C ++ akzeptiert eine mit einer leeren Parameterliste deklarierte Funktion keine Argumente. In C bedeutet leere Parameterliste, dass die Anzahl und der Typ der Funktionsparameter unbekannt sind.
quelle
sizeof(char*)
könnte 100 sein. In diesem Fall würde das erste Beispiel das gleiche beobachtbare Verhalten in C und C ++ erzeugen (dh obwohl die Methode zum Erhaltens
unterschiedlichs
wäre , würde es am Ende 100 sein). Das OP erwähnte, dass diese Art von implementierungsdefiniertem Verhalten in Ordnung sei, da er nur Antworten von Sprachanwälten vermeiden wollte, so dass der erste seiner Ausnahme nach in Ordnung ist. Aber der zweite ist auf jeden Fall gut.char arr[sizeof(char*)+1]; int s = sizeof(0, arr);
void *arr[100]
. In diesem Fall hat ein Element dieselbe Größe wie ein Zeiger auf dasselbe Element. Solange zwei oder mehr Elemente vorhanden sind, muss das Array größer sein als die Adresse seines ersten Elements.Dieses Programm druckt
1
in C ++ und0
in C:Dies geschieht , weil es
double abs(double)
Überlast in C ++, soabs(0.6)
kehrt ,0.6
während in C gibt es0
wegen der impliziten double-to-int Umwandlung vor dem Aufrufint abs(int)
. In C müssen Sie verwendenfabs
, um mit zu arbeitendouble
.quelle
stdlib.h
definiert nurabs(int)
undabs(long)
; Die Versionabs(double)
wird von deklariertmath.h
. Daher kann dieses Programm dieabs(int)
Version weiterhin aufrufen . Es ist ein Implementierungsdetail, obstdlib.h
auch die Aufnahme verursacht wirdmath.h
. (Ich denke, es wäre ein Fehler, wennabs(double)
es aufgerufen würde, aber andere Aspekte vonmath.h
waren nicht enthalten).<math.h>
auch die zusätzlichen Überladungen enthält; In der Praxis stellt sich heraus, dass alle wichtigen Compiler diese Überladungen nur enthalten, wenn das Formular<cmath>
verwendet wird.In C wird gedruckt, was auch immer der Wert von
sizeof(int)
auf dem aktuellen System ist, was normalerweise4
in den meisten heute gebräuchlichen Systemen der Fall ist.In C ++ muss dies 1 drucken.
quelle
%d
ist nicht der richtige Formatbezeichner fürsize_t
.Eine weitere
sizeof
Falle: Boolesche Ausdrücke.Dies entspricht
sizeof(int)
in C, da der Ausdruck vom Typ istint
, in C ++ jedoch normalerweise 1 (obwohl dies nicht erforderlich ist). In der Praxis sind sie fast immer unterschiedlich.quelle
!
sollte für eine reichenbool
.sizeof(0)
ist4
sowohl in C als auch in C ++, da0
es sich um einen ganzzahligen Wert handelt.sizeof(!0)
ist4
in C und1
in C ++. Logical NOT arbeitet mit Operanden vom Typ bool. Wenn der int-Wert0
implizit infalse
(einen Bool-Wert) konvertiert wird, wird er umgedreht, was zutrue
. Beidetrue
undfalse
sind Bool-Werte in C ++ und dassizeof(bool)
ist1
. In C wird jedoch!0
ausgewertet1
, was ein Wert vom Typ int ist. Die Programmiersprache C hat standardmäßig keinen Bool-Datentyp.Die Programmiersprache C ++ (3. Ausgabe) enthält drei Beispiele:
sizeof ('a'), wie @Adam Rosenfield erwähnte;
//
Kommentare, die zum Erstellen von verstecktem Code verwendet werden:Strukturen usw. verstecken Dinge in unseren Bereichen, wie in Ihrem Beispiel.
quelle
Eine alte Kastanie, die vom C-Compiler abhängt und keine C ++ - Zeilenende-Kommentare erkennt ...
quelle
Eine weitere im C ++ Standard aufgeführte:
quelle
x
oben. Ich dachte, Sie sagten "das Arraya
".Inline-Funktionen in C verwenden standardmäßig den externen Bereich, wo dies in C ++ nicht der Fall ist.
Das Zusammenstellen der folgenden zwei Dateien würde im Fall von GNU C das "Ich bin inline" drucken, aber nichts für C ++.
Datei 1
Datei 2
Außerdem behandelt C ++ implizit alle
const
globalenstatic
Elemente so, als ob sie nicht explizit deklariert wärenextern
, im Gegensatz zu C, in dem diesextern
der Standard ist.quelle
extern
das wird hier demonstriert?struct fun
vsfn
) zurückzuführen und hat nichts damit zu tun, ob die Funktion inline ist. Das Ergebnis ist identisch, wenn Sie dasinline
Qualifikationsmerkmal entfernen .inline
wurde erst in C99 hinzugefügt, kann aber in C99fun()
nicht ohne einen Prototyp im Umfang aufgerufen werden. Ich gehe also davon aus, dass diese Antwort nur für GNU C gilt.Gibt den Exit-Code 0 in C ++ oder 3 in C zurück.
Dieser Trick könnte wahrscheinlich verwendet werden, um etwas Interessanteres zu tun, aber ich konnte mir keinen guten Weg vorstellen, einen Konstruktor zu erstellen, der für C schmackhaft wäre. Ich habe versucht, mit dem Kopierkonstruktor ein ähnlich langweiliges Beispiel zu erstellen, das ein Argument zulässt bestanden werden, wenn auch in einer eher nicht tragbaren Weise:
VC ++ 2005 weigerte sich jedoch, dies im C ++ - Modus zu kompilieren, und beschwerte sich darüber, wie "Exit-Code" neu definiert wurde. (Ich denke, dies ist ein Compiler-Fehler, es sei denn, ich habe plötzlich vergessen, wie man programmiert.) Es wurde jedoch mit einem Prozess-Exit-Code von 1 beendet, wenn es als C kompiliert wurde.
quelle
exit(code)
ist anscheinend eine gültige Deklaration einer Variablencode
vom Typexit
. (Siehe "am ärgerlichsten analysieren", was ein anderes, aber ähnliches Problem ist).Dieses Programm druckt
128
(32 * sizeof(double)
), wenn es mit einem C ++ - Compiler4
kompiliert wird und wenn es mit einem C-Compiler kompiliert wird.Dies liegt daran, dass C nicht den Begriff der Bereichsauflösung hat. In C werden Strukturen, die in anderen Strukturen enthalten sind, in den Bereich der äußeren Struktur gestellt.
quelle
32*sizeof(double)
nicht 32 :))size_t
mit%d
Vergessen Sie nicht die Unterscheidung zwischen den globalen Namespaces C und C ++. Angenommen, Sie haben eine foo.cpp
und ein foo2.c
Angenommen , Sie haben einen main.c und main.cpp , die beide wie folgt aussehen:
Bei der Kompilierung als C ++ wird das Symbol im globalen C ++ - Namespace verwendet. in C wird das C verwendet:
quelle
foo
). Es gibt keine separaten "globalen Namespaces".Dies ist insofern ziemlich eigenartig, als es in C ++ und in C99, C11 und C17 gültig ist (obwohl es in C11, C17 optional ist); aber nicht gültig in C89.
In C99 + wird ein Array mit variabler Länge erstellt, das gegenüber normalen Arrays seine eigenen Besonderheiten aufweist, da es einen Laufzeittyp anstelle eines Typs zur Kompilierungszeit hat und
sizeof array
in C kein ganzzahliger konstanter Ausdruck ist. In C ++ ist der Typ vollständig statisch.Wenn Sie versuchen, hier einen Initialisierer hinzuzufügen:
ist C ++ gültig, aber nicht C, da Arrays variabler Länge keinen Initialisierer haben können.
quelle
Dies betrifft lWerte und rWerte in C und C ++.
In der Programmiersprache C geben sowohl die Operatoren vor als auch nach dem Inkrement r-Werte zurück, nicht l-Werte. Dies bedeutet, dass sie sich nicht auf der linken Seite des
=
Zuweisungsoperators befinden können. Diese beiden Anweisungen geben einen Compilerfehler in C aus:In C ++ gibt der Operator vor dem Inkrementieren jedoch einen Wert zurück , während der Operator nach dem Inkrementieren einen Wert zurückgibt. Dies bedeutet, dass ein Ausdruck mit dem Vorinkrementierungsoperator auf der linken Seite des
=
Zuweisungsoperators platziert werden kann!Warum ist das so? Das Nachinkrement erhöht die Variable und gibt die Variable wie zuvor zurück dem Inkrementieren war. Dies ist eigentlich nur ein Wert. Der frühere Wert der Variablen a wird als temporäres in ein Register kopiert und dann wird a inkrementiert. Aber der frühere Wert von a wird vom Ausdruck zurückgegeben, es ist ein r-Wert. Es repräsentiert nicht mehr den aktuellen Inhalt der Variablen.
Das Vorinkrement erhöht zuerst die Variable und gibt dann die Variable zurück, wie sie nach dem Inkrement geworden ist. In diesem Fall müssen wir den alten Wert der Variablen nicht in einem temporären Register speichern. Wir rufen nur den neuen Wert der Variablen ab, nachdem sie inkrementiert wurde. Das Vorinkrement gibt also einen l-Wert zurück, es gibt die Variable a selbst zurück. Wir können diesen Wert etwas anderem zuweisen, es ist wie in der folgenden Anweisung. Dies ist eine implizite Umwandlung von lvalue in rvalue.
Da das Vorinkrement einen l-Wert zurückgibt, können wir ihm auch etwas zuweisen. Die folgenden zwei Aussagen sind identisch. Bei der zweiten Zuweisung wird zuerst a inkrementiert, dann wird sein neuer Wert mit 2 überschrieben.
quelle
Leere Strukturen haben die Größe 0 in C und 1 in C ++:
quelle