1. #1
    Membre du Club Avatar de Redgard
    Homme Profil pro
    x
    Inscrit en
    décembre 2014
    Messages
    90
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : x

    Informations forums :
    Inscription : décembre 2014
    Messages : 90
    Points : 60
    Points
    60

    Par défaut allocation dynamique d'un tableau

    Bonjour,

    J'aimerais créer un tableau qui accueillerait les nombres d'un fichier.txt et dont la taille dependrait directement de la quantité de nombre contenu dans ce fichier.txt.
    J'ai donc créé une fonction "CountLines", qui va dénombrer la quantité de nombre, et une fonction "SizeTab" qui va faire l'allocation dynamique du tableau.

    Le problème c'est que j'ai un peu l'impression de faire ça comme un péon et de prendre le problème dans le mauvais sens :/ ... Est-ce que vous pourriez me donner votre avis?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    int CountLines(char* fileName, int* numbLines) {
    	FILE* fileBuffer;
     
    	if ((fileBuffer = fopen(fileName, "r")) == NULL) {
    		printf("! Erreur -  Echec de l'ouverture du fichier %s !\n", fileName);
    		// sentry value
    		return 1;
    	}
     
    	while (!feof(fileBuffer))
    	{
    		char number[25 + 1];
    		if ((fscanf(fileBuffer, "%d\n", number)) != NULL)
    			++numbLines;				
    	}
     
    	fclose(fileBuffer);
     
    	return 0;
    }
     
    int SizeTab(char* tab, int sizeTab) {
     
    	tab = malloc(sizeof(int)*(SizeTab + 1));
     
    	if (tab == NULL) {
    		puts("! Erreur - Echec de l'allocation de memoire");
    		return 1;
    	}
     
    	return 0;
    }
    Autres Questions:
    • Quelqu'un m'a dit que depuis la norme C99, l'obligation de déclarer toutes ses variables au début avait sauté et qu'il fallait maintenant les déclarer au plus près pour diminuer leur vie. Qu'en pensez-vous? Est-ce que vous auriez des sources donnant des recommandations à ce sujet?
    • Quelle est l'intérêt/ la diférence entre fputs("",stdout) et puts("") pour afficher du texte à l'écran sachant qu'ils viennent tout deux de la même librairie? mémoire? temps d'exécution? bug?




    Merci d'avance,
    Red'

  2. #2
    Membre averti
    Avatar de exe2bin
    Profil pro
    Passionné de programmation
    Inscrit en
    mars 2009
    Messages
    494
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Passionné de programmation

    Informations forums :
    Inscription : mars 2009
    Messages : 494
    Points : 324
    Points
    324
    Billets dans le blog
    3

    Par défaut

    Citation Envoyé par Redgard Voir le message
    Le problème c'est que j'ai un peu l'impression de faire ça comme un péon et de prendre le problème dans le mauvais sens :/ ... Est-ce que vous pourriez me donner votre avis?
    Salut, d'abord je souhaite dire que l'important(dans un premier temps) c'est que le programme fonctionne ; heureusement il y a maintes façons de faire.
    Ensuite, je pense que tu as saisi l'idée : lire un fichier et compter ses lignes puis allouer la mémoire en conséquence .

    Au sujet de puts() et fputs() on a les signatures suivantes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    // écrit la chaîne d'adresse "chaine" sur le "flux" mentionné (sans caractère de fin de chaîne \0).
    // Fournit une valeur non négative lorsque le fonctionnement a été correct et la valeur EOF en cas d'erreur.
    int futs(const char *chaine,FILE flux);
     
    // écrit sur l'unité standard de sortie (stdout) la chaîne d'adresse "chaine", suivie d'un caractère de fin de ligne \n (le caractère de fin de chaîne \0 n'est pas écrit).
    // Fournit EOF en cas d'erreur et une valeur non négative dans le cas contraire.
    int puts(const char *chaine);
    Comme tu le constate la différence essentielle est la direction de sortie de la chaîne ;dans un cas tu n'as pas le choix (stdout), dans l'autre tu peux
    écrire dans un flux quelconque.

  3. #3
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    6 308
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 6 308
    Points : 17 700
    Points
    17 700
    Billets dans le blog
    1

    Par défaut

    Bonjour

    Ton code est pas mal pour un Peon (WarcraftII est de retour ? ). J'ai juste deux remarques
    La première: conventionnellement, on renvoie plutôt un nombre négatif en cas d'échec. Surtout pour une fonction sensée renvoyer un nombre de trucs, si tu obtiens "1" comment sauras-tu si tu as eu "un truc" ou bien une erreur ??? Et une fonction sensée compter des trucs autant lui faire renvoyer le nb de trucs comptés. Mais bon, ça ce n'est pas grave.

    La seconde un peu plus sérieuse vient de ta boucle de lecture while (!feof(fileBuffer)) parce que là ce n'est pas bon. En fait, feof() ne permet pas de détecter une fin de fichier mais permet de détecter, une fois qu'on ne lit plus le fichier, si la "non lecture" est due à un "eof" ou autre chose (erreur, etc).
    Il s'ensuit que pour arriver à obtenir un "eof", il est nécessaire de lire "un de plus" que ce qu'il y a à lire. Et donc, ceui qui écrit un truc de ce genre while (!feof(...)) { lecture+traitement } fait une fois de plus la lecture+traitement que ce qu'il faut.

    Toi tu as contourné le souci en testant si la lecture avait réussi ce qui est fonctionnel. Mais justement, pourquoi ne pas alors boucler directement sur la lecture elle-même ??? Et en concret c'est exactement ce qu'on fait d'habitude. On lit et on boucle tant que la lecture réussit.
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    int CountLines(char* fileName) {
    	FILE* fileBuffer;
    	int numbLines=0;
     
    	if ((fileBuffer = fopen(fileName, "r")) == NULL) {
    		printf("! Erreur -  Echec de l'ouverture du fichier %s !\n", fileName);
    		// sentry value
    		return -1;
    	}
     
    	char number[25 + 1];
    	while (fscanf(fileBuffer, "%d\n", number) != NULL) ++numbLines;				
     
    	fclose(fileBuffer);
     
    	return numbLines;
    }

    Pour l'histoire des variables au plus près c'est juste à la fois plus logique et à la fois plus pratique pour vérifier un code en cas d'erreur. Perso j'aurais tout de même défini la zone "number" en dehors de la boucle pour éviter justement la répétition "allocation/libération" mais je ne suis presque sûr que l'optimiseur de code l'aurait fait pour moi.

    Et je préfère fputs() mais c'est juste une question de goût. Peut-être pour une question d'équité avec fgets() qui, elle, est préférable à la très décriée et totalement non sécurisée gets().
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  4. #4
    Membre du Club Avatar de Redgard
    Homme Profil pro
    x
    Inscrit en
    décembre 2014
    Messages
    90
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : x

    Informations forums :
    Inscription : décembre 2014
    Messages : 90
    Points : 60
    Points
    60

    Par défaut

    C'est vrai que ma fonction n'est pas censé retourner des nombres négatifs, donc ça pourrait être effectivement intéressant d'utiliser cette propriété pour l'optimiser.


    Pour feof:
    Je me suis basé sur cette description: http://www.cplusplus.com/reference/cstdio/feof/?kw=feof
    J'ai un peu du mal à comprendre, mais si j'ai bien compris c'est un peu comme un do-while ce qui risque de biaiser ma résultat vu que la condition est évalué à la fin de la boucle, or il faudrait que ça soit fait au début.
    De plus, le second problème est plus un problème de gestion des erreurs, qui serait interprété de la même mannière qu'un EOF, vu qu'il évalue la lecture et ne cherche pas une commande en particulier.


    Pourquoi ne pas directement évaluer la condition dans la boucle:
    Bah, je savais pas trop comment faire et donc je suis partie dans des chipotages, n'ayant pas une vue très claire. Or le problème quand on a la tête dans le guidon, c'est "pourquoi faire simple, quand on peut faire compliqué"...
    Je trouve la description du fonctionnement des fonctions pas toujours très claire et donc tu sais pas réellement ce quelle font dans la machine.

    Un truc que j'ai vraiment du mal à me figurer, c'est pourquoi la boucle while passe à la ligne suivante du FILE. Ca n'est pas expliqué et donc j'arrive pas à intégrer cette notion. J'ai vraiment du mal avec les "c'est comme ça".
    Cette fonction pourrait très bien boucler à l'infinie sur la même ligne.

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    6 308
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 6 308
    Points : 17 700
    Points
    17 700
    Billets dans le blog
    1

    Par défaut

    Citation Envoyé par Redgard Voir le message
    Pour feof:
    Je me suis basé sur cette description: http://www.cplusplus.com/reference/cstdio/feof/?kw=feof
    Oui, c'est vrai qu'il manque un truc. Il est écrit "Checks whether the end-of-File indicator associated with stream is set, returning a value different from zero if it is." ce qui signifie "vérifie que l'indicateur end-of-file associé à un flux (sous-entendu IO) est positionné". Il manque juste l'indication "quand est-ce que ce putain d'indicateur end-of-file est positionné" et ça, je pense que tu l'obtiens en allant lire la doc des fonctions de lecture. Mais encore faut-il le savoir.

    Citation Envoyé par Redgard Voir le message
    J'ai un peu du mal à comprendre, mais si j'ai bien compris c'est un peu comme un do-while ce qui risque de biaiser ma résultat vu que la condition est évalué à la fin de la boucle, or il faudrait que ça soit fait au début.
    C'est un peu ça.
    D'ailleurs, si tu regardes bien en toute impartialité, quand tu lis un livre, même quand tu lis la dernière phrase, le dernier mot ; tu ne sais pas alors que tu es sur le dernier mot. C'est quand tu "regardes après" ce dernier mot que tu te rends compte que le livre est terminé. Bien sûr c'est indiscernable tellement c'est fait de façon subconsciente mais c'est quand-même comme ça que ça se passe.

    Si tu veux mieux, on va (en fait je vais) écrire 3 codes qui lisent et affichent un fichier. Bon, je passe les étapes du fopen/fclose et j'écris juste la lecture et l'affichage
    Première façon: utiliser feof
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    char ligne[1000 + 1];
    unsigned short i=0;
    while (!feof(fp)) {
    	fgets(ligne, 1000 + 1, fp);
    	// Suppression du '\n'
    	char *pt;
    	if ((pt=strchr(ligne, '\n')) != NULL) *pt=0;
     
    	// Affichage de la ligne lue
    	i++;
    	printf("ligne %hu: [%s]\n", i, ligne);
    }

    Tu peux claquer ce code sur n'importe quel fichier, tu auras un printf() de plus que ce qu'il y a de lignes réelles. Parce que, quand tu lis la dernière ligne du fichier, feof() renvoie faux. Ensuite tu lis une ligne de plus, tu l'affiches (donc tu affiches rien) et là feof() renvoie "vrai" et tu quittes la boucle.

    Donc (2° étape): puisque la lecture peut ne pas réussir, tu vérifies que tu as bien lu avant d'afficher...
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    char ligne[1000 + 1];
    unsigned short i=0;
    while (!feof(fp)) {
    	if (fgets(ligne, 1000 + 1, fp) != NULL) {
    		// Suppression du '\n'
    		char *pt;
    		if ((pt=strchr(ligne, '\n')) != NULL) *pt=0;
     
    		// Affichage de la ligne lue
    		i++;
    		printf("ligne %hu: [%s]\n", i, ligne);
    	}
    }

    Ca fonctionne et ça correspond effectivement à ce que tu avais écrit.
    Sauf que de là, le corps de boucle n'étant composé que d'un seul et unique "if", on se dit alors qu'il y a de quoi optimiser (ou alléger). Et ce, en testant directement la lecture.

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    char ligne[1000 + 1];
    unsigned short i=0;
    while (fgets(ligne, 1000 + 1, fp) != NULL) {
    	// Suppression du '\n'
    	char *pt;
    	if ((pt=strchr(ligne, '\n')) != NULL) *pt=0;
     
    	// Affichage de la ligne lue
    	i++;
    	printf("ligne %hu: [%s]\n", i, ligne);
    }

    Et là, ça fonctionne tout pareil... mais écrit de la façon la plus simple. Et c'est là qu'on se rend compte que feof() ne sert pas parce qu'en fait il ne sert pas à détecter un "EOF". C'est la lecture qui détecte le EOF (ou toute autre raison de s'interrompre) ; et feof() détecte si l'interruption est due à un "EOF" ou pas. Accessoirement tu as aussi ferror() qui détecte si l'interruption est due à une erreur ou pas. Donc grosso-modo, quand la lecture s'arrête, soit on a feof(), soit on a ferror() de positionné (parce que je ne vois pas trop d'autre possibilité...)

    Citation Envoyé par Redgard Voir le message
    Je trouve la description du fonctionnement des fonctions pas toujours très claire et donc tu sais pas réellement ce quelle font dans la machine.
    Moui... Là je ne peux pas trop te répondre car trop subjectif. Je pense qu'avec le temps tu prendras l'habitude de ce genre de description et tu pigeras de mieux en mieux.
    Je ne sais pas trop quelle doc tu utilises... mais les manpages fr sont (pour ma part) assez explicites. ici celle qui explique le fscanf()...

    Citation Envoyé par Redgard Voir le message
    Un truc que j'ai vraiment du mal à me figurer, c'est pourquoi la boucle while passe à la ligne suivante du FILE. Ca n'est pas expliqué et donc j'arrive pas à intégrer cette notion. J'ai vraiment du mal avec les "c'est comme ça".
    Non, ce n'est pas "c'est comme ça". Ca va avec la façon dont fonctionnent les outils de lecture/écriture (que ce soit en bufferisé avec fopen ou en bas niveau avec open).
    Chaque fonction d'ouverture te renvoie un "identifiant fichier", que tu récupères dans une variable appropriée (ici un "FILE *"). Ce que moi j'appelle la "tête de lecture" ou le "crayon".
    Ensuite, chaque fois que tu invoques un accès au fichier (lecture/écriture), cette opération déplace alors le crayon du nombre d'octets traités. Si tu invoques 3 fois fgetc(fp) alors ton crayon "fp" sera en fin d'opérations à 3 octets plus loin. C'est vrai que ça fait un peu du "c'est comme ça" parce que je ne sais pas comment ça a été programmé en interne mais bon, je sais que le code a été voulu pour fonctionner de cette façon, et qu'il a été programmé pour fonctionner de cette façon et que ça fonctionne donc j'arrive alors à faire abstraction du "c'est comme ça".
    D'ailleurs, en y réfléchissant, je pense même que ça dépasse le cadre du C et que ça se passe directement au niveau du driver disque dur.

    Ceci dit, tu as aussi des possibilités de contrôle de ton crayon. tu peux utiliser ftell() pour connaitre sa position, et fseek() pour le placer où tu veux.

    Exemple: une fonction "countLines" qui peut compter les lignes d'un fichier déjà ouvert (il ne faut alors pas que l'utilisateur qui l'invoque perde son crayon donc la fonction le sauvegarde et le restore)

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    long countLines(FILE *fp) {
    	long pos;
     
    	// On mémorise la position 	
    	pos=ftell(fp);
     
    	// On se place au début
    	rewind(fp);
     
    	// On compte les lignes
    	long nLig;
    	char ligne[1000 + 1];
    	for (nLig=0; fgets(ligne, 1000 + 1, fp) != NULL; nLig++);
     
    	// On se replace à la position
    	fseek(fp, pos, SEEK_SET);
     
    	// Fini
    	return nLig;
    }

    Et si tu te demandes pourquoi la fonction fgets() lit une ligne entière, c'est parce qu'elle est programmée pour boucler jusqu'à rencontrer un '\n', caractère délimitant une fin de ligne. Donc la fonction fgets() lit des octets à partir d'un pointeur qui se déplace automatiquement à chaque octet lu, et s'arrête à la fin de la ligne. Quel que soit le sens dans lequel on prend le bouzin, on se mord la queue (au figuré parce que sinon c'est qu'on n'a plus de colonne vertébrale) et on arrive à la conclusion du "c'est comme ça" auquel on peut rajouter "mais ça fonctionne".

    PS: mon code n'a qu'un seul défaut: il considère qu'une ligne ne fera jamais plus de 1000 octets (ce qui est généralement le cas de la presque totalité des fichiers textes). Mais si une ligne fait plus de 1000, alors fgets() s'arrêtera à 1000 et ma fonction me donnera (au-moins) une ligne de plus que ce qu'il y a réellement.
    Il est possible de blinder le truc en utilisant getline(), fonction pouvant lire une ligne virtuellement infinie. Mais cette fonction alloue de la mémoire au fur et à mesure qu'elle a besoin.
    Et donc le choix final n'est qu'une question de compromis entre "truc top méga blindé mais top méga lourd" et "truc moins blindé mais plus rapide avec risque statistiquement acceptable".
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [debutant] allocation dynamique d'un tableau.
    Par méphistopheles dans le forum Débuter
    Réponses: 3
    Dernier message: 16/03/2007, 12h45
  2. Réponses: 2
    Dernier message: 05/03/2007, 18h37
  3. Réponses: 67
    Dernier message: 13/02/2007, 18h08
  4. Réponses: 13
    Dernier message: 01/10/2006, 00h25
  5. [PRO*C] Allocation dynamique d'un tableau de VARCHAR
    Par NéalZheimer dans le forum Interfaces de programmation
    Réponses: 5
    Dernier message: 07/07/2006, 13h02

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo