Kann Code, der sowohl in C als auch in C ++ gültig ist, beim Kompilieren in jeder Sprache ein anderes Verhalten erzeugen?

664

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.
user541686
quelle
11
Übrigens kann es nützlich sein, in einem Dialekt zu programmieren, der gleichzeitig C und C ++ ist. Ich habe dies in der Vergangenheit und in einem aktuellen Projekt getan: der TXR-Sprache. Interessanterweise haben die Entwickler der Lua-Sprache dasselbe getan und nennen diesen Dialekt "Clean C". Sie profitieren von einer besseren Überprüfung der Kompilierungszeit und möglicherweise zusätzlichen nützlichen Diagnosen von C ++ - Compilern, behalten jedoch die C-Portabilität bei.
Kaz
9
Ich habe die ältere Frage in diese Frage eingefügt, da diese mehr Ansichten und positive Antworten enthält. Dies ist immer noch ein Beispiel für eine nicht konstruktive Frage, aber es ist ziemlich grenzwertig, da es SO-Benutzern etwas beibringt. Ich schließe es als nicht konstruktiv, nur um den Stand der Frage vor dem Zusammenführen wiederzugeben. Fühlen Sie sich frei zu widersprechen und wieder zu öffnen.
George Stocker
13
Die Abstimmung zur Wiedereröffnung kann meines Erachtens objektiv mit einem "Ja" gefolgt von einem Beispiel beantwortet werden (wie unten gezeigt). Ich finde es konstruktiv, dass Menschen daraus relevantes Verhalten lernen können.
Anders Abel
6
@AndersAbel Die reine Anzahl von Antworten, die alle richtig sind, zeigt eindeutig, dass es sich weiterhin um eine Make-a-List-Frage handelt. Auf keinen Fall hätten Sie diese Frage stellen können, ohne eine Liste zu erhalten.
dmckee --- Ex-Moderator Kätzchen
2
@dmckee Für das, was es wert ist, stimme ich Ihnen zu. Die C ++ - Tag-Leute sind jedoch ... Sollen wir sagen ... lebhaft .
George Stocker

Antworten:

397

Folgendes, das in C und C ++ gültig ist, wird (höchstwahrscheinlich) zu unterschiedlichen Werten iin C und C ++ führen:

int i = sizeof('a');

Siehe Größe des Charakters ( ‚a‘) in C / C ++ für eine Erklärung des Unterschieds.

Ein weiterer aus diesem Artikel :

#include <stdio.h>

int  sz = 80;

int main(void)
{
    struct sz { char c; };

    int val = sizeof(sz);      // sizeof(int) in C,
                               // sizeof(struct sz) in C++
    printf("%d\n", val);
    return 0;
}
Alexey Frunze
quelle
8
Ich habe das definitiv nicht erwartet! Ich hatte auf etwas Dramatischeres gehofft, aber das ist immer noch nützlich, danke. :) +1
user541686
17
+1 Das zweite Beispiel ist gut für die Tatsache, dass C ++ keine structvor Strukturnamen benötigt.
Seth Carnegie
1
@Andrey Ich dachte vor einiger Zeit dasselbe und testete es und es funktionierte auf GCC 4.7.1 ohne den Standard, entgegen meiner Erwartung. Ist das ein Fehler in GCC?
Seth Carnegie
3
@SethCarnegie: Ein nicht konformes Programm muss nicht versagen, aber es funktioniert auch nicht garantiert.
Andrey Vihrov
3
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 ).
Martin Bonner unterstützt Monica
464

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:

#include <stdio.h>

struct f { int x; };

int main() {
    f();
}

int f() {
    return printf("hello");
}

In C ++ wird nichts gedruckt, weil ein temporäres fObjekt erstellt und zerstört wird. In C90 wird es gedruckt, helloweil Funktionen aufgerufen werden können, ohne deklariert worden zu sein.

Für den Fall, dass Sie sich gefragt fhaben, struct fob 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, structwenn Sie die Funktion möchten.

