Je crée ce sujet car j'ai un problème avec mon programme, je ne suis qu'au tout début de la programmation en C. Je souhaite faire faire une opération à l'ordinateur : 5+3 (évidemment nous connaissons tous la réponse le but est de simplement tester le programme, qui en l'occurrence, ne marche pas) voici mon programme :
Je ne sais pas si c'est le cours de Mathieu Nebra ... Mais justement les débutants n'utilisent pas de fonction complexes ou exotiques.
La plupart du temps, ils n'ont besoin que de stdio.h pour les entrées-sorties standard.
Je ne sais pas ce qu'on leur explique par la suite, mais le prof doit insister sur le fait que le fichier .h contient les définitions associés à la fonction en question.
Et que ce n'est pas une décoration pour faire bien.
Le Tout est souvent plus grand que la somme de ses parties.
Et donc inclure <stdlib.h> est inutile ... pour le moment.
Mais cela n'apporte aucun problème de lisibilité. Elle finira par devenir utile si le projet devient un peu plus conséquent. (et c'est déjà prêt)
Pour d'autre include j'aurais été d'accord.
Sinon on peut prendre la même argumentation et dire : pourquoi inclure tout stdio.h alors qu'on utilise que printf ? Autant marquer le prototype de printf et pas besoin d'inclure !
int printf ( const char * format, ... );
int main(void)
{
printf("Hello world !\n");
return 0;
}
Et la réponse que j'ai faite pour stdlib.h fonctionne aussi ici. Si j'ai besoin d'une autre fonctionnalité de stdio (scanf?) , et bien avec l'include ... c'est déjà fait et on ne se prend pas la tête.
De plus , les constante EXIS_SUCCESS et EXIT_FAILURE sont dans stdlib.h et si on voulait vraiment faire un code ultra propre il faudrait , à la fin du main , faire un return EXIT_SUCCESS; et donc , il faudrait toujours inclure stdlib, même pour un hello world...
Mon avis est que mettre stdio et stdlib pour chaque projet lorsque l'on débute, permet de mieux se focaliser sur les bases du langage (variable, structure conditionnelle , pointeurs,structure,fonction, etc..).
Pour le int i=0
int i;
//...
for(i=0;///...
ou bien
int i=0
//...
for(;//...
J'ai une préférence pour la seconde. (mais les deux sont corrects), mais faire une initialisation inutile (qui sera géré par le compilateur), est-ce un problème en soit ? Objectivement je ne sais pas. Personnellement, je m'en fiche, ça ne gène pas la lecture du code.
Sachant que la personne débute et n'a pas encore l'habitude d'utiliser printf... pas besoin de lui prendre la tête sur des petits détails sans conséquences.
Je suis contre l'idée d'initialiser des variables comme 'resultat' si on doit leur donner une valeur juste après.
(c'est ce qu'on enseigne dans les cours, mais c'est le mauvais moyen d'éviter les bugs)
.
Entièrement d'accord ! De plus, je trouve qu'on a trop souvent tendance à faire croire qu'initialiser une variable consiste à lui donner la valeur 0. Ben non, parfois la valeur initiale pertinente (lorsqu'il y en a une) est non nulle. Par exemple dans un calcul de factorielle il faut initialiser à 1, surtout pas à 0...
Après le problème du stdlib.h, n'est pas le principal problème de ton code !
Comme on te l'a dit plus haut les principaux problème sont : tu n'as pas de fonction main dans ton code, l'utilisation de la fonction printf n'est pas correcte et une instruction doit se terminer par un point virgule !
Justement lorsque je fais des erreurs sur mon code, il y a seulement un petit carré rouge sur le coté, mais y a-t-il une possibilité de connaître ce qui est faux dans la ligne?
Michel t'a donné la réponse. Si tu es choqué par quelqu'un qui écrit "cheuval" au lieu de "cheval", tu vas quand même comprendre le sens de la phrase. L'ordi, enfin, le compilo, lui, si tu déclares "resultat" et que tu affectes "Resultat" sans déclaration en amont, il va pas chercher à comprendre, il va t'insulter.
En clair, tu déclares "resultat", puis tu affectes "Resultat" qui est inconnu au bataillon.
Oui mais il y a forcément un message d'erreur quelque part. Peut-être faut-il cliquer sur le carré rouge, je ne sais pas, mais il y a forcément un message d'erreur, il faut juste le trouver. (Si quelqu'un connaît Code::Blocks, il pourra indiquer où il se trouve.)
De mémoire, par défaut, il y a un emplacement pour les erreurs. Je soupçonne CV de faire une fixation visuelle sur le code-source en plein milieu de l'écran de CB. Si ça n'a pas changé, il lui suffit juste de... baisser les yeux d'environ 17°.
Autrefois, il y avait des langages où on n'était pas obligé de déclarer les variables.
En Fortran, la "règle IJKLMN" disait que quand un nom de variable commençait par une de ces lettres, la variable était de type INTEGER, et les autres REAL. Et voilà, magique.
Ça allait bien tant qu'on faisait des programmes de gorets de quelques dizaines de lignes, à tendance matheuse sur des entiers i, j et des vecteurs v1 et v2. Mais quand on s'est mis à utiliser des noms de variables plus longs, c'était la cata avec les fautes de frappe.
Finalement la déclaration implicite, sous prétexte de simplifier la vie, était une très mauvaise idée.
Donc maintenant, on déclare ce qu'on utilise, et le compilateur nous signale gentiment qu'on s'est trompé, ce qui arrive forcément. Ce ne sont pas des insultes, mais une aide pour écrire des programmes qui marchent. Le compilateur est votre ami.
- Edité par michelbillaud 19 janvier 2020 à 7:13:00
Pour ce qui est de savoir si code::blocks est un bon outil, tu vas avoir des réponses relatives à chacun. Dans l'absolu, le bon outil est celui dont tu sais te servir correctement et qui correspond à tes attentes.
Pour afficher les résultats de compilation, c'est la touche F2 puis l'onglet "Build messages". Le compilo y affiche le type de l'erreur (ou de l'alerte), il suffit de cliquer dessus pour qu'il t'emmène à la ligne concernée. à toi de relire la ligne et de trouver l'erreur correspondante.
Quant aux initialisations à la déclaration, je suis partisan de ne pas déclarer une variable tant qu'on en a pas besoin, de préférence avec une valeur "utile", et pour aller plus loin, le faire localement autant que possible.
Je ne ferais pas :
int i,
resultat;
for(i=0; i<5, i++)
{
resultat = 2*i;
...;
}
mais :
for (int i=0; i<5; i++)
{
int resultat = 2*i;
...;
}
Après, si tu veux t'acharner à coder en strict C89, on ne peut blâmer personne de vivre encore au millénaire dernier... Mais bon, si tu tiens à rester en 1989, tu vas être obligé d'écouter Bernard Minet chanter "Dis-moi Bioman", ça ne fait pas envie .
Une petite question un peu hors-sujet à propos des déclarations de variables à l'extérieur du bloc où elles peuvent être utiles...
Mettons que j'ai un programme qui réalise des millions de calculs en chaîne, dans lesquels il y a une permutation (c'est exemple simple d'endroit où on va avoir besoin de déclarer une variable locale) :
Une demi-seconde d'écart sur un milliard d'appels, ça reste peu. Je comprend parfaitement qu'il puisse exister des cas d'applications pour lesquelles ces 10% de gains vont être critiques. Une demi-seconde de retard sur la correction de trajectoire d'une sonde spatiale qui voyage à 17km/s peut effectivement poser problème pour peu que ça nécessite autant d'opérations.
Maintenant, dans les faits, n'étant pas un puriste de l'optimisation mais un "utilisateur" du langage, je vais préférer faciliter l'écriture et la relecture pour une maintenance aisée puis laisser le compilateur faire son boulot comme il l'entend. Je ne suis pas en train de dire qu'il faut faire n'importe quoi en se reposant uniquement sur les capacités assez extraordinaires des bécanes actuelles : je ne bosse pas chez Ubisoft . Mais la norme le permet et il ne me semble pas que cette possibilité qui est offerte fasse vraiment débat au sein de la communauté (on est loin de la tempête qu'engendre un goto), l'utilisation de variables globales pour tout et n'importe quoi est déjà un peu plus discutable en terme d'état de l'art.
Pour faire de petits jeux sans prétention (ou pas trop), je me rend compte que pour mon plus gros projet existant, je suis obligé de coller du SDL_Delay() pour l'empêcher de tourner trop vite et que chaque boucle du programme consacre presque 90% du temps à attendre la fin de cette pause. Gagner le milliardième d'une demi-seconde importe peu à ce niveau là.
@robun : Ne le prend surtout pas comme une attaque personnelle, j'ai très bien compris que tu t'étais intéressé uniquement aux écarts qu'une méthode ou une autre pouvait engendrer sans pour autant prendre parti. Je me permet juste de recadrer pour les petits malins qui s'amusent à compter les coups d'horloge pour faire ceci ou cela et pondent du code imbitable sous prétexte qu'un bout d'algo va se faire en 5 coups d'horloges plutôt qu'en 7. Ou pire, un débutant qui va se persuader que son programme sera plus efficace s'il passe tout en variables globales...
Je pense que dans les cas où le temps de calcul est critique, gagner 10 % n'est pas énorme, mais c'est l'accumulation de petits gains de 10 % qui peut devenir intéressante.
J'ai fait autrefois des études de maths appliquées, et j'ai retrouvé récemment, dans mes vieux cours, une photocopie d'un document parlant de calcul en Fortran : il expliquait toutes les petites optimisations qu'on pouvait réaliser pour grapiller un peu de temps. Chaque optimisation ne faisait pas gagner grand chose, mais leur accumulation, si.
C'est un peu dans cet esprit que je me suis posé la question.
J'aime bien définir les variables locales dans les blocs où elles sont utilisées, c'est pour moi une question de cohérence et de lisibilité. Et puis je n'aime pas le principe des noms de variables explicites faisant plusieurs mots (sans doute par manque d'habitude et parce que je programme surtout des calculs, je trouve ça illisible) du coup mes variables ont souvent des noms courts, et là il est indispensable qu'elles soient déclarées pile à l'endroit où elles servent. Bref, je ne vais pas changer mes habitudes pour ces 10 %, et je recommanderai toujours de déclarer ses variables pile à l'endroit où elles servent.
(Précision : je ne fais pas du C en professionnel, c'est juste pour mes loisirs, donc en effet je me pose des questions mais je n'affirme rien, hein !)
Non la déclaration d'une variable dans une boucle ne va pas demander la "création" de quoi que ce soit au moment de l'exécution de cette boucle. Arrêtez avec cette "création", ça veut rien dire. On déclare ou on définit des variables, et on les utilise. C'est tout.
Il faudrait que vous regardiez le code assembleur généré par le compilateur, ça vous éviterait de fantasmer.
Allez, une fonction qui échange le contenu de deux tableaux. Deux versions :
void echange (int t1[], int t2[], int n) {
for (int i = 0; i < n; i ++) {
int tmp = t1[i];
t1[i] = t2[i];
t2[i] = tmp;
}
}
void echange2 (int t1[], int t2[], int n) {
int i;
int tmp;
for (int i = 0; i < n; i ++) {
tmp = t1[i];
t1[i] = t2[i];
t2[i] = tmp;
}
}
Le code produit par le compilateur (gcc -Os) est EXACTEMENT LE MEME
Le registre eax sert à faire le compteur de boucle i, et le yoyotage avec le variable tmp est remplacé par l'utilisation de 2 registres, comme si on avait fait
int tmp1 = t1[i];
int tmp2 = t2[i];
t1[i] = tmp2;
t2[i] = tmp1;
parce que c'est mieux pour l'utilisation du cache, me semble-t-il. Le compilateur en fait exactement la même chose.
Conclusion : quand on dit que, quand on rentre dans un bloc, ça alloue des variables, c'est une FICTION. Ca fait comme si, du point de vue du programmeur naïf, mais en fait non. D'une part beaucoup sont éliminées, ou remplacées par des registres, et en général, si il y a besoin de réserver quelque chose sur la pile pour stocker, c'est généralement fait dans l'entrée dans la fonction.
Donc n'imaginez pas des impacts sur les performances sans y regarder de plus près. Sinon vous tombez dans la superstition.
(les complications avec les VLA variable length arrays viennent en réalité de là, pas du risque de débordement qu'on évoque habituellement. Pour peu qu'on ait deux VLA dans la même fonction, bonjour la galère pour y accéder efficacement).
- Edité par michelbillaud 19 janvier 2020 à 23:08:07
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Le Tout est souvent plus grand que la somme de ses parties.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.
Bonhomme !! | Jeu de plateforme : Prototype.
Bonhomme !! | Jeu de plateforme : Prototype.
Le Tout est souvent plus grand que la somme de ses parties.