Différence de résultat entre compilateurs

Le
YannicK
Bonjour,

éternel débutant en C pour mon plaisir, je me permets de venir vous
demander quelques éclaircissements sur une situation que je n'arrive pas
à comprendre :

J'utilise le cours en ligne spécial "grand débutant" du "site du zéro" :
<http://www.siteduzero.com/tutoriel-3-14189-apprenez-a-programmer-en-c.html>

Je réalise les exercices du cours dans deux environnements différents :
- sous windows vista avec l'IDE visual C++ 2008 express
- sous linux ubuntu 9.04 avec gcc

J'ai écrit un programme dans le cadre des exercices proposés sur les
tableaux par ce cours en ligne. Le fichier en question peut être
téléchargé ici :
< http://dl.free.fr/to7PFReLM/tableau.c>

Ce qui m'étonne, c'est que j'arrive à compiler sans difficulté ce code
sous Linux, et que le programme se comporte exactement comme je le
souhaite. Par contre, sous Windows, impossible de compiler, l'IDE me
renvoie 42 erreurs et 31 avertissements !!! La plupart des erreurs
semblent être liées aux variables. Par exemple :
"erreur de syntaxe : absence de ';' avant 'type'"
"identificateur non déclaré"

Or, j'ai beau lire et relire mon code, les variables me sembles toutes
déclarées correctement et il ne manque à mon sens pas de ";" en fin
d'instructions. De plus, comme je le disais au début, le même code se
compile sans aucune erreur sous Linux

Alors, comment expliquer que deux compilateurs réagissent aussi
différemment, et où et mon erreur ?

Merci par avance du temps que vous pourrez me consacrer,



--
YannicK
yann801 *arobase* yahoo *point* fr
yann801 *at* yahoo *dot* fr
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 30
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Xavier Roche
Le #20081751
YannicK a écrit :
Or, j'ai beau lire et relire mon code, les variables me sembles toutes
déclarées correctement



Mais pas en début de bloc ; ce qui est interdit par C89, et comme Visual
est uniquement "C89" pour le C, c'est considéré comme une erreur.

Pour repérer ce type de souci, utiliser le drapeau
"-Wdeclaration-after-statement" de gcc

(gcc -W -Wall -Wdeclaration-after-statement ..)
Éric Lévénez
Le #20081741
YannicK a écrit :

Or, j'ai beau lire et relire mon code, les variables me sembles toutes
déclarées correctement et il ne manque à mon sens pas de ";" en fin
d'instructions.



gcc sait compiler en C99, mais je crois que les outils Microsoft ne sont
pas totalement compatibles.

Quand je compile avec gcc, j'ai quand même des warnings :

tableau.c:6: warning: function declaration isn’t a prototype
tableau.c:15: warning: function declaration isn’t a prototype
tableau.c:100: warning: function declaration isn’t a prototype

Et pour info ton programme ne teste pas le code de retour de scanf, ce
qui fait que si on entre une lettre, ou si l'entrée standard est fermée,
le programme va boucler sans cesse.


--
Éric Lévénez
FAQ de fclc :
YannicK
Le #20082081
Éric Lévénez a écrit :
YannicK a écrit :

Or, j'ai beau lire et relire mon code, les variables me sembles toutes
déclarées correctement et il ne manque à mon sens pas de ";" en fin
d'instructions.



gcc sait compiler en C99, mais je crois que les outils Microsoft ne sont
pas totalement compatibles.

Quand je compile avec gcc, j'ai quand même des warnings :

tableau.c:6: warning: function declaration isn’t a prototype
tableau.c:15: warning: function declaration isn’t a prototype
tableau.c:100: warning: function declaration isn’t a prototype

Et pour info ton programme ne teste pas le code de retour de scanf, ce
qui fait que si on entre une lettre, ou si l'entrée standard est fermée,
le programme va boucler sans cesse.




Merci à tous les deux pour vos réponses rapides. La cause du problème
venait effectivement de variables déclarées en cours de programmes,
après des instructions.

En déplaçant toutes mes variables au début de main(), cela compile sans
incident avec visual c++ 2008. Je n'ai plus que 5 avertissement relatifs
au manque de sécurité de la fonction scanf(), problème que je tenterai
de régler demain.



--
YannicK
yann801 *arobase* yahoo *point* fr
yann801 *at* yahoo *dot* fr
-ed-
Le #20085041
On 6 sep, 21:59, YannicK
Bonjour,

éternel débutant en C pour mon plaisir, je me permets de venir vous
demander quelques éclaircissements sur une situation que je n'arrive pa s
à comprendre :

J'utilise le cours en ligne spécial "grand débutant" du "site du zé ro" :

Je réalise les exercices du cours dans deux environnements différents :
- sous windows vista avec l'IDE visual C++ 2008 express
- sous linux ubuntu 9.04 avec gcc

J'ai écrit un programme dans le cadre des exercices proposés sur les
tableaux par ce cours en ligne. Le fichier en question peut être
téléchargé ici :

Ce qui m'étonne, c'est que j'arrive à compiler sans difficulté ce c ode
sous Linux, et que le programme se comporte exactement comme je le
souhaite. Par contre, sous Windows, impossible de compiler, l'IDE me
renvoie 42 erreurs et 31 avertissements !!! La plupart des erreurs
semblent être liées aux variables. Par exemple :
"erreur de syntaxe : absence de ';' avant 'type'"
"identificateur non déclaré"



Ton code n'est pas conforme à C90 :


-------------- Build: Debug in hello ---------------

Compiling: main.c
Linking console executable: binDebughello.exe
C:devhellomain.c:6: warning: function declaration isn't a prototype
C:devhellomain.c:15: warning: function declaration isn't a
prototype
C:devhellomain.c: In function `main':
C:devhellomain.c:17: warning: ISO C90 forbids mixed declarations
and code
C:devhellomain.c:33: warning: ISO C90 forbids mixed declarations
and code
C:devhellomain.c:38: warning: ISO C90 does not support the `%lf'
printf format
C:devhellomain.c:48: warning: ISO C90 forbids mixed declarations
and code
C:devhellomain.c:67: warning: ISO C90 forbids mixed declarations
and code
C:devhellomain.c:83: warning: ISO C90 forbids mixed declarations
and code
C:devhellomain.c: At top level:
C:devhellomain.c:100: warning: function declaration isn't a
prototype
Output size is 23.09 KB
Process terminated with status 0 (0 minutes, 0 seconds)
0 errors, 9 warnings

ceci est correct :

#include #include
/* Exercices sur les tableaux */

void affiche_tableau (int tableau[], int tailleTableau)
{
int i = 0;
for (i = 0; i < tailleTableau; i++)
{
printf ("cellule %d : %dn", i, tableau[i]);
}
}

int sommeTableau (int tableau[], int tailleTableau)
{
int somme = 0;
int i = 0;
for (i = 0; i < tailleTableau; i++)
{
somme += tableau[i];
}
return somme;
}

double moyenneTableau (int tableau[], int tailleTableau)
{
double somme = sommeTableau (tableau, tailleTableau);
double moyenne = somme / tailleTableau;
return moyenne;
}

void copie (int tableauOriginal[], int tableauCopie[], int
tailleTableau)
{
int i = 0;
for (i = 0; i < tailleTableau; i++)
{
tableauCopie[i] = tableauOriginal[i];
}
}

void maximumTableau (int tableau[], int tailleTableau, int valeurMax)
{
int i = 0;
for (i = 0; i < tailleTableau; i++)
{
if (tableau[i] > valeurMax)
{
tableau[i] = 0;
}
}
}

void ordonnerTableau (int tableau[], int tailleTableau)
{
int i = 0;
int j = 0;
int swap = 0;
for (i = 0; i < tailleTableau; i++)
{
for (j = 0; j < tailleTableau; j++)
{
if (i != j)
{
if (tableau[j] >= tableau[i])
{
swap = tableau[j];
tableau[j] = tableau[i];
tableau[i] = swap;
}
}
}
}
}

static void pause (void)
{
int suite = -1;
while (suite < 0)
{
printf ("Entrez un chiffre pour continuer : n");
scanf ("%d", &suite);
}

suite = -1;
}

int main (void)
{
int tableau1[4] = { 1, 3, 7, 11 };
int somme_tableau = 0;
printf ("Le tableau No 1 contient les valeurs suivantes : n");
affiche_tableau (tableau1, 4);
somme_tableau = sommeTableau (tableau1, 4);
printf ("La somme des elements du tableau 1 est egale a : %dn",
somme_tableau);
printf ("Fin de l'exercice No 1.n");
pause ();

{
int tableau11[4] = { 1, 3, 7, 11 };
double moyenne_tableau = 0;
printf ("Le tableau No 11 contient les valeurs suivantes : n");
affiche_tableau (tableau11, 4);
moyenne_tableau = moyenneTableau (tableau11, 4);
printf ("La moyenne des elements du tableau 11 est egale a : %f
n",
moyenne_tableau);
}
printf ("Fin de l'exercice No 2.n");
pause ();

{
int tableau12[4] = { 1, 3, 7, 11 };
int tableau2[4];
printf ("Le tableau No 1 contient les valeurs suivantes : n");
affiche_tableau (tableau12, 4);
printf ("Le tableau No 2 contient les valeurs suivantes : n");
affiche_tableau (tableau2, 4);
printf ("Je transfere le Tableau1 dans Tableau2 ...");
copie (tableau12, tableau2, 4);
printf
("Le tableau No 2 contient desormais les valeurs suivantes :
n");
affiche_tableau (tableau2, 4);
}
printf ("Fin de l'exercice No 3.n");
pause ();

{
int tableau3[4] = { 1, 3, 237, 511 };
printf ("Le tableau No 3 contient les valeurs suivantes : n");
affiche_tableau (tableau3, 4);
printf ("Je remets a zero toutes les valeurs superieures a
500n");
maximumTableau (tableau3, 4, 500);
printf ("Le tableau No 3 contient les valeurs suivantes : n");
affiche_tableau (tableau3, 4);
}
printf ("Fin de l'exercice No 4.n");
pause ();

{
int tableau4[4] = { 230, 812, 37, 51 };
printf ("Le tableau No 4 contient les valeurs suivantes : n");
affiche_tableau (tableau4, 4);
printf ("Je trie par ordre croissant les donnees du tableau ...
n");
ordonnerTableau (tableau4, 4);
printf ("Le tableau No 4 contient les valeurs suivantes : n");
affiche_tableau (tableau4, 4);
}
printf ("Fin de l'exercice No 5.n");
pause ();
return 0;
}
-ed-
Le #20085031
On 6 sep, 22:59, YannicK
En déplaçant toutes mes variables au début de main(), cela compile sans
incident avec visual c++ 2008. Je n'ai plus que 5 avertissement relatifs
au manque de sécurité de la fonction scanf(), problème que je tente rai
de régler demain.



Si tu veux écrire du code portable, il faut désactiver le flag qui va
bien comme indiqué dans le message d'avertissement de Visual C++.
Marc Boyer
Le #20085551
Le 06-09-2009, YannicK
Bonjour,


[SNIP]
Alors, comment expliquer que deux compilateurs réagissent aussi
différemment, et où et mon erreur ?



Il faut commencer par dire qu'il n'existe pas "le langage C",
mais (et je simplifie déjà), deux normes ANSI/ISO connues
sous les noms de C89 et C99.

Par défaut, gcc compile un langage un peu intermédiaire
entre C89 et C99, qui entre autre accepte les commentaires
du type // et les déclarations de variables au milieu
de blocs, choses que tu utilise.

Pour dire à gcc de compiler du C89, on écrit
gcc -stdÈ9
et pour du c99
gcc -stdÉ9

Ton compilo Windows doit avoir un réglage par défaut
différent de gcc, mais il doit bien y avoir un moyen de
lui dire quelle variation du langage tu veux utiliser.

Marc Boyer
--
En prenant aux 10% des francais les plus riches 12% de leurs revenus,
on pourrait doubler les revenus des 10% les plus pauvres.
http://www.inegalites.fr/spip.php?article1&id_mot0
zwim
Le #20086801
Le Mon, 7 Sep 2009 01:13:15 -0700 (PDT)
-ed- a écrit

Au fait à propos de ce codage :


[instructions ...]

{
int tableau3[4] = { 1, 3, 237, 511 };
printf ("Le tableau No 3 contient les valeurs suivantes : n");
affiche_tableau (tableau3, 4);
printf ("Je remets a zero toutes les valeurs superieures a
500n");
maximumTableau (tableau3, 4, 500);
printf ("Le tableau No 3 contient les valeurs suivantes : n");
affiche_tableau (tableau3, 4);
}

[instructions ...]




j'ai une question sur l'évolution de la taille de la pile.

J'avais remarqué dans le passé que le fait d'utiliser un nouveau
contexte comme ceci au milieu des instructions d'une fonction
augmentait parfois sensiblement l'utilisation de la stack dédiée à la
fonction.

Quand on écrit

if(condition)
{
[instructions]
}

ça reste stable (juste un jmp en assembleur), en revanche

[instructions1]
{
[instructions2]
}
[instructions3]

faisait augmenter la stack (ou du moins il y a des opérations sur sp
en asm), ** même si [instructions2] ne contenait pas de déclaration
de variables ** , il s'agissait d'un compilo ADS1.2 pour arm9 je
crois.

Savez-vous si c'est général, ou si c'est une spécificité de cet
environnement ?

La norme parle de conditions de visibilité des symboles présents dans
[instructions2] mais mes connaissances sur les effets de bords
d'écriture d'un bloc { } sont très limitées.


--
zwim.
Rien n'est impossible que la mesure de la volonté humaine...
YannicK
Le #20088481
-ed- a écrit :




Ton code n'est pas conforme à C90 :




Merci pour cette correction qui m'a permis d'apprendre quelques trucs au
niveau de la syntaxe, et sur la possibilité d'inclure au sein d'une
fonction des blocs d'instructions entre {}

Par contre tu as supprimé le prototypage des fonctions pour les déclarer
avant main(). Or, j'avais cru comprendre que c'était une bonne habitude
à prendre pour quand j'aurais à écrire des programmes modulaires ...
Est-ce aussi pour un problème de respect de la norme ?




--
YannicK
yann801 *arobase* yahoo *point* fr
yann801 *at* yahoo *dot* fr
zwim
Le #20089751
Le Mon, 07 Sep 2009 18:42:48 +0200
YannicK a écrit
-ed- a écrit :




Ton code n'est pas conforme à C90 :




Merci pour cette correction qui m'a permis d'apprendre quelques trucs au
niveau de la syntaxe, et sur la possibilité d'inclure au sein d'une
fonction des blocs d'instructions entre {}

Par contre tu as supprimé le prototypage des fonctions pour les déclarer
avant main(). Or, j'avais cru comprendre que c'était une bonne habitude
à prendre pour quand j'aurais à écrire des programmes modulaires ...
Est-ce aussi pour un problème de respect de la norme ?



A priori la "tradition" est de mettre les corps des fonctions dans un
fichier toto.c et les prototypes dans un fichier toto.h (généralement
de même nom que le fichier .c).

De ce fait le #include "toto.h" quasi inévitablement inclus par toto.c
recopie ces prototypes en début du fichier C.

Les prototypes étant présents on peut par la suite définir les corps
de fonctions dans l'ordre que l'on veut.

Et finalement on réserve l'écriture des prototypes des fonctions dans
le fichier .c uniquement pour les fonctions statiques (puisque de
portée locale au fichier .c).

Ca c'est l'usage, maintenant au niveau de ce qui est requis, il n'est
pas obligatoire de déclarer un prototype de fonction si le corps de la
fonction est écrit avant sa première utilisation, c'est en gros le
compilo qui se charge de mémoriser le prototype.

Du coup pour un simple fichier d'exemple ou de test (sans fichier .h
donc), il est plus simple de ranger les fonctions dans un ordre tel
qu'aucune fonction n'utilise une autre fonction qui ne serait pas
définie avant, ce qui est généralement assez facile.

Les cas où ça ne marcherait pas sont les appels cycliques (foo appelle
bar qui elle-même appelle foo), dans ce cas on doit écrire
explicitement le prototype de bar ou de foo avant utilisation.

Toutefois l'écriture d'un header pour un programme plus élaboré
devient vite nécessaire, ne serait-ce que pour le merge.

Imagines que tu as un fichier toto.c avec disons 10 fonctions qui vit
depuis un certain temps.
Un jour tu ajoutes une 11 eme fonction mais au lieu de la mettre à la
fin du fichier, tu la mets en 4eme position parce que c'est une
variante de la fonction 3.
Manque de pot, elle utilise la fonction 9, qui en l'absence de header
devra donc être déplacée en début de fichier.
Il devient alors pénible de merger car les outils de merge n'aiment
pas beaucoup les déplacements de blocs.

Avec un fichier header, tu peux mettre la fonction ou bon te semble ce
qui permet de garder une certaine logique dans l'organisation du
fichier.


--
zwim.
Rien n'est impossible que la mesure de la volonté humaine...
zwim
Le #20090021
Le Mon, 07 Sep 2009 18:42:48 +0200
YannicK a écrit
-ed- a écrit :




Ton code n'est pas conforme à C90 :




Merci pour cette correction qui m'a permis d'apprendre quelques trucs au
niveau de la syntaxe, et sur la possibilité d'inclure au sein d'une
fonction des blocs d'instructions entre {}

Par contre tu as supprimé le prototypage des fonctions pour les déclarer
avant main(). Or, j'avais cru comprendre que c'était une bonne habitude
à prendre pour quand j'aurais à écrire des programmes modulaires ...
Est-ce aussi pour un problème de respect de la norme ?



A priori la "tradition" est de mettre les corps des fonctions dans un
fichier toto.c et les prototypes dans un fichier toto.h (généralement
de même nom que le fichier .c).

De ce fait le #include "toto.h" quasi inévitablement inclus par toto.c
recopie ces prototypes en début du fichier C.

Les prototypes étant présents on peut par la suite définir les corps
de fonctions dans l'ordre que l'on veut.

Et finalement on réserve l'écriture des prototypes des fonctions dans
le fichier .c uniquement pour les fonctions statiques (puisque de
portée locale au fichier .c).

Ca c'est l'usage, maintenant au niveau de ce qui est requis, il n'est
pas obligatoire de déclarer un prototype de fonction si le corps de la
fonction est écrit avant sa première utilisation, c'est en gros le
compilo qui se charge de mémoriser le prototype.

Du coup pour un simple fichier d'exemple ou de test (sans fichier .h
donc), il est plus simple de ranger les fonctions dans un ordre tel
qu'aucune fonction n'utilise une autre fonction qui ne serait pas
définie avant, ce qui est généralement assez facile.

Les cas où ça ne marcherait pas sont les appels cycliques (foo appelle
bar qui elle-même appelle foo), dans ce cas on doit écrire
explicitement le prototype de bar ou de foo avant utilisation.

Toutefois l'écriture d'un header pour un programme plus élaboré
devient vite nécessaire, ne serait-ce que pour le merge.

Imagines que tu as un fichier toto.c avec disons 10 fonctions qui vit
depuis un certain temps.
Un jour tu ajoutes une 11 eme fonction mais au lieu de la mettre à la
fin du fichier, tu la mets en 4eme position parce que c'est une
variante de la fonction 3.
Manque de pot, elle utilise la fonction 9, qui en l'absence de header
devra donc être déplacée en début de fichier.
Il devient alors pénible de merger car les outils de merge n'aiment
pas beaucoup les déplacements de blocs.

Avec un fichier header, tu peux mettre la fonction ou bon te semble ce
qui permet de garder une certaine logique dans l'organisation du
fichier.


--
zwim.
Rien n'est impossible que la mesure de la volonté humaine...
Publicité
Poster une réponse
Anonyme