C++ pour les programmeurs C

Decouvrez les incompatibilites entre C et C++ : declarations de fonctions, prototypes, types et bonnes pratiques pour migrer du C vers C++.

Mahmoud DEVO
Mahmoud DEVO
December 25, 2025 10 min read
C++ pour les programmeurs C

Les Incompatibilites entre C++ et C : Guide Complet

Introduction

Le langage C++ a ete concu par Bjarne Stroustrup au debut des annees 1980 comme une extension du langage C. L’objectif initial etait ambitieux : creer un langage qui conserverait la puissance et l’efficacite du C tout en ajoutant des fonctionnalites de programmation orientee objet. Cette philosophie a conduit a l’expression celebre : “C++ est un meilleur C”.

Cependant, cette vision idealiste a rencontre des obstacles pratiques. Au fil des annees et des evolutions des deux normes (C89, C99, C11, C17, C23 pour le C et C++98, C++11, C++14, C++17, C++20, C++23 pour le C++), les deux langages ont diverge sur certains points. Ce qui etait autrefois du code parfaitement valide en C peut ne plus compiler en C++, et vice versa.

Comprendre ces incompatibilites est essentiel pour plusieurs raisons :

  • Migration de code legacy : De nombreux projets industriels sont ecrits en C et necessitent une modernisation vers C++
  • Interoperabilite : Les projets mixtes C/C++ sont courants dans le developpement systeme
  • Eviter les bugs subtils : Certaines differences semantiques peuvent introduire des comportements inattendus
  • Meilleures pratiques : Connaitre les pieges permet d’ecrire du code plus portable

Dans cet article, nous allons explorer en profondeur les principales incompatibilites entre C et C++, des differences fondamentales aux subtilites les plus obscures. Que vous soyez un programmeur C chevronné souhaitant migrer vers C++ ou un developpeur C++ travaillant avec du code C existant, ce guide vous fournira les connaissances necessaires pour naviguer entre ces deux langages.


Les Declarations de Fonctions

Style K&R vs ANSI

Historiquement, le langage C proposait deux syntaxes pour definir les fonctions. La premiere, connue sous le nom de style “K&R” (Kernighan et Ritchie), date des origines du langage :

/* Style K&R - ancien style C */
double fexple(u, v)
int u;
double v;
{
    return u * v;
}

Dans ce style, les parametres sont declares entre la liste des arguments et le corps de la fonction. Cette syntaxe, bien que toujours acceptee par de nombreux compilateurs C pour des raisons de compatibilite, presente plusieurs inconvenients majeurs :

  • Pas de verification de type lors de l’appel
  • Code moins lisible
  • Source potentielle de bugs difficiles a detecter

La norme ANSI C (C89) a introduit une nouvelle syntaxe, dite “style prototype” :

/* Style ANSI - style moderne */
double fexple(int u, double v) {
    return u * v;
}

En C++, seul le style ANSI est accepte. Toute tentative d’utiliser le style K&R provoquera une erreur de compilation :

// ERREUR en C++ !
double fexple(u, v)
int u;
double v;
{
    return u * v;  // Ne compile pas
}

Prototypes Obligatoires en C++

En C, il est possible (bien que deconseille) d’appeler une fonction sans declaration prealable. Le compilateur suppose alors que la fonction retourne un int et accepte n’importe quels arguments :

/* Valide en C (mais dangereux !) */
int main() {
    double result = mafonction(42);  // Pas de prototype !
    return 0;
}

double mafonction(int x) {
    return x * 2.5;
}

Ce code compile en C avec un simple avertissement (ou meme sans), mais echoue en C++ avec une erreur :

error: 'mafonction' was not declared in this scope

En C++, toute fonction doit etre declaree avant son utilisation. Cette regle stricte permet au compilateur de verifier la coherence des types et d’eviter de nombreux bugs :

/* Correct en C++ */
double mafonction(int x);  // Declaration (prototype)

int main() {
    double result = mafonction(42);
    return 0;
}

double mafonction(int x) {
    return x * 2.5;
}

Type de Retour Explicite

En C89, une fonction sans type de retour explicite etait supposee retourner un int :

/* Valide en C89 */
mafonction(int x) {  // Type de retour implicite : int
    return x * 2;
}

Cette “fonctionnalite” a ete supprimee en C99 et n’a jamais ete acceptee en C++ :

/* ERREUR en C++ et C99+ */
mafonction(int x) {
    return x * 2;  // Erreur : type de retour manquant
}

/* Correct */
int mafonction(int x) {
    return x * 2;
}

Les Fonctions Sans Arguments

Une difference subtile mais importante concerne la declaration des fonctions sans arguments :

/* En C */
void fonction();   // Declaration : nombre et types d'arguments inconnus
void fonction(void);  // Declaration : aucun argument

/* En C++ */
void fonction();   // Declaration : aucun argument (equivalent a void)
void fonction(void);  // Declaration : aucun argument (equivalent)

En C, void fonction() signifie que la fonction accepte un nombre inconnu d’arguments, tandis qu’en C++, cela signifie qu’elle n’accepte aucun argument. Pour un code portable, utilisez toujours void explicitement :

/* Code portable C/C++ */
void fonction(void);

Incompatibilites Majeures

La Taille d’un Caractere Litteral

L’une des differences les plus surprenantes concerne l’operateur sizeof applique a un caractere litteral :

/* En C */
printf("%zu\n", sizeof('a'));  // Affiche : 4 (sur systeme 32/64 bits)
/* En C++ */
std::cout << sizeof('a') << std::endl;  // Affiche : 1

En C, un caractere litteral comme 'a' est de type int, ce qui explique que sizeof('a') retourne la taille d’un entier (generalement 4 octets). En C++, un caractere litteral est de type char, donc sizeof('a') retourne 1.

Cette difference peut avoir des consequences importantes dans du code generique ou des macros :

/* Code problematique */
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))

char message[] = "Hello";
/* En C : ARRAY_SIZE(message) utilise sizeof(int) pour le diviseur... */

Conversion de void*

En C, un pointeur void* peut etre implicitement converti vers n’importe quel autre type de pointeur :

/* Valide en C */
int* ptr = malloc(sizeof(int) * 10);
char* str = malloc(100);

En C++, cette conversion implicite n’est pas autorisee. Un cast explicite est necessaire :

/* ERREUR en C++ */
int* ptr = malloc(sizeof(int) * 10);  // Erreur : conversion invalide

/* Correct en C++ */
int* ptr = static_cast<int*>(malloc(sizeof(int) * 10));
/* Ou mieux, utilisez new */
int* ptr = new int[10];

Cette restriction en C++ est une mesure de securite typographique qui empeche des conversions accidentelles potentiellement dangereuses.

Mots-cles Reserves

Le C++ a introduit de nombreux mots-cles qui ne sont pas reserves en C. L’utilisation de ces mots comme identificateurs en C produira des erreurs en C++ :

Mot-cle C++Usage
classDefinition de classe
newAllocation dynamique
deleteLiberation de memoire
templateProgrammation generique
namespaceEspaces de noms
virtualMethodes virtuelles
private, public, protectedControle d’acces
thisPointeur vers l’instance
try, catch, throwGestion des exceptions
bool, true, falseType booleen
explicitConstructeurs explicites
mutableMembres modifiables
typenameSpecification de type
usingAlias de types/namespaces
operatorSurcharge d’operateurs

Exemple de code C valide mais invalide en C++ :

/* Valide en C */
int new = 42;
int class = 10;
int delete = 5;

void template(int private) {
    int public = private * 2;
}
/* ERREUR en C++ - tous ces noms sont reserves */

Structures et Enumerations

En C, les noms de structures et d’enumerations ne sont pas automatiquement des noms de types. Il faut utiliser les mots-cles struct ou enum, ou creer un typedef :

/* En C */
struct Point {
    int x, y;
};

struct Point p1;  // OK : utilisation de 'struct'
Point p2;         // ERREUR en C : 'Point' n'est pas un type

/* Solution en C avec typedef */
typedef struct Point {
    int x, y;
} Point;

Point p3;  // OK maintenant

En C++, les noms de structures et d’enumerations sont automatiquement des noms de types :

/* En C++ */
struct Point {
    int x, y;
};

Point p;  // OK : 'Point' est directement un type

De meme pour les enumerations :

/* En C */
enum Color { RED, GREEN, BLUE };
enum Color c = RED;  // OK
Color c2 = GREEN;    // ERREUR en C
/* En C++ */
enum Color { RED, GREEN, BLUE };
Color c = RED;  // OK

Initialisation des Tableaux de Caracteres

Une difference subtile existe dans l’initialisation des tableaux de caracteres :

/* Valide en C */
char str[5] = "Hello";  // OK : le '\0' final est ignore
/* ERREUR en C++ */
char str[5] = "Hello";  // Erreur : chaine de 6 caracteres dans tableau de 5

En C, si le tableau est trop petit pour contenir le caractere nul terminal, celui-ci est simplement omis. En C++, c’est une erreur de compilation.


Incompatibilites Mineures

Commentaires // en C89

Les commentaires sur une seule ligne (//) n’etaient pas supportes en C89. Ils ont ete ajoutes en C99 :

/* C89 : seuls ces commentaires sont valides */

// C99+ et C++ : commentaires sur une ligne acceptes

Pour du code strictement C89, utilisez uniquement /* */.

Declaration de Variables

En C89, toutes les variables devaient etre declarees au debut d’un bloc, avant toute instruction :

/* C89 */
void fonction() {
    int a = 10;
    int b = 20;

    a = a + b;

    int c = 30;  /* ERREUR en C89 ! */
}

C99 et C++ autorisent les declarations n’importe ou dans un bloc :

/* C99 et C++ */
void fonction() {
    int a = 10;
    a = a + 5;

    int b = 20;  /* OK */

    for (int i = 0; i < 10; i++) {  /* OK */
        // ...
    }
}

Constantes de Chaines de Caracteres

En C, les chaines litterales sont de type char*, tandis qu’en C++, elles sont de type const char* :

/* En C */
char* message = "Hello";  // OK (mais dangereux)
message[0] = 'J';         // Comportement indefini !
/* En C++ */
char* message = "Hello";        // Avertissement ou erreur
const char* message = "Hello";  // Correct

Le C++ encourage (et parfois impose) l’utilisation de const pour refleter le fait que les chaines litterales sont stockees en memoire en lecture seule.

Linkage des Constantes

En C, les variables const ont par defaut une liaison externe (external linkage). En C++, elles ont une liaison interne (internal linkage) :

/* fichier1.c */
const int MAX = 100;  // Liaison externe par defaut

/* fichier2.c */
extern const int MAX;  // OK
/* fichier1.cpp */
const int MAX = 100;  // Liaison interne par defaut

/* fichier2.cpp */
extern const int MAX;  // ERREUR : MAX n'est pas trouve

Pour obtenir une liaison externe en C++, utilisez extern explicitement :

/* fichier1.cpp */
extern const int MAX = 100;  // Liaison externe

/* fichier2.cpp */
extern const int MAX;  // OK

Initialisation des Unions

L’initialisation des unions differe legerement :

/* En C89, seul le premier membre peut etre initialise */
union Data {
    int i;
    float f;
};

union Data d = { 42 };  // Initialise 'i'

C99 a introduit les “designated initializers” :

/* En C99 */
union Data d = { .f = 3.14f };  // Initialise 'f' explicitement

C++ n’a pas supporte les designated initializers jusqu’a C++20 :

/* C++20 */
union Data d = { .f = 3.14f };  // OK en C++20

Migration de C vers C++

Etapes Recommandees

La migration d’un projet C vers C++ doit etre faite de maniere methodique. Voici les etapes recommandees :

1. Preparation du code C

Avant toute migration, assurez-vous que votre code C est propre et conforme aux bonnes pratiques :

# Compiler avec tous les avertissements
gcc -Wall -Wextra -Wpedantic -std=c11 code.c

# Utiliser des analyseurs statiques
cppcheck --enable=all code.c

2. Correction des incompatibilites syntaxiques

Commencez par resoudre les problemes les plus evidents :

  • Ajoutez des prototypes pour toutes les fonctions
  • Remplacez les definitions K&R par le style ANSI
  • Ajoutez les types de retour explicites
  • Renommez les identificateurs utilisant des mots-cles C++

3. Ajustement des conversions de pointeurs

/* Avant (C) */
void* ptr = malloc(100);
int* iptr = ptr;

/* Apres (compatible C++) */
void* ptr = malloc(100);
int* iptr = (int*)ptr;

4. Gestion des constantes de chaines

/* Avant */
char* msg = "Hello";

/* Apres */
const char* msg = "Hello";

5. Compilation incrementale

Migrez fichier par fichier, en utilisant extern "C" pour l’interoperabilite :

/* Dans les headers */
#ifdef __cplusplus
extern "C" {
#endif

void ma_fonction_c(int x);

#ifdef __cplusplus
}
#endif

Outils Utiles

Plusieurs outils peuvent faciliter la migration :

Compilateurs avec mode strict

# GCC en mode C++
g++ -std=c++17 -Wall -Wextra -Wpedantic code.c

# Clang avec analyse statique
clang++ -std=c++17 -Weverything code.c

Analyseurs statiques

  • cppcheck : Detection d’erreurs communes et problemes de portabilite
  • clang-tidy : Modernisation automatique du code
  • PVS-Studio : Analyse approfondie (commercial)

Formateurs de code

  • clang-format : Formatage automatique conforme aux standards
  • astyle : Formateur multilangage

Scripts de conversion

# Exemple de script pour renommer les fichiers .c en .cpp
for f in *.c; do
    mv "$f" "${f%.c}.cpp"
done

Bonnes Pratiques

Pour un Code Portable C/C++

Si vous devez maintenir du code compatible avec les deux langages, suivez ces recommandations :

1. Utilisez les macros de detection de langage

#ifdef __cplusplus
    /* Code specifique C++ */
    #define MALLOC(type, n) static_cast<type*>(malloc(sizeof(type) * (n)))
#else
    /* Code specifique C */
    #define MALLOC(type, n) malloc(sizeof(type) * (n))
#endif

2. Preferez les headers C++

Quand vous utilisez des headers C standard en C++, utilisez la version C++ :

/* Prefere en C++ */
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>

/* Au lieu de */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

3. Evitez les identificateurs reserves

Meme en C, evitez d’utiliser des noms qui sont reserves en C++ :

/* A eviter meme en C */
int class;     /* Reserve en C++ */
int new;       /* Reserve en C++ */
int template;  /* Reserve en C++ */

4. Declarez explicitement void pour les fonctions sans parametres

/* Portable */
int fonction(void);

/* Non portable */
int fonction();  /* Signifie different choses en C et C++ */

5. Utilisez des casts explicites pour malloc

/* Compatible C et C++ */
int* ptr = (int*)malloc(sizeof(int) * 10);

Organisation du Projet

Pour un projet mixte C/C++, organisez votre code de maniere claire :

projet/
├── include/
│   ├── module_c.h      # Headers C avec extern "C"
│   └── module_cpp.hpp  # Headers C++ purs
├── src/
│   ├── module_c.c      # Sources C
│   └── module_cpp.cpp  # Sources C++
└── Makefile

Template de header compatible

/* module.h */
#ifndef MODULE_H
#define MODULE_H

#ifdef __cplusplus
extern "C" {
#endif

/* Declarations C */
void fonction_c(int x);
int autre_fonction(double y);

#ifdef __cplusplus
}
#endif

#endif /* MODULE_H */

Conclusion

Les incompatibilites entre C et C++ sont le resultat de l’evolution independante de deux langages qui partagent une histoire commune mais ont pris des directions differentes. Bien que C++ ait ete concu pour etre “un meilleur C”, les exigences de la programmation orientee objet et le renforcement de la securite typographique ont introduit des divergences significatives.

Tableau Recapitulatif des Incompatibilites

AspectCC++
Style K&R pour fonctionsAccepteRefuse
Prototype obligatoireNon (C89), Oui (C99+)Oui
Type retour implicite intOui (C89)Non
sizeof('a')sizeof(int)sizeof(char)
Conversion void*ImpliciteExplicite requise
Mots-cles class, new, etc.Identificateurs validesReserves
Nom struct comme typeNon (typedef requis)Oui
Declaration variablesDebut de bloc (C89)N’importe ou
Constantes de chaineschar*const char*
Linkage des constExterneInterne
Commentaires //Non (C89), Oui (C99+)Oui

Points Cles a Retenir

  1. Toujours utiliser des prototypes pour toutes les fonctions
  2. Declarer explicitement les types de retour pour eviter les ambiguites
  3. Utiliser des casts explicites pour les conversions de void*
  4. Eviter les mots-cles C++ comme identificateurs en C
  5. Utiliser extern “C” pour l’interoperabilite C/C++
  6. Compiler avec les avertissements actives pour detecter les problemes tot

La comprehension de ces differences est essentielle pour tout developpeur travaillant dans des environnements ou C et C++ coexistent. En suivant les bonnes pratiques decrites dans cet article, vous pouvez ecrire du code plus robuste, plus portable et plus facile a maintenir.


Ressources Supplementaires

Pour approfondir vos connaissances sur les differences entre C et C++, voici quelques ressources recommandees :

  • “The C++ Programming Language” par Bjarne Stroustrup - Le livre de reference par le createur de C++
  • “Expert C Programming: Deep C Secrets” par Peter van der Linden - Pour comprendre les subtilites du C
  • Documentation officielle - cppreference.com pour les standards C et C++
  • Compilateurs en ligne - godbolt.org pour tester la compatibilite entre compilateurs
Advertisement

In-Article Ad

Dev Mode

Share this article

Mahmoud DEVO

Mahmoud DEVO

Senior Full-Stack Developer

I'm a passionate full-stack developer with 10+ years of experience building scalable web applications. I write about Vue.js, Node.js, PostgreSQL, and modern DevOps practices.

Enjoyed this article?

Subscribe to get more tech content delivered to your inbox.

Related Articles