1

yop,

Je fais du C, je compile avec gcc 4.5.2. Je suis sous Linux.
Code::Blocks utilise ça pour compiler chacun de mes fichiers :
gcc -Wmain -pedantic -Wextra -Wall -g -c /mnt/Data/Programmation/PC/Programmes/A68k/src/main.c -o ../obj/Debug/main.o

J'ai du code qui dit ça :
#include "strings.h"

[...]

    fprintf(stderr, StrError);

Dans strings.h :
extern const char StrError[];
Et dans strings.c :
const char StrError[] = "\nERROR:\n";
Sous Windows : compile sans problème avec gcc sous MinGW.
Sous Linux, j'ai un warning : cmdline.c|67|warning: format not a string literal and no format arguments|

Donc il râle parce que :
- le second argument de fprintf n'est pas littéral (tout va bien si je remplace StrError par "abcde")
- il n'y a pas d'argument format (ben si ducon, c'est le deuxième justement !)

Pourquoi ? Qu'ai-je fumé encore ?


(l'a de la chance que je l'ai pas sous la main le {¹~@{\ de ¹{|[¹^\@[@ de @{^#|{# qui m'a dit "Folco, fais du C, ça accélère le développement" smile #itm#vtff

2

(et si Zeph voulait bien me donner son feu vert pour poster notre conversation sur #mirari, ça serait cool grin)

3

ça marche pas mieux avec sprintf à la place de fprintif ?
avatar
Webmaster du site Ti-FRv3 (et aussi de DevLynx)
Si moins de monde enculait le système, alors celui ci aurait plus de mal à nous sortir de si grosses merdes !
"L'erreur humaine est humaine"©Nil (2006) // topics/6238-moved-jamais-jaurais-pense-faire-ca

4

À mon avis, il est en train de te dire que fputs() serait plus approprié.
avatar
Maintenant j'ai la flemme de garder une signature à jour sur ce site. Je n'ai même plus ma chaîne Exec sous la main.

5

Pour résumer ici ce que j'ai posté sur IRC :

Les va_args qu'utilise printf sont lus en fonction du contenu du format, c'est à dire que le nombre de "%s", "%i" & co doivent exactement correspondre aux arguments passés à la fonction. Ces arguments sont connus à la compilation, donc en toute logique on peut s'attendre à ce que le format le soit également, et donc qu'il soit spécifié sous la forme d'une chaine littérale à l'endroit où est appelée printf (en quelques sortes ça fait partie de la signature de l'appel, donc ça doit apparaître au même endroit). GCC en profite d'ailleurs pour vérifier que le format et les arguments de printf sont bien cohérents (nombre + types), mais il ne peut faire ça que si le format est passé sous la forme d'une chaine littérale.

Bref, même si techniquement utiliser une variable comme chaine de format est valide, ça n'est pas une bonne idée, et c'est ce qui motive ce warning. Après, pourquoi tu l'as sous Linux et pas avec MinGW, aucune idée. Google me dit que ce warning est activé par le switch "-Wformat-nonliteral", peut-être l'as-tu utilisé dans un cas et pas dans l'autre ? Ou bien est-ce que c'est une feature non supportée par le GCC utilisé par MinGW ?

Dans tous les cas, deux remarques restent à mon avis vraies :

- Ce warning est justifié, le format de printf devrait toujours être une chaine littérale pour éviter les erreurs
- Dans ton cas, Link a raison, printf ne sert à rien et pourrait être remplacé par fputs (plus rapide)
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

6

Ok, c'est bien ça en effet, merci pour le pourquoi du comment.
Plus de détail ici : http://linux.die.net/man/1/gcc (voir -Wformat et suivant pour les détails).
Apparemment, ce serait -Wall qui provoquerait ce warning, et non -pedantic comme je le pensais.

Sinon, man fputs. Oui, je suis une quiche en stdlib et stdio...

Merci à vous tous. smile

7

programmeurs d'écrire le code que tu as mis, pour des raisons de sécurité (si ta variable StrError contient des %, c'est le crash assuré et les failles qui vont avec). La version correcte est : fprintf(stderr, "%s", StrError);Ce warning sert à dissuader les
iwannabeamaki (./5) :
printf ne sert à rien et pourrait être remplacé par fputs (plus rapide)
gcc le fait automatiquement. Cela dit c'est toujours bien d'avoir le réflexe de le faire soi-même.
So much code to write, so little time.

8

C'est vrai, vos explications se tiennent.

Seulement, je vois ça :
http://linux.die.net/man/3/printf :
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...); 

Ou encore http://www.cplusplus.com/reference/clibrary/cstdio/fprintf/ :
int fprintf ( FILE * stream, const char * format, ... );

Partout on voit du const char*.

Je passe même mieux que du const char, je passe du const char[]. Et si gcc se fait chier à vérifier les fonctions f* (ce qui est hardcodé et non générique), il a parfaitement les capacités de voir que ce qui se ballade au bout de ma variable est parfaitement valide.

Et de toute façon, le warning qu'il pond outrepasse complètement son rôle : pourquoi envoyer un warning alors que mon appel est, selon le prototype, parfaitement valide ? confus Je déteste vraiment ce genre d'intrusion. Surtout injusifiée dans mon cas (mon appel à fprintf ne plantera jamais tel qu'il est écrit).

D'ailleurs, en lisant la doc autour de -Wformat dont j'ai posté le lien hier soir, j'ai trouvé une autre anomalie conceptuelle, je sais plus laquelle. Je sais pas pourquoi ils se carent en tête de pondre ce genre de warning mal conçu chez les GNU, plutôt que de bosser sur le compilateur proprement dit, ou sur les backends (qui a dit 68k ??).

9

C'est pas une question de signature de fonction, c'est un traitement spécifique pour les fonctions *printf, qui provoque ce warning et ceux qui apparaissent quand la chaine de format ne correspond pas aux arguments spécifiés. Encore une fois, ton appel est tout à fait valide d'un point de vue strictement "C". Mais ce n'est pas l'utilisation conseillée de la fonction printf, d'où les warnings.
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

10

"m'sieur, m'sieur, j'ai du mal à l'admettre" grin
Je trouve qu'ils en font trop avec ce warning, stou ^^ Ou s'ils veulent jouer à ça, qu'ils fassent les choses bien et se rendent compte que mon appel ne peut pas être invalide dans ce cas précis. C'est pas une mauvaise idée que ce warning, c'est vrai qu'il y a des appels (à base de const char*) qui pourraient être foireux. Mais pas celui-ci.
Jusqu'à preuve du contraire, c'est un bug à mes yeux.

11

Le compilateur ne peut pas savoir si ta chaine est valide puisse qu'elle se trouve dans un fichier externe.
Si tu mettais la définition de la chaine dans le même fichier sans la déclarer extern, je ne sais pas s'il produirait toujours ce warning.
Je pense qu'il le mettrai aussi car tu peux très bien modifier la chaine en la castant vers char* non const
avatar

12

Folco (./10) :
"m'sieur, m'sieur, j'ai du mal à l'admettre" biggrin.gif
C'est comme ça et pas autrement embarrassed
Si tu insistes tu seras trifouetté !

13

aze (./11) :
Le compilateur ne peut pas savoir si ta chaine est valide puisse qu'elle se trouve dans un fichier externe.

Ah, en effet, il n'a pas la notion de projet. Mais purée, il pourrait faire confiance au proto que je lui passe... roll

14

Ben non, l'erreur ne porte pas sur le fait que tu ne lui passes pas une chaine, mais pas une chaine littérale dans laquelle il puisse vérifier que le format est correct
avatar

15

Et puis ce n'est pas une erreur, c'est un warning, autrement dit un truc que le compilateur considère comme suspect mais sans en avoir la certitude.
if (a = b) génère un warning aussi, mais ça peut avoir du sens dans certains cas.
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

16

Je pensais exactement à ce genre d'exmple tout à l'heure. Il suffit d'expliciter ce genre de chose pour éviter le warning (if ((a=b) != 0)).
J'ai pas trouvé dans mon cas.
Et j'aime compiler sans warnings. J'aime pas compiler des softs qui font ressortir des warnings, ça fait pas sérieux amha.

Bref, faire du code spécifique et intrusif dans les compilateurs, pour râler alors qu'il n'y a pas lieu, je n'aime définitivement pas grin

17

Folco (./16) :
J'aime pas compiler des softs qui font ressortir des warnings, ça fait pas sérieux amha.
Je te comprends, mais dans ce cas faut aussi tester ton code avec d'autres compilateurs, parce que les warnings ne seront pas les mêmes gni
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

18

Folco (./10) :
Je trouve qu'ils en font trop avec ce warning, stou ^^ Ou s'ils veulent jouer à ça, qu'ils fassent les choses bien et se rendent compte que mon appel ne peut pas être invalide dans ce cas précis. C'est pas une mauvaise idée que ce warning, c'est vrai qu'il y a des appels (à base de const char*) qui pourraient être foireux. Mais pas celui-ci.
Jusqu'à preuve du contraire, c'est un bug à mes yeux.

Comme le dit aze, si tout est dans le même fichier gcc ne fait pas de warning. Dans des modules différents, il n'a juste aucun moyen d'analyser quoi que ce soit. Et il ne va certainement pas te faire confiance, aujourd'hui tu sais ce que tu fais, mais quand dans 6 mois tu vas vouloir modifier ta chaîne de caractère est-ce que tu vas te souvenir qu'il faut mettre %% et surtout pas % parce que tu as utilisé un printf qq part dans un autre fichier ? A priori non smile
So much code to write, so little time.

19

sinon utilise une macro, tout est tellement plus simple avec des macros cheeky
avatar

20

@Folco: Regarde la seconde partie du warning: gcc gueule parce que tu lui passes une chaîne non-vérifiable et aucun autre argument. C'est un cas dans lequel fprintf() est superflu, et peut-être même une faille de sécurité (vu qu'il ne sait pas d'où vient la chaîne) ---> Il a raison, utilise fputs().
avatar
Maintenant j'ai la flemme de garder une signature à jour sur ce site. Je n'ai même plus ma chaîne Exec sous la main.

21

Le truc que je comprends pas, c'est qu'au pire mettre ta chaine ailleurs que dans l'appel à printf ajoute un risque de plantage, et au mieux c'est illogique et ça ne sert à rien. Cette chaine de format conditionne la signature de la méthode, pourquoi est-ce que tu tiens absolument à la mettre ailleurs, c'est pour obliger les gens qui vont lire ton code à lire deux endroits différents pour comprendre ce que tu fais ?

Et puis bon, tu actives volontairement ce warning à la compilation, et tu te plains ensuite du fait que le compilateur te reproche de faire un truc qu'il considère comme crade (et c'est tout à fait justifié amha). Si tu ne veux pas de warning, ne les demande pas grin
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

22

^En fait, c'est plutôt qu'il ne désactive pas le warning. Savais-tu que dans la version la plus diffusée de MinGW (gcc 3.4.2) il y a au moins un warning qui est activé par -Wall et n'a pas d'option de désactivation? (cette version ne supporte pas -Wno-missing-initializers).
avatar
Maintenant j'ai la flemme de garder une signature à jour sur ce site. Je n'ai même plus ma chaîne Exec sous la main.

23

iwannabeamaki (./21) :
Le truc que je comprends pas, c'est qu'au pire mettre ta chaine ailleurs que dans l'appel à printf ajoute un risque de plantage, et au mieux c'est illogique et ça ne sert à rien.

Je suis con mais quand même grin C'est pour centraliser les chaines de caractères dans un fichier. Mes équipes de traductionapprécient tripo
En assembleur, les chaines sont nécessairement délocalisées, donc autant les avoir toutes dans un source dédié, c'est une solution très propre.
Mais en fait ok, je devrais utiliser une macro.

Le fait de faire référence à une seule chaine dans le code vient aussi de mon côté assembleuriste : je veux pas de duplication de la chaine dans mon binaire, et j'ai pas trop confiance dans son --merge-truc-machin pour y arriver. Je sais, c'est super très con.

24

Folco (./23) :
Je suis con mais quand même biggrin.gif

Folco (./23) :
Je sais, c'est super très con.


Tu réalises que tu parviens à te contredire dans le même post ? cheekytongue
avatar
Webmaster du site Ti-FRv3 (et aussi de DevLynx)
Si moins de monde enculait le système, alors celui ci aurait plus de mal à nous sortir de si grosses merdes !
"L'erreur humaine est humaine"©Nil (2006) // topics/6238-moved-jamais-jaurais-pense-faire-ca

25

C'est justement un preuve de connerie, non ? grin

26

Folco (./23) :
Mais en fait ok, je devrais utiliser une macro.

Rah mais non, tu le fais exprès ? grin

Cette chaine de format n'a rien à faire ailleurs qu'à l'endroit où se trouve l'appel à printf, ce n'est pas une chaine à traduire qu'on regroupe dans un fichier commun, et elle n'est jamais supposée changer. Elle fait partie intégrante de ton appel à printf, pourquoi tu veux absolument séparer deux choses qui sont aussi étroitement liées ?
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

27

Pour présenter ça autrement, tu dois distinguer 3 élements :
1) Le texte
2) Le format
3) Le variables

Le texte peut en effet être prévu en N langues d'une manière ou d'une autre, le format est FIXE et doit être une partie intégrante de ton source et enfin les variables permettent de remplir les données à afficher le texte.
avatar
Webmaster du site Ti-FRv3 (et aussi de DevLynx)
Si moins de monde enculait le système, alors celui ci aurait plus de mal à nous sortir de si grosses merdes !
"L'erreur humaine est humaine"©Nil (2006) // topics/6238-moved-jamais-jaurais-pense-faire-ca

28

Aaaaaaaaaaaah ok, la lumière commence à se faire magic

Donc je devrais avoir au minimum un "%s" comme format, voire plus évolué s'il le faut ("%s: -%c" par exemple) ?
Et ça permet à gcc de vérifier deux choses :
- le nombre d'arguments passés, en rapport avec le nombre de %? dans le format
- le type de ces arguments, en rapport avec les %? du format
et donc de me foutre la paix côté warnings. Et ça me permet de mon côté d'avoir toutes mes chaines bien rangées quelque part comme j'aime le faire.

C'est-y bien ça ? fear

29

Et sinon, histoire que je configure mes warnings intelligemment, j'aimerais savoir, la norme C actuelle, c'est le C99 ? La norme ANSI est dépassée et plus valable ? Parce que j'ai vu qu'il est aussi question de C89 et de C94 ici ou là...

Mon objectif est d'écrire du C correspondant à la dernière norme officielle, mais sans extension (genre celles de GNU) pour, sur le papier du moins, compiler partout proprement.

Au passage, les nouveautés du C99 sur le C ANSI ont l'air plutôt sympas, avec entre autres, le support des booléens, des commentaires C++ etc... : http://nicolasj.developpez.com/articles/c99/ . Qu'en pensez-vous ? J'espère que je mets pas les pieds dans un sujet à troll encore une fois grin

Tiens : http://gcc.gnu.org/gcc-4.6/c99status.html
Tout est pas implémenté :/

30

Folco (./28) :
Donc je devrais avoir au minimum un "%s" comme format, voire plus évolué s'il le faut ("%s: -%c" par exemple) ?
Voilà. Et en plus ça évite que quelqu'un de mal intentionné puisse introduise une faille de sécurité en ajoutant des caractères de format dans sa traduction (c'est déjà arrivé).
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo