Programme C sans en-tête

J’écris le programme “hello world” en C.

void main() { printf("Hello World"); } // note that I haven't included any header file 

Le programme comstack avec avertissement comme

 vikram@vikram-Studio-XPS-1645:~$ gcc hello.c hello.c: In function 'main': hello.c:2:2: warning: incompatible implicit declaration of built-in function 'printf' vikram@vikram-Studio-XPS-1645:~$ ./a.out Hello Worldvikram@vikram-Studio-XPS-1645:~$ 

Comment est-ce possible? Comment le système d’exploitation relie-t-il une bibliothèque sans inclure d’en-tête?

Le compilateur construit votre fichier source avec une référence à une fonction appelée printf() , sans savoir quels arguments il prend réellement ou quel est son type de retour. L’assembly généré contient un push de l’adresse de la chaîne "Hello World" dans la zone de données statiques de votre programme, suivie d’un call à printf .

Lors de la liaison de votre fichier object à un exécutable, l’éditeur de liens voit une référence à printf et fournit la fonction de bibliothèque standard C printf() . Par coïncidence , l’argument que vous avez passé ( const char* ) est compatible avec la déclaration de la vraie printf() , de sorte qu’il fonctionne correctement. Cependant, notez que le printf() que votre programme déclare implicitement a le type de retour int (je pense), que le printf() standard a également; mais s’ils différaient et que vous deviez affecter le résultat de l’appel de printf() à une variable, vous auriez un comportement indéfini et vous obtiendriez probablement une valeur incorrecte.

Longue histoire courte: #include les en-têtes corrects pour obtenir les déclarations correctes des fonctions que vous utilisez, car ce type de déclaration implicite est obsolète, car il est sujet aux erreurs.

La fonction printf trouve dans la bibliothèque C (la libc dans votre cas) qui est liée implicitement (en fait, gcc a une fonction printf intégrée mais elle n’est pas pertinente).

Inclure l’en-tête n’apporte aucune fonction à l’éditeur de liens, il informe simplement le compilateur de leurs déclarations (c’est-à-dire “à quoi ils ressemblent “).

Évidemment, vous devez toujours inclure les en-têtes, sinon vous forcez le compilateur à faire des hypothèses sur l’apparence des fonctions.

En C, si vous utilisez une fonction de bibliothèque standard, vous devez inclure l’en-tête standard dans lequel la fonction est déclarée. Pour printf vous devez inclure le fichier d’en-tête stdio.h .

Dans C89 (et GNU C89 qui est le langage par défaut sur gcc ), une déclaration de fonction peut parfois être omise car il existe une fonctionnalité appelée déclaration de fonction implicite: lorsqu’un identifiant de fonction foo est utilisé et que la fonction n’a pas été déclarée, l’implémentation utiliserait cette déclaration:

  /* foo is a function with an unspecified number of arguments */ extern int foo(); 

Mais cette déclaration n’est valide que pour les fonctions qui retournent int avec un nombre d’arguments non spécifié mais fixe . Si la fonction accepte un nombre variable d’arguments (comme printf ), ce programme invoquerait un comportement indéfini.

Voici ce que dit C89 / C90:

(C90, 6.7.1) “Si une fonction qui accepte un nombre variable d’arguments est définie sans une liste de types de parameters qui se termine par la notation de points de suspension, le comportement est indéfini.

Donc, gcc est assez gentil pour comstackr même en C89 et GNU C89: un compilateur pourrait refuser de comstackr.

Notez également que

 void main() { ... } 

n’est pas une définition valide pour main (du moins sur les implémentations hébergées, ce qui est probablement votre cas).

Si votre fonction principale ne prend aucun argument, utilisez cette définition valide:

 int main(void) { ... } 

L’en-tête 1 contient généralement uniquement des déclarations de fonctions, des constantes symboliques et des définitions de macros; il n’inclut généralement pas les définitions de fonctions.

Tout ce que stdio.h vous donne est la déclaration du prototype pour printf :

 int printf(const char * ressortingct format, ...); // as of C99 

L’ implémentation de printf trouve dans un fichier de bibliothèque séparé auquel votre code est lié.

Votre code “fonctionne” pour deux raisons:

  1. Sous C89 et les versions antérieures, si le compilateur voit un appel de fonction avant une déclaration ou une définition de cette fonction, il supposera que la fonction renvoie int et prend un nombre non spécifié de parameters.

  2. L’implémentation de printf renvoie un int , et vous avez passé un argument compatible avec les printf de l’implémentation de printf pour le premier argument.

Et pour faire écho à ce que tout le monde dit, utilisez int main(void) ou int main(int argc, char **argv) ; à moins que la documentation de votre compilateur répertorie explicitement void main() tant que signature légale, son utilisation invoquera un comportement indéfini (ce qui signifie tout: votre code s’exécutant sans problème apparent, un crash à la sortie ou un chargement complet).


  1. Je dis “d’habitude”; J’ai rencontré des entêtes contenant du code, mais celles-ci étaient généralement écrites par des personnes qui ne savaient pas ce qu’elles faisaient. Il peut arriver très rarement que l’insertion de code dans un en-tête soit justifiée, mais en règle générale, c’est une mauvaise pratique.

hello.c: 2: 2: avertissement: déclaration implicite incompatible avec la fonction intégrée ‘printf’

Pour traiter cet avertissement, vous devez inclure le fichier d’en-tête ( stdio.h ). Vous utilisez accidentellement une ancienne fonctionnalité de C qui est obsolète depuis 1999.

De plus, le fait que le lien n’échoue pas signifie simplement que la bibliothèque C standard est liée par défaut. Que vous ayez inclus ou non l’en-tête concerné est sans importance.