Bonjour,
Je voulais savoir si l'on pouvait se dispenser d'initialiser un pointeur si plus tard dans le programme on fait un malloc de ce pointeur et que jusqu'a ce malloc on ne se sert pas du pointeur en question?
merci
Version imprimable
Bonjour,
Je voulais savoir si l'on pouvait se dispenser d'initialiser un pointeur si plus tard dans le programme on fait un malloc de ce pointeur et que jusqu'a ce malloc on ne se sert pas du pointeur en question?
merci
Tu peux, rien ne t'empeche. Cependant, si on conseille d'initialiser les pointeurs, c'est pour éviter un comportement dangereux et permettre un debuggage plus facile.
Imagine que tu n'as pas initialiser ton pointeur a null, et tu passes ton programme a quelqu'un...lui il a pas trop suivi ce que t'as fait et il tente d'utiliser ton pointeur avant le malloc : tu vas te retrouver avec un programme au comportement indéfini qui pourrait écrire des données a de mauvaises adresses etc...Bref, un truc faux et pas évident a débugger. Au moins en initialisant ton pointeur a null tu ne risque pas d'écrire à un mauvais emplacement et en cas d'utilisation du pointeur, le programme plantera net à chaque fois (au lieu d'avoir un comportement flou)
C'est techniquement possible mais formellement déconseillé. http://forum-images.hardware.fr/imag...ou_grilled.gifCitation:
Envoyé par sone47
ou mieux (ce n'est pas toujours possible)Code:
1
2
3
4 T *p = NULL; p = malloc (...);
Nota. On ne fait pas un "malloc de ce pointeur", mais "on alloue un bloc avec malloc(), dont on stocke l'adresse dans ce pointeur".Code:
1
2 T *p = malloc (...);
Attention à utiliser la bonne terminologie, sinon, tu donnes l'impression de ne pas maitriser ton sujet...
Oui, et surtout, tu peux le détecter avec un if (p != NULL)Citation:
Envoyé par Ksempac
Salut,
Théoriquement, rien ne t'empêche de déclarer un pointeur sans l'initialiser dans la foulée...
Cependant, il faut bien comprendre que, quand tu déclare une variable, meme si, comme pour un pointeur, elle va contenir une adresse mémoire, sa valeur avant d'être initialisée est... tout et n'importe quoi...
Pour etre précis, la valeur est... ce qui s'est trouvé à l'adresse utilisée par la mémoire la dernière fois qu'elle a été utilisée (ca peut etre des valeurs qui étaient utilisées par une application qui a été fermée depuis des heures... voir des jours)
Il s'en suit que, tant que tu n'es pas sur que l'adresse que la variable contient est belle et bien valide, le fait d'essayer d'accéder à cette adresse va provoquer des catastrophes pouvant aller jusqu'au crash système dans les cas les plus graves...
C'est la raison pour laquelle il est très fortement conseillé d'initialiser les pointeurs à NULL (car, au moins, c'est une valeur facilement testable) tant qu'ils ne prennent pas une adresse valide, et de systématiquement vérifier les valeurs des pointeurs avant d'essayer d'y accéder et/ou apres une fonction d'allocation ;)
et de remettre à NULL après libération de la ressource pointée...Citation:
Envoyé par koala01
:oops: oui... évidemment... sinon le reste n'a aucun sens :oops:Citation:
Envoyé par Emmanuel Delahaye
d'accord merci,
je demandais ceci car un prof m'avais barré mes init pointeurs car plus tard on allouait de la memoire.
L'action de mon prof aurait été plus credible si on allouait dessuite apres avoir creé le pointeur.
Est il possible de savoir de quel taille a été une allocation en ayant juste l'adresse premiere case car je n'ai pas saisi si pour toutes les chaines la terminaison était toujours '\0'?
Merci
Desormais je mettrai toujours a NULL quelque soit le cas ceci ne peut donc que m'éviter ds prob.
merci
Une autre question dans l'elan si j'utilise un pointeur qui pointe vers un autre pointeur comment se definit le premier?
Pour ce qui est des chaines de caractères, et uniquement des tableaux de caractères considérés comme tel (car un char peut très bien etre considéré comme un entier... ne permettant qu'un nombre restreint de valeur), il faut savoir que toutes les fonctions qui les manipulent vont rechercher ce fameux caractères '\0' pour déterminer si la fin de la chaine a été atteinte...
On en arrive aux situations assez particulieres que:
- si le caractère '\0' a été "oublié", ta chaine sera considérée comme continuant bien au delà de la mémoire qui lui a été allouée (jusqu'à ce que l'équivalent d'un '\0' soit trouvé), ... avec des résultats... surprenants...
- Si tu as un caractère '\0', par exemple à la 10 eme position dans un tableau de 100 caractères, les 90 caractères qui suivent sont purement et simplement ignorés... Ce qui ne veut nullement dire que tu obtiendras une erreur si, par la suite tu essayes de mettre une chaine de 20 caractères dans le tableau ;)
- il est courent, quand tu veux "juste" vider une chaine de caractères, en attendant de la réutiliser, ou quand tu veux supprimer une partie de la fin de la chaine, de placer le caractère '\0' en chaine[0]; dans le premier cas et en chaine[nombre_de_caractères_gardés+1] dans le deuxième (où chaine est, bien entendu, la variable qui correspond à ta chaine de caractères ;))
Autrement dit:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 int main() { char *chaine1;/* Danger important: non initialisé et non NULL (intestable) */ char *chaine2=NULL;/* Danger important: non initialisé (mais testable :D)*/ char *chaine3=malloc(taille);/* Danger moyen: initialisé mais pas forcément vide (sizeof(char)==1 => taille==taille*sizeof(char) ;) risques lors de l'affichage ;) */ char chaine4[100]="salut";/* surprise: 100 caractères dispos, mais la chaine n'en fait que 5 ;) */ chaine4[3]='\0';/* chaine4 vaut "sal" */ chaine4[0]='\0';/* chaine4 est considérée comme vide strlen(chaine4)==0 alors qu'en mémoire on trouve "\0al\0ut\0...." ;)*/ }
Merci mais donc en utilisant calloc, le fait d'init ne place pas le caractere '\0' en fin.
Ce que je me pose comme question c'est si on alloue de la memoire via calloc et que l'on passe dans une fonction l'adresse de la premiere case comment faire pour savoir jusqu'ou a été l'allocation?
Je me pose également la question plus haute sur les pointeurs de pointeurs car j'ai pas trouvé d'exemple sur le net.
merci
je dirais plutôt équivalent au précédent, les 2 à danger moyens. Le premier car possbile utilisation si on oublie malloc, le second car on est pas sûr que l'allocation se soit bien déroulée. Donc dans tous les cas tester...Citation:
Envoyé par koala01
Pour les pointeurs de pointeurs, tu reprends le principe des pointeurs, et tu fais pareil:
Pour définir un pointeur, c'est
Code:
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 /* définition "statique" d'un pointeur */ int a=100: int *pta=&a;/* prend comme valeur l'adresse à laquelle se trouve a */ /* définition dynamique d'un pointeur */ int *ptab=malloc(sizeof(int)*taille); (...)/* principalement, tester que ptab ne vaut pas NULL avant toute chose*/ /* définition "statique" d'un pointeur de pointeur (prend l'adresse d'un pointeur défini précédemment)*/ int **ppta=&pa; /* définition dynamique d'un pointeur de pointeur: on signale qu'il nous faut la taille nécessaire à un pointeur sur entier */ int **pptab=malloc(sizeof(int*)*taille); /* et, ce n'est qu'une fois que l'on a notre pointeur de pointeur qu'on peut allouer la mémoire pour les pointeur qu'il contient... si l'allocation de pptab a réussi uniquement */ if(pptab!=NULL)/* on n'entre pas ici si l'allocation de pptab a échoué */ { for(i=0;i<taille;i++)/* on passe chaque pointeur en revue */ { pptab[i]=malloc(sizeof(int)*taillecolone);/* pour chacun d'eux, on alloue la mémoire nécessaire */ (...) /* à chaque fois tester que l'allocation a réussi... sinon, il faudra gérer l'erreur (*vraissemblablement* libérer la mémoire de tout et quitter ;)*/ } }
Citation:
Envoyé par souviron34
:
- malloc ne fait qu'une chose: fournir un espace mémoire de la taille que tu lui demande... Ce qui se trouve dans cet espace mémoire est... tout ce qui s'y trouvait du fait d'une utilisation antérieur de la mémoire... des crasses ;)
- realloc fait un peu plus: copier ce qui se trouve dans l'espace dont on demande la réallocation dans l'espace alloué, de telle sorte que le plus petit rentre dans le plus grand (==> danger si tu fais un realloc qui demande une taille inférieure à celle que tu avais avant: troncature possible de la chaine)
- calloc, je ne m'y suis jamais intéressé :oops:
je ne sais pas pourquoi tu dis ça en me citant 8OCitation:
Envoyé par koala01
Le point 1 est exactement ce que je disais : meême si tu utilises en initalisation malloc, il faut quand même tester que l'allocation s'est bien déroulées avant d'utiliser le pointeur.
Le point 2 : le danger ne vient pas de ce que tu dis. Il vient du fait au contraire que si tu demandes plus grand, il est possbile que ça échoue. Et donc il vaut mieux faire un realloc dans un pointeur temporaire, véfifier, et ensuite affecter.
Le point 3 est justement très pratique pour les chaînes et les structures, puisqu'il initialise tout à NULL (je crois). Donc aucun danger (sauf si on dépasse la taille allouée bien entendu) d'oublier un '\0', et si on fait des copies par opération de pointeur, on est cependant certain que ce sera terminé par un '\0'..
.
Merci d'avoir pris le temps de rep.
En utilisant ces explications si je veux passer en parametre d'une fonction plusieurs tableaux je créé un pointeur sur un autre pointeur avec chaque case de mon premier pointeur qui pointe sur le pointeur de mes tab.
Ceci differe t il de ce que vous m'avez marqué plus haut a savoir de ne pas attribuer de memoire sur les 'premiers' pointeurs tant que le pointeur de pointeur n'a pas eut sa memoire allouée?
Dans mon cas est ce necessaire vu que mes 'premiers' pointeurs sont deja pointés vers une zone allouée.
la je veux passer deux tableaux en parmetre:Code:
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 void main() { float init_pt1; float init_pt2; float * pt1; float * pt2; float ** pptr; pt1=&init_pt1; pt2=&init_pt2; pptr=pt1; .../*travail sur pt1 et pt2 allocation remplissage*/ float **pptr = (float **)calloc(2,sizeof(float *)); if (pptr==NULL) { printf("Error of memory allocation !!!"); exit(1); } else { *(pptr)=pt1; *(pptr+1)=pt2; } ... /* et la je peux passer pptr en parametre pour travailler dans ma fonction sur les deux tableaux*/ return 0; }
Tu crois mal. calloc() initialise tout à "all bits to zero", ce qui marche sur certaines architectures, notamment nos PC x86 et les nombres à virgule flottante IEEE 754 mais PAS PARTOUT.Citation:
Envoyé par souviron34
D'ailleurs, il n'est même pas garanti qu'un pointeur nul, qui vu du C vaut zéro, soit bien représenté par "all bits to zero" en mémoire.
Edit: Ni même un entier (un bète int), je pense...
Bref, ce n'est pas portable.
Non, à 0. Cf les multiples discussions sur "NULL n'est pas nécessairement 0".Citation:
Envoyé par souviron34
Cela dit, dans la majorité des cas, NULL = 0, et de même 0.0 se code avec tout à zéro.
autant pour moi, souviron... j'ai cliqué sur le mauvais bouton...
Ce que je voulais citer, c'était le post juste au dessus, de saune:
:oops::oops:Citation:
Merci mais donc en utilisant calloc, le fait d'init ne place pas le caractere '\0' en fin.
Ce que je me pose comme question c'est si on alloue de la memoire via calloc et que l'on passe dans une fonction l'adresse de la premiere case comment faire pour savoir jusqu'ou a été l'allocation?
Je me pose également la question plus haute sur les pointeurs de pointeurs car j'ai pas trouvé d'exemple sur le net.
merci
Ben 0x0 ça donne pas des zéros partout ??? je sais pas , j'ai jamais compté ni en hexa, ni en octal, mais il me semblait bien.. `Citation:
Envoyé par Médinoc
Et de toutes façons je ne vois pas en quoi ça a un rapport avec "ça n'est pas portable".. calloc fait partie du langage standard... Pour une utilisation "à priori" oui.
Je préfère de toutes façons utiliser calloc que malloc, toujours, car comme on l'a dit plus haut, on se sait pas ce qui traîne, donc ça pourrait être par exemple une instruction , un chiffre qui viendra mettre la pagaille dans le tableau, etc.... Donc je préfère savoir qu'il y a des zéros partout....
[EDIT]
et d'ailleurs, c'est entier, ce qu'il met, car en fait ça utilise memset, et memset pour que ce soit à 0 tu lui passes 0L ....
[/EDIT]
Pour ce qui est de calloc, il initialise, selon opengroup tout l'espace à 0...ce qui, comme l'a si bien fait remarquer médinoc, ne correspond pas *forcément* à NULL, selon les implémentations/le matériel...
La difficulté de compréhension venant surement du fait que null, en anglais, c'est 0... mais ici, on est dans le cadre d'une définition de valeur (un simple #define) qui peut tres bien changer ;)
je disais juste que je préfère avoir un tableau intialisé à une valeur connue que d'avoir n'importe quoi, y compris possiblement une mauvaise instruction ou un chiffre abherrant...Citation:
Envoyé par koala01
Il n'est même pas ici question du define.Citation:
Envoyé par koala01
La norme C garanti que vu du C, le pointeur NULL sera égal à zéro.
Le problème, c'est que memset() et calloc() ne connaissent pas le type, donc bypassent complètement l'aspect "vu du C" en mettant une succession de bits ou d'autre chose à zéro, sans garantir du tout que cela corresponde vraiment à zéro pour le type concerné...
Ce que je voulais surtout faire remarquer c'est qu'il faut etre prudent dans les termes utilisésCitation:
Envoyé par Médinoc
ne peut *pas forcément* etre concidérée comme identique àCitation:
une valeur nulle
simplement parce que NULL peut etre redéfini à volonté (ou presque)... et que, bien qu'il puisse sembler que les deux termes soient identiques, il n'en reste pas moins que ca revient presque à comparer une pomme avec une poire ;)Citation:
NULL
Non. C'est à toi de prendre des notes. Je recommande ceci :Citation:
Envoyé par sone47
Code:
1
2
3
4
5
6 struct tab_T { size_t nb_elem; T* array; };
Une chaine valide est toujours terminée par un 0, mais ça ne donne aucune indication sur la taille réelle du tableauCitation:
car je n'ai pas saisi si pour toutes les chaines la terminaison était toujours '\0'?
on passe une structure avec le nombre d'elements et l'adresse, mais je ne comprend l'annotation que vous avez utiliséEt pour les pointeurs de pointeurs plus haut cela correspond il?Code:T* array;
Euh, non. Il initialise à 'all-bits-to-zero', ce qui ne signifie pas forcément 0, 0.0 ni NULL... Ca dépend de l'implémentation. C'est donc une fausse bonne idée (même si, dans la pratique, on voit rarement de différence).Citation:
Envoyé par souviron34
Ce n'est pas garanti. En C99, il existe la notion de 'trap representation' pour les entiers autres que char. Rien ne dit que 'all-bit-to-zero' n'en soit pas une.Citation:
Envoyé par souviron34
En C90, son utilisation n'est portable que pour des entiers. En C99, uniquement pour des des [[un]signed ]char.Citation:
Et de toutes façons je ne vois pas en quoi ça a un rapport avec "ça n'est pas portable".. calloc fait partie du langage standard... Pour une utilisation "à priori" oui.
Mais il y a une différence entre mettre des 0 partout ce qui est correct et souhaitable et mettre tous les bits à 0, ce qui produit un effet non portable.Citation:
Je préfère de toutes façons utiliser calloc que malloc, toujours, car comme on l'a dit plus haut, on se sait pas ce qui traîne, donc ça pourrait être par exemple une instruction , un chiffre qui viendra mettre la pagaille dans le tableau, etc.... Donc je préfère savoir qu'il y a des zéros partout....
Je recommande ceci qui est portable :
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 T *p = malloc (sizeof *T * N); if (p != NULL) { size_t i; for (i = 0; i < N; i++) { static const T z = {0}; p[i] = z; } /* etc. */ }
On passe un entier a memset(), dont les bits de poids faibles (unsigned char) sont recopiée tels quels dans les bytes de la zone mémoire considérée. Il n'y a pas de notion de 'valeur' , mais une simple recopie d'un pattern de bits.Citation:
et d'ailleurs, c'est entier, ce qu'il met, car en fait ça utilise memset, et memset pour que ce soit à 0 tu lui passes 0L ....
Sauf si cette combinaison de bits provoque un trap à la premier lecture de la variable concernée... Il vaut soit se renseigner sur l'implémentation courante, soit utiliser une initialisation portable.Citation:
Envoyé par souviron34
eh bien ça veut donc dire que je programme de manière très sûre alors, si c'est aussi dangereux que ça... :mrgreen: vu que je n'ai aucun bug de mémoire dans ma très grosse application, et qu'il y a plus de milles calloc et realloc.... :yaisse2:
Il faut croire ;)...
Tu sais, il est des gens qui n'utilisent que scanf, et qui pourtant, comme ils l'utilisent correctement, fournissent des programmes tres stables et sécurisants ;)
Je suis intimement persuadé que ce qui fait la stabilité d'une application tiens finalement en peu de mots:
- concetption et algorithmie correctes,
- utilisation en conscience des fonctions appropriées
Si tu travailles en gardant ces deux grandes lignes en tete, il est déjà fort vraissemblable que tu codes de manière très sure ;)
Tu as une drôle de façon de raisonner...Citation:
Envoyé par souviron34
Un programme peut avoir un comportement parfaitement défini et stable sur une plateforme, mais, parce qu'il n'est pas écrit de façon portable, avoir un comportement indéfini sur une autre. Ca n'a rien à voir avec "je programme de manière très sûre".
Si la portabilité n'entre pas en compte, c'est OK. Sinon, le comportement est indéfini. C'est tout.
Ce qui fait que le codage est 'de qualitaÿ', c'est la maitrise absolue des comportements indéfinis. Il ne doit pas y en avoir. Pour ça, il faut très bien connaitre le C de l'intérieur (la norme), c'est pour ça que je dis souvent que le C n'est pas un langage de débutant. C'est plutôt un langage pour professionnels avertis.
"C is a sharp tool"
Et il faut cesser de croire que le comportement visible suffit pour qualifier un code.
Ada est beaucoup plus clair pour ça. Il a été conçu de A à Z pour justement éviter les comportements indéfini.
Je ne vois pas trop où tu as vu ça...Citation:
Envoyé par Emmanuel Delahaye
C99 :
http://www.open-std.org/JTC1/SC22/WG...docs/n1124.pdf
C89 : (K&R)Citation:
7.20.3.1 The calloc function
Synopsis
1 #include <stdlib.h>
void *calloc(size_t nmemb, size_t size);
Description
2 The calloc function allocates space for an array of nmemb objects, each of whose size is size. The space is initialized to all bits zero.
3 The calloc function returns either a null pointer or a pointer to the allocated space
8OCitation:
void *calloc(size_t n, size_t size)
returns a pointer to enough free space for an array of n objects of the specified size, or NULL if the request cannot be satisfied. The storage is initialized to zero.
The pointer returned by malloc or calloc has the proper alignment for the object in question, but it must be cast into the appropriate type, as in
int *ip;
ip = (int *) calloc(n, sizeof(int));
Il a vu ça ici:
Citation:
Envoyé par Ta citation
Non je parlais que c'est portable pour les entiers en 90 (et les char en 99)...Citation:
Envoyé par Médinoc
Cette histoire de portabiblité ou non est liée à ça.
Il doit donc y avoir quelque chose dans la norme qui indique si un entier est ou non compatible avec "all-bits-to-zero", mais certainement pas dans la spec de calloc()...
ben de toutes façons je mentionnais simplement que j'utilisais systématiquement calloc plutôt que malloc pour avoir quelque chose d'initialisé et non pas du "garbage", mais en aucun cas je ne m'en sers... C'est juste une sécurité d'esprit... :)
Dans N1124, cherche 'trap representation'Citation:
Envoyé par souviron34
Attention, la norme, c'est pas un mode d'emploi et encore moins un livre de cours, c'est une référence. Il faut savoir ce qu'on cherche.
j'ai cherché :D et je ne vois pas en quoi ça altère mon raisonnement :
Si on se sert de calloc uniquement pour SAVOIR que la plage mémoire est initialisée, sans aller atteindre une valeur avant de l'avoir assignée, je ne vois pas le "danger"...
Quant à ce que tu marques ici :
ça n'est pas vraiment plus sûr, puisque l'assignation n'assigne pas les bits de padding, et qu'ils sont indéfinis (d'après cette norme).Citation:
static const T z = {0};
p[i] = z;
En fait j'utilises calloc uniquement pour économiser une ligne de memset..
Mais je ne me sers JAMAIS du fait que que c'est initialisé. Comme je disais plus haut, c'est uniquement une tranquillité d'esprit du fait de savoir qu'il n'y a pas n'importe quoi, mais que je sais ce qu'il y a ...