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




qu est ce que c# (11)

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

#include <stdio.h>

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

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

C et C ++ ont de nombreuses différences, et tout le code C valide n'est pas un code C ++ valide.
(Par "valide", je veux dire le code standard avec un comportement défini, c'est-à-dire pas spécifique à l'implémentation / indéfini / etc.)

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

Pour en faire une comparaison raisonnable / utile (j'essaie d'apprendre quelque chose d'utile, de ne pas essayer de trouver des failles évidentes dans la question), supposons:

  • Rien lié au préprocesseur (ce qui signifie qu'il n'y a pas de hacks avec #ifdef __cplusplus , pragmas, etc.)
  • Tout ce qui est défini par l'implémentation est le même dans les deux langues (par exemple, les limites numériques, etc.)
  • Nous comparons des versions raisonnablement récentes de chaque norme (par exemple, C ++ 98 et C90 ou plus tard)
    Si les versions comptent, veuillez indiquer quelles versions de chaque produit produisent un comportement différent.

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 double abs(double) en C ++, donc abs(0.6) renvoie 0.6 alors qu'en C il retourne 0 cause de la conversion implicite double-à-int avant d'invoquer int abs(int) . En C, vous devez utiliser des fabs pour travailler en double .


Les fonctions intégrées dans C par défaut à la portée externe, comme celles de C ++, ne le sont pas.

Compiler les deux fichiers suivants ensemble imprimerait 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");
} 

De même, C ++ traite implicitement n'importe quel const global comme static moins qu'il ne soit explicitement déclaré extern , contrairement à C dans lequel extern est la valeur par défaut.


Ne pas oublier 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 ayez un fichier main.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:

$ 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

Pour C ++ et C90, il existe au moins une façon d'obtenir un comportement différent qui n'est pas défini par l'implémentation. C90 n'a pas de commentaires sur une seule ligne. Avec un peu de soin, nous pouvons l'utiliser pour créer une expression avec des résultats entièrement différents en C90 et en C ++.

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

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

int a = 10 + 3;

Puisque C90 n'a pas de commentaires sur une seule ligne, seul le /* comment */ est un commentaire. Le premier / et le 2 sont les deux parties de l'initialisation, donc ça 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.


Un autre sizeof trap: expressions booléennes.

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

Il est égal à sizeof(int) en C, car l'expression est de type int , mais est généralement 1 en C ++ (bien que ce ne soit pas obligatoire). En pratique, ils sont presque toujours différents.


Un autre listé 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)); 
}

Un vieux châtaigne qui dépend du compilateur C, ne reconnaissant pas les commentaires de fin de ligne C ++ ...

...
int a = 4 //* */ 2
        +2;
printf("%i\n",a);
...

Le langage de programmation C ++ (3ème édition) donne trois exemples:

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

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

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


#include <stdio.h>

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

En C, cela imprime quelle que soit la valeur de sizeof(int) sur le système actuel, ce qui est généralement 4 dans la plupart des systèmes couramment utilisés aujourd'hui.

En C ++, ceci doit imprimer 1.


struct abort
{
    int x;
};

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

Renvoie avec le code de sortie 0 en C ++ ou 3 en C.

Cette astuce pourrait probablement être utilisée pour faire quelque chose de plus intéressant, mais je ne pouvais pas penser à un bon moyen de créer un constructeur qui serait acceptable pour C. J'ai essayé de faire un exemple aussi ennuyeux avec le constructeur de copie, cela laisserait un argument être passé, quoique 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 ++, se plaignant de la redéfinition du «code de sortie». (Je pense que c'est un bogue du compilateur, sauf si j'ai soudainement oublié comment programmer.) Il s'est terminé avec un code de sortie de processus de 1 lorsqu'il est compilé en tant que C si.





c