Go Down

Topic: Un pointeur sachant pointer qui ne pointe pas ! (Read 4509 times) previous topic - next topic

nulentout

Coucou les copains,
Je me "pointe" sur le forum car je me cogne contre un mur … c'est-à-dire que je commence à étudier les POINTEURS. Mon but "final" consiste à pouvoir explorer la mémoire des données pour "visualiser" la façon dont le C organise ses variables. Je sais parfaitement aller écrire et lister le contenu de l'EEPROM d'Arduino par utilisation de la bibliothèque EEPROM.h. Mais pas moyen de faire fonctionner mes POINTEURS pour me promener dans la RAM.
Voici mon intention :
1)   Déclarer une variable quelconque : Pour simplifier un int,
2)   Déclarer un pointeur sur cette variable,
3)   Faire venir l'adresse mémoire de cette variable dans le pointeur,
4)   Faire afficher sur la ligne série L'ADRESSE de ma variable, donc le contenu du POINTEUR.
Ayant lu et relu plein plein plein de tutoriels sur les POINTEURS, j'ai trouvé une foule d'explications claires et simples … pourtant je n'y arrive pas.
Voici mon programme :
Code: [Select]
/* petit programme pour expérimenter les pointeurs  */
// & donne la Référence : L'adresse.
// * Donne le contenu.

// variables du programme.
int *PTR= NULL; // Pointeur de variable de type int.
int X = 3333; // Valeur numérique parfaitement repérable.

void setup() { Serial.begin(115200);}
 
void loop() {
PTR = &X; // PTR reçoit l'adresse de X ... en principe !
Serial.print("Pointeur = >");
Serial.print(String(*PTR)); // Afficher le contenu de PTR ... Ben NON !
Serial.println('<');

  INFINI: goto INFINI;
   }

Je m'attendais à ce que l'affichage entre les « >» et « < »  soit une adresse listée en numérique. (J'ai placé les délimiteurs « >» et « < » pour vérifier si parfois l'affichage est vide, ce qui m'est déjà arrivé.)
Donc une valeur du genre >2314< en fonction des emplacements déjà réservés en RAM.
BEN NON !
C'est le contenu de X qui est affiché et non son ADRESSE ???
Pourriez-vous s'il vous plait :
•   M'expliquer pourquoi le contenu et non l'adresse,
•   Et surtout me donner le code pour faire afficher le contenu du POINTEUR.
J'ai testé plein de formules, mais chaque fois le compilateur se fâche tout orange.
S'il vous plait, du vrai code, c'est-à-dire quelques lignes qui vraiment passent à la compilation avec un affichage banal, sans formatage exigeant une quelconque bibliothèque.
Merci d'avance les copains et surtout … ne me pointez pas du doigt, car je fais vraiment des efforts pour ne pas rester sans « domicile ». (Domicile au sens d'adresse, vous l'avez compris  :smiley-mr-green: )

fdufnews

partant de cette définition
int *PTR= NULL; // Pointeur de variable de type int.


print(PTR)         <--- imprime l'adresse pointée par PTR
print(*PTR)       <--- imprime le contenu de la mémoire pointée par PTR
PTR étant un pointeur sur une variable de type int lorsque tu feras PTR++  ou PTR = PTR+1 il avancera de 2 (un int est stocké sur 2 octets)
Si tu veux te déplacer dans la RAM comme tu le veux, de 1 en 1, il faut définir un pointeur de type byte ou void

Viproz

Bon, un petit exemple vaut mieux qu'un long discours :
Code: [Select]
int var = 30; //La valeur de var est 30
int *ptr = &var; //ptr est un pointeur de var
int numb = *ptr; //numb prend la valeur de la mémoire pointée par ptr, c'est grave à l'étoile
int *autrePtr = ptr; //une simple copie de l'adresse du pointeur dans un autre pointeur


donc
Code: [Select]
Serial.print(String(*PTR)); // Afficher le contenu de PTR ... Ben NON !
Ici on affiche la valeur pointée par PTR et non l'adresse en mémoire, pour afficher l'adresse il faudrait écrire PTR ou bien &X
Par contre je ne pense pas que tu puisse afficher ça avec Serial.print(), essais plutot cela :
Code: [Select]
sprintf("%p", &X); //%p permet d'afficher l'adresse en hexa

N'oublie pas une chose, par convention en C++ le nom des variables s'écrit en commençant par une minuscule et le nom des classes par une majuscule, ce n'est pas grand chose mais c'est de bonnes habitudes à prendre pour la suite !

icare

Bonjour,
Un bon petit tuto sur les pointeurs : http://forum.arduino.cc/index.php?topic=101780.msg763529#msg763529
2B OR NOT(2B) = FF
Arduino 1.0.5 à 1.8.5 + gEdit + Ubuntu 18.04 LTS

nulentout

#4
Apr 29, 2014, 12:38 pm Last Edit: Apr 29, 2014, 12:52 pm by nulentout Reason: 1
« Ici on affiche la valeur pointée par PTR et non l'adresse en mémoire, pour afficher l'adresse il faudrait écrire PTR ou bien &X »
Exact, j'ai testé les deux formules mais le compilateur n'accepte pas.
essais plutot cela :
sprintf("%p", &X); //%p permet d'afficher l'adresse en hexa

ça aussi je l'ai essayé, mais là encore le compilateur n'accepte pas. Il faut certainement inclure la bibliothèque spécifique. J'en ai bien installé une, mais ça n'a rien changé. C'est la raison pour laquelle je souhaitais du code d'affichage « banal » sur la ligne série.
Peux-tu me donner un lien pour télécharger la bonne bibliothèque d'affichage série formatés s'il te plait ?

N'oublie pas une chose, par convention en C++ le nom des variables s'écrit en commençant par une minuscule et le nom des classes par une majuscule, ce n'est pas grand chose mais c'est de bonnes habitudes à prendre pour la suite !
Philosophiquement j'ai du mal à m'y faire, car depuis plus de 40 ans je considère que dans un programme, l'important ce sont les objets que l'on manipule. De ce fait je commence souvent leur identificateur par une majuscule. Pour le type, les classes etc, je « considère » que c'est plus banal, puisque ce ne sont « que des conventions » si je puis m'exprimer ainsi.
C'est même plus profond que ça comme maladie. Pour mois quand j'écris LED = ! LED, c'est une phrase, et j'ai vraiment du mal à commencer par une lettre minuscule. D'autres parts, LED n'est pas un mot, mais un sigle. A ce titre il doit être constitué de majuscules. Alors question habitudes, il n'est pas facile d'en changer, surtout si nous avons le sentiment que nos "tics" restent pleinement justifiés. Naturellement, il serait préférable de s'aligner sur les conventions admises par "tous", ainsi quand on rend public un programme personnel, il sera plus directement accessible par les copains ... mais statistiquement je passe des heures et des heures sur mes programmmes, alors que je ne poste que très rarement en proportion.
On ne change pas des conventions utilisées durant toute une vie aussi facilement que ça !  ;)

Oui icare, merci pour le lien. J'y suis déjà allé, mais ça ne m'a pas beaucoup aidé pour le moment.
En fait, je crois que mon problème vient du vait que serial.h n'accepte pas d'afficher la valeur numérique d'un pointeur. C'est curieux, ca en mémoire ce n'est pas autre chose qu'un entier codé sur deux octets vu que notre µP adresse la mémoire sur 16 bits.

XavierMiller

quel message d'erreur as-tu ? As-tu essayé en faisant un "cast" vers (unsigned) ?

fdufnews


essais plutot cela :
sprintf("%p", &X); //%p permet d'afficher l'adresse en hexa

ça aussi je l'ai essayé, mais là encore le compilateur n'accepte pas. Il faut certainement inclure la bibliothèque spécifique. J'en ai bien installé une, mais ça n'a rien changé. C'est la raison pour laquelle je souhaitais du code d'affichage « banal » sur la ligne série.
Peux-tu me donner un lien pour télécharger la bonne bibliothèque d'affichage série formatés s'il te plait ?

Pas de bibliothèque à inclure mais sprintf fait une "impression" dans une chaîne de caractères et donc il faut lui passer le pointeur vers la chaîne en question

Code: [Select]

char chaine[20] ;
int X;
int *PTR;

void setup(void){
  Serial.begin(9600);
  sprintf(chaine,"%p", &X);
  Serial.println(chaine);       // l'adresse
  Serial.println(X);                 // le contenu
  sprintf(chaine,"%p", PTR);
  Serial.println(chaine);       // l'adresse
  Serial.println(*PTR);          // le contenu
}

le %p demande l'affichage d'un pointeur

nulentout

Hé bé, heureusement qu'il y a quelques temps j'ai appris ce que c'était un "cast", du coup j'ai été en mesure de tenter la chose.
>>> Effectivement imposer 16 bits avec un « cast » dans l'affichage résout le problème.
En codant >>> Serial.print( (uint16_t) (PTR)); >>> le programme liste bien l'adresse.
Du reste, pour vérifier, j'ai déclaré plusieurs variables de taille différentes. Effectivement leurs adresses sont bien séparées du nombre d'octets les constituant.
MON PROBLÈME EST RÉSOLU et je vous en remercie vivement.
Il y a toujours cette difficulté sur printf.h, mais ce n'est pas urgent pour le moment. Je vais épuiser mes expérimentations sur les pointeurs avant de passer à autre chose. Quand j'aborde un sujet, je tente de l'étudier sous tous ses aspects avant de "changer de boutique".
Chic chic chic je vais me balader dans la mémoire des données. Vérifier ce qui est décrit dans les documents : Mise à zéro automatique de certains octets, délimiteurs de chaîne etc. Rien ne vaut une observation directe pour s'assurer que l'on a « tout compris ».  :smiley-eek-blue:
Est-il possible d'aller voir également les octets en mémoire programme ?  :smiley-sweat:
OUPS ! Là je sens confusément que j'exagère un chtipeu !  :*

XavierMiller

Pour étudier comment le C et C++ structurent la mémoire, une autre solution est d'étudier le .hex avec un outil de désassemblage, ou de demander à gcc de produire le code assembler intermédiaire (un .S).

derder9161

#9
Apr 29, 2014, 06:49 pm Last Edit: Apr 29, 2014, 06:52 pm by derder9161 Reason: 1

Est-il possible d'aller voir également les octets en mémoire programme ?


Ouai c'est tout à fait possible il faut, comme l'a dit XavierMiller, regarder le code assembleur généré.

Regarder dans la mémoire programme le contenu des instructions, c'est des octets à des adresses... Et s'amuser à décortiquer une instruction codée c'est quand même complexe. Surtout quand tu sais que certaines sont  sur plusieurs octets.

Pour voir à quoi corresponde les lignes d'assembleur que genere l'IDE arduino tu peux te referer à la partie : Instruction Set Summary à partir de la page 427 de la datasheet du ATMEL qu'équipe ton arduino. La méthode de codage des instruction se trouve facilement dans des documentations technique ATMEL sur internet

Clément


nulentout

Houlalalala, le désassemblage ... j'en suis gavé. J'ai travaillé en assembleur désassembleur et même en binaire pour sur :
Le 6502 de MOS TECHNOLOGIE,
Sur le 6800, le 6802, le 6809 et le 6502 de MOTOROLA.
Ensuite je suis passé aux 68HC1 et compagnie.
Enfin j'ai titillé pas mal de PIC.
Alors aller voir les octets juste pour en observer le contenu : OUI, mais me cogner l'assembleur franchement NON, je n'ai plus du tout envie.
Pensez qu'avec mon 6502 j'ai programmé moi même un assembleur/Désassembleur symbolique avec son éditeur qui tournait sur 8k d'EPROM. Je me suis fabriqué le programmateur d'EPROM. Avant cet assembleur je travaillais en binaire codé hexadécimal. Ce n'est pas le clavier qui tenait le plus de place sur mon atelier !
Puis, sur une autre carte de 8k d'EPROM je me suis codé un éditeur interpréteur BASIC. Comme code c'était sacrément optimisé. Mais maintenant, il y a le C, alors vive les bibliothèques.
Pour revenir aux pointeurs je me suis rendu compte d'une chose assez étonnante.
Je fais une liste de déclarations de variables de taille diverses.
Le compilateur les place les unes à la suite des autres si chaque donnée est liée à la déclaration d'un pointeur. Par contre, si certaines ne sont pas « pointées », visiblement elles ne sont pas rangées au même endroit dans la mémoire.
Je n'ai pas terminé mes investigations, mais il y a encore plusieurs « points noirs qui ne sont pas clairs »  dans mes expérimentations. Je vais travailler la question plus en profondeur, et si je n'arrive pas à trouver les explications, je vous adresserai une requête pour ne pas me chopper un zéro pointé !

fdufnews


XavierMiller

Nulentout,

Les variables globales seront à un endroit (espace "TEXT"), les variables locales dans la pile (stack), et celles réservées par malloc/free encore ailleurs (heap).

nulentout

Bonjour les copains,
Merci fdufnews pour ton lien. Je suis toujours curieux de la façon dont un logiciel gère l'espace mémoire disponible, et c'est tout particulièrement vrai sur le µP ou la taille disponible reste relativement réduite. Entra autre, je me demandais comment Arduino gère sa PILE. En effet, je me suis toujours méfié d'un débordement de S.P. dans le code ou les données. Du reste, dans mes programmes en langage machine je commence par mettre à 00 toute la zone que je réserve au pointeur de pile. Puis, quand je soupçonne une procédure (Récursive par exemple) de trop "plonger" dans la pile, je surveille à dis où douze octets avant la saturation. Si le contenu devient différent de zéro, alors je génère un message d'erreur qui prévient que la saturation de PILE n'est pas loin et que le risque de collision s'approche.
Oui Xavier, je sais que tout programme gère à sa façon les données statyques et les variables dynamiques. Souvent on place en page mémoire zéro les variables les plus sollicitées. Ainsi on diminue considérablement la taille du programme. mais il y a des choix à faire dans la répartition, et c'est un peu le but que je visais pour mieux cerner le compilateur C d'Arduino.
Je vais chercher sur la toile, mais il doit forcément y avoir des explications relatives à notre compilateur favori.
Bonne journée les copains, je vais continuer à travailler ces sacré nom d'une pipe de POINTEURS, c'est à la fois évident et incompréhensible ces entités bitranges autant qu'ézares.
Bien que ce soit hors sujet, travaillant sur les pointeurs je suis tombé sur le codage des entiers. Si j'ai parfaitement compris le but des préfixes pour imposer de l'octal, de l'hexadécimal, je n'ai pas réussi à faire "fonctionner" les suffixes "U" et "L". Vous avez un exemple concret de leur utilité et de la façon de coder, car sur les explications de syntaxe je n'ai pas trouvé comment faire. Quand j'utilise ces deux options, les résultats obtenus semblent inchangés.
A bientôt les copains, je vais continuer à Pointer ...

B@tto

Pour surveiller la ram dispo, et donc la place entre le heap et le stack, tu peux utiliser cette simple routine : https://learn.adafruit.com/memories-of-an-arduino/measuring-free-memory ==> FreeRam()

Blog électronique : battomicro.wordpress.com
Photographie : www.interactive-celebration.fr
Fablab de Montpellier : www.labsud.org

Go Up