Le C est connu pour ses pointeurs. Les pointeurs sont une merveille pour certains, une horreur pour d'autre. Je sais qu'il s'agit d'un nième débat religieux par ici, mais parlons de C et de pointeurs !
Un vrai moment de détente pour le week-end :D
Le noyau Linux utilise une forme particulière de listes chaînées qui nous permet d'apprécier ce genre de code include/linux/kernel.h:683:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
Et quand mon apprenti tombe sur ce genre de truc, je me dois de lui expliquer.
Pour ceux qui se posent la question, ce petit code permet de trouver l'adresse d'une structure à l'aide de l'adresse d'un membre de la structure.
Imaginons la structure suivante :
struct x {
int a;
int b;
};
struct x X;
Et admettons :
- X est à l'adresse
0x14C4
- X.a est à l'adresse
0x14C8
- X.b est à l'adresse
0x14CC
Donc, si ma structure X était à l'adresse 0x0000
, nous aurions :
- X.a à l'adresse
0x0004
- X.b à l'adresse
0x0008
Donc si j'ai l'adresse de X.a, j'ai l'adresse de X, car :
&X == &(X.a) - ((struct x *)0)->a;
Tout ceci est bien connu et est disponible dans stddef.h
à l'aide de la macro offsetof
:
#include <stddef.h>
offsetof(struct x, a);
Tous ces éléments mis ensemble nous permettent d'obtenir des structures de données un peu différentes :
struct s_list {
struct s_list * next;
struct s_list * previous;
};
struct s_structure_x {
/* membres */
struct s_list list;
};
En lieu et place de :
struct s_structure_x {
/* membres */
struct s_structure_x * next;
struct s_structure_x * previous;
};
ou encore :
struct s_list {
void * data;
size_t length;
int type;
struct s_list * next;
struct s_list * previous;
};
et variantes.
Voilà, je suis pas certain d'être clair, mais je fais vite car ce n'est pas le sujet. En expliquant ce sujet, une idée m'est venue (et ça c'est tout l'intérêt de former des jeunes) pour mon projet actuel. Dans les grandes lignes, le projet est de faire communiquer des appareils entre eux.
Afin de faire ça proprement, j'ai, bien entendu, défini quelques couches, 3 en l'occurrence et chacune encapsule l'autre. Un couche réseau, une couche application et une couche donnée.
Dans la réflexion, le fait de travailler sur des micro-contrôleurs n'ayant que 512 octets de mémoires RAM et que la quantité de données transmises peut atteindre 150 octets, plus quelques 10 octets pour les couches, un encodage des données qui ajoutent une 20aine d'octets (1 bit perdu par octet transmis) et quelques variables d'état, l'utilisation de mémoire tampon pour le réseau est à proscrire ; on arrive presque à la moitié de la RAM juste pour les communications (dont la majeure partie n'est que ce qui se trouve déjà en mémoire mais sous une autre forme).
Le code réseau est donc prévu pour travailler en flux. Les données sont transformées et transmises à mesure. Quelques variables d'états plus tard, le réseau ne coûte qu'une ou deux dizaines d'octets en mémoire.
Le problème vient de l'enchaînement des couches dans le code, par exemple :
void hw_send( /* ... */ ) {
/* ... */
l1_send();
/* ... */
}
void l1_send( /* ... */ ) {
/* ... */
l2_send();
/* ... */
}
/* ... */
Ça marche, mais si je veux, par exemple, ajouter un traitement (chiffrement ?) entre la couche 1 et 2, le code doit être modifié de manière dramatique. Ou si je veux réutiliser la couche 2 dans une couche 1bis. Non ce qu'il me faut, c'est une sorte de liste chaînée qui traversent les couches et qui me permettent, le cas échéant, d'intercaler un traitement particulier.
Et du noyau vint la solution qui me semble, pour l'instant, la plus intéressante (on verra si ça tient jusqu'à lundi (dans mon esprit (dégénéré))). J'ai fais un petit test et ça donne ça :
#include <stdio.h>
#include <string.h>
#include <stddef.h>
/* Structure pour mes fonctions de communications
*
* send et receive reçoivent en premier paramètre une variable
* (ComFunctions *). Le deuxième paramètre de send est une variable d'état,
* quand elle est à 0xFF, il n'y a plus rien à envoyer. Pour receive, il
* s'agit de l'octet reçu par le réseau.
* send retourne l'octet à transmettre et receive l'état qui, une fois à
* 0xFF indique qu'il n'y a plus rien à recevoir.
* La variable lower contient l'adresse de la couche en-dessous de l'actuelle.
*/
typedef struct s_com_funcs ComFunctions;
struct s_com_funcs {
ComFunctions * lower;
unsigned char (*send)(void *, unsigned char *);
unsigned char (*receive)(void *, unsigned char);
};
/* Mes structures avec les valeurs nécessaires pour chaque couche ainsi
* qu'une instance de ComFunctions.
*/
struct l1 {
unsigned char val1;
unsigned char val2;
unsigned char val3;
unsigned char state;
ComFunctions f;
};
struct l2 {
unsigned char val1;
unsigned char val2;
unsigned char state;
ComFunctions f;
};
/* La fonction send. En paramètre, nous avons la couche la plus haute,
* retourne 0 quand la transmission est terminée (pour permettre de faire
* while(send(...));)
*/
char send(ComFunctions * highest)
{
unsigned char res=0x00;
printf("0x%02X ", highest->send(highest, &res));
if(res==0xFF) {
printf("\n");
return 0;
}
return 1;
}
/* Des init bidons, juste pour le test */
void init_l2(struct l2 * me) { me->val1='Z'; me->val2='F'; me->state=0x00; }
void init_l1(struct l1 * me) { me->val1='a'; me->val2='b'; me->val3='c';
me->state=0x00; }
/* Des fonctions send, juste un aperçu, mais le code est trivial */
unsigned char send_l2(void * com_funcs, unsigned char * state)
{
struct l2 * me=(struct l2 *)((com_funcs)-offsetof(struct l2, f));
/* ... */
}
unsigned char send_l1(void * com_funcs, unsigned char * state)
{
struct l1 * me=(struct l1 *)((com_funcs)-offsetof(struct l1, f));
unsigned char lower_state=0x00, tmp=0x00;
switch(me->state) {
/* ... plein de code ... */
case 2:
tmp='!';
if(me->f.lower==NULL) {
me->state++;
} else {
tmp=me->f.lower->send(me->f.lower, &lower_state);
if(lower_state==0xFF) me->state++;
}
*state=me->state;
return tmp;
/* ... encore plein de code ... */
}
/* Une fonction main */
int main(int argc, char ** argv)
{
/* Que deux couches, trop flemmard pour la troisième ^^ */
struct l1 x;
struct l2 y;
init_l1(&x);
init_l2(&y);
x.f.lower=&(y.f);
x.f.send=send_l1;
y.f.lower=NULL;
y.f.send=send_l2;
while(send(&(x.f)));
return 0;
}
Donc le fonctionnement est très simple, chaque couche peut appeler la couche sous-jacente et traiter son résultat avant de l'intégrer dans sa propre sortie. Ça permet d'intégrer des filtres entre les couches (j'ai testé avec un chiffrement fort (xor :-p), c'est démentiel).
Rien de bien révolutionnaire (déjà je travail pas chez Apple, c'est donc mal parti) et certainement déjà utilisé dans bien des projets, mais je cherchais juste une excuse pour faire un journal sur les pointeurs C (et les pointeurs de fonctions). Et ça fait jamais de mal de revoir un peu de C :)
Ah ! et je voulais aussi que ça ce sache : j'aime quand ça pointe !
# Moi aussi!
Posté par Atem18 (site web personnel) . Évalué à 1.
Je préfère le Python au C, mais moi aussi, j'aime quand ça pointe!
[^] # Re: Moi aussi!
Posté par Octabrain . Évalué à 7.
http://sam.linuxfr.org/pointeur
[^] # Re: Moi aussi!
Posté par Etienne Bagnoud (site web personnel) . Évalué à 10.
Quand tu auras fais tourner Python avec 512 octets de RAM, 1Ko d'EEPROM et 8Ko de flash pour le code, je ferais mon code en Python.
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: Moi aussi!
Posté par Atem18 (site web personnel) . Évalué à 0.
Pas faux, mais tout dépend les besoins après. C'est sûr que je doute que python soit bon en tant que langage de haut niveau, au vu de la tache à accomplir avec si peu de ressources. C'est pour faire quoi en fait?
[^] # Re: Moi aussi!
Posté par Marotte ⛧ . Évalué à 2.
Il programme un micro-contrôleur, faut lire TOUS les commentaires ;)
Tu voulais dire bas niveau plutôt je suppose.
[^] # Re: Moi aussi!
Posté par Bayet Thierry . Évalué à 1. Dernière modification le 19 août 2012 à 17:36.
Non, non, il voulait bien dire ce qu'il a dit. --> sort
[^] # Re: Moi aussi!
Posté par Etienne Bagnoud (site web personnel) . Évalué à 6.
Faire des mesures, traiter ces mesures et les communiquer. Les valeurs sont sur 16 bits, il y'a une dizaine de capteurs ayant un identifiant sur 64 bits. On ajoute les valeurs max et min, donc 14 octets par capteurs, multiplié par 10, 140 octets de données pouvant être transférés et stockés en RAM et/ou EEPROM.
Et c'est toujours un plaisir de coder sur des micro-contrôleurs, la différence entre un
short
etint
prend tout son sens et il faut réfléchir dix fois avant de déclarer une variable : "En ai-je vraiment besoin ?""It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: Moi aussi!
Posté par Nicolas Boulay (site web personnel) . Évalué à 1.
"réfléchir dix fois avant de déclarer une variable "
Euh ?! Ton compilateur ne fait pas le boulot ?
Pour compacter à mort du code, le plus efficace que j'ai trouvé est l'inlining et le mot clef "static" (suivi des simplifications du compilo) pour éviter les .o où la moitié du code n'est pas utilisé et une fonctionnalité de récupération de fin de fonction par le compilateur. Je ne me rappelle plus le nom, mais en gros, si 2 fonctions se finissent de façon identique, l'une d'elle contient un goto vers la fin de la suivante. Cela permet de faire des choses, que la factorisation de code ne permet pas.
"La première sécurité est la liberté"
[^] # Re: Moi aussi!
Posté par Etienne Bagnoud (site web personnel) . Évalué à 5.
Il ne va pas savoir si tu aurais pu utiliser un
char
oushort
au lieu d'unint
. Ensuite il y'a plein de trucs qui peuvent être utilisés :prend plus de place que :
Ou encore préférer l'utilisation de
switch-case
au lieu deif-elseif-else
.Ton compilateur fait beaucoup de travail, mais bien réfléchir l'aide.
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: Moi aussi!
Posté par Nicolas Boulay (site web personnel) . Évalué à 2.
Si tu parles de variable automatique, en déclarant un char, un short ou un int, le compilateur te collera un int (16 ou 32 bit selon la plateforme), en tout cas sur tous les compilos que j'ai vu.
Concernant le retournement de compteur de boucle, il me semble que gcc fait ce genre de transformation dans toutes les boucles car la comparaison à zéro est toujours plus facile.
Concernant le switch-case, j'aime beaucoup l'utiliser mais il utilise un tas de pointeur de fonction, ce qui peut être très lent sur les cpu "simples".
Enfin, le plus simple est toujours de regarder la sortie assembleur pour voir ce que sort le compilateur.
"La première sécurité est la liberté"
[^] # Re: Moi aussi!
Posté par Etienne Bagnoud (site web personnel) . Évalué à 2.
Ça dépend. Si ton architecture est 8 bits, ton
char
va rester unchar
. Unshort
a des risques d'être transformé enint
car pas de différence.Dans tous les cas, chaque fois que je me documente sur le choix des types, le conseil est "utilisez le plus petit type possible".
Pas GCC 3.3.5. J'avais fait un travail d'optimisation sur un programme, l'inversion manuel des boucles apportaient un joli gain (mais ça date). Dans tous les cas, ça ne coûte rien de le faire soit même.
La documentation du micro-contrôleur apporte ce genre d'info.
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: Moi aussi!
Posté par Nicolas Boulay (site web personnel) . Évalué à 2.
Dans tous les cas, chaque fois que je me documente sur le choix des types, le conseil est "utilisez le plus petit type possible".
Le compilo va prendre le plus rapide qui est en général la taille des registres.
Pas GCC 3.3.5. J'avais fait un travail d'optimisation sur un programme, l'inversion manuel des boucles apportaient un joli gain (mais ça date). Dans tous les cas, ça ne coûte rien de le faire soit même.
si des bugs, car c'est moins lisible. Parfois, le compilateur ne peut pas faire la transformation à cause de condition extérieur au fichier C ou à cause d'une variable d'induction.
La documentation du micro-contrôleur apporte ce genre d'info.
j'imagine. Mais dans le cas que j'avais en tête cela n'y était pas.
"La première sécurité est la liberté"
[^] # Re: Moi aussi!
Posté par Anthony Jaguenaud . Évalué à 2.
En fait, ça dépend.
Si tu fais un switch-case avec 0, 1, 2, 3… il va faire un tableau des adresses où sont les instructions suivante. Si tu as des valeurs trop différentes ou des grandes valeurs, il génère (le compilo) une dichotomie. Pour optimiser vraiment, il faut connaître la répartition des valeurs quand on rentre dans le switch.
Ainsi, si 8 est la valeur dans 90% des cas, il vaut mieux :
Sauf que là, l’auteur cherche à réduire la taille du code, enfin surtout de la RAM utilisé. Ce qui veut dire réduire la pile ce qui implique d’éviter trop de profondeur d’appel et de variables locales. L’optimisation en vitesse est généralement incompatible avec l’optimisation en taille.
[^] # Re: Moi aussi!
Posté par Nicolas Boulay (site web personnel) . Évalué à 2.
"L’optimisation en vitesse est généralement incompatible avec l’optimisation en taille."
Souvent mais pas toujours. L'inline est un bon exemple. En général, il est vu comme une augmentation de la taille du code. Dans un driver qui manipulait beaucoup de constantes, j'ai pu gagner beaucoup de place de code avec de l'inlining qui se simplifiait beaucoup (élimination de constante, etc…).
"La première sécurité est la liberté"
[^] # Re: Moi aussi!
Posté par Amine "nh2" Brikci-Nigassa (site web personnel) . Évalué à 2.
et moi qui me plaignait que 1 Ko c'était pas assez… J'ai un bolide avec mon extension 16K !
GNU's Not Unix / LINUX Is Not Unix Xernel
[^] # Re: Moi aussi!
Posté par Juke (site web personnel) . Évalué à 2.
avez vous testé ce genre de chose :
https://code.google.com/p/python-on-a-chip/
[^] # Re: Moi aussi!
Posté par Etienne Bagnoud (site web personnel) . Évalué à 2.
Trop gros :
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
# 0xB16B00B5
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 10.
En tout cas, si tu as besoin d'une constante arbitraire quelque part, Microsoft te montre la voie : 0xB16B00B5.
[^] # Re: 0xB16B00B5
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 2.
D'ailleurs ça manque d'une nimage pour illustrer tout ça, ton article…
[^] # Re: 0xB16B00B5
Posté par Etienne Bagnoud (site web personnel) . Évalué à 4.
Ça poserait des problèmes avec la modérations …
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: 0xB16B00B5
Posté par B16F4RV4RD1N . Évalué à 7.
non, sans doute pas avec la modération, plutôt avec les pisses-froids qui n'apprécient pas l'humour potache de linuxfr et qui combattent le sexisme sous toutes ses formes (attention, pas trop de formes quand même, ça donne une image biasée de la femme)
Only wimps use tape backup: real men just upload their important stuff on megaupload, and let the rest of the world ~~mirror~~ link to it
[^] # Re: 0xB16B00B5
Posté par Obsidian . Évalué à 4.
Je prends le risque :
# ça marche !
Posté par moi1392 . Évalué à 10.
Ça me parait un peu compliqué, mais ça marche !
Moi, je me serait contenté d'une liste chainée simple de pointeur de fonctions dans le genre :
et derrière, tes fonctions deviennent :
Tu peux facilement ajouter un truc au milieu de ta couche en ajoutant une fonction au milieu de liste layer.
Et sinon, juste pour le fun :
Et admettons :
chez moi, l'adresse de X.a est la même que X ;)
[^] # Re: ça marche !
Posté par Etienne Bagnoud (site web personnel) . Évalué à 6.
Oui, mais mon compilateur fait du padding avant le premier membre pour arranger mon explication !
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: ça marche !
Posté par Obsidian . Évalué à 2.
Si tu as trouvé un compilateur qui accepte de faire du copinage, c'est très bien. :-)
Mais autrement, d'une manière générale, la norme empêche les compilateurs de faire cela. Elle te garantit que le premier membre est bien aligné en début de structure et/ou d'union. Ça permet entre autres de faire des unions de structures commençant toutes par un champ « type », par exemple, qui permet de savoir tout de suite ce qu'il y a à l'intérieur, comme avec les XEvents de X-Window.
[^] # Re: ça marche !
Posté par Etienne Bagnoud (site web personnel) . Évalué à 3.
C'est une habitude que j'ai prise avec les apprentis : je prends toujours des valeurs, fonctionnement, …, absurdes afin qu'ils ne prennent pas l'habitude de penser en terme de valeurs. Mes "bytes" font souvent 13 bits, par exemple, car rien ne m'empêche d'avoir un système qui fonctionne ainsi. Et pour mon ordinateur, le seul compilateur existant, c'est un compilateur qui met du padding avant le premier membre.
J'essaie surtout de leur apprendre à penser de manière abstraite.
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: ça marche !
Posté par Obsidian . Évalué à 3.
C'est une très bonne chose en soi ! Mais il est important de le faire uniquement pour les cas non définis par la norme ou alors explicitement marqués comme indéfinis par elle. Et en l'occurrence, dans le dernier draft de C99 (n1256.pdf), section § 6.7.2.1, on lit :
Donc, tu es sûr que l'adresse du premier élément d'une structure sera toujours l'adresse de la structure elle-même avec tout compilateur qui se respecte (et qui, surtout, respecte la norme), ce qui permet d'échafauder de façon tout-à-fait officielle et sans warning des montages comme celui de XEvent.
Ça s'explique également par le fait que le padding est généralement utilisé à des fins d'alignement et que, si tu dois aligner un membre, alors tu auras besoin d'aligner de la même façon la structure entière d'une manière générale.
[^] # Re: ça marche !
Posté par Etienne Bagnoud (site web personnel) . Évalué à 5.
Sauf que chaque couche à sa propre structure avec ses propres données. Donc tu vas avoir :
Et ça devient moins clair. On un
void
dans la structure au lieu d'un joli type qui nous guide dans le code. Ensuite, avec un petit jeu de fonctions, la partie pour traverser les couches va devenir une petite bibliothèques (à grand coup de macros). En utilisant la méthode du noyau, l'utilisateur de la bibliothèque peut facilement placer où il veut, en mémoire, ses structures, la bibliothèque n'en n'est pas gênée. Et quand tu n'as pas de MMU, ça peut aider."It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: ça marche !
Posté par Didier (site web personnel) . Évalué à 2.
La solution évite le void * effectivement, mais elle a un autre inconvénient : il n'est pas évident à la lecture d'une partie du code (l'appel à la fonction send par exemple) que la fonction va utiliser les structures parentes du paramètre d'entrée de la fonction. Cela n'aide donc pas à la compréhension du code (je ne dis pas que le void * est particulièrement mieux non plus sur ce point…).
# Faire ça au runtime ? je préfère faire ça à la compilation.
Posté par Batchyx . Évalué à 10.
C'est bête de faire ça au runtime, alors que tu pourrai donner à ton compilateur toutes les informations nécessaire sur ta pile de couche pour qu'il te merge tout ça et te l'optimise au petit oignons, plutôt que de devoir parcourir une liste chaînée qui contient toujours les même valeurs.
Parce que, ce que tu nous décris ressemble fortement à ce que l'on fait en C++ avec du Policy-based_design :
[^] # Re: Faire ça au runtime ? je préfère faire ça à la compilation.
Posté par Etienne Bagnoud (site web personnel) . Évalué à 3.
Je suis d'accord. Bien que je ne connaisse pas assez le C++ pour bien comprendre ton code, au final tout ce qui peut être pré-calculé à la compilation, le sera.
Mais là le but, c'est plus l'approche. L'optimisation viendra ensuite. Mais d'abord un code qui marche bien pas optimisé, ensuite un code optimisé.
Sinon pour
> unsigned char send(unsigned char* lower_state_jcomprend_pas_trop) {
les fonctions doivent retourner plusieurs valeurs : la valeur à transmettre et la progression de l'encodage (ou de décodage, je ne fais que la moitié du travail dans l'exemple) des trames :
+----------+
| L1 |
| +------+ |
| | L2 | |
| | | |
| +------+ |
| |
+----------+
Je ne connais ni la taille de L1, ni celle de L2 avant la transmission, donc j'ai une valeur d'arrêt (0xFF). Toutes les autres valeurs peuvent être utilisé par les fonctions pour leur propre besoin. Dans un sens, je n'aurais pas besoin de remettre cette valeur dans la structure.
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: Faire ça au runtime ? je préfère faire ça à la compilation.
Posté par Damien Thébault . Évalué à 4.
Le C++ permet d'utiliser des templates, qui sont résolues à la compilation.
Pour de l'embarqué à ce niveau il faut faire un tout petit peut attention à la taille que rajoute le C++, en général il suffit de désactiver le RTTI et tout roule !
Les templates permettent de faire des horreurs, mais ça permet aussi de faire de très bonnes choses. Ça demande une conception assez différente d'une programmation classique et doit donc en général être imaginée dès le départ.
[^] # Re: Faire ça au runtime ? je préfère faire ça à la compilation.
Posté par syj . Évalué à 2.
Plutôt que de comparer, çela a un usage de classe avec template, j'aurai tout simplement comparé son code au pattern adapter.
C'est à dire chaque classe implémente une interface Layer.
En plus de l'adapter, elle sont soit chainé entre elle, soit elle connaisse l'objet parent qui les agrège dans une liste de Layer.
D'ailleurs dans ta structure, tu devrais songer au destructeur :).
Enfin de souvenir, GTK est un regorge d'exemple de structure type objet composé de pointeur fonction qui sont chainé entre elle pour copier les mécanismes de l'objet (héritage, surcharge, etc …)
[^] # Re: Faire ça au runtime ? je préfère faire ça à la compilation.
Posté par Etienne Bagnoud (site web personnel) . Évalué à 2.
Je suis sur un micro-contrôleur, pas d'allocation dynamique car pas de MMU. Pas besoin de destructeur :)
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: Faire ça au runtime ? je préfère faire ça à la compilation.
Posté par Batchyx . Évalué à 6.
Je ne compare pas son code avec du chained-policy, je dit juste que, vu qu'il est dans l'embarqué, faire des choses au runtime est bête lorsqu'on peut faire la même chose à la compilation, surtout si ça élimine du code. Le policy-based design en C++ est juste un exemple de comment faire ça avec un langage moderne.
L'équivalent en orienté objet ne serait pas vraiment Adapter (on adapte rien, c'est la même interface partout), mais plutôt une chaîne de pattern Strategy.
# Marre du hors-sujet.
Posté par ahuillet (site web personnel) . Évalué à -3.
Ce sexisme est intolérable. En outre, tu as oublié la nimage.
[^] # Re: Marre du hors-sujet.
Posté par Etienne Bagnoud (site web personnel) . Évalué à 10.
Bon … alors une image de pointe … … image de wikipedia.
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: Marre du hors-sujet.
Posté par windu.2b . Évalué à 6.
La Nimage-ki-va-bien…
[^] # Re: Marre du hors-sujet.
Posté par Donk . Évalué à 6.
C'est quoi cette image sexiste, où pendant que les hommes jouent aux boules, les femmes font la vaisselle.
;)
[^] # Re: Marre du hors-sujet.
Posté par Zenitram (site web personnel) . Évalué à 2.
Si il faut équilibrer
[^] # Re: Marre du hors-sujet.
Posté par rakoo (site web personnel) . Évalué à 6.
C'est quoi cette image sexiste, où pendant que les femmes jouent aux boules, les hommes travaillent dur et ramènent l'argent à la maison ?
# Java
Posté par Philippe M (site web personnel) . Évalué à 0.
Je dirais :
Il faut bien ça pour comprendre ;)
Born to Kill EndUser !
# Lapin compris
Posté par MCMic (site web personnel) . Évalué à 2.
Bon, mon C est un peu rouillé, je suis passé au C++ depuis quelques années, et j'ai récemment pas fait tant de C++ que ça non plus, en fait.
Mais je comprends pas du tout pourquoi il y a ce besoin d'accéder la structure parente d'une variable donnée, pourquoi ne pas directement donner la structure en paramètre?
Ces lignes ne me paraissent pas logiques:
J'aurai plutôt vu:
[^] # Re: Lapin compris
Posté par banks_the_megalith (site web personnel) . Évalué à 2.
Hmm, les types ne matchent pas dans ton assignation, t'as d'un côté un pointeur vers une structure ComFunctions et de l'autre un pointeur vers l2…
Après ta remarque rentre peut être un peu dans le cas qu'il décrit là : https://linuxfr.org/nodes/95222/comments/1379891, où avec une structure layer comme celle là
On pourrait écrire, si je dis pas de bêtises
[^] # Re: Lapin compris
Posté par MCMic (site web personnel) . Évalué à 5.
Oui voilà, j'ai oublié de supprimer le .f, disons que je comprends pas pourquoi le chainage se fait sur un membre de la structure et pas sur la structure, et surtout pourquoi on passe en paramètre un membre de la structure et pas la structure elle même.
# Économiser quelques octets
Posté par FreeB5D . Évalué à 1.
me semble possible en changeant, dans struct l1 et l2, le membre f (de type ComFunctions) en un pointeur vers des ComFunctions, vu que si j'ai bien compris il y aurait une unique copie des ComFunctions pour chaque niveau.
[^] # Re: Économiser quelques octets
Posté par Etienne Bagnoud (site web personnel) . Évalué à 3.
Non tu perds de la place :
On va dire que toutes les variables font Z octets et que nous n'avons pas de bourrage, cas sans pointeur (6Z) :
cas avec pointeur (7Z):
Pour chaque structure (l1, l2, …) tu dois avoir une structure ComFunctions differentes.
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
[^] # Re: Économiser quelques octets
Posté par FreeB5D . Évalué à 1.
Donc il y a une seule instance de structure pour chaque niveau… effectivement dans ce cas le pointeur est inutile.
# Y aurait pas une 'tite erreur?
Posté par FantastIX . Évalué à 2.
Ne serait-ce pas
au lieu de
?
[^] # Re: Y aurait pas une 'tite erreur?
Posté par Etienne Bagnoud (site web personnel) . Évalué à 3.
Pas vu, pas pris ! Oui, c'est une erreur de ma part.
"It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.