Partage
  • Partager sur Facebook
  • Partager sur Twitter

free un malloc retourné par une autre fonction

malloc non stocké, impossible a free

Sujet résolu
    22 février 2021 à 14:27:56

    Bonjour,

    Je suis en train de vérifier si j'ai bien free tous mes mallocs dans mon programme mais j'ai un petit doute sur quelque chose si quelqu'un peux m'expliquer svp. 

    imaginons que j'ai une fonction qui retourne un char *, que j'ai precedent malloc ex : 

    char *fonction (int a, char *str)
    {
    	char *s; 
    
    	s = NULL; 
    	s = malloc(sizeof(char) * 10);
    	if (a == 1)
    		return(NULL); 
    	if (a == 2)
    	{
    		s = strncpy(s, str, ft_strlen(str); 
    		return(s); 
    	}
    }

    et que j'appel cette fonction de cette maniere : 

    int main ()
    {
    
    	if (fonction(2, "salut"));
    		printf("salut");
    	else
    		printf("bonjour");
    	return(0);
    }

    Le main appel la fonction pour voir si ca retourne quelque chose ou si ca retourne null. 

    Sauf que la fonction a créer un malloc, mais que je ne peux pas free. il aurait fallu que je le stock dans une variable en faisant : 

    char *x;
    
    x = fonction(2, "salut");
    if (x != NULL)
    	printf("salut");
    else
    	printf("bonjour");

    Ma question est donc de savoir si je peux faire comme dans le premiere main ou pour pouvoir free, je suis obligée de faire la deuxième soluce ? si je peux faire la premiere, est ce que j aurai un probleme de leaks ? 

    Ces fonctions sont a titre d exemple. 

    Merci pour votre réponse.




    • Partager sur Facebook
    • Partager sur Twitter
      22 février 2021 à 16:56:14

      Il faut avoir accès à l'adresse renvoyée par malloc, il n'y a pas le choix ! Cette adresse est contenu dans le pointeur a qui elle a été affectée.

      Si tu veux donc libérer la mémoire en dehors de la fonction, tu dois retourner cette adresse.

      Je vois que ta fonction peut retourner NULL, comment va tu avoir accès à l'adresse de l'allocation en dehors de la fonction dans ce cas ?

      PS :

      3 lignes pour initialiser le pointeur s, des fois on en voit 2 mais 3 c'est rare ! Tu peux tout faire d'un coup d'un seul :

          char *s = malloc(sizeof(char) * 10);



      • Partager sur Facebook
      • Partager sur Twitter
        22 février 2021 à 17:24:16

        Et même 

        char *s = malloc(10);

        Puisqu'en C la taille est exprimée en nombre de chars.

        Un peu de logique : si on a une fonction dont le but est de retourner un  truc alloué dynamiquement, il faut garder une trace du résultat en le notant quelque part. Et ça permettra de le frire si tu as envie à l'huile, à la margarine, peu importe.



        -
        Edité par michelbillaud 22 février 2021 à 17:26:16

        • Partager sur Facebook
        • Partager sur Twitter
          22 février 2021 à 18:09:21

          Toujours le sizeof(char) qui traine un peu partout.
          Et la margarine, c'est pour aller avec le béton à la farine de White Crow?
          Même si c'est rare qu'on a le problème, il faudrait vérifier que malloc retourne bien un pointeur non NULL.
          Si on ne sais pas toujours où on est rendu quand on fait le free() ...
          Il faudrait vérifier avant de le faire que le pointeur est non NULL (il peut être faux, mais c'est une autre histoire ...)
          et ensuite mettre le pointeur à NULL après le free().t
          • Partager sur Facebook
          • Partager sur Twitter

          Le Tout est souvent plus grand que la somme de ses parties.

            22 février 2021 à 19:23:35

            Ce code ne compilera pas: un ; en trop ligne 4 du main()

            -
            Edité par edgarjacobs 22 février 2021 à 20:08:30

            • Partager sur Facebook
            • Partager sur Twitter

            On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

              22 février 2021 à 20:31:39

              marioonb a écrit:

              Bonjour,

              Je suis en train de vérifier si j'ai bien free tous mes mallocs dans mon programme mais j'ai un petit doute sur quelque chose si quelqu'un peux m'expliquer svp. 

              [...]

              Hello,

              il existe des outils comme valgrind qui t'aident à lever les doutes … savoir s'en servir (memory profiler, debuger, profiler, …) est une compétence inestimable.

              En attendant, oui ton code est hyper mal foutu ! 

              char *fonction (int a, char *str)
              {
                  char *s;
               
                  s = NULL;
                  s = malloc(sizeof(char) * 10);
                  if (a == 1)
                      return(NULL);
                  if (a == 2)
                  {
                      s = strncpy(s, str, ft_strlen(str);
                      return(s);
                  }
              }

              Pue importe la valeur de a, il y aura une allocation mémoire. Mais le résultat de cette allocation ne sera communiquée à l'extérieur de la fonction que lorsque a vaudra 2. Si a vaut 1, on renvoie NULL ⇒ le bloc alloué ne pourra plus être libéré par un free !!!! pas bon !!!!!

              Mais il y a pire !!!! Si a ne vaut ni 1 ni 2 que se passe-t-il ? …

              Ce genre de code est forcément source de bugs.

              char *fonction (int a, char *str)
              {
                  char *s=NULL;
               
                  if (a == 2) {
                      s = malloc( ft_strlen(str) + 1 );
                      if (s!=NULL)
                          ft_strcpy(s, str);
                  }
              
                  return s;
              }

              Ce code est un peu plus compact et surtout correct. Tous les chemins renvoient une valeur, et il n'y a une allocation que si une valeur non NULL est renvoyée et cette valeur sera celle du début du bloc alloué.

              Très personnellement, plus que l'utilisation du sizeof(char) c'est l'utilisation du keyword return (entre autre) comme une fonction qui me fait saigner les yeux dans la norme.




              • Partager sur Facebook
              • Partager sur Twitter
                22 février 2021 à 20:55:14

                Je vais faire une premiere réponse, je n'ai pas dit que ce code compilait, j essai de faire un exemple pour que ce soit claire, et eviter dque vosu ayez a checker un code complet pour uin question d'ordre generale. 

                Je suis a 42, je ne peux pas initialiser en même temps que je déclare car je dois suivre leur norme. 

                Mon projet c'est un minishell, je n'arrive pas a faire passer valgrind dessus, j'avoue que je n'ai pas essayé de trouver pourquoi, car j'en suis pas encore à ça. 

                J ai expliqué que ce code EST A TITRE D EXEMPLE pour illustrer ma question.

                LA fontion avec laquelle je me pose cette question me sert a recuperer une chaine generale mais je l utilise aussi parfois pour verifier si elle renvoi quelque chose.

                Je n utilise pas de a == 1 ou a ==2, je ne retourne pas de cette maniere, ce code est encore une fois un titre d exemple pour que vous compreniez ma question. 

                Je verifie bien que les mallocs n'ont pas échoué non plus. 

                Je vais mettre mon code pour éviter les moqueries et la condescendance puis ensuite je lirai les réponses que vous m'avez faite et qui réponde a ma question. je vous en remercie. 

                char	*find_var(char *str, char **env)
                {
                	int	i;
                	int	lenght;
                
                	i = 0;
                	while (env[i])
                	{
                		lenght = var_lenght(env[i]);
                		if (ft_strncmp(str, env[i], lenght) == 0
                			&& (int)ft_strlen(str) <= lenght)
                			return (ft_substr(env[i] + 1, lenght, ft_strlen(env[i]) - lenght));
                		i++;
                	}
                	return (NULL);
                }
                
                
                void	treat_var(char *str, t_env *env)
                {
                	int		i;
                	char	*bin;
                
                	i = 0;
                	while (str[i] != '=' && str[i])
                		i++;
                	if (i < (int)ft_strlen(str)) //&& strchr(str; '='))
                	{
                		bin = find_bin(str, '=', i);
                		if (find_var(bin, env->env) != NULL)
                			replace_var_env(bin, str, env);
                		else
                			change_env_add(str, env);
                		if (find_var(bin, env->export) != NULL)
                			replace_var_export(bin, str, env);
                		else
                			change_export_add(str, env);
                	}
                	else
                	{
                		bin = find_bin(str, '\0', i);
                		if (!(find_var(bin, env->export) != NULL))
                			change_export_add(str, env);
                	}
                	free(bin);
                }
                static void	exec_chdir(char **tab, char *pwd, t_env *env)
                {
                	char	*oldpwd;
                	int	lenght;
                	char	*pwd2;
                	char	*oldpwd2;
                
                	oldpwd2 = NULL;
                	oldpwd = NULL;
                	pwd2 = NULL;
                	if (!tab[1])
                		pwd = find_var("HOME", env->env);
                	lenght = 4 + ft_strlen(pwd) + 1;
                	pwd2 = malloc(sizeof(char) * (lenght));
                	concat_for_change(pwd, "PWD=", lenght, env);
                	oldpwd = find_var("PWD", env->env);
                	lenght = 7 + ft_strlen(oldpwd) + 1;
                	concat_for_change(oldpwd, oldpwd2, lenght, env);
                	free(pwd);
                	free(oldpwd);
                }
                int	get_path(char **cmd, t_env *env)
                {
                	char	*path;
                	char	*bin;
                	char	**path_split;
                
                	path = NULL;
                	bin = NULL;
                	path_split = NULL;
                	if (!cmd[0])
                		return (0);
                	if (cmd[0][0] != '/' && strncmp(cmd[0], "./", 2) != 0)
                	{
                		path = find_var("PATH", env->env);
                		if (path == NULL)
                			return (0);
                		path_split = ft_split(path, ':');
                		free(path);
                		path = NULL;
                		bin = check_tab_path(path_split, bin, cmd[0]);
                		free_tab_char(path_split);
                		//free(cmd[0]);
                		cmd[0] = bin;
                	}
                	else
                	{
                		free(path);
                		path = NULL;
                	}
                	if (bin == NULL)
                		return (0);
                	else
                		return (1);
                }
                
                char	*find_var_doll(char *tab, int fd, char **env)
                {
                	char	*str_var;
                	int		i;
                	char	*str;
                
                	str = NULL;
                	str_var = NULL;
                	i = 0;
                	str_var = malloc(sizeof(char) * ft_strlen(tab) + 1);
                	if (str_var == NULL)
                		ft_error_malloc();
                	while (check_caractere_name_var(*tab) == 1)
                		str_var[i++] = *tab++;
                	str_var[i] = '\0';
                	if ((str = find_var(str_var, env)) != NULL)
                		ft_out(str, fd);
                	free(str_var);
                	free(str);
                	printf("tab est a %s", tab);
                	return (tab);
                }
                
                



                -
                Edité par marioonb 22 février 2021 à 20:57:10

                • Partager sur Facebook
                • Partager sur Twitter
                  22 février 2021 à 21:11:07

                  marioonb a écrit:

                  Je vais faire une premiere réponse, je n'ai pas dit que ce code compilait, j essai de faire un exemple pour que ce soit claire, et eviter dque vosu ayez a checker un code complet pour uin question d'ordre generale. 


                  Ce qui est top :)
                  Et la réponse est claire : si tu fais un malloc dans une fonction, que ce malloc n'échoue pas alors il faut impérativement communiquer la valeur de retour du malloc à l'extérieur de la fonction pour pouvoir faire un free dessus.

                  Si tu as des mallocs sans free ce sont des fuites de mémoires irrémédiables !

                  c'est le sens de mon intervention :

                  «Tous les chemins renvoient une valeur, et il n'y a une allocation que si une valeur non NULL est renvoyée et cette valeur sera celle du début du bloc alloué.»

                  marioonb a écrit:

                  Mon projet c'est un minishell, je n'arrive pas a faire passer valgrind dessus, j'avoue que je n'ai pas essayé de trouver pourquoi, car j'en suis pas encore à ça. 

                  [...]

                  -

                  Edité par marioonb il y a moins de 30s

                  Et pourtant ça t'aiderait énormément … il vaut mieux avoir les mauvaises nouvelles le plus tôt possible pour pouvoir corriger les tirs !

                  Prendre le temps d'apprendre à utiliser valgrind ne sera jamais un perte de temps. D'autant plus que ça se résume bien souvent à :

                  valgrind --leak-check=full --track-origins=yes --show-reachable=yes exe_a_tester

                  avec exe_a_tester ayant été compilé en mode debug, bien évidemment. Tu testes les chemins dans ton exe et tu as directement tous les problèmes mémoires … magique et indispensable !

                  marioonb a écrit:

                  Je verifie bien que les mallocs n'ont pas échoué non plus. 

                  [...]

                  Pas tous 👿

                  marioonb a écrit:

                  Je suis a 42, je ne peux pas initialiser en même temps que je déclare car je dois suivre leur norme. 

                  [...]

                  Bah ouais … pas de bol … une norme pas très adaptée mais oui, tu dois la suivre ; et nous comme d'hab on la critique ^_^





                  • Partager sur Facebook
                  • Partager sur Twitter
                    22 février 2021 à 22:34:41

                    Super je te remercie pour ta réponse et ces précisions, en fait tout est un problème de norme et c'est pénible... Nous ne pouvons faire que 25 lignes et donc cette façon de faire me permet de gagner des lignes, parce qu'encore une fois à cause de la norme, si je veux stocker le retour, ça me prend 3 lignes supplémentaires... du découpages de fonction qui n'ont aucun sens et le temps qui va avec...

                    J'ai espéré qu'il y ait une solution mais du coup d'après ta réponse et celles des autres, je ne pense pas. 

                    Concernant valgrind j'utilisais --leak-check=full –track-origins=yes sur mes autres projets déjà, mais pour celui ci, impossible de faire tourner valgrind même sans flag...

                    Tu as raison je devrais m'y pencher un peu plus mais c'est surtout par manque de temps que je ne le fais pas, meme si au final ca peut m'en faire gagner, voilà avec ta commande, ce que j ai :

                    et ça reste comme ça... ça ne me donne rien de plus, je suis pas très calée au niveau de valgrind et ça me semble une montagne de trouver d'ou vient le problème... Du coup je pensais que c'était peut être du au type de projet, puisqu'il attend des commandes sur STDIN. Tu me dit de tester en mode debug, mais je t'avoue que je ne sais pas ce que tu entends par la. Peut être as tu une réponse à m'apporter ? 

                    En effet je ne vérifie pas mon malloc de pwd2, je te remercie car j'ai verif ne comprenant pas ta remarque et en fait cette ligne n'a rien a foutre là, encore à cause de la norme j ai du découper cette fonction et le malloc se fait dans concat_for_change(j ai plus d'inspi pour les noms lol), j ai du oublié de le supprimer, une erreur de moins à chercher lol.

                    Et tu fais bien de critiquer la norme, autant elle nous permet de la rigueur, autant parfois elle n'est en effet pas adaptée...



                    -
                    Edité par marioonb 22 février 2021 à 22:39:15

                    • Partager sur Facebook
                    • Partager sur Twitter
                      23 février 2021 à 2:11:31

                      As-tu affaire à un prof qui vous fait payer à la ligne la publication de votre code?
                      Ce n'est pas une moquerie contre toi, mais contre ton prof.
                      Je pourrais dire que tu avais utilisé 3 lignes pour faire ce qui se fait en 1 ligne:
                      char *s = malloc(10);     // pas besoin d'initialiser à NULL avant.
                      et pourquoi ne pas vérifier la longueur de la chaîne que tu vas recopier "avant" de faire le malloc et réserver la bonne grandeur en n'oubliant pas la fin de chaîne.
                      • Partager sur Facebook
                      • Partager sur Twitter

                      Le Tout est souvent plus grand que la somme de ses parties.

                        23 février 2021 à 7:13:41

                        Le respect de norme à la con, c'est pas de la rigueur, mais de la discipline. Vous ne faites pas ce que le chef vous interdit de faire, sans discuter, même si ça serait mieux.

                        La rigueur, c'est pas l'application d'une consigne externe. Ça consiste à faire attention, à avoir un regard critique constamment,  et du soin pour arranger les choses quand on sent qu'on peut faire mieux.

                        Quand valgrind ne se lance pas, revoir les commandes de compilation et d'édition des liens. Les options utiles (-g avec gcc) y sont elles ?

                        -
                        Edité par michelbillaud 23 février 2021 à 7:15:58

                        • Partager sur Facebook
                        • Partager sur Twitter
                          23 février 2021 à 8:19:09

                          J'ai déjà parlé de mon principe «de la balle de tennis» :)
                          La balle ne peut pas être des deux coôtés du filet en même temps.
                          En fait, l'idée est de ne garder qu'un pointeur sur une zone allouée par malloc ou une autre ressource.
                          Si tu trimbales des pointeurs n'importe où sans savoir ce qu'ils contiennent, tu cours à l'échec.
                          On ne devrait même pas à faire ce que j'ai dit: vérifier si le pointeur est NULL avant le free() et le mettre à NULL après.
                          • Partager sur Facebook
                          • Partager sur Twitter

                          Le Tout est souvent plus grand que la somme de ses parties.

                            23 février 2021 à 8:30:07

                            marioonb a écrit:

                            [...]
                            Concernant valgrind j'utilisais --leak-check=full –track-origins=yes sur mes autres projets déjà, mais pour celui ci, impossible de faire tourner valgrind même sans flag...

                            Tu as raison je devrais m'y pencher un peu plus mais c'est surtout par manque de temps que je ne le fais pas, meme si au final ca peut m'en faire gagner, voilà avec ta commande, ce que j ai :

                            et ça reste comme ça... ça ne me donne rien de plus, je suis pas très calée au niveau de valgrind et ça me semble une montagne de trouver d'ou vient le problème... 

                            Alors parfois le plus simple est de chercher le message d'erreur sur le net, en l'occurrence «unknown task message». En lisant en diagonale les pages intéressantes, on apprend qu'il s'agit d'un problème d'instabilité de cette version de non mainstream de Valgrind sur Mac, ou quelque chose dans le genre. Il est conseillé d'utiliser la version de mainstream de valgrind … https://www.valgrind.org/downloads/repository.html 

                            Je ne connais pas l'environnement mac, mais peut être qu'une âme charitable ici pourra t'aider ? je suppose que lui donner la version de valgrind utilisée (valgrind --version), ta version de mac, comment tu as installé valgrind, … seront des infos utiles.

                            marioonb a écrit:

                            Tu me dit de tester en mode debug, mais je t'avoue que je ne sais pas ce que tu entends par la. Peut être as tu une réponse à m'apporter ? 

                            -

                            Edité par marioonb il y a environ 9 heures

                             La même que Michel :)

                            michelbillaud a écrit:

                            Quand valgrind ne se lance pas, revoir les commandes de compilation et d'édition des liens. Les options utiles (-g avec gcc) y sont elles ?

                            -
                            Edité par michelbillaud il y a environ 1 heure


                            Il faut compiler ton projet en mode debug, cela signifie en général l'utilisation du flag -g lors de la compilation, et surtout ne pas mettre de flag -O (moins grand oh) ⇒ cela produit une version de l'exécutable avec les infos de debug sans optimisation. Les infos de debug permettront, entre autre, à valgrind de te dire où se produit l'erreur, à quelle ligne de code tu malloc de la mémoire que tu ne free pas, …


                            • Partager sur Facebook
                            • Partager sur Twitter
                              23 février 2021 à 10:00:08

                              et puis il faut écrire length :-) parce que sinon ça fait mal aux yeux.

                              Perfide Albion : length, width, mais aussi height

                              ---

                              Le code de

                              static void exec_chdir(char **tab, char *pwd, t_env *env)
                              {
                                  char    *oldpwd;
                                  int lenght;
                                  char    *pwd2;
                                  char    *oldpwd2;
                               
                                  oldpwd2 = NULL;
                                  oldpwd = NULL;
                                  pwd2 = NULL;
                                  if (!tab[1])
                                      pwd = find_var("HOME", env->env);
                                  lenght = 4 + ft_strlen(pwd) + 1;
                                  pwd2 = malloc(sizeof(char) * (lenght));
                                  concat_for_change(pwd, "PWD=", lenght, env);
                                  oldpwd = find_var("PWD", env->env);
                                  lenght = 7 + ft_strlen(oldpwd) + 1;
                                  concat_for_change(oldpwd, oldpwd2, lenght, env);
                                  free(pwd);
                                  free(oldpwd);
                              }


                              a l'air un peu compliqué

                              Si je comprends bien, il s'agit de modifier env, pour y mettre  "PWD=destination", la destination étant

                              • t[1] si il est présent
                              • sinon la valeur de HOME dans l'environnement.
                              Il serait raisonnable de définir et utiliser deux fonctions pour manipuler l'environnement
                              • get_env(env, "HOME")
                              • set_env(env, "PWD", destination)


                              -
                              Edité par michelbillaud 23 février 2021 à 10:09:51

                              • Partager sur Facebook
                              • Partager sur Twitter
                                23 février 2021 à 14:44:50

                                Pierrot le fou : Je n'ai pas de prof mais mes projets à rendre sont soumis à une norme, je ne peux malheureusement la contourner. Alors oui en effet, pour ce type de déclaration c'est assez relou. C'est pareil pour check la longueur de la ligne avant, ça me fait une ligne en moins de faire comme je fais. Concernant le fait de remettre le pointeur a NULL après le free, je le fais parfois, j'ai justement essayé de me documenter la dessus, mais il semble qu'il ne faille pas le faire dans tous les cas, pour des raisons que je n'ai pas bien comprises, du coup j'hésite à le faire partout... Mais peut être que tu en sais plus sur le sujet pour pouvoir m'éclairer.

                                MichelBillaud, quand je parle de rigueur, je parle plutôt du reste, l'indentation, l'utilisation des variables, la structure du projet etc... La norme 42 ce n'est pas seulement les 25 lignes, alors oui ça me permet d'avoir une certaine rigueur, parfois je vois des codes, c est une cata, illisibles. Ya des choses bonnes et mauvaise a prendre dans la norme de 42, les 25 lignes ça évite les fonctions interminables et dans certains cas c'est trop strict, concernant les déclarations de variables ça nous évite de les déclarer à n'importe quel endroit, quand on à leur initialisation on est d'accord, c est pas ce qu'ils ont trouvé de mieux.

                                Je précise que tous nos projets passent par un programme qui vérifie si il est normé... si ce n'est pas le cas c'est 0, donc je ne peux même pas débattre avec eux sur le sujet, le plus important pour moi est de passer le projet à 100% que je sois d accord avec leur norme ou non.

                                Pour le lenght oui sorry, tu me l'a deja dit en plus lol. Je vais faire plus attention, généralement, je renomme après mais la ce projet est interminable.... Pour la fonction exec_chdir, suite au post de de white crow, c'est celle la que j ai changé, car c'est une erreur en voulant la découper, en fait elle est comme cela :

                                static void	exec_chdir(char **tab, char *pwd, t_env *env)
                                {
                                	char	*oldpwd;
                                	int		lenght;
                                	char	*pwd2;
                                	char	*oldpwd2;
                                
                                	oldpwd2 = NULL;
                                	oldpwd = NULL;
                                	pwd2 = NULL;
                                	if (!tab[1])
                                		pwd = find_var("HOME", env->env);
                                	lenght = 4 + ft_strlen(pwd) + 1;
                                	concat_for_change(pwd, "PWD=", lenght, env);
                                	oldpwd = find_var("PWD", env->env);
                                	lenght = 7 + ft_strlen(oldpwd) + 1;
                                	concat_for_change(oldpwd, "OLDPWD=", lenght, env);
                                	free(pwd);
                                	free(oldpwd);
                                }

                                *en retirant les variables pwd2 et oldpwd2

                                Je n ai pas le droit d utiliser get_env et set_env... mais c'est vrai que je l'ai fait un peu à ma sauce mais je vais check ces deux fonctions pour essayer de m'en inspirer et les réécrire, la partie de mon shell pour le builtin CD, n'est pas encore totalement terminé, mais je prend tes remarques en note, je te remercie.

                                oui pour le -g sorry, biensure que je l'ai mis, je pensais que lorsque vous avez évoqué le mode debug c'était encore une autre façon de faire, mais en effet j'aurai du penser que vous parliez du flag -g qui est donc bien mis.

                                White crow, merci je vais check pour mettre une autre version, franchement je me souviens plus comment je l'ai installé ça date et j'avais du suivre un tuto, a l'époque j'étais pas vraiment a l'aise encore, j'ai peut être pas pris la bonne version. En tous cas ça a toujours marché sur mes autres projets, je vais check ça pour une autre version. L'actuelle est : valgrind-3.17.0.GIT. Je n'utilise pas le flag -O, juste -Wall -Wextra -Werror -fsanitize=address -g3... 

                                Merci pour vos réponses pertinentes et constructives en tous cas... 

                                -
                                Edité par marioonb 23 février 2021 à 15:37:46

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  23 février 2021 à 17:23:58

                                  Arrrg !!!!

                                  oublie le fsanitize=address !!!! cela va interférer avec valgrind …

                                  Quand tu utilises cette option, ton exécutable va embarquer un code qui va  faire planter ton programme avec un message si jamais tu accèdes à de la mémoire à laquelle tu n'avais pas le droit d'accéder !!!!

                                  -Wall -Wextra : ok indispensables

                                  -Werror : un peu overkill

                                  -g3 : parfait, même is un simple -g sufiit.

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    15 mars 2021 à 15:57:33

                                    Oh merde, je ne vois ta reponse que maintenant... et pourtant elle m'aurai bien servi...

                                    Oui j'ai fini par avoir cette information lol, la ça marche nickel, mais j'aurai pu gagner du temps. -Werror je l'enleve generalement mais je le remet pour rendre mes projets car on est obligé. ok ça marche pour -g3, je met -g généralement et -g3 quand j'utilise fsanitize, mais je ne saurai expliquer pourquoi lol. 

                                    Merci a tous pour votre aide, je met le sujet comme résolu !!

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      15 mars 2021 à 17:21:09

                                      Il vaut mieux mettre -Werror dès le début.  Les débutants ne font pas de trucs rock'n'roll où il faudrait outrepasser les avertissements qui seraient des faux positifs

                                      Dès lors tout avertissement signale un problème potentiel, qu'il est préférable de régler immédiatement. Sinon on accumule les avertissements, on ne les lit plus, et on finit par se retrouver avec un merdier ingérable (la dette technique !)

                                      Je me permets quand même  -Wno-unused, quand j'ai des pointeurs vers des fonctions qui doivent avoir le même prototype, mais dont les paramètres ne servent pas forcément.

                                      Un jour, il y aura un attribut pour le déclarer dans le code https://en.cppreference.com/w/c/language/attributes/maybe_unused

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        15 mars 2021 à 18:42:54

                                        michelbillaud a écrit:

                                        Il vaut mieux mettre -Werror dès le début.  Les débutants ne font pas de trucs rock'n'roll où il faudrait outrepasser les avertissements qui seraient des faux positifs

                                        Dès lors tout avertissement signale un problème potentiel, qu'il est préférable de régler immédiatement. Sinon on accumule les avertissements, on ne les lit plus, et on finit par se retrouver avec un merdier ingérable (la dette technique !)

                                        Je me permets quand même  -Wno-unused, quand j'ai des pointeurs vers des fonctions qui doivent avoir le même prototype, mais dont les paramètres ne servent pas forcément.

                                        Un jour, il y aura un attribut pour le déclarer dans le code https://en.cppreference.com/w/c/language/attributes/maybe_unused

                                        La contre-partie d'un -Werror est de se retrouver avec des patchs à la mords-moi le nœud … genre des cast pour faire taire des warnings qu'on a du mal à comprendre, Difficile pour un débutant de concilier et -Wall -Wextra (on en rajoute des warnings) et -Werror (il faut impérativement les faire disparaître) … potentiellement n'implique pas l'existence réelle d'une erreur.

                                        Sinon on peut déjà profiter des attributs en spécifiant le standard C2x avec gcc ou en utilisant une version récente de clang, ou si on a pas froid aux yeux, utiliser les extensions que ces compilos proposent déjà.

                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          15 mars 2021 à 19:38:03

                                          > genre des cast pour faire taire des warnings qu'on a du mal à comprendre

                                          C'est justement le bon moment pour essayer de les comprendre, plutôt que de faire en sorte de ne pas les voir. A priori, je ne vois pas d'exemple de warning bénin qui ne soit pas facile à régler proprement.

                                          Je me rappelle avoir signalé, il y a très longtemps, un bug dans la commande netdate de linux, qui aurait été évident si le programmeur avait daigné regarder les warnings.  Genre le résultat d'un read affecté dans un non-signé, ce qui faisait louper la détection de problèmes ensuite (échec du read à travers le réseau, résultat négatif).

                                          Le type devait juger que "un warning n'est pas forcément une erreur".

                                          > et -Werror (il faut impérativement les faire disparaître) …

                                          Absolument. Des warnings -> pas d'exécutable. Ca évite de tirer des conclusions hâtives à partir de l'exécution "pour voir" d'un source a moitié compilé de travers.

                                          C'est une discipline de programmation. De nos jours, les ordinateurs vont vite, et on peut compiler très souvent. D'ailleurs de plus en plus les IDE passent leur temps à faire compiler les programmes pendant la frappe.

                                          -
                                          Edité par michelbillaud 15 mars 2021 à 19:41:42

                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            15 mars 2021 à 21:29:06

                                            michelbillaud a écrit:

                                            > genre des cast pour faire taire des warnings qu'on a du mal à comprendre

                                            C'est justement le bon moment pour essayer de les comprendre, plutôt que de faire en sorte de ne pas les voir. A priori, je ne vois pas d'exemple de warning bénin qui ne soit pas facile à régler proprement.

                                            Je me rappelle avoir signalé, il y a très longtemps, un bug dans la commande netdate de linux, qui aurait été évident si le programmeur avait daigné regarder les warnings.  Genre le résultat d'un read affecté dans un non-signé, ce qui faisait louper la détection de problèmes ensuite (échec du read à travers le réseau, résultat négatif).

                                            Le type devait juger que "un warning n'est pas forcément une erreur".

                                            > et -Werror (il faut impérativement les faire disparaître) …

                                            Absolument. Des warnings -> pas d'exécutable. Ca évite de tirer des conclusions hâtives à partir de l'exécution "pour voir" d'un source a moitié compilé de travers.

                                            C'est une discipline de programmation. De nos jours, les ordinateurs vont vite, et on peut compiler très souvent. D'ailleurs de plus en plus les IDE passent leur temps à faire compiler les programmes pendant la frappe.

                                            -
                                            Edité par michelbillaud il y a environ 1 heure

                                            Je me demande bien pourquoi le compilateur/standard fait une différence entre un warning et un error ? Y aurait-il une raison que même un débutant pourrait être amené à comprendre ? ptêt qu'il s'agit même du fameux «un programmeur C sait ce qu'il fait, même quand il se tire dans le pied» 😄

                                            La discipline de programmation n'a pas besoin de «tout warning est une erreur, les warnings bénins n'existent que dans le pays des merveilles et ici c'est le vrai monde réel», mais plus de «tout warning se doit d'être pris en compte». On fait du C, on est pas en train d'apprendre Ada par exemple. Il ne faut pas non plus oublier que les messages de diagnostiques se sont largement améliorés depuis quelques temps … ils en sont au stade où ils sont même clairs … pour dire 🤗

                                            michelbillaud a écrit:

                                            [...]

                                            Le type devait juger que "un warning n'est pas forcément une erreur".

                                            [...]

                                            Bah tu juges toi-même que tous les warnings ne sont pas des erreurs puisque :

                                            michelbillaud a écrit:

                                            Je me permets quand même  -Wno-unused, quand j'ai des pointeurs vers des fonctions qui doivent avoir le même prototype, mais dont les paramètres ne servent pas forcément.

                                            Pourquoi ne pas classiquement faire un cast d'une expression sur (void) ?

                                            Pourquoi ne pas simplement forcer une autre signature ?

                                            Il y aurait des moyens de corriger cette erreur

                                            Quand aux programmeurs qui ne font que peu ou pas gaffe aux warnings il y en a toujours eu et il y en aura toujours, -Werror ou pas …

                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              16 mars 2021 à 11:11:42

                                              White Crow a écrit:

                                              Pourquoi ne pas classiquement faire un cast d'une expression sur (void) ?

                                              Je fais un peu d'embarqué (en amateur), et les options du compilo m'obligent à coller un (void) pour ignorer tous les retours de fonctions dont je ne fais rien.

                                              Comme ça, on est sûr que je n'ai pas ignoré que la fonction retournait quelque chose. En gros, je dois m'en occuper, ou signer une décharge en mettant explicitement un (void).

                                              Pour l'histoire des arguments non utilisés, c'est parce que la norme du langage est (pour l'instant) légèrement déficiente de ce côté-là. Mais elle se soigne. Sinon il y a  l'alternative

                                              #define UNUSED_ARG (void)
                                              
                                              
                                              void foo(int bar, int baz) {
                                                 UNUSED_ARG baz;
                                                 ...
                                              }
                                              

                                              Ca marche, c'est lisible, c'est maintenable, mais c'est pas standard.



                                              Ici on est dans le contexte de programmes de débutants. A priori, les warnings des exercices sont parfaitement solubles sans faire de pirouettes, quand ils sont bénins. Et ça vaut le coup d'examiner ce qui les déclenche.

                                              Le type de programme où j'ai le souci, c'est par exemple un "shell", avec des actions à lancer en fonction d'un mot

                                              struct commandes[] {
                                                  { "quit" , do_quit },
                                                  { "truc",  do_truc },
                                                  ....
                                              };
                                              

                                              et qu'après avoir trouvé le mot dans la table on lance la fonction correspondante

                                              for (....) {
                                                  if (strcmp(....)) {
                                                         commandes[i].action(argc, argv);
                                                         break;
                                                  }
                                              }
                                              


                                              A priori les actions ont le même prototype (un nombre, un tableau de chaines), mais dans certains cas (quit, on peut imaginer), elle n'utilise pas le tableau de chaines args (parce qu'on sait que argc == 1, et qu'on sait ce qu'il y a dans argv[0]).

                                              Donc il n'y  pas de raison de changer la signature, encore moins de la forcer. C'est juste qu'on n'utilise pas le tableau, qui a parfaitement raison d'être là quand même.



                                              -
                                              Edité par michelbillaud 16 mars 2021 à 11:21:54

                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                16 mars 2021 à 11:50:53

                                                michelbillaud a écrit:

                                                White Crow a écrit:

                                                Pourquoi ne pas classiquement faire un cast d'une expression sur (void) ?

                                                Je fais un peu d'embarqué (en amateur), et les options du compilo m'obligent à coller un (void) pour ignorer tous les retours de fonctions dont je ne fais rien.

                                                compilos non standard …

                                                michelbillaud a écrit:

                                                Pour l'histoire des arguments non utilisés, c'est parce que la norme du langage est (pour l'instant) légèrement déficiente de ce côté-là. Mais elle se soigne. Sinon il y a  l'alternative

                                                #define UNUSED_ARG (void)
                                                
                                                
                                                void foo(int bar, int baz) {
                                                   UNUSED_ARG baz;
                                                   ...
                                                }
                                                

                                                Ca marche, c'est lisible, c'est maintenable, mais c'est pas standard.

                                                La norme ni ne définit ni n'impose l'émission d'un quelconque warning. Les implémentations sont libres de faire ce qu'elle veulent. La construction que tu montres est standard dans le sens que c'est une expression acceptée par le standard, tu l'utilises pour faire taire un warning du compilo … et effectivement c'est lisible et maintenable.

                                                Le problème principal de -Werror est qu'il transforme un warning (définit par l'implémentation) en erreur (qui tient au standard). Tu peux du jour au lendemain te retrouver avec un programme qui ne compile plus car après un update de ton compilo tu as de nouveaux warnings … Le comportement n'est plus reproductible ce qui produit un programme un peu plus difficile à maintenir, car tu vas le patcher de plus en plus pour faire taire les warnings …



                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                  16 mars 2021 à 15:06:47

                                                  Là, c'est pas "patcher" pour faire taire, ça serait absurde parce que contraire à l'idée du truc.

                                                  C'est patcher pour _résoudre_ le petit souci qui se pose.

                                                  Par exemple, ça coûte pas cher, en voyant

                                                  a.c:6:20: warning: comparison of integer expressions of different signedness: ‘int’ and ‘size_t’ {aka ‘long unsigned int’} [-Wsign-compare]
                                                    for (int i = 0; i < strlen(string); i++) {
                                                                      ^
                                                  

                                                  de rectifier le type de i, qui devrait être un size_t.

                                                  Dans d'autres contextes, la valeur du size_t pourrait être trop grande pour tenir sur un int, et si on ne fait pas gaffe à comparer des choses comparables, OSEF ça va marcher, on risque de se faire pincer très fort.




                                                  -
                                                  Edité par michelbillaud 16 mars 2021 à 15:08:17

                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    30 mars 2021 à 15:29:50

                                                    Pour le -Werror, je l'enleve et le remet, c'est surtout car il me renvoi des erreurs de variables non utilisées et quand tu es en train de modifier des fonctions juste pour test deux ou trois trucs, t es obligé de modifier toutes les focntions pour que ca compile.

                                                    Je sais pas si c est claire lol, mais c est juste pour tester deux ou trois truc, un fois que j ai trouvé je le remet. De toute façon meme si tu ne le met pas, tu as quand meme les warning non ? Perso, je ne laisse jamasi des warnings, pour moi je considere ça comme une erreur, quoi qu'il arrive. 

                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      30 mars 2021 à 15:57:46

                                                      Le problème de laisser s'afficher des messages qu'on a décidé d'ignorer, c'est qu'au bout de 5 minutes on ne lit plus les messages, et qu'on ne voit pas les nouveaux qui se glissent dedans.

                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        30 mars 2021 à 16:22:30

                                                        tout comme le problème de vouloir absolument faire disparaître des messages qui ne sont que des warnings c'est qu'au bout de 2 messages, donc même pas 5 minutes, on va faire des cast sauvages, des tournures de codes inutiles pour éviter des messages, … on rend le code illisible.

                                                        La philosophie du C c'est pas celle d'Ada …

                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                          30 mars 2021 à 18:20:47

                                                          Là on est dans le vague. La philosophie, ça se mange pas en salade.

                                                          Concrètement, les avertissements sur des paramètres non utilisés, ça rattrape des erreurs du genre

                                                          float produit_tordu(float x1, float y1, float x2, float y2, float x3, float y3)
                                                          {
                                                              return x1*y2 + x2*y3 + x1*y2;
                                                          }
                                                          
                                                          // tiens on cause pas de y1 ?
                                                          


                                                          Peux-tu donner un exemple concret où le rattrapage donne

                                                          • des cast sauvages,
                                                          • des tournures de code inutiles
                                                          et qu'_en plus_ de ce fait ça rendrait le code illisible ?
                                                          PS: "on n'est pas en ADA", certes, mais justement : en C où le compilateur vérifie moins de choses, il faut davantage de rigueur. Il faudrait.

                                                          -
                                                          Edité par michelbillaud 30 mars 2021 à 19:30:38

                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                            30 mars 2021 à 21:30:48

                                                            Évidemment qu'il faut de la rigueur ! 

                                                            Ce ne sont pas les warnings que je remets en cause  … c'est l'utilisation de -Werror. Un warning c'est un avertissement, un error c'est une erreur → deux choses bien différentes. Considérer les warnings comme des erreurs c'est un non sens. Les considérer et avec rigueur les examiner c'est normal, c'est cela qu'il faut apprendre, même aux débutants. Vouloir les enlever à tout prix parce qu'on a un -Werror pour «avoir une compilation qui passe crème» c'est du n'imp.

                                                            Le seul et unique moment où on doit avoir une absence totale de warnings (et d'erreurs évidemment) c'est lorsqu'on développe une bibliothèque. À aucun moment, l'inclusion d'un header pour utiliser ladite bibliothèque ne doit générer des messages.

                                                            Donc on s'entend bien … je te parle bien de l'utilisation de -Werror … pas de l'utilité évidente des warnings et autres messages de diagnostiques.

                                                            • cast sauvage ?
                                                              n'importe quel apprenant castera pour pouvoir compiler s'il a un message du genre int * expected char *found …
                                                              ah ben oui … après ça compile mais ça bugue et en beauté …
                                                              et on a droit à des messages du genre «je comprends pas, ça compile mais ça plante»

                                                            • tournures de code inutiles ? mmm je ne sais pas moi … par exemple :

                                                              michelbillaud a écrit:

                                                              #define UNUSED_ARG (void)
                                                              
                                                              
                                                              void foo(int bar, int baz) {
                                                                 UNUSED_ARG baz;
                                                                 ...
                                                              }
                                                              

                                                            et encore c'est la «moins pire des solutions». Parce  qu'il y a une forme de lisibilité, enfin jusqu'au jour où tu trouves un bug car le paramètre n'est pas utilisé mais tu n'as pas de warnings parce que tu l'auras fait taire pour simplement pouvoir compiler … 

                                                            Le compilateur ne vérifie pas moins de choses. Et c'est là un argument supplémentaire contre l'utilisation du -Werror, au moins dans un contexte de déploiement. En effet les compilateurs diagnostiquent de mieux en mieux. Chaque nouvelle version vient avec un lot supplémentaire de diagnostiques. Utilise un -Werror et un simplement changement de version de ton compilo fout en l'air tes tests automatiques, tes builds automatiques, etc … et tout ça pour un warning qui n'était pas diagnostiqué avec gcc 9 mais l'est avec gcc 10 ?

                                                            Je ne dis pas qu'il ne faut pas le vérifier, je dis que ça ne doit en aucun cas être bloquant.

                                                            Et oui, c'est là où la philosophie du C (le programmeur sait ce qu'il fait) intervient. Si tu veux une compilation qui passe sans aucun message pour avoir un exécutable alors ne fait pas du C, fait de l'Ada ou du Rust (et encore … rust ça va).

                                                            L'esprit du C :

                                                            • Trust the programmer.
                                                            • Don't prevent the programmer from doing what needs to be done.
                                                            • Keep the language small and simple.
                                                            • Provide only one way to do an operation.
                                                            • Make it fast, even if it is not guaranteed to be portable.
                                                            • Make support for safety and security demonstrable.

                                                            Et on peut d'autant plus croire le dèv qu'on possède un compilo qui diagnostique bien, et un -Werror c'est juste empêcher le dèv de faire ce qu'il faut (parfois) …

                                                            Enfin, ce n'est que mon avis hein 😇

                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                              30 mars 2021 à 23:54:19

                                                              Dans ton cast sauvage, c'est une erreur qui ne se corrige ni par un cast, ni par une absence de cast. Les avertissement signalent un probleme potentiel, mettre WError oblige à se pencher dessus. Mais bien sûr ils n'empecheront personne d'être stupide. Je ne vois pas où est l'argument. 

                                                              Le cas des parametres non utilisés devrait etre réglé bientôt par un attribut (prochain standard ?)

                                                              Et tes systèmes de build automatique, c'est très bien, mais là on parle de l'enseignement aux débutants. Il faut qu'ils lisent, comprennent et rectifient. 

                                                              Les points que tu cites comme étant "l'esprit du C " etaient un article de foi du premier standard "le programmeur infaillible sait ce qu'il fait LOL", le comité de normalisation est un peu revenu sur terre ensuite.

                                                              Si le compilateur version N détecte mieux un lézard que la version N-1, il faudrait s'en priver ?

                                                              -
                                                              Edité par michelbillaud 31 mars 2021 à 0:03:36

                                                              • Partager sur Facebook
                                                              • Partager sur Twitter

                                                              free un malloc retourné par une autre fonction

                                                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                              • Editeur
                                                              • Markdown