Oui, c'est parce que j'ai un raccourci trop rapide, en effet.
Le principe demeure cela dit mais en fait, c'est déjà ce que tu fais à l'entrée. Tu décales à l'empilement et tu vas chercher la bonne position au dépilement. C'est effectivement l'un ou autre.
Oui, mais mon compilateur fait du padding avant le premier membre pour arranger mon explication !
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.
Quand tu fais ceci, tu récupères l'octet situé à la position « count », mais tu n'effaces pas le contenu de « data », et tu ne tiens pas compte du fait non plus que ta file peut être vide (sauf pour la gestion de « count »). Ça veut dire d'une part que si tu continues à dépiler une file vide, tu vas obtenir à chaque fois la dernière valeur dépilée qui peut être non nulle.
Puisque tu utilises déjà des opérateurs de décalage sur des registres du même format, pourquoi ne t'en sers-tu pas pour décaler directement le contenu de ta file ?
Mouais, c'est un débat récurrent. Il y a même un flag à passer à GCC si on veut qu'il nous signale ce genre de choses. Pour autant, je trouve que c'est encore une des nombreuses mesures parties d'un bon sentiment et qui se sont avérées plus pénibles qu'autre chose à l'usage. Il ne faut pas oublier que le propre d'une fonction, en mathématiques, est d'être une expression évaluable et dont la valeur dépend du paramètre. Qu'on utilise les arguments en entrée et en sortie et qu'on utilise le code de retour que comme état du bon déroulement du processus, c'est un paradigme intéressant, certes, mais il s'agit alors de simples procédures, et plus de fonctions à proprement parler.
Ça commence à devenir franchement lourdingue lorsque ce flag est imposé à la va-vite en entreprise et que ça t'empêche de faire un usage judicieux de tes structures. Le cas d'école que l'on brandit à chaque fois est celui des nombres complexes, bien sûr. Mais de façon similaire, et c'est intéressant parce que c'est un cas vécu, on pourrait parler des timestamps, par exemple.
J'ai travaillé sur un filtre qui loguait certains événements sous forme d'objets datés avec une « struct timeval » utilisée entre autres par gettimeofday() et qui embarque un nombre de secondes et un nombre de micro-secondes. Au bout d'un moment, pour pouvoir les comparer facilement et traiter les échéances, j'ai fini par écrire une panoplie de fonctions pour pouvoir additionner, soustraire et comparer des durées, ainsi que quelques autres opérations.
struct timeval timeadd (struct timeval, struct timeval); /* Additionne deux durées */
struct timeval timesub (struct timeval, struct timeval); /* Soustrait deux durées */
int timecmp (struct timeval, struct timeval); /* Compare deux durées */
ce qui me permettait d'écrire directement des trucs du style :
if (timecmp(timesub(datefin,datedebut),dureemaximum)>0) action_approriee();
Essaie de faire facilement la même chose avec des pointeurs. Tu te retrouves obligé de tout allouer toi-même et décomposer ton calcul. Ensuite, soit tu fais des malloc()/free() en gérant les erreurs éventuelles ce qui, même à l'exécution, est autrement plus long à traiter que huit octets passés dans la pile, soit tu déclares à l'avance des variables locales pour recevoir des résultats et là, dans un cas comme dans l'autre, les résultats se retrouveront dans la pile, avec la conséquence supplémentaire que tes variables auront une existence plus longue que les paramètres et la valeur de retour de chacune de ces fonctions.
Alors autant je suis le premier à dire qu'il faut s'efforcer autant possible d'être sobre avec la pile (on voit beaucoup de débutants allouer des tableaux d'entiers de 2 Gio) et ne pas l'encombrer pour rien, autant je pense que dans ce genre de cas, aujourd'hui, on peut se permettre de passer des objets jusqu'à une centaine d'octets quand leur taille est fixe, qu'il faut en produire beaucoup et qu'ils sont éphémères.
Ce qu'il faut surtout savoir, c'est que le C est un langage très proche du fonctionnement réel du micro-processeur et, quand on connaît celui-ci, les caractéristiques et les limitations du C deviennent naturelles.
Le micro-processeur est indépendant, en lui-même, de la machine dans laquelle il est installé et on peut ainsi retrouver le même CPU dans des ordinateurs très différents. Mais dans tous les cas, il ne communique avec l'environnement qu'avec un bus d'adresse, un bus de données et une ligne R/W (Read or Write). Les « bus » sont un ensemble de lignes électriques parallèles distribuées simultanément à tous les périphériques d'un ordinateur. Le bus d'adresse sert donc à coder un nombre binaire indiquant le numéro de l'octet en mémoire auquel on cherche à accéder, le bus de données à véhiculer la donnée, soit pour lire son contenu, soit pour écrire dedans et la ligne R/W à savoir justement laquelle des deux opérations on cherche à faire.
Ça veut dire que la seule chose que voit le micro-processeur, c'est une longue plage continue d'octets, eux-mêmes numérotés de 00000000 à FFFFFFFF sur les architectures 32 bits. Ce plan mémoire est principalement occupé par des plages de RAM, quelques plages de ROM, quelques zones vides éventuellement (il y a quelques années, on aurait dit « pratiquement toujours » mais aujourd'hui, les machines équipées de 4 Gio de RAM au moins deviennent courantes) et quelques mini-plages réservées aux ports d'entrées-sorties servant à piloter les différents périphériques et composants de la machine. Ça veut dire également que la seule chose, en fin de compte, que sait faire un micro-processeur, c'est lire des données à un certain endroit, appliquer une opération logique ou arithmétique dessus, et la ré-écrire à un autre. Il est donc possible, en théorie, de piloter un ordinateur entier uniquement avec l'équivalent de « PEEK » et « POKE » en Basic.
Donc, lorsque tu fais « x=1 » dans n'importe quel langage pour affecter la valeur « 1 » à la variable « x », en particulier avec un langage interprété, tu fais appel aux routines du logiciel que tu utilises qui, elles, vont aller déposer cette valeur quelque part en mémoire, dans une zone réputée décrire le contenu de la variable « x ». Le processus est généralement invisible au programmeur mais si tu te débrouilles pour savoir quel est l'adresse de cet emplacement, alors tu peux le modifier en écrivant dedans. Évidemment, c'est de la bidouille et tu fais ta manip' dans le dos du langage, mais c'est marrant de voir sa variable « muter » comme par magie.
En C, en revanche, c'est totalement normal : on manipule directement les adresses mémoire sans utiliser de système sous-jacent, ne serait-ce que parce que le langage C sert justement à écrire ces systèmes. En fait, c'est pour cela qu'il a été conçu : compiler des programmes en un code autonome et restant au plus proche de la machine tout en s'affranchissant de l'assembleur et en écrivant donc des programmes en principe portables partout. Il faut aussi souligner que le C est apparu en 1972 et qu'à cette époque, les ressources systèmes étaient autrement plus limitées qu'elles le sont aujourd'hui.
Maintenant, un pointeur est, comme son nom l'indique, quelque chose qui « pointe » une autre chose en mémoire, donc qui indique l'emplacement. Un pointeur est donc l'adresse mémoire de quelque chose et, par extension, la variable qui contient cette adresse. Un pointeur est donc une variable de type « adresse mémoire » et a généralement la largeur du bus d'adresse, donc quatre octets sur une machine 32 bits. En ce sens, un pointeur est donc en fait un entier non signé mais, sémantiquement parlant, il a été décidé d'en faire un type à part entière. D'abord pour informer le compilateur de ce dont il s'agit, ensuite parce que le format minimum des entiers imposé par la norme C ne correspond pas forcément avec celui du bus d'adresse de la machine cible, et enfin parce qu'il existe une arithmétique propre aux pointeurs, alors qu'en revanche, toutes les opérations applicables aux entiers numériques n'ont pas forcément de sens avec un pointeur, par exemple, le multiplier. Pas plus qu'additionner deux pointeurs d'ailleurs : ce n'est pas parce que toi, tu habites au № 5 et un de tes amis au № 8 qu'à vous deux, vous habitez au № 13. :-)
Si j'utilise « & », je peux connaître l'emplacement en mémoire de mes variables et du code de mes fonctions (plus précisément, leur point d'entrée). Par exemple, en écrivant ceci :
#include <stdio.h>
int main (void)
{
int x;
int y;
printf ("Emplacement de x : %p\n",&x);
printf ("Emplacement de y : %p\n",&y);
return 0;
}
… j'obtiens :
Emplacement de x : 0x7fff93cc99dc
Emplacement de y : 0x7fff93cc99d8
On voit donc clairement où mes variables sont matérialisées, et on voit également que « x » se trouve quatre octets après « y » parce mes entiers tiennent sur 32 bits eux aussi.
Par contre, le seul service que te garantit un pointeur est conserver une adresse mémoire, à laquelle est censé se trouver un objet de type connu. Mais il n'y a aucune garantie que cette adresse soit valide si tu ne l'as pas initialisé correctement. Par exemple, dans l'exemple précédent, si j'affecte à mon pointeur l'adresse de « y » et que je me débrouille pour le faire avancer d'exactement deux octets, je vais me retrouver « à cheval » entre deux variables.
#include <stdio.h>
int main (void)
{
int x = 0;
int y = 0;
unsigned int * ptr = NULL;
printf ("Avant : x=%d ; y=%d\n",x,y);
ptr = (unsigned int *)(((char *)&y)+2);
*ptr = 0xffffffff;
printf ("Après : x=%d ; y=%d\n",x,y);
printf ("%p %p %p\n",&x,&y,ptr);
return 0;
}
… ce qui donne :
Avant : x=0 ; y=0
Après : x=65535 ; y=-65536
0x7fff720800b4 0x7fff720800b0 0x7fff720800b2
On voit que les deux variables sont affectés. Le C me laisse le faire. Il me laisse même écrire n'importe où en mémoire si j'en ai envie, écrasant éventuellement les autres programmes ou le système d'exploitation, comme on pourrait le faire en assembleur. Heureusement, le mode protégé est désormais sur toutes les machines et c'est le micro-processeur lui-même qui refusera de continuer si on lui demande de lire ou d'écrire dans une zone non explicitement déclarée par ton O.S., déclenchant la célibrissime « segfault ».
Une fonction teste une expression et retourne un pointeur vers la fin de l'expression si elle est trouvée. J'aimerais qu'elle retourne en outre un pointeur vers le début de l'expression, soit deux pointeurs. Comment faire ça proprement?
Tu reçois donc un pointeur vers le début d'une chaîne de caractères (donc « char * ») et tu veux retourner deux pointeurs vers d'autres endroits de la même chaîne. Ces deux pointeurs de retour seront donc du même type que le paramètre. Évidemment, une fonction ne peux pas prendre deux valeurs différentes pour un même paramètre, donc il va falloir les encapsuler dans quelque chose d'autres. L'idée du tableau n'était donc pas mauvaise, mais ne sera pas la plus appropriée ici :
Tu ne peux pas retourner directement un tableau ni passer son contenu en paramètre parce qu'un tableau en C n'est pas à proprement parler un objet. C'est seulement l'instanciation de n variables du même type et consécutives en mémoire, ni plus ni moins. Il n'y a aucune méta-données associées qui te permette de savoir à l'exécution quelle est la taille du tableau, par exemple. Il y a donc seulement deux cas où un nom de tableau est traité comme tel : sizeof, qui te donne en octets la taille du tableau en mémoire… si elle est connue à la compilation, et l'opérateur unaire « & » qui t'en renvoie l'adresse : « tab » est alors équivalent à « &tab » et renvoie la même valeur. Dans tous les autres cas, le nom d'un tableau au sein d'une expression est développé en pointeur vers le premier élément, comme pour une chaîne de caractères (ou de n'importe quoi d'autre).
Ça veut dire que comme tu ne peux pas le transmettre directement en tant qu'objet, tu es obligé de l'allouer quelque part. Tu peux alors passer en argument un pointeur vers ce tableau pour qu'elle le remplisse. C'est ce qui se passe avec la fonction pipe(), par exemple.
Toutefois, le plus approprié ici reste la définition d'une structure : elle, est définie comme un nouveau type qui peut donner naissance à des variables. Elle contient un certain nombre de sous-variables. Ces membres sont en nombre fixe mais c'est bien le cas dans la situation qui t'occupe. Donc :
En dehors des usages triviaux, j'ai encore beaucoup de mal avec les pointeurs, aussi, la résolution de ce petit problème m'aiderait beaucoup.
Ben là, c'est un problème très particulier qui t'oblige en plus à suivre plusieurs lièvres à la fois. Donc clairement pas ce qu'il y a de plus didactique.
Pour commencer, et pour t'aider avec les pointeurs, as-tu une idée claire de ce qu'est une adresse mémoire ou pas ?
Le problème, c'est que malgré une république française qui se veut laïque, ce jour est férié. Que le pays ait des restes de culture chrétienne, ça semble logique. Mais je ne comprends pas qu'en 2012, on arrête toute l'économie d'un pays laïc pour une fête religieuse.
Autant que je sache, il y a beaucoup de musulmans, en France, qui fêtent Noël quand même. On supprime le 25 décembre et toute l'ambiance qui va autour également ?
Si mon PC est trop récent, c’est bien que Linux a quand même du retard… (Je suis bien conscient que ce n’est pas évident, avec les constructeurs qui ne s’occupent pas du tout de Linux pour la plupart, mais du coup c’est pas cool quoi…)
À dire vrai, Microsoft ne s'occupe pas non plus du matériel. Ce sont les constructeurs qui doivent mettre au point leurs pilotes pour un système cible sous peine de ne pas réussir à vendre leur matériel. On l'oublie un peu trop mais : quand est-ce que tu as installé un pilote sous Linux pour la dernière fois ? La plupart des choses sont immédiatement détectées et reconnues par le système lui-même, tant et si bien qu'on n'y prête même plus attention.
À titre de comparaison, quand les clés USB ont commencé à apparaître, Linux les a reconnues sans problème pratiquement dès le départ. C'était censé être normal car l'USB encore tout jeune a ce moment avait défini d'emblée des classes de périphérique et il suffisait donc de s'appuyer sur la norme. Malgré cela, toutes les boîtes étaient vendues accompagnées d'une disquette-pilote, ceci parce que la prise en charge de l'USB n'était pas encore native sur l'O.S. le plus répandu du marché.
À partir de cela, si ce ne sont pas les constructeurs eux-mêmes qui, soit mettent à disposition un pilote ou un module du noyau, soit fournissent à l'avance à la communauté les spécifications d'utilisation d'un matos à sortir (ce qui est très risqué dans un milieu aussi concurrent), alors on est obligé d'attendre que l'appareil sorte et que quelqu'un veuille bien faire un peu de reverse engineering dessus. Donc, oui, il y aura du retard et ce sera commun à tous les systèmes qui n'intéresse pas directement le fabricant.
Maintenant, 2 ans, c'est un peu long et ton PC a le temps de vieillir, mais si c'est un seul développeur qui fait cela bénévolement, alors il se peut que ce soit tout simplement le temps minimum nécessaire. Par exemple, j'ai utilisé pendant longtemps un portable Toshiba Satellite 2410 qui était très bien pour son temps, mais qui avait le gros défaut d'afficher une barre noire de deux centimètres d'épaisseur sur le côté droit. Après avoir farfouillé un peu, je me suis rendu compte que c'était l'EDID qui contenait une valeur pourrie concernant le nombre de colonnes du panneau LCD. J'ai donc fini par télécharger mon EDID, le corriger à la main et demander à X de suivre ce fichier plutôt que ce qu'il recevait de l'écran.
Bien sûr, c'est inadmissible pour le grand public mais :
— L'EDID était bien pourri et patché via les pilotes sur les systèmes pris en charge par le constructeur ;
— Quel autre système m'aurait permis d'aller chercher mon EDID en passant deux ou trois options ?
— Quel autre système m'aurait offert la possibilité d'utiliser éventuellement l'EDID de mon choix ?
Je crois que c'est la plus grande force des Unixoïdes et surtout du logiciel libre : si ça casse, tu as les moyens de réparer et de repartir.
— man humour ;
— Tout le monde ici sait que Linux est une marque déposée par Linus Torvalds ;
— Une marque se dépose dans un certain secteur d'activité et peut donc avoir un homonyme dans un autre secteur. Une marque se dépose également dans un pays donné et peut déjà exister dans un autre ;
— De fait, Linux est effectivement une marque de lessive, qui est également réputée sur ce site.
Le gros point négatif, c'est qu'en matière d'accident industriel, il en reste encore un bien plus énorme, Microsoft, qui je pense nous pourrira la vie en entreprise jusqu'à notre retraite.
Le terme le plus exact est « développement des paramètres », comme on développe le contenu des parenthèses dans une équation. Seulement, quand on ne sait pas déjà de quoi il s'agit, le terme peut laisser perplexe quand même parce que le mot « développement » est déjà utilisé sous un grand nombre de formes différentes.
Paradoxalement, s'il peut rester des services minitels actifs quelque part, ce sera bien par RTC. Ayant pas mal joué avec cet appareil et traîné sur différents serveurs avant la démocratisation du Net, il me reste un certain nombre de ressources, mais tout est sur disquettes :-) Il va me falloir un peu de temps pour tout recompiler sur un DVD.
[^] # Re: Constante invalide
Posté par Obsidian . En réponse au journal Parlons C, parlons pipe !. Évalué à 2.
Oui, c'est parce que j'ai un raccourci trop rapide, en effet.
Le principe demeure cela dit mais en fait, c'est déjà ce que tu fais à l'entrée. Tu décales à l'empilement et tu vas chercher la bonne position au dépilement. C'est effectivement l'un ou autre.
[^] # Re: ça marche !
Posté par Obsidian . En réponse au journal Et Dieu inventa le soutien gorge !. É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: compteur ?
Posté par Obsidian . En réponse au journal Parlons C, parlons pipe !. Évalué à 4.
Si.
# Constante invalide
Posté par Obsidian . En réponse au journal Parlons C, parlons pipe !. Évalué à 10.
Moi, je lis « Enormous Boobs ». C'est sexiste ! Je te demande de changer de constante.
Quand tu fais ceci, tu récupères l'octet situé à la position « count », mais tu n'effaces pas le contenu de « data », et tu ne tiens pas compte du fait non plus que ta file peut être vide (sauf pour la gestion de « count »). Ça veut dire d'une part que si tu continues à dépiler une file vide, tu vas obtenir à chaque fois la dernière valeur dépilée qui peut être non nulle.
Puisque tu utilises déjà des opérateurs de décalage sur des registres du même format, pourquoi ne t'en sers-tu pas pour décaler directement le contenu de ta file ?
[^] # Re: Structure
Posté par Obsidian . En réponse au message retourner un pointeur vers un tableau de pointeurs. Évalué à 4.
Mouais, c'est un débat récurrent. Il y a même un flag à passer à GCC si on veut qu'il nous signale ce genre de choses. Pour autant, je trouve que c'est encore une des nombreuses mesures parties d'un bon sentiment et qui se sont avérées plus pénibles qu'autre chose à l'usage. Il ne faut pas oublier que le propre d'une fonction, en mathématiques, est d'être une expression évaluable et dont la valeur dépend du paramètre. Qu'on utilise les arguments en entrée et en sortie et qu'on utilise le code de retour que comme état du bon déroulement du processus, c'est un paradigme intéressant, certes, mais il s'agit alors de simples procédures, et plus de fonctions à proprement parler.
Ça commence à devenir franchement lourdingue lorsque ce flag est imposé à la va-vite en entreprise et que ça t'empêche de faire un usage judicieux de tes structures. Le cas d'école que l'on brandit à chaque fois est celui des nombres complexes, bien sûr. Mais de façon similaire, et c'est intéressant parce que c'est un cas vécu, on pourrait parler des timestamps, par exemple.
J'ai travaillé sur un filtre qui loguait certains événements sous forme d'objets datés avec une « struct timeval » utilisée entre autres par gettimeofday() et qui embarque un nombre de secondes et un nombre de micro-secondes. Au bout d'un moment, pour pouvoir les comparer facilement et traiter les échéances, j'ai fini par écrire une panoplie de fonctions pour pouvoir additionner, soustraire et comparer des durées, ainsi que quelques autres opérations.
ce qui me permettait d'écrire directement des trucs du style :
Essaie de faire facilement la même chose avec des pointeurs. Tu te retrouves obligé de tout allouer toi-même et décomposer ton calcul. Ensuite, soit tu fais des malloc()/free() en gérant les erreurs éventuelles ce qui, même à l'exécution, est autrement plus long à traiter que huit octets passés dans la pile, soit tu déclares à l'avance des variables locales pour recevoir des résultats et là, dans un cas comme dans l'autre, les résultats se retrouveront dans la pile, avec la conséquence supplémentaire que tes variables auront une existence plus longue que les paramètres et la valeur de retour de chacune de ces fonctions.
Alors autant je suis le premier à dire qu'il faut s'efforcer autant possible d'être sobre avec la pile (on voit beaucoup de débutants allouer des tableaux d'entiers de 2 Gio) et ne pas l'encombrer pour rien, autant je pense que dans ce genre de cas, aujourd'hui, on peut se permettre de passer des objets jusqu'à une centaine d'octets quand leur taille est fixe, qu'il faut en produire beaucoup et qu'ils sont éphémères.
[^] # Re: 0xB16B00B5
Posté par Obsidian . En réponse au journal Et Dieu inventa le soutien gorge !. Évalué à 4.
Je prends le risque :
[^] # Re: Structure
Posté par Obsidian . En réponse au message retourner un pointeur vers un tableau de pointeurs. Évalué à 10.
Ce qu'il faut surtout savoir, c'est que le C est un langage très proche du fonctionnement réel du micro-processeur et, quand on connaît celui-ci, les caractéristiques et les limitations du C deviennent naturelles.
Le micro-processeur est indépendant, en lui-même, de la machine dans laquelle il est installé et on peut ainsi retrouver le même CPU dans des ordinateurs très différents. Mais dans tous les cas, il ne communique avec l'environnement qu'avec un bus d'adresse, un bus de données et une ligne R/W (Read or Write). Les « bus » sont un ensemble de lignes électriques parallèles distribuées simultanément à tous les périphériques d'un ordinateur. Le bus d'adresse sert donc à coder un nombre binaire indiquant le numéro de l'octet en mémoire auquel on cherche à accéder, le bus de données à véhiculer la donnée, soit pour lire son contenu, soit pour écrire dedans et la ligne R/W à savoir justement laquelle des deux opérations on cherche à faire.
Ça veut dire que la seule chose que voit le micro-processeur, c'est une longue plage continue d'octets, eux-mêmes numérotés de 00000000 à FFFFFFFF sur les architectures 32 bits. Ce plan mémoire est principalement occupé par des plages de RAM, quelques plages de ROM, quelques zones vides éventuellement (il y a quelques années, on aurait dit « pratiquement toujours » mais aujourd'hui, les machines équipées de 4 Gio de RAM au moins deviennent courantes) et quelques mini-plages réservées aux ports d'entrées-sorties servant à piloter les différents périphériques et composants de la machine. Ça veut dire également que la seule chose, en fin de compte, que sait faire un micro-processeur, c'est lire des données à un certain endroit, appliquer une opération logique ou arithmétique dessus, et la ré-écrire à un autre. Il est donc possible, en théorie, de piloter un ordinateur entier uniquement avec l'équivalent de « PEEK » et « POKE » en Basic.
Donc, lorsque tu fais « x=1 » dans n'importe quel langage pour affecter la valeur « 1 » à la variable « x », en particulier avec un langage interprété, tu fais appel aux routines du logiciel que tu utilises qui, elles, vont aller déposer cette valeur quelque part en mémoire, dans une zone réputée décrire le contenu de la variable « x ». Le processus est généralement invisible au programmeur mais si tu te débrouilles pour savoir quel est l'adresse de cet emplacement, alors tu peux le modifier en écrivant dedans. Évidemment, c'est de la bidouille et tu fais ta manip' dans le dos du langage, mais c'est marrant de voir sa variable « muter » comme par magie.
En C, en revanche, c'est totalement normal : on manipule directement les adresses mémoire sans utiliser de système sous-jacent, ne serait-ce que parce que le langage C sert justement à écrire ces systèmes. En fait, c'est pour cela qu'il a été conçu : compiler des programmes en un code autonome et restant au plus proche de la machine tout en s'affranchissant de l'assembleur et en écrivant donc des programmes en principe portables partout. Il faut aussi souligner que le C est apparu en 1972 et qu'à cette époque, les ressources systèmes étaient autrement plus limitées qu'elles le sont aujourd'hui.
Maintenant, un pointeur est, comme son nom l'indique, quelque chose qui « pointe » une autre chose en mémoire, donc qui indique l'emplacement. Un pointeur est donc l'adresse mémoire de quelque chose et, par extension, la variable qui contient cette adresse. Un pointeur est donc une variable de type « adresse mémoire » et a généralement la largeur du bus d'adresse, donc quatre octets sur une machine 32 bits. En ce sens, un pointeur est donc en fait un entier non signé mais, sémantiquement parlant, il a été décidé d'en faire un type à part entière. D'abord pour informer le compilateur de ce dont il s'agit, ensuite parce que le format minimum des entiers imposé par la norme C ne correspond pas forcément avec celui du bus d'adresse de la machine cible, et enfin parce qu'il existe une arithmétique propre aux pointeurs, alors qu'en revanche, toutes les opérations applicables aux entiers numériques n'ont pas forcément de sens avec un pointeur, par exemple, le multiplier. Pas plus qu'additionner deux pointeurs d'ailleurs : ce n'est pas parce que toi, tu habites au № 5 et un de tes amis au № 8 qu'à vous deux, vous habitez au № 13. :-)
Si j'utilise « & », je peux connaître l'emplacement en mémoire de mes variables et du code de mes fonctions (plus précisément, leur point d'entrée). Par exemple, en écrivant ceci :
… j'obtiens :
On voit donc clairement où mes variables sont matérialisées, et on voit également que « x » se trouve quatre octets après « y » parce mes entiers tiennent sur 32 bits eux aussi.
Par contre, le seul service que te garantit un pointeur est conserver une adresse mémoire, à laquelle est censé se trouver un objet de type connu. Mais il n'y a aucune garantie que cette adresse soit valide si tu ne l'as pas initialisé correctement. Par exemple, dans l'exemple précédent, si j'affecte à mon pointeur l'adresse de « y » et que je me débrouille pour le faire avancer d'exactement deux octets, je vais me retrouver « à cheval » entre deux variables.
… ce qui donne :
On voit que les deux variables sont affectés. Le C me laisse le faire. Il me laisse même écrire n'importe où en mémoire si j'en ai envie, écrasant éventuellement les autres programmes ou le système d'exploitation, comme on pourrait le faire en assembleur. Heureusement, le mode protégé est désormais sur toutes les machines et c'est le micro-processeur lui-même qui refusera de continuer si on lui demande de lire ou d'écrire dans une zone non explicitement déclarée par ton O.S., déclenchant la célibrissime « segfault ».
[^] # Re: Merci!
Posté par Obsidian . En réponse au message retourner un pointeur vers un tableau de pointeurs. Évalué à 4.
C'est souvent le cas ! :-) Un problème bien compris et/ou bien exposé est à moitié résolu.
Bonne chance pour la suite.
# Structure
Posté par Obsidian . En réponse au message retourner un pointeur vers un tableau de pointeurs. Évalué à 6.
Tu reçois donc un pointeur vers le début d'une chaîne de caractères (donc « char * ») et tu veux retourner deux pointeurs vers d'autres endroits de la même chaîne. Ces deux pointeurs de retour seront donc du même type que le paramètre. Évidemment, une fonction ne peux pas prendre deux valeurs différentes pour un même paramètre, donc il va falloir les encapsuler dans quelque chose d'autres. L'idée du tableau n'était donc pas mauvaise, mais ne sera pas la plus appropriée ici :
Tu ne peux pas retourner directement un tableau ni passer son contenu en paramètre parce qu'un tableau en C n'est pas à proprement parler un objet. C'est seulement l'instanciation de n variables du même type et consécutives en mémoire, ni plus ni moins. Il n'y a aucune méta-données associées qui te permette de savoir à l'exécution quelle est la taille du tableau, par exemple. Il y a donc seulement deux cas où un nom de tableau est traité comme tel : sizeof, qui te donne en octets la taille du tableau en mémoire… si elle est connue à la compilation, et l'opérateur unaire « & » qui t'en renvoie l'adresse : « tab » est alors équivalent à « &tab » et renvoie la même valeur. Dans tous les autres cas, le nom d'un tableau au sein d'une expression est développé en pointeur vers le premier élément, comme pour une chaîne de caractères (ou de n'importe quoi d'autre).
Ça veut dire que comme tu ne peux pas le transmettre directement en tant qu'objet, tu es obligé de l'allouer quelque part. Tu peux alors passer en argument un pointeur vers ce tableau pour qu'elle le remplisse. C'est ce qui se passe avec la fonction pipe(), par exemple.
Toutefois, le plus approprié ici reste la définition d'une structure : elle, est définie comme un nouveau type qui peut donner naissance à des variables. Elle contient un certain nombre de sous-variables. Ces membres sont en nombre fixe mais c'est bien le cas dans la situation qui t'occupe. Donc :
Ben là, c'est un problème très particulier qui t'oblige en plus à suivre plusieurs lièvres à la fois. Donc clairement pas ce qu'il y a de plus didactique.
Pour commencer, et pour t'aider avec les pointeurs, as-tu une idée claire de ce qu'est une adresse mémoire ou pas ?
[^] # Re: Oubli
Posté par Obsidian . En réponse au journal Webcrise: ébauche d'architecture. Évalué à 8.
Je préfère celle-ci…
[^] # Re: À propos de la pertinence des accents
Posté par Obsidian . En réponse au journal Conseils aux libristes, 1ere partie: eviter de sous-estimer la competition sur le plan technique. Évalué à 6.
Apparemment non, si l'on en croit les témoignages de ceux qui y ont un jour mis un pied ! :-)
[^] # Re: Moi j'aime bien.
Posté par Obsidian . En réponse au journal Mort aux fêtes religieuses fériées !. Évalué à 2.
Ouais. Autrement dit : « Ah ouais, mais non, c'est pas pareil ! ».
Moi toutes ces fêtes me vont très bien, et ce n'est pas pour autant que je me déplace à l'église pour les célébrer.
# Moi j'aime bien.
Posté par Obsidian . En réponse au journal Mort aux fêtes religieuses fériées !. Évalué à 2.
Autant que je sache, il y a beaucoup de musulmans, en France, qui fêtent Noël quand même. On supprime le 25 décembre et toute l'ambiance qui va autour également ?
[^] # Re: coup de gueule
Posté par Obsidian . En réponse au message Que de déboires sous Linux…. Évalué à 5.
À dire vrai, Microsoft ne s'occupe pas non plus du matériel. Ce sont les constructeurs qui doivent mettre au point leurs pilotes pour un système cible sous peine de ne pas réussir à vendre leur matériel. On l'oublie un peu trop mais : quand est-ce que tu as installé un pilote sous Linux pour la dernière fois ? La plupart des choses sont immédiatement détectées et reconnues par le système lui-même, tant et si bien qu'on n'y prête même plus attention.
À titre de comparaison, quand les clés USB ont commencé à apparaître, Linux les a reconnues sans problème pratiquement dès le départ. C'était censé être normal car l'USB encore tout jeune a ce moment avait défini d'emblée des classes de périphérique et il suffisait donc de s'appuyer sur la norme. Malgré cela, toutes les boîtes étaient vendues accompagnées d'une disquette-pilote, ceci parce que la prise en charge de l'USB n'était pas encore native sur l'O.S. le plus répandu du marché.
À partir de cela, si ce ne sont pas les constructeurs eux-mêmes qui, soit mettent à disposition un pilote ou un module du noyau, soit fournissent à l'avance à la communauté les spécifications d'utilisation d'un matos à sortir (ce qui est très risqué dans un milieu aussi concurrent), alors on est obligé d'attendre que l'appareil sorte et que quelqu'un veuille bien faire un peu de reverse engineering dessus. Donc, oui, il y aura du retard et ce sera commun à tous les systèmes qui n'intéresse pas directement le fabricant.
Maintenant, 2 ans, c'est un peu long et ton PC a le temps de vieillir, mais si c'est un seul développeur qui fait cela bénévolement, alors il se peut que ce soit tout simplement le temps minimum nécessaire. Par exemple, j'ai utilisé pendant longtemps un portable Toshiba Satellite 2410 qui était très bien pour son temps, mais qui avait le gros défaut d'afficher une barre noire de deux centimètres d'épaisseur sur le côté droit. Après avoir farfouillé un peu, je me suis rendu compte que c'était l'EDID qui contenait une valeur pourrie concernant le nombre de colonnes du panneau LCD. J'ai donc fini par télécharger mon EDID, le corriger à la main et demander à X de suivre ce fichier plutôt que ce qu'il recevait de l'écran.
Bien sûr, c'est inadmissible pour le grand public mais :
— L'EDID était bien pourri et patché via les pilotes sur les systèmes pris en charge par le constructeur ;
— Quel autre système m'aurait permis d'aller chercher mon EDID en passant deux ou trois options ?
— Quel autre système m'aurait offert la possibilité d'utiliser éventuellement l'EDID de mon choix ?
Je crois que c'est la plus grande force des Unixoïdes et surtout du logiciel libre : si ça casse, tu as les moyens de réparer et de repartir.
[^] # Re: Quid des clients SCO Unix Server ?
Posté par Obsidian . En réponse à la dépêche SCO : Game Over. Évalué à 2.
Non, eux, ils sont déjà sous le soleil de la Réunion.
→[]
[^] # Re: YOUPI
Posté par Obsidian . En réponse au journal 5-sigma: le boson de Higgs est débusqué !. Évalué à 1.
C'est pour cela qu'on a eu tant de mal : en fait, le boson de Higgs se cachait en Afghanistan ! :-)
# Nimage
Posté par Obsidian . En réponse à la dépêche SCO : Game Over. Évalué à 10.
C'est le bon moment pour ressortir ça :
[^] # Re: Pas Linux, mais Ubuntu
Posté par Obsidian . En réponse au journal Linux, ce méconnu. Évalué à 3.
Bon, alors, en quatre points :
— man humour ;
— Tout le monde ici sait que Linux est une marque déposée par Linus Torvalds ;
— Une marque se dépose dans un certain secteur d'activité et peut donc avoir un homonyme dans un autre secteur. Une marque se dépose également dans un pays donné et peut déjà exister dans un autre ;
— De fait, Linux est effectivement une marque de lessive, qui est également réputée sur ce site.
[^] # Re: Souvenir souvenir ...
Posté par Obsidian . En réponse à la dépêche SCO : Game Over. Évalué à 4.
« Et au delà » ! Fort probablement. :-)
[^] # Re: Pas Linux, mais Ubuntu
Posté par Obsidian . En réponse au journal Linux, ce méconnu. Évalué à 1.
Et plus précisément une marque de lessive !
[^] # Re: Divers
Posté par Obsidian . En réponse au message [Optimisation] Mieux vaut utiliser : des programmes ? Ou des fonctions ?. Évalué à 2.
Le terme le plus exact est « développement des paramètres », comme on développe le contenu des parenthèses dans une équation. Seulement, quand on ne sait pas déjà de quoi il s'agit, le terme peut laisser perplexe quand même parce que le mot « développement » est déjà utilisé sous un grand nombre de formes différentes.
[^] # Re: Mémoire?
Posté par Obsidian . En réponse à la dépêche Appel à contributions : Archives et contenus BBS et Minitel. Évalué à 5.
Cela dit, « les civilisations bien antérieures à la nôtre » remontent à un peu plus que quinze ans, tout de même.
# Ââh, les RTC…
Posté par Obsidian . En réponse à la dépêche Appel à contributions : Archives et contenus BBS et Minitel. Évalué à 5.
Paradoxalement, s'il peut rester des services minitels actifs quelque part, ce sera bien par RTC. Ayant pas mal joué avec cet appareil et traîné sur différents serveurs avant la démocratisation du Net, il me reste un certain nombre de ressources, mais tout est sur disquettes :-) Il va me falloir un peu de temps pour tout recompiler sur un DVD.
[^] # Re: Forum Astuces.divers— donnée chriffée
Posté par Obsidian . En réponse au message donnée chriffée. Évalué à 9.
Si tu en as et que tu ne sais pas quoi en faire, envoie-les moi, je tâcherai de me débrouiller avec ! :-)
[^] # Re: Jack et le temps réel
Posté par Obsidian . En réponse à la dépêche KLANG - Kernel Level Audio Next Generation. Évalué à 10.
Moi, je suis étonné que personne n'ait encore fait de jeu de mot avec JACK / KLANG …