Question sur les structures - C - Programmation
Marsh Posté le 28-05-2007 à 16:53:18
1/ Euh il me semble que la taille d'une struct est la taille des champs qui la composent.
2/ Donc non.
Marsh Posté le 28-05-2007 à 16:54:45
Ca dépend. Ca dépend du compilateur, de l'architecture de la machine cible, des options de compilation... Et la raison pour laquelle c'est variable, c'est que les compilos ajoutent parfois du padding pour avoir des champs alignés comme il faut.
Marsh Posté le 28-05-2007 à 16:55:37
ReplyMarsh Posté le 28-05-2007 à 17:22:38
Hein? La taille de la plus grande des parties? Tu as compris quelque chose aux structures ou bien ta dernière phrase a une signification qui m'échappe?
Une structure est au moins aussi grande que la somme des tailles des parties qui la compose, c'est tout ce qu'on peut dire. Certains compilos auront besoin de plus de place, d'autres pas.
Marsh Posté le 28-05-2007 à 17:35:51
Ne pas confondre les structs et les unions. Dans une union, tous les champs partagent le même espace. Un seul n'a de sens à un instant donné, et effectivement, la taille de l'union, c'est la taille du plus grand de ses champs.
Dans une struct, les champs sont placés les uns à côté des autres. Donc comme dit PhosphoReloaded, la taille de la struct est la somme des tailles des champs qui la composent (plus la taille des espaces vides ajoutés pour aligner chaque champ sur un début de mot machine)
Marsh Posté le 28-05-2007 à 17:41:12
BifaceMcLeOD a écrit : Ne pas confondre les structs et les unions. Dans une union, tous les champs partagent le même espace. Un seul n'a de sens à un instant donné, et effectivement, la taille de l'union, c'est la taille du plus grand de ses champs. |
d'accord. Merci
Marsh Posté le 29-05-2007 à 10:41:09
Je rejoins ton sujet sur les pointeurs et ton tableau de int.
Pareil ici, si tu fais un tableau nomé T de 5 structure, si tu fais T+1, il te renverra correctement l'adresse de la prochaine structure car il sait quelle taille fait ta structure
Marsh Posté le 29-05-2007 à 18:47:26
nORKy a écrit : Je rejoins ton sujet sur les pointeurs et ton tableau de int. |
Ce qu'on nomme communément "arithmétique des pointeurs"
=> "pt + n" donne comme résultat une adresse égale à "pt + n * sizeof(*pt)"
Marsh Posté le 29-05-2007 à 20:50:05
_darkalt3_ a écrit : 1/ Euh il me semble que la taille d'une struct est la taille des champs qui la composent. |
Non. Le compilo va organiser la structure de façon à ce que les champs s'alignent sur la taille des mots utilisés par la cible, de façon à optimiser l'accès aux données par pointeur. Cela signifie qu'il va padder pour que la taille de la structure soit un multiple entier du mot mémoire. Cela dépend donc de l'architecture cible.
Marsh Posté le 29-05-2007 à 20:59:09
Ca dépend aussi des options de compil comme je l'ai déjà dit. En particulier les compilos ont généralement une option pour "packer" les structures, c'est-à-dire ne pas utiliser de padding. Dans ce cas la taille de la structure et la somme des tailles des membres.
Marsh Posté le 30-05-2007 à 16:30:54
nORKy a écrit : Je rejoins ton sujet sur les pointeurs et ton tableau de int. |
Merci. ok, donc, quand on a un tableau T et qu'on se déplace sur les adresses, on a besoin de faire
Code :
|
et non pas
Code :
|
c'est bien cela ?....?
j'aimerai savoir si ce que dis ce site est vrai :
http://rperrot.developpez.com/articles/c/genericite/
notamment dans la section III :
Citation :
void parcours( void * data , size_t nb_elt , size_t size ) Mais nous avons vu que le calcul d'adresse ne pouvait pas être effectué sur un pointeur générique, il faut donc passer par un autre pointeur. |
car ca compile chez moi, et il me semblait que on faisait T+5 et non pas T+5*sizeof(type_de_T) car quand on se déplace sur un adresse, c'est une addresse donc peut importe vers quoi ca pointe c'est la taille d'une case contenant une addresse (4 octets)
merci
Marsh Posté le 31-05-2007 à 08:22:45
Ben oui mais justement, pour un void * on ne sait pas sur quoi il pointe... C'est pour celà qu'on ne peut pas faire d'arithmétique de pointeur sur un void *. On ne peut pas non plus le déréfencer.
Par contre une façon correcte d'écrire parcours est (je n'ai pas le temps de tester, donc attention je peux faire une connerie) :
Code :
|
En castant data en char *, tu peux faire de l'arithmétique de pointer dessus. Et par définition sizeof (char *) == 1, donc tu te déplace bien de "size" octets à chaque fois.
Marsh Posté le 31-05-2007 à 09:52:58
Faux : sizeof (char *) == 1,
c'est sizeof char == 1, attention.
Marsh Posté le 31-05-2007 à 11:19:00
Trap D a écrit : Faux : sizeof (char *) == 1, |
ok, merci
donc, si je résume : pour créer une fonction générique on a besoin d'un prototype dans le genre de :
Code :
|
et si on veut faire des opérations sur les adresses de cet objet, il faut faire un cast en (char*) ? Par exemple si je veux faire une fonction addition générique, je ne peux pas faire:
Code :
|
car void n'est pas déréférencable. Est ce que je pourrai faire un truc du genre :
Code :
|
mais je sens que c'est crado . Je fais avec les pointeurs de fonctions ????
help plz
Marsh Posté le 31-05-2007 à 11:30:33
(void*)(*(int*)a + *(int*)b);
c'est beau ça, si tu as a->3 et b->5 ça retourne (void*)8 ...
Marsh Posté le 31-05-2007 à 11:30:56
fais des macros ...
Marsh Posté le 31-05-2007 à 11:35:03
Taz a écrit : (void*)(*(int*)a + *(int*)b); c'est beau ça, si tu as a->3 et b->5 ça retourne (void*)8 ... |
et si je fais ça, est-ce bien :
Code :
|
comment ça marche avec les macros ???
Marsh Posté le 31-05-2007 à 12:13:52
ReplyMarsh Posté le 31-05-2007 à 12:34:30
Taz a écrit : (void*)&add_integer pourquoi tu fais ça ... |
sionon j'ai le message :
warning: initialization from incompatible pointer type
Marsh Posté le 31-05-2007 à 12:38:20
ReplyMarsh Posté le 31-05-2007 à 12:43:17
Trap D a écrit : Faux : sizeof (char *) == 1, |
Oui bien sûr, je voulais dire "sizeof (char) == 1". D'ailleurs c'est pas non plus "sizeof char == 1" puisqu'avec sizeof un type doit être parenthèses
Marsh Posté le 31-05-2007 à 12:55:08
Citation : D'ailleurs c'est pas non plus "sizeof char == 1" puisqu'avec sizeof un type doit être parenthèses |
Ben non puisqu'en C sizeof est un opérateur pas une fonction
Marsh Posté le 31-05-2007 à 13:20:31
while aussi c'est operateur, pourtant il demande des parenthèses... Avec sizeof, un nom de variable ne nécessite pas de parenthèses. Un type nécessite des parenthèses. Bref c'est "sizeof a", et "sizeof (char)". "sizeof char" ne compile pas.
Marsh Posté le 31-05-2007 à 13:32:05
Taz a écrit : bah corrige les types au lieu de caster |
comme ca alors ?????????????????????
Code :
|
merci pour le feedback sur la conception, quel est le mieux ?
Marsh Posté le 01-06-2007 à 10:54:15
up ......
quelqu'un pourrait t-il me dire qu'elle est la meilleure solution parmi les bout de code avec les pointeurs de fonctions ? Je ne sais pas a quel niveau faire les casts ...
merci par avance
Marsh Posté le 01-06-2007 à 13:08:39
C'est quoi ce pointeur casté en int (c) ?
Le 3ème argument de add_gen_pf() est sensé être un pointeur de fonction ? Dans ce cas ta déclaration est mauvaise. Il faut :
Code :
|
Marsh Posté le 01-06-2007 à 14:39:20
matafan a écrit : C'est quoi ce pointeur casté en int (c) ?
|
mais je croyais que void c'est rien ? raaaaaa, je comprend plus rien
Marsh Posté le 01-06-2007 à 14:44:36
in_your_phion a écrit : mais je croyais que void c'est rien ? raaaaaa, je comprend plus rien |
Tu confonds le type "void" (appliqué uniquement aux fonctions et qui permet d'indiquer qu'une fonction ne renverra rien) et "void étoile" signifiant "pointeur universel". Les deux ne sont pas la même chose.
C'est un peu comme si tu confondais "double" (variable de 64 bits permettant de stocker une valeur en virgule flottante) et "double étoile" (variable de 16 ou 32 bits (ça dépend de l'architecture et on peut même en avoir 64) permettant de stocker l'adresse d'une variable de type "double" )
Donc un pointeur universel (de type "void étoile" ) est un pointeur pouvant stocker l'adresse de n'importe quelle variable ou fonction. Les concepteurs ont réutilisé le mot "void" car c'était plus pratique que d'en inventer un nouveau...
Marsh Posté le 01-06-2007 à 14:45:09
et ta méthode ne marche pas si sizeof(type) > sizeof(void*). Utilise un argument supplémentaire pour le stockage du résultat
Marsh Posté le 01-06-2007 à 14:47:47
Sve@r a écrit : Tu confonds le type "void" (appliqué uniquement aux fonctions et qui permet d'indiquer qu'une fonction ne renverra rien) et "void étoile" signifiant "pointeur universel". Les deux ne sont pas la même chose. |
d'accord, merci pour ta réponse .... Mais alors - car il y a toujours un mais - comment je pourrais faire avec ma fonction générique qui renvoie un type générale, un double un float ou un canard ? Est ce que ma premier solution est bonne alors ??
Marsh Posté le 01-06-2007 à 14:59:05
Au fait, fais gaffe : tout à l'heure j'ai supposé que ta fonction add_type ne renvoyait rien, Si elle doit renvoyer un void *, alors ça devient :
Code :
|
Marsh Posté le 01-06-2007 à 14:59:08
in_your_phion a écrit : d'accord, merci pour ta réponse .... Mais alors - car il y a toujours un mais - comment je pourrais faire avec ma fonction générique qui renvoie un type générale, un double un float ou un canard ? Est ce que ma premier solution est bonne alors ?? |
J'ai pas bien regardé mais une fonction ne peut renvoyer qu'un seul truc
Si ce truc est un truc simple (char, short, long , double, etc) alors ta fonction est de ce type.
Si ce truc est plus complexe (structure, tableau) alors ta fonction doit être de type "pointeur sur structure" ou "pointeur sur type du tableau" car sinon, ce serait trop lourd (t'imagines une structure de 300ko intégralement recopiée lors du return ? => vaut mieux copier une adresse de 4 octets que 300 => plus rapide).
Bien évidemment, il ne faut pas que ta fonction renvoie un pointeur sur une zone déclarée en "auto" car la zone disparait avec la fin de fonction et tu récupères un pointeur sur que dalle. Donc dans ce cas là, ta fonction n'a que 3 options
Dans 99% des cas, le truc à renvoyer est connu à l'avance donc la fonction renvoie un pointeur de type "truc *". Mais il peut arriver que ta fonction doive renvoyer un pointeur sur un truc qui n'est pas connu à l'avance. dans ce cas, elle renvoie un pointeur universel "void *".
Marsh Posté le 04-06-2007 à 16:27:40
Sve@r a écrit : J'ai pas bien regardé mais une fonction ne peut renvoyer qu'un seul truc
|
Ok, merci beaucoup pour ta réponse. Dans la dernière phrase, tu dis que si on ne sait pas ce que renvoi la fonction, alors on lui fait renvoyer un type "void *". C'est la que j'ai un léger problème, car si c'est une valeur que l'on renvoie ? comme dans mon exemple ou la fonction peut renvoyer un int, un double ou un float ..... Dans ce cas comment fait on ?
merci encore
Marsh Posté le 04-06-2007 à 17:19:24
in_your_phion a écrit : Ok, merci beaucoup pour ta réponse. Dans la dernière phrase, tu dis que si on ne sait pas ce que renvoi la fonction, alors on lui fait renvoyer un type "void *". C'est la que j'ai un léger problème, car si c'est une valeur que l'on renvoie ? comme dans mon exemple ou la fonction peut renvoyer un int, un double ou un float ..... Dans ce cas comment fait on ? |
Impossible. Une fonction ne peut renvoyer qu'un truc et si ce truc est simple, il ne peut pas être polymorphe.
La seule façon de ruser consiste à travailler avec une union. Une union est comme une structure sauf que tous les champs occupent la même place mémoire donc à un instant donné, un seul champ est utilisable.
Donc tu utilises ton union comme une structure (voir mon post précédent). Ensuite, ta fonction, selon le cas, var remplir tel ou tel champ de l'union que l'appelant récupère ensuite...
Marsh Posté le 04-06-2007 à 18:12:42
Sve@r a écrit : Impossible. Une fonction ne peut renvoyer qu'un truc et si ce truc est simple, il ne peut pas être polymorphe. |
Merci !!
Pour répondre à ta question sur les unions, est-ce alors le moyen ? Par exemple avec une fonction qui fait une division générique :
Code :
|
merki bien par avance, je ne vois pas comment faire autrement
Marsh Posté le 04-06-2007 à 20:53:12
in_your_phion a écrit : Merci !!
|
Ben voilà. C'est un joli exercice de style sur les fonctions universelles. Reste le problème de la fonction "affiche_division". Dans ton test, tu lui fais diviser deux doubles donc elle appelle "division_double" et affiche le résultat avec "%f". Mais si elle devait diviser 2 int, il faudrait qu'elle appelle "division_int" et afficher le résultat avec "%d". Donc il te faut un paramètre en plus que tu passes depuis le main et qui indique si tu travailles avec des double ou des int...
[edit] Pas la peine de mettre "&" pour récupérer l'adresse d'une fonction => son nom est déjà son adresse
Marsh Posté le 04-06-2007 à 23:02:49
Sve@r a écrit : Ben voilà. C'est un joli exercice de style sur les fonctions universelles. Reste le problème de la fonction "affiche_division". Dans ton test, tu lui fais diviser deux doubles donc elle appelle "division_double" et affiche le résultat avec "%f". Mais si elle devait diviser 2 int, il faudrait qu'elle appelle "division_int" et afficher le résultat avec "%d". Donc il te faut un paramètre en plus que tu passes depuis le main et qui indique si tu travailles avec des double ou des int... |
ok, je crois que je vois. J'avais fait un exemple "un peu bato" pour me permettre de comprendre. Pour les trois cas que tu cites :
Citation :
* elle renvoie un pointeur sur une zone static (avec les dangers que cela suppose dans le cas des appels concurrents) |
je crois que je comprend avec les pointeurs sur une zone allouée :
Code :
|
Mais comment faire avec une zone static (cas numéro un) ? Y'a til une de ces solutions qui est mieux d'un point de vue théorique ou pratique ??
merci beaucoup encore
Marsh Posté le 05-06-2007 à 07:49:31
in_your_phion a écrit : ok, je crois que je vois. J'avais fait un exemple "un peu bato" pour me permettre de comprendre. Pour les trois cas que tu cites :
|
Ok pour tes malloc. Mais faut aussi les libérer ensuite dans "affiche_division" => free(RES) (évite les noms en majuscules, on pourrait les confondre avec des macros)
Avec une zone static c'est comme ça
Code :
|
Idem pour "division_int" sinon le reste ne change pas sauf que t'as absolument pas besoin de cette variable "f" dans ton main => affiche_division( &d_un, &d_deux, division_double );
Maintenant, d'un point de vue pratique, avoir une zone statique c'est très dangereux. J'ai voulu une fois découper une ligne construite ainsi
info1 info2 x:y:z info3
J'ai donc initialisé une boucle à coup de strtok pour découper chaque info sur la tabulation. Puis j'ai initialisé une seconde boucle à coup de strtok pour découper le x:y:z sur le ":" et c'est complètement parti en torche. A la sortie de la 2° boucle, le pointeur interne de strtok (il n'y en a qu'un seul) était totalement à l'ouest et ne m'a jamais trouvé "info3".
D'ailleurs, si tu veux avoir un exemple concret du problème du static, utilise ma fonction donnée en exemple et essaye ceci :
Code :
|
Mais d'abord, essaye d'imaginer quel sera ton résultat avant de tester et de regarder la réalité...
Marsh Posté le 28-05-2007 à 16:41:13
Bonjour,
J'aimerai savoir si quand on déclare une structure, quelle taille de mémoire ça prend ? Est ce que la taille allouée change si on réorganise les champs?
merci