Mélanger déclarations et code en C89: largesses de gcc

Une des différences fondamentales entre C89 et C99 est la possibilité offerte par la seconde norme mais interdite par la première de mélanger code et déclarations. En C89, il faut d’abord déclarer toutes les variables et ensuite les utiliser. En C99, on peut déclarer les variables « où » l’on veut.

Le code suivant est donc correct en C99 mais ne l’est pas en C89 :

#include
#include

int main(void)
{
   puts("Code first");
   int declare_next = 9;
   return declare_next;
}

Votre compilateur devrait donc générer une erreur à la ligne 7 si l’option C99 n’est pas activée. À cela prêt que le compilateur vedette du C (notamment sous Linux) n’est exactement de cet avis : gcc ne génère pas d’erreur ici. En soit, cela n’est pas dramatique sauf pour les débutants qui ne sont pas au courant des différentes normes et de ce qui changent entre les versions. Cela peut quand même être embêtant de ne pas être prévenu de problèmes de compatibilité avec la norme, si on souhaite faire une application non dépendante d’un compilateur. Même un programmeur averti des différences entre les versions de la norme peut faire une erreur d’inattention. Je retrace ici des éléments d’une conversation de Developpez.com pour voir comment faire pour que gcc soit un peu plus rigoureux.

Pour démarrer, commençons par compiler le code ci-dessus avec les options normales :

$ gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658)
(LLVM build 2336.1.00)
[...]
$ gcc declare_first.c -Wall -Wextra
$

Dans la suite de l’article, j’expliquerai pourquoi cette commande n’affiche aucun message et comment générer une erreur à la ligne 7.

La première chose à savoir est la version du langage utilisée. gcc supporte plusieurs versions du langage C : celles de la norme ISO mais aussi des extensions GNU (le support du C++ ne rentre pas dans le cadre de cet article, je n’en parlerai donc pas). Cette page indique que gcc utilise la version par défaut :

The default, if no C language dialect options are given, is -std=gnu90; this will change to -std=gnu99 or -std=gnu11 in some future release when the C99 or C11 support is complete. Some features that are part of the C99 standard are accepted as extensions in C90 mode, and some features that are part of the C11 standard are accepted as extensions in C90 and C99 modes.

On se doute donc que si la commande ci-dessus n’affiche pas de message, c’est sans doute parce que le standard GNU90 inclut cette fonctionnalité du C99. Il n’y donc pas de raison qu’il nous dise quoi que ce soit. Maintenant qu’on sait pourquoi gcc ne nous a rien dit, on va voir comment faire pour qu’il nous crie dessus.

On notera par ailleurs que les options -ansi, -std=c89 et -std=c90 sont équivalentes. Si je reprend la commande précédente et que j’y ajoute l’option -std=c99, je n’ai toujours aucun message mais c’est tout à fait normal. Ce qui est plus étonnant, c’est que je n’ai toujours pas de message en utilisant l’option -ansi. gcc prend ici une petite largesse avec la norme. Il faut rajouter l’option -pedantic qui force gcc à vraiment respecter la norme .

$ gcc declare_first.c -Wall -Wextra -ansi -pedantic
$ declare_first.c: In function ‘main’:
$ declare_first.c:7: warning: ISO C90 forbids mixed declarations and code

C’est déjà mieux ! Pourtant, gcc génère un warning et non une erreur. Il existe pour cela l’option -pedantic-errors :

$ gcc declare_first.c -Wall -Wextra -ansi -pedantic-errors
$ declare_first.c: In function ‘main’:
$ declare_first.c:7: error: ISO C90 forbids mixed declarations and code

On observe là un respect strict de la norme. Si vous souhaitez réellement respecter la norme et espérer que votre code compile sans problème avec un autre compilateur, ces options seront vos alliées.

Un dernier point intéressant à noter est le résultat de la commande suivante (l’option -std=gnu90 étant implicite) :

$ gcc declare_first.c -Wall -Wextra -pedantic
$ declare_first.c: In function ‘main’:
$ declare_first.c:7: warning: ISO C90 forbids mixed declarations and code

La réponse est ici :

Where the standard specified with -std represents a GNU extended dialect of C, such as `gnu90′ or `gnu99′, there is a corresponding base standard, the version of ISO C on which the GNU extended dialect is based. Warnings from -Wpedantic are given where they are required by the base standard. (It does not make sense for such warnings to be given only for features not in the specified GNU C dialect, since by definition the GNU dialects of C include all features the compiler supports with the given option, and there would be nothing to warn about.)

L’option -std=gnu90 est donc implicitement convertie en -std=c90, qui n’autorise pas le mélange déclarations et code, et l’option -pedantic est là pour interdire toute largesse.

Publicités

3 Réponses

  1. gallier2

    Juste une petite remarque concernant En C99, on peut déclarer les variables « où » l’on veut.. Ce n’est pas tout à fait vrai. Il y a un endroit où on n’a pas le droit de déclarer une variable, c’est à l’endroit d’un label.

    label:
    int toto;

    donnera une erreur de compilation.
    Mais c’est surtout à l’intérieur d’un switch/case que l’on rencontrera le plus souvent le cas.
    Il y a deux solutions à ce « problème », l’ancienne, celle qui marchait déjà avec C90, c.à.d. ouvrir un bloc { } ou l’autre de rajouter un ; après le label, ce qui clos l’expression.

    label:;
    int toto;

    J'aime

    2 juillet 2013 à 6:33

    • int main(void)
      {
      	int a = 1;
      
      	switch(a)
      	{
      		case 1 :;
      			int b = 3;
      			printf("case 1 : %d", b);
      			break;
      		case 2:
      		{
      			int b = 3;
      			printf("case 2 : %d", b);	
      			break;
      		}
      
      		default:
      			puts("default");
      	}
      
      	return a;
      }

      Intéressant à savoir. La phrase était en effet un peu large, mais je ne connaissais pas ce cas. Encore que j’ai eu un soucis il y a quelques temps avec le compilateur ARMCC de Keil pour une variable déclarée dans un case sans accolade ni ; mais il émettait juste un warning. Quelque chose du genre « declaration by-pass initialization process ».

      Les accolades ont quand même la qualité de masquer la variable dans les autres cases.

      J'aime

      3 juillet 2013 à 10:46

  2. Pingback: Le compilateur C18 et le mélange déclarations / code | Pierre Gradot

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s