Seth Carnegie
quelle
7
Genau genommen wird dies unter C nicht kompiliert, da die Deklaration von "int f ()" nach der Definition von "int main ()" erfolgt :)
Sogartar
15
@Sogartar, wirklich? codepad.org/STSQlUhh C99-Compiler geben eine Warnung aus, aber Sie können sie trotzdem kompilieren.
Jrajav
22
@Sogartar in C-Funktionen dürfen implizit deklariert werden.
Alex B
11
@AlexB Nicht in C99 und C11.
6
@jrajav Das sind also keine C99-Compiler. Ein C99-Compiler erkennt nicht deklarierte Bezeichner als Syntaxfehler. Ein Compiler, der dies nicht tut, ist entweder ein C89-Compiler oder ein Pre-Standard- oder eine andere Art von nicht konformem Compiler.
430

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.

int a = 10 //* comment */ 2 
        + 3;

In C ++ ist alles vom //bis zum Ende der Zeile ein Kommentar, daher funktioniert dies wie folgt:

int a = 10 + 3;

Da C90 keine einzeiligen Kommentare enthält, ist nur das /* comment */ein Kommentar. Der erste /und der 2sind beide Teile der Initialisierung, daher ergibt sich Folgendes:

int a = 10 / 2 + 3;

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.

Jerry Sarg
quelle
34
WHOA das ist umwerfend !! Von allen möglichen Dingen hätte ich nie gedacht, dass Kommentare verwendet werden könnten, um das Verhalten zu ändern, haha. +1
user541686
89
auch ohne das 2würde es lauten wie 10 / + 3es gültig ist (unary +).
Benoit
12
Ändern Sie es jetzt zum Spaß so, dass sowohl C als auch C ++ unterschiedliche arithmetische Ausdrücke berechnen, um das gleiche Ergebnis zu erzielen.
Ryan C. Thompson
21
@ RyanThompson Trivial. s /
2/1
4
@Mehrdad Bin ich falsch oder Kommentare beziehen sich auf den Präprozessor? Sie sollten daher als mögliche Antwort von Ihrer Frage ausgeschlossen werden! ;-)
Ale
179

C90 vs. C ++ 11 ( intvs. double):

#include <stdio.h>

int main()
{
  auto j = 1.5;
  printf("%d", (int)sizeof(j));
  return 0;
}

In C autobedeutet lokale Variable. In C90 ist es in Ordnung, Variablen- oder Funktionstypen wegzulassen. Der Standardwert ist int. In C ++ 11 autobedeutet dies etwas völlig anderes. Es weist den Compiler an, den Typ der Variablen aus dem Wert abzuleiten, der zum Initialisieren verwendet wurde.

verstimmt
quelle
10
C90 hat auto?
Seth Carnegie
22
@ SethCarnegie: Ja, es ist eine Speicherklasse; Dies geschieht standardmäßig, wenn Sie es weglassen, sodass niemand es verwendet hat und die Bedeutung geändert wurde. Ich denke, es ist intstandardmäßig. Das ist klug! +1
user541686
5
C11 hat keine implizite int.
R .. GitHub STOP HELPING ICE
23
@ KeithThompson Ah, ich denke du meinst das Abgeleitete 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.
Jim Balter
11
"Jede Variable MUSS eine explizite Speicherklasse haben. Mit freundlichen Grüßen, oberes Management."
Btown
120

Ein weiteres Beispiel, das ich noch nicht erwähnt habe, das einen Präprozessorunterschied hervorhebt:

#include <stdio.h>
int main()
{
#if true
    printf("true!\n");
#else
    printf("false!\n");
#endif
    return 0;
}

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.

godlygeek
quelle
2
Interessant. Kennt jemand die Gründe für diese Änderung?
antred
3
Da "wahr" ein Schlüsselwort / ein gültiger Wert ist, wird es wie jeder "wahre Wert" (also wie jede positive ganze Zahl) als wahr bewertet. Sie können immer noch #define true false
ausführen
22
#define true false ಠ_ಠ
Bryan Boettcher
2
@DarioOO führt eine solche Neudefinition nicht zu UB?
Ruslan
3
@DarioOO: Ja, du liegst falsch. Eine Neudefinition von Schlüsselwörtern ist nicht zulässig, die Bestrafung bleibt dem Schicksal überlassen (UB). Der Präprozessor ist eine separate Phase der Kompilierung, die nicht standhält.
Deduplikator
108

Gemäß C ++ 11 Standard:

ein. Der Kommaoperator führt die Konvertierung von lWert in rWert in C durch, jedoch nicht in C ++:

   char arr[100];
   int s = sizeof(0, arr);       // The comma operator is used.

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.

   enum E { a, b, c };
   sizeof(a) == sizeof(int);     // In C
   sizeof(a) == sizeof(E);       // In C++

Dies bedeutet, dass sizeof(int)möglicherweise nicht gleich ist sizeof(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.

   int f();           // int f(void) in C++
                      // int f(*unknown*) in C
Kirill Kobelev
quelle
Der erste ist ebenfalls implementierungsdefiniert wie der von Alexey. Aber +1.
Seth Carnegie
1
@Seth, Alle oben genannten Materialien stammen direkt aus Anhang C.1 des C ++ 11-Standards.
Kirill Kobelev
Ja, aber es ist immer noch implementierungsdefiniert. 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 Erhalten sunterschiedlich swä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.
Seth Carnegie
5
Es gibt eine einfache Lösung - ändern Sie einfach das Beispiel in:char arr[sizeof(char*)+1]; int s = sizeof(0, arr);
Mankarse
5
Um implementierungsdefinierte Unterschiede zu vermeiden, können Sie auch verwenden 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.
Finnw
53

Dieses Programm druckt 1in C ++ und 0in C:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int d = (int)(abs(0.6) + 0.5);
    printf("%d", d);
    return 0;
}

Dies geschieht , weil es double abs(double)Überlast in C ++, so abs(0.6)kehrt , 0.6während in C gibt es 0wegen der impliziten double-to-int Umwandlung vor dem Aufruf int abs(int). In C müssen Sie verwenden fabs, um mit zu arbeiten double.

Pavel Chikulaev
quelle
5
musste den Code von jemand anderem mit diesem Problem debuggen. Oh, wie ich das geliebt habe. Auf jeden Fall druckt Ihr Programm auch in C ++ 0. C ++ muss den Header "cmath" verwenden, siehe Vergleich. Erster Rückgabe in 0 ideone.com/0tQB2G 2. Rückgabe 1 ideone.com/SLeANo
CoffeDeveloper
Es freut mich zu hören, dass ich nicht der einzige bin, der diesen Unterschied durch Debuggen findet. Gerade in VS2013 getestet, gibt eine leere Datei mit nur diesem Inhalt 1 aus, wenn die Erweiterung .cpp ist, und 0, wenn die Erweiterung .c ist. <Math.h> ist anscheinend indirekt in VS enthalten.
Pavel Chikulaev
Und wie in VS C ++ aussieht, enthält <math.h> C ++ - Inhalte in den globalen Namespace, wo dies bei GCC nicht der Fall ist. Unsicher, welches Standardverhalten ist.
Pavel Chikulaev
2
Dieses spezielle Codebeispiel ist implementierungsabhängig: stdlib.hdefiniert nur abs(int)und abs(long); Die Version abs(double)wird von deklariert math.h. Daher kann dieses Programm die abs(int)Version weiterhin aufrufen . Es ist ein Implementierungsdetail, ob stdlib.hauch die Aufnahme verursacht wird math.h. (Ich denke, es wäre ein Fehler, wenn abs(double)es aufgerufen würde, aber andere Aspekte von math.hwaren nicht enthalten).
MM
1
Ein sekundäres Problem ist, dass, obwohl der C ++ - Standard zu sagen scheint, dass das Einschließen <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.
MM
38
#include <stdio.h>

int main(void)
{
    printf("%d\n", (int)sizeof('a'));
    return 0;
}

In C wird gedruckt, was auch immer der Wert von sizeof(int)auf dem aktuellen System ist, was normalerweise 4in den meisten heute gebräuchlichen Systemen der Fall ist.

In C ++ muss dies 1 drucken.

Adam Rosenfield
quelle
3
Ja, ich war mit diesem Trick tatsächlich vertraut, da 'c' ein Int in C und ein Zeichen in C ++ ist, aber es ist immer noch gut, ihn hier aufgelistet zu haben.
Sean
9
Das wäre eine interessante Interviewfrage - besonders für Leute, die c / c ++ Experten in ihren Lebenslauf aufnehmen
Martin Beckett
2
Ein bisschen hinterhältig. Der gesamte Zweck von sizeof besteht darin, dass Sie nicht genau wissen müssen, wie groß ein Typ ist.
Dana the Sane
13
In C ist der Wert implementierungsdefiniert und 1 ist eine Möglichkeit. (In C ++ muss 1 wie angegeben gedruckt werden.)
Windows-Programmierer
3
Tatsächlich hat es in beiden Fällen ein undefiniertes Verhalten. %dist nicht der richtige Formatbezeichner für size_t.
R .. GitHub STOP HELPING ICE
37

Eine weitere sizeofFalle: Boolesche Ausdrücke.

#include <stdio.h>
int main() {
    printf("%d\n", (int)sizeof !0);
}

Dies entspricht sizeof(int)in C, da der Ausdruck vom Typ ist int, in C ++ jedoch normalerweise 1 (obwohl dies nicht erforderlich ist). In der Praxis sind sie fast immer unterschiedlich.

Alex B.
quelle
6
Man !sollte für eine reichen bool.
Alexey Frunze
4
!! ist der Konvertierungsoperator von int zu boolesch :)
EvilTeach
1
sizeof(0)ist 4sowohl in C als auch in C ++, da 0es sich um einen ganzzahligen Wert handelt. sizeof(!0)ist 4in C und 1in C ++. Logical NOT arbeitet mit Operanden vom Typ bool. Wenn der int-Wert 0implizit in false(einen Bool-Wert) konvertiert wird, wird er umgedreht, was zu true. Beide trueund falsesind Bool-Werte in C ++ und das sizeof(bool)ist 1. In C wird jedoch !0ausgewertet 1, was ein Wert vom Typ int ist. Die Programmiersprache C hat standardmäßig keinen Bool-Datentyp.
Galaxy
26

Die Programmiersprache C ++ (3. Ausgabe) enthält drei Beispiele:

  1. sizeof ('a'), wie @Adam Rosenfield erwähnte;

  2. // Kommentare, die zum Erstellen von verstecktem Code verwendet werden:

    int f(int a, int b)
    {
        return a //* blah */ b
            ;
    }
  3. Strukturen usw. verstecken Dinge in unseren Bereichen, wie in Ihrem Beispiel.

derobert
quelle
25

Eine alte Kastanie, die vom C-Compiler abhängt und keine C ++ - Zeilenende-Kommentare erkennt ...

...
int a = 4 //* */ 2
        +2;
printf("%i\n",a);
...
dmckee --- Ex-Moderator Kätzchen
quelle
21

Eine weitere im C ++ Standard aufgeführte:

#include <stdio.h>

int x[1];
int main(void) {
    struct x { int a[2]; };
    /* size of the array in C */
    /* size of the struct in C++ */
    printf("%d\n", (int)sizeof(x)); 
}
Johannes Schaub - litb
quelle
Sie bekommen also Polsterungsunterschiede?
v.oddou
Ah, tut mir leid, ich habe es, da ist noch eine xoben. Ich dachte, Sie sagten "das Array a".
v.oddou
20

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

#include <stdio.h>

struct fun{};

int main()
{
    fun();  // In C, this calls the inline function from file 2 where as in C++
            // this would create a variable of struct fun
    return 0;
}

Datei 2

#include <stdio.h>
inline void fun(void)
{
    printf("I am inline\n");
} 

Außerdem behandelt C ++ implizit alle constglobalen staticElemente so, als ob sie nicht explizit deklariert wären extern, im Gegensatz zu C, in dem dies externder Standard ist.

fkl
quelle
Das glaube ich wirklich nicht. Wahrscheinlich haben Sie den Punkt verpasst. Es geht nicht um die Definition von struct st, die lediglich verwendet wird, um den Code in c ++ gültig zu machen. Der Punkt ist, dass es das unterschiedliche Verhalten von Inline-Funktionen in c vs c ++ hervorhebt. Gleiches gilt für extern. Keines davon wird in einer der Lösungen diskutiert.
fkl
2
Was ist das unterschiedliche Verhalten von Inline-Funktionen und externdas wird hier demonstriert?
Seth Carnegie
Es ist ziemlich klar geschrieben. "Inline-Funktionen in c werden standardmäßig für den externen Bereich verwendet, wo dies in C ++ nicht der Fall ist (Code zeigt dies). Außerdem behandelt C ++ jede globale Konstante implizit als Dateibereich, sofern sie nicht explizit als extern deklariert wird, im Gegensatz zu C, in dem extern die Standardeinstellung ist. Eine ähnliche Funktion Beispiel kann dafür erstellt werden ". Ich bin verwirrt - Ist es nicht verständlich?
fkl
12
@fayyazkl Das angezeigte Verhalten ist nur auf den Unterschied bei der Suche zurückzuführen (struct fun vs fn) zurückzuführen und hat nichts damit zu tun, ob die Funktion inline ist. Das Ergebnis ist identisch, wenn Sie das inlineQualifikationsmerkmal entfernen .
Alex B
3
In ISO C ist dieses Programm schlecht formuliert: inlinewurde erst in C99 hinzugefügt, kann aber in C99 fun()nicht ohne einen Prototyp im Umfang aufgerufen werden. Ich gehe also davon aus, dass diese Antwort nur für GNU C gilt.
MM
16
struct abort
{
    int x;
};

int main()
{
    abort();
    return 0;
}

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:

struct exit
{
    int x;
};

int main()
{
    struct exit code;
    code.x=1;

    exit(code);

    return 0;
}

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
Ihr zweites Beispiel mit exit kompiliert leider nicht auf gcc oder g ++. Es ist jedoch eine gute Idee.
Sean
1
exit(code)ist anscheinend eine gültige Deklaration einer Variablen codevom Typ exit. (Siehe "am ärgerlichsten analysieren", was ein anderes, aber ähnliches Problem ist).
user253751
16
#include <stdio.h>

struct A {
    double a[32];
};

int main() {
    struct B {
        struct A {
            short a, b;
        } a;
    };
    printf("%d\n", sizeof(struct A));
    return 0;
}

Dieses Programm druckt 128( 32 * sizeof(double)), wenn es mit einem C ++ - Compiler 4kompiliert 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.

wefwefa3
quelle
Dieser ist interessant! (Ich denke du meinst aber 32*sizeof(double)nicht 32 :))
user1354557
3
Beachten Sie, dass Sie bekommen UB durch Drucken size_tmit%d
phuclv
7

Vergessen Sie nicht die Unterscheidung zwischen den globalen Namespaces C und C ++. Angenommen, Sie haben eine foo.cpp

#include <cstdio>

void foo(int r)
{
  printf("I am C++\n");
}

und ein foo2.c

#include <stdio.h>

void foo(int r)
{
  printf("I am C\n");
}

Angenommen , Sie haben einen main.c und main.cpp , die beide wie folgt aussehen:

extern void foo(int);

int main(void)
{
  foo(1);
  return 0;
}

Bei der Kompilierung als C ++ wird das Symbol im globalen C ++ - Namespace verwendet. in C wird das C verwendet:

$ diff main.cpp main.c
$ gcc -o test main.cpp foo.cpp foo2.c
$ ./test 
I am C++
$ gcc -o test main.c foo.cpp foo2.c
$ ./test 
I am C
user23614
quelle
Du meinst die Verknüpfungsspezifikation?
user541686
Name Mangling. C ++ - Namen haben Präfixe und Suffixe, während C nicht
CoffeDeveloper
Das Mangeln von Namen ist nicht Teil der C ++ - Spezifikation. Ist es in C verboten?
Skyking
5
Dies ist undefiniertes Verhalten (Mehrfachdefinition von foo). Es gibt keine separaten "globalen Namespaces".
MM
4
int main(void) {
    const int dim = 5; 
    int array[dim];
}

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 arrayin C kein ganzzahliger konstanter Ausdruck ist. In C ++ ist der Typ vollständig statisch.


Wenn Sie versuchen, hier einen Initialisierer hinzuzufügen:

int main(void) {
    const int dim = 5; 
    int array[dim] = {0};
}

ist C ++ gültig, aber nicht C, da Arrays variabler Länge keinen Initialisierer haben können.

Antti Haapala
quelle
0

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:

int a = 5;
a++ = 2;  /* error: lvalue required as left operand of assignment */
++a = 2;  /* error: lvalue required as left operand of assignment */

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!

int a = 5;
a++ = 2;  // error: lvalue required as left operand of assignment
++a = 2;  // No error: a gets assigned to 2!

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.

int x = a;
int x = ++a;

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.

int a;
a = 2;
++a = 2;  // Valid in C++.
Galaxis
quelle
3
Hier gibt es kein "gültig in C".
o11c
0

Leere Strukturen haben die Größe 0 in C und 1 in C ++:

#include <stdio.h>

typedef struct {} Foo;

int main()
{
    printf("%zd\n", sizeof(Foo));
    return 0;
}
Daniel Frużyński
quelle
1
Nein, der Unterschied ist , dass C tut nicht leer Strukturen haben, es sei denn als Compiler - Erweiterung, dh , dieser Code nicht übereinstimmt „gilt in C und C ++“
Antti Haapala