C Cpp
Le C++ s’est vu comme une surcouche du C, mais dès le début des différences existent et un code valide en ANSI C peut ne pas compiler en C++.
Incompatibilités depuis l’ANSI C
Appeller la fonction main récursivement
En C on peut appeller la fonction main(), pas en C++
int point_d_entree_alternatif_du_programme() {
// ...
main(); // illegal en C++
}
Accéder à une variable déclarée dans une instruction for
Le C ANSI ne permet pas de déclarer une variable dans une boucle for , mais cela a été possible de-facto par gcc avant le C99. La durée de vie de cette variable est automatique mais sa portée est visible dans le bloc où elle est déclarée.
Code avant l’Ansi C
#include <stdio.h>
/* Avant l'ANSI C */
int main() {
int i;
int j = 23;
for(i = 0; i < 3 ; i++) {
printf("%d", i); // 0, 1, 2
}
printf("%d", i); // 3
}
Depuis l’Ansi C (GCC et clang, ne compile pas avec MSVC)
#include <stdio.h>
// Depuis l'ANSI C
int main() {
// illégal en K&R C, toléré de facto en C Ansi, avec règles de
for(int i = 0; i < 3 ; i++) {
printf("%d", i);
}
printf("%d", i); // légal en C ansi, illégal en C99 et C++
// légal en C99, illégal en C ansi,
// considéré comme une redéclaration
for(int i = 0; i < 3 ; i++) {
printf("%d", i);
}
{
int j = 23; // portee limitée au bloc
// on peut déclarer les variables
// ailleurs qu'en tête de fonction
printf("%d\n", j); // 23
// legal en C gcc, la portee de i est limitée au bloc
for(int i = 0; i < 3 ; i++) {
printf("%d", i);
}
}
// printf("%d\n", j); // illégal dans tous les cas
}
Le C ansi permet de facto de déclarer une variable au sein d’une boucle for
, mais la portée de celle ci est celle de la
fonction :
#include <stdio.h>
main() {
printf("test\n");
for(int i = 0; i < 3 ; i++) {
printf("%d\n", i);
}
printf("%d\n", i);
}
// gcc -std=c89 ./test.c && ./a.out
En compilant en mode ANSI le programme affiche
test
0
1
2
3
Depuis le C99, une variable déclarée dans une instruction
for
a une portée limitée à l’instruction. En C++
la portée d’une telle variable est étendue au bloc
contrôlé par l’instruction.
#include <stdio.h>
int main() {
printf("test\n");
for(int i = 0; i < 3 ; i++) {
int i = 42; // illégal en C++
}
}
Zero initialisation de structures avec enumerations
En C, admettons que nous ayons une structure de la sorte
struct toto {
enum tata {
TATA, TOTO, TITI
} state;
int length;
float time;
}
On pourra la “zero initialiser” de la sorte en C
int main() {
struct toto toto = { 0 };
}
En C++, l’enumeration en premiere position l’empeche et il faut ecrire
int main() {
toto toto = { TATA };
// le reste de la structure est zero initialisee
}
Mais si on declare la structure en ne mettant pas d’enumération en première position, on pourra zero initialiser la structure en C et C++.
struct toto {
int _placeholder_do not_use;
enum tata {
TATA, TOTO, TITI
} state;
int length;
float time;
}
int main() {
struct toto toto = { 0 }; // fonctionne en C et C++
// le mot clé struct est optionel en C++
}
Incompatibilités depuis le C99
Très peu de nouveautés du C99 sont utilisables en C++
VLA, pointer-to-vla, flexible length arrays, restrict
,
specificateurs static de pointeurs,
compound statements, designated initialisers.
Rendre un programme compatible en C et C++
Pour qu’un programme puisse compiler en C/C++ il
faut généralement l’écrire en C, en évitant certaines
constructions incompatibles évoquées dans cette page,
notammenent il faut cast le type de retour de malloc
même si le type de variable cible permet de le deviner.
#include <stdlib.h>
int main() {
int * toto = malloc(sizeof *toto); // ne fonctionne qu'en C
int * tata = (int*)malloc(sizeof *tata); // ok en C/C++
}
Il faut également spécifier au compilateur C++ un linkage de type C pour les identifiants globaux externes.
// header.h
extern "C" int toto();
// ou encore
extern "C" {
int tata();
}
Un compilateur C ne reconnait pas la construction
en bloc d’extern ni le mot clé "C"
, on ajoute
donc une définition de préprocesseur ainsi pour
signifier que ca ne servira que à un compilateur C++.
// header.h
#ifdef __cplusplus
extern "C" {
#endif
int toto();
int tata();
#ifdef __cplusplus
} // #ifdef __cplusplus
#endif
En se limitant au C ansi (avec quelque précautions), castant le type de retour de malloc et en déclarant les identificateurs externes avec la convention du C, on arrive à écrire un programme « C/C++ » qui fonctionne avec les deux langages. Ce dialecte est surtout utile pour les librairies utilitaires.
Apports du C++ dans l’ANSI C
Fontions a prototypes et eumerations.
Apports du C++ dans le C23
Les attributes, variables auto
, les lambdas
Voila ce qu’il faut changer dans un programme pour qu’il compile a la fois en C et en C++.
Les fonctionalités du C adoptées par le C++17 (structures initialisée designées).
Convergence prévue pour le C/C++23
Fonctionalités du C++ dans le C (fonctions a prototypes, enums, constexpr)