Un code valide en C et c++ peut-il produire un comportement différent lorsqu'il est compilé dans chaque langue?

C et c++ ont beaucoup de différences, et tous les codes C valides ne sont pas des codes C++ valides.

(Par "valide", j'entends un code standard avec un comportement défini, c'est-à-dire non spécifique à la mise en œuvre/non défini/etc.)

y a-t-il un scénario dans lequel un morceau de code valide en C et c++ produirait un comportement différent lorsqu'il est compilé avec un compilateur standard dans chaque langue?

pour en faire un document raisonnable / utile la comparaison (je suis en train d'apprendre quelque chose de pratique, de ne pas essayer de trouver des lacunes évidentes dans la question), supposons:

  • rien de relié au préprocesseur (ce qui signifie pas de hacks avec #ifdef __cplusplus , pragmas, etc.)
  • Rien définis par l'implémentation est la même dans les deux langues (par exemple numérique des limites, etc.)
  • nous comparons des versions relativement récentes de chaque norme (par exemple, c++98 et C90 ou plus tard)

    Si les versions ont de l'Importance, Veuillez indiquer quelles versions de chacune produisent un comportement différent.
603
c++ c
demandé sur Luchian Grigore 2012-10-15 03:54:26
la source

17 ответов

ce qui suit, valide en C et c++, va (très probablement) donner des valeurs différentes en i en C et C++:

int i = sizeof('a');

Voir Taille de caractère ('a') en C/C++ pour une explication de la différence.

un autre de cet article :

#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;
}
356
répondu Alexey Frunze 2017-12-03 18:44:47
la source

voici un exemple qui tire profit de la différence entre les appels de fonction et les déclarations d'objet en C et C++, ainsi que du fait que C90 permet l'appel de fonctions non déclarées:

#include <stdio.h>

struct f { int x; };

int main() {
    f();
}

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

en C++ ceci n'imprimera rien parce qu'un f temporaire est créé et détruit, mais en C90 il imprimera hello parce que les fonctions peuvent être appelées sans avoir été déclarées.

au cas où vous vous demandiez A propos du nom f utilisé deux fois, les standards C et C++ le permettent explicitement, et pour faire un objet vous devez dire struct f pour désambiguer si vous voulez la structure, ou laisser struct si vous voulez la fonction.

453
répondu Seth Carnegie 2016-05-09 07:19:09
la source

pour C++ VS C90, il y a au moins une façon d'obtenir un comportement différent qui n'est pas une implémentation définie. C90 n'a pas de commentaires en une seule ligne. Avec un peu de soin, nous pouvons utiliser cela pour créer une expression avec des résultats complètement différents en C90 et en C++.

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

En C++, tout de // à la fin de la ligne est un commentaire, si cela fonctionne comme:

int a = 10 + 3;

depuis C90 n'a pas de commentaires en une seule ligne, seul le /* comment */ est un commentaire. Le premier / et le 2 sont les deux parties de l'initialisation, il sort:

int a = 10 / 2 + 3;

ainsi, un compilateur C++ correct donnera 13, mais un compilateur C correct 8. Bien sûr, je viens de choisir des nombres arbitraires ici -- vous pouvez utiliser d'autres nombres comme bon vous semble.

402
répondu Jerry Coffin 2012-10-15 10:10:51
la source

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

#include <stdio.h>

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

C auto signifie que la variable locale. En C90, il est possible d'omettre la variable ou le type de fonction. Par défaut, c'est int . Dans C++11 auto signifie quelque chose de complètement différent, il dit au compilateur de inférer le type de la variable de la valeur utilisée pour l'initialiser.

161
répondu detunized 2014-02-28 00:43:35
la source

un autre exemple que je n'ai pas encore vu mentionné, celui-ci mettant en évidence une différence de préprocesseur:

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

affiche" false "en C et" true " en C++ - en C, toute macro non définie est évaluée à 0. En C++, il y a une exception: "true" évalue à 1.

107
répondu godlygeek 2012-10-17 06:17:59
la source

Per C++11 standard:

A. l'opérateur comma effectue la conversion de la valeur en valeur en C mais pas en C++:

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

en C++ la valeur de cette expression sera 100 et en C ce sera sizeof(char*) .

b. En C++, le type de l'agent recenseur est son enum. En C, le type de recenseur est int.

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

cela signifie que sizeof(int) peut ne pas être égal à sizeof(E) .

C. en C++ une fonction déclarée avec une liste de paramètres vide ne prend aucun argument. Dans C la liste des paramètres vides signifie que le nombre et le type de paramètres de fonction est inconnu.

   int f();           // int f(void) in C++
                      // int f(*unknown*) in C
96
répondu Kirill Kobelev 2017-01-04 10:54:36
la source

ce programme imprime 1 en C++ et 0 en C:

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

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

cela se produit parce qu'il y a double abs(double) surcharge en C++, donc abs(0.6) retourne 0.6 alors qu'en C il retourne 0 à cause de la conversion double-à-int implicite avant d'invoquer int abs(int) . Dans C, vous devez utiliser fabs pour travailler avec double .

45
répondu Pavel Chikulaev 2012-12-17 22:44:23
la source

l'Autre sizeof piège: des expressions booléennes.

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

est égal à sizeof(int) en C, parce que l'expression est de type int , mais est typiquement 1 en C++ (bien que ce ne soit pas obligatoire). Dans la pratique, elles sont presque toujours différentes.

36
répondu Alex B 2012-10-16 03:31:14
la source
#include <stdio.h>

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

en C, cette impression quelle que soit la valeur de sizeof(int) est sur le système actuel, qui est typiquement 4 dans la plupart des systèmes couramment utilisés aujourd'hui.

En C++, ce doit d'impression 1.

36
répondu Adam Rosenfield 2014-02-28 01:56:37
la source

une vieille marron qui dépend du compilateur C, qui ne reconnaît pas les commentaires de fin de ligne C++...

...
int a = 4 //* */ 2
        +2;
printf("%i\n",a);
...
22
répondu dmckee 2012-11-19 06:09:00
la source

le langage de programmation C++ (3e édition) donne trois exemples:

  1. sizeof('a'), comme @Adam Rosenfield l'a mentionné;

  2. // commentaires utilisés pour créer le code caché:

    int f(int a, int b)
    {
        return a //* blah */ b
            ;
    }
    
  3. les Structures, etc. cacher des trucs dans les étendues, comme dans votre exemple.

22
répondu derobert 2017-12-03 18:58:44
la source

une autre listée par la norme C++:

#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)); 
}
19
répondu Johannes Schaub - litb 2009-02-24 03:28:16
la source

fonctions en ligne en C par défaut à portée externe où comme ceux en C++ ne le font pas.

compiler les deux fichiers suivants ensemble permettrait d'imprimer le "je suis en ligne" dans le cas de GNU C mais rien pour C++.

Fichier 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;
}

Fichier 2

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

aussi, C++ traite implicitement tout const global comme static à moins qu'il ne soit explicitement déclaré extern , contrairement à C dans lequel extern est la valeur par défaut.

18
répondu fayyazkl 2016-05-09 07:32:00
la source
struct abort
{
    int x;
};

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

retourne avec un code de sortie de 0 en C++, ou de 3 en C.

cette astuce pourrait probablement être utilisée pour faire quelque chose de plus intéressant, mais je ne pouvais pas penser à une bonne façon de créer un constructeur qui serait acceptable à C. j'ai essayé de faire un exemple tout aussi ennuyeux avec le constructeur de copie, qui laisserait un argument être passé, bien que d'une manière plutôt non portable:

struct exit
{
    int x;
};

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

    exit(code);

    return 0;
}

VC++ 2005 a refusé de compiler cela en mode c++ , cependant, se plaindre de la façon dont le" code de sortie " a été redéfini. (Je pense que c'est un bug de compilateur, à moins que j'ai soudainement oublié comment programmer.) Il est sorti avec un code de sortie de processus de 1 lorsqu'il est compilé comme C.

15
répondu 2009-02-24 03:17:03
la source
#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;
}

ce programme imprime 128 ( 32 * sizeof(double) ) lorsqu'il est compilé avec un compilateur C++ et 4 lorsqu'il est compilé avec un compilateur C.

c'est parce que C n'a pas la notion de résolution de la portée. Dans C Les structures contenues dans d'autres structures se mettent dans la portée de la structure extérieure.

11
répondu wefwefa3 2016-02-18 19:23:12
la source

N'oubliez pas la distinction entre les espaces de noms globaux C et C++. Supposons que vous ayez un foo.cpp

#include <cstdio>

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

et un foo2.c

#include <stdio.h>

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

supposons maintenant que vous avez un principal.c et main.cpp qui ressemblent tous deux à ceci:

extern void foo(int);

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

lorsqu'il est compilé en C++, il utilisera le symbole dans L'espace de noms global C++ ; en C il utilisera le C Un:

$ 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
4
répondu user23614 2014-10-17 13:27:43
la source

cela concerne les valeurs L et R en C++.

dans le langage de programmation C, tant les opérateurs pré-incrément que post-incrément renvoient des valeurs R, et non des valeurs l. Cela signifie qu'ils ne peuvent pas se trouver du côté gauche de l'opérateur d'affectation = . Ces deux déclarations donneront une erreur de compilateur dans C:

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

en C++ cependant, l'opérateur de pré-incrément renvoie une valeur , tandis que la l'opérateur de post-incrément renvoie une valeur de R. Cela signifie qu'une expression avec l'opérateur pre-increment peut être placée sur le côté gauche de l'opérateur d'affectation = !

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

pourquoi est-ce ainsi? Le post-incrément augmente la variable, et il renvoie la variable comme il était avant l'incrément s'est produit. C'est en fait juste une rvalue. L'ancienne valeur de la variable a est copié dans un registre temporaire, puis a est incrémenté. Mais l'ancienne valeur de a est retourné par l'expression, c'est une rvalue. Il ne représente plus le contenu de la variable.

le pré-incrément augmente d'abord la variable, puis il renvoie la variable comme il est devenu après l'incrément s'est produit. Dans ce cas, nous n'avons pas besoin de stocker l'ancienne valeur de la variable dans un registre temporaire. Nous récupérons juste la nouvelle valeur de la variable après qu'elle ait été incrémenté. Ainsi le pré-incrément renvoie une valeur de l, il renvoie la variable a elle-même. Nous pouvons utiliser attribuer cette valeur à quelque chose d'autre, c'est comme la déclaration suivante. C'est une conversion implicite de la lvalue en rvalue.

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

puisque le pré-incrément renvoie une valeur l, nous pouvons aussi lui assigner quelque chose. Les deux énoncés suivants sont identiques. Dans la deuxième affectation, a est incrémenté, puis sa nouvelle valeur est écrasée avec 2.

int a;
a = 2;
++a = 2;  // Valid in C++.
0
répondu Galaxy 2018-06-22 06:06:58
la source

Autres questions sur c++ c