Free Memory : pas le même résultat

Bonjour

Ci dessous 2 méthodes pour connaitre la quantité de sram dispo et pas le même résultat. Excusez moi pour la présentation, mais je teste différentes méthodes et c'est brouillon pour le moment.

Le sketch avec FreeRam qui tourne en même temps que MemoryFree (http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1213583720/15)

Soit il y a un bug dans le code, soit (je demande confirmation), le code est chargée de façon dynamique pendant son exécution et ce qui est pas nécessaire est déchargé puis rechargé au prochain loop(). Et donc FreeRam consomme plus que FreeMemory (une dizaine d'octets environ sur cet exemple).

Y a t il une méthode fiable savoir combien il en reste, parce qu'avec quelques Ko de sram c'est pas le moment de gaspiller ?

#include "MemoryFree.h"

/** Return the number of bytes currently free in RAM. /
static int FreeRam(void) {
extern int __bss_end;
extern int
__brkval;
int free_memory;
if (reinterpret_cast(__brkval) == 0) {
// if no heap use from end of bss section
free_memory = reinterpret_cast(&free_memory)

  • reinterpret_cast(&__bss_end);
    } else {
    // use from top of stack to heap
    free_memory = reinterpret_cast(&free_memory)
  • reinterpret_cast(__brkval);
    }
    return free_memory;
    }

void setup() // run once, when the sketch starts
{
Serial.begin(9600);

}

void loop() // run over and over again
{
Serial.print("freeMemory() reports ");
Serial.println( freeMemory() );
Serial.print("freeRam() reports ");
Serial.println( FreeRam() );

}

MemoryFree.cpp

//
#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

extern unsigned int __heap_start;
extern void *__brkval;

/*

  • The free list structure as maintained by the
  • avr-libc memory allocation routines.
    */
    struct __freelist {
    size_t sz;
    struct __freelist *nx;
    };

/* The head of the free list structure */
extern struct __freelist *__flp;

#include "MemoryFree.h";

/* Calculates the size of the free list /
int freeListSize() {
struct __freelist
current;
int total = 0;

for (current = __flp; current; current = current->nx) {
total += 2; /* Add two bytes for the memory block's header */
total += (int) current->sz;
}

return total;
}

int freeMemory() {
int free_memory;

if ((int)__brkval == 0) {
free_memory = ((int)&free_memory) - ((int)&__heap_start);
} else {
free_memory = ((int)&free_memory) - ((int)__brkval);
free_memory += freeListSize();
}
return free_memory;
}

et MemoryFree.h

// MemoryFree library based on code posted here:
// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1213583720/15
//
// Extended by Matthew Murdoch to include walking of the free list.

#ifndef MEMORY_FREE_H
#define MEMORY_FREE_H

#ifdef __cplusplus
extern "C" {
#endif

int freeMemory();

#ifdef __cplusplus
}
#endif

#endif

Lorsque tu appelles FreeRam ou freeMemory il y a création de variables locales par la fonction et donc tu ne peux pas trouver exactement la même valeur d'une fonction à l'autre.

fdufnews:
Lorsque tu appelles FreeRam ou freeMemory il y a création de variables locales par la fonction et donc tu ne peux pas trouver exactement la même valeur d'une fonction à l'autre.

Très clair, merci.

Je ne suis pas sûr qu'il y ait allocation dynamique sur les variables locales (ce serait un grosse erreur sur un µP aussi lent). Pour moi, les variables locales sont allouées dans la ram à la compilation, donc ce n'est pas à ce niveau que ça coince.

Je pense plutôt que c'est au niveau de la méthode de calcul, il faudrait regarder dans les codes, et à quoi se réfèrent les variables / constantes.

De plus, comme dans freeRam(), la bonne méthode est de compter la diff entre la fin des variables et le début de la pile. Mais la pile est variable et dépend des différentes fonctions en cours et de leurs paramètres.

peux-tu nous donner les différentes valeurs renvoyées? car si la différence n'est que de 5 octets, c'est peut-être négligeable?

Bonjour,

La différence est de combien d'octets ?

Tu as plusieurs facteurs qui peuvent entrer en ligne de compte :

  • l'allocation de variables pour l'exécution de la fonction elle même,
  • le nombre d'adresses de retour de fonctions dans la pile d'appel à un instant t

Tes deux codes se base sur le nombre d'octets restant dans la ram calculé en fonction de ce que les fonctions de l'avr-libc a alloué / libéré.
Donc les deux fonctions devrait donner un résultat trés proche.

@Super_Cinci

En C, les variables locales des fonctions sont forcement allouées "dynamiquement", sinon le code ne serait pas récursif.

L'espace mémoire DATA est généralement divisé en 3 portions :

  • la bss qui contient toutes les variables globales, allouées statiquement au moment de l'édition de lien.
  • la pile (stack) qui contient les :
  • les paramètres des fonctions
  • l'adresse retour
  • les variables locales
    La pile est une zone de mémoire qui se remplit et se vide de manière linéaire et continue lorsque on appelle des fonctions ou retourne d'une fonction.
  • le tas (heap) qui est destiné aux allocations dynamique dans lequel on peut allouer (malloc/calloc/new) et libérer (free/delete) des blocs de mémoire. Le tas devient généralement fragmenté au bout d'un moment ce qui peut le rendre inutilisable. L'utilisation de mémoire dynamique doit être réduit au minimum, si possible uniquement durant les phases d'initialisations quand on peut encore dire 'Help, pas assez de mémoire".
    par exemple la classe String est énormément basé sur l'allocation dynamique. Utiliser String conduit à fragmenter sa mémoire très rapidement.

Il est clair que FreeRam() ne fonctionne pas de la même façon que freeMemory()
FreeRam() fait un simple calcul alors que freeMemory() parcours le tas pour accumuler la taille des blocs libre.

Donc je pense que freeMemory() doit donner une valeur significativement plus faible mais plus juste que freeRam().

A creuser...

barbudor:
@Super_Cinci

En C, les variables locales des fonctions sont forcement allouées "dynamiquement", sinon le code ne serait pas récursif.
...

  • le tas (heap) qui est destiné aux allocations dynamique dans lequel on peut allouer (malloc/calloc/new) et libérer (free/delete) des blocs de mémoire. Le tas devient généralement fragmenté au bout d'un moment ce qui peut le rendre inutilisable. L'utilisation de mémoire dynamique doit être réduit au minimum, si possible uniquement durant les phases d'initialisations quand on peut encore dire 'Help, pas assez de mémoire".
    par exemple la classe String est énormément basé sur l'allocation dynamique. Utiliser String conduit à fragmenter sa mémoire très rapidement.

N'y a t il pas de "défragmenteur" ?

Il est clair que FreeRam() ne fonctionne pas de la même façon que freeMemory()
FreeRam() fait un simple calcul alors que freeMemory() parcours le tas pour accumuler la taille des blocs libre.

Donc je pense que freeMemory() doit donner une valeur significativement plus faible mais plus juste que freeRam().

A creuser...

Pour le moment mon projet en est à ses début. Je fais un suivi avec FreeRam en permanence et ponctuellement avec freeMemory.

D’ailleurs qu'en est il du taux d'occupation de la charge du microprocesseur ? Peut on avoir une idée comme c'est le cas pour la sram ?

Merci pour toutes ces informations qui m'éclaire mieux sur le fonctionnement de la bête.

barbudor:
@Super_Cinci

En C, les variables locales des fonctions sont forcement allouées "dynamiquement", sinon le code ne serait pas récursif.

Bien vu! Merci, je m'a donc trompé. Mais ça veut-il dire que si on appelle une fonction qui contient beaucoup de variables locales, on perd un temps précieux d'allocation de ces variables? ou alors c'est juste un bloc mémoire dont la taille est connue à la compilation, et quelque soit le nombre de variables, le temps d'allocation sera toujours le même (seconde hypothèse qui me paraît plus proche de la réalité...)

N'y a t il pas de "défragmenteur" ?

Il me semble avoir déjà vu traîner une question du genre, et je m'étais dit que c'était assez dangereux, car si on déplace une variable en cours d'utilisation, ça risque de par faire du bien...

Pour ce qui concerne la charge CPU, il est en général occupé à 100%, puisqu'il ne fonctionne pas en multi-tâche. Même s'il ne fait rien pendant qq µs, il tourne quand même dans des boucles d'attente (while (Serial.available() == 0); par exemple ou le CPU déclenche la fonction Serial.available() en continu et compare sa sortie à chaque tour)

J'ai quelques programmes qui tournent via des interruptions, c-à-d que loop()est vide. Dans ces programmes, dès l'entrée dans une ISR (Interrupt Sub Routine, fonction déclenchée par une interruption), je mets à 1 une pin de sortie, et je la mets à 0 à la sortie de l'ISR. Avec l'oscillo, ça me permet de voir le temps "mort" du CPU qu'il me reste pour faire des traitements globaux, ainsi que mesurer le temps de traitement desdites ISR. Mais il est rare d'en arriver à ce point.

On peut toujours utiliser ce flag matériel (mettre la pin à 0 dès qu'on entre dans une boucle d'attente par exemple) dans tout le code et mesurer la valeur moyenne de la tension sur cette pin, 0V donnera un CPU qui ne fait rien d'utile, 2.5V : traitement utile à 50%, 5V : CPU chargé à mort...

Super_Cinci:

barbudor:
@Super_Cinci

En C, les variables locales des fonctions sont forcement allouées "dynamiquement", sinon le code ne serait pas récursif.

Bien vu! Merci, je m'a donc trompé. Mais ça veut-il dire que si on appelle une fonction qui contient beaucoup de variables locales, on perd un temps précieux d'allocation de ces variables? ou alors c'est juste un bloc mémoire dont la taille est connue à la compilation, et quelque soit le nombre de variables, le temps d'allocation sera toujours le même (seconde hypothèse qui me paraît plus proche de la réalité...)

N'y a t il pas de "défragmenteur" ?

Il me semble avoir déjà vu traîner une question du genre, et je m'étais dit que c'était assez dangereux, car si on déplace une variable en cours d'utilisation, ça risque de par faire du bien...

Pour ce qui concerne la charge CPU, il est en général occupé à 100%, puisqu'il ne fonctionne pas en multi-tâche. Même s'il ne fait rien pendant qq µs, il tourne quand même dans des boucles d'attente (while (Serial.available() == 0); par exemple ou le CPU déclenche la fonction Serial.available() en continu et compare sa sortie à chaque tour)

Ok, donc si je comprend bien plus on lui donne de tâche à faire, moins il vérifiera le serial, les pin, etc

J'ai quelques programmes qui tournent via des interruptions, c-à-d que loop()est vide. Dans ces programmes, dès l'entrée dans une ISR (Interrupt Sub Routine, fonction déclenchée par une interruption), je mets à 1 une pin de sortie, et je la mets à 0 à la sortie de l'ISR. Avec l'oscillo, ça me permet de voir le temps "mort" du CPU qu'il me reste pour faire des traitements globaux, ainsi que mesurer le temps de traitement desdites ISR. Mais il est rare d'en arriver à ce point.

On peut toujours utiliser ce flag matériel (mettre la pin à 0 dès qu'on entre dans une boucle d'attente par exemple) dans tout le code et mesurer la valeur moyenne de la tension sur cette pin, 0V donnera un CPU qui ne fait rien d'utile, 2.5V : traitement utile à 50%, 5V : CPU chargé à mort...

ça soulève une autre question. Si le CPU est chargé à mort et qu'il rame, y a t il un risque que le cpu ne détecte pas un appui sur un bouton (entré digitale), ou bien y a t il un tampon ?

Ah... nous voilà dans le vif d'un sujet auquel il faut faire super attention, car ça plante bien des gens :

Le fonctionnement d'un arduino n'a absolument rien à voir avec une plateforme ou OS (windows, linux...) d'un PC!

Premièrement, la résolution 8 bits contre les 32 ou 64 bits d'un PC. Il est utile de connaître précisément le traitement que l'on fait sur les variables pour savoir si on a vraiment besoin d'un INT alors qu'un byte irait très bien pour la variable X. Le traitement d'un INT (16 bits) est beaucoup plus long qu'un byte (8 bits), il est traité en deux bytes, avec les histoires de carry et tout le toutim, travailler avec une variable 16 bits prend au minimum trois fois plus de temps qu'en 8 bits. dans un for (x=0; x<20; x++), on choisira un type byte pour x, puisque sa valeur sera comprise entre 0 et 19... Dans un PC, c'est tellement rapide que le type la variable importe peu, d'autant plus qu'il n'y a pas de limitation de mémoire...

Ensuite, la gestion des ES...

Il faut considérer que chaque valeur que l'on veut traiter (état des pins, valeur de timer, données reçues sur un port série, valeur de conversion analogique...) se trouve dans une case mémoire précise (un registre) en dehors du CPU lui-même. chaque registre est en liaison permanente avec la "valeur" extérieure. par exemple, coller 5V sur la pin 3 engendrera immédiatement la valeur "1" sur la pin PIND[3], soit le registre PIND = Bxxxx1xxx, x étant la valeur précédente de chaque bit). C'est au programme d'aller chercher la valeur dans le registre au bon moment (ou le plus souvent possible) afin de récupérer ce 1 avant que le 5V disparaisse de la pin 3...
Il n'y a pas de buffer dans un arduino : les registres peuvent changer d'un moment à l'autre sans prévenir (à part via certaines interruptions). chaque fonction matérielle est un périphérique externe au CPU (même si tout ça est physiquement dans un petit cachou). A chaque périphérique sont associés plusieurs registres (adresses mémoire). Il revient au programmeur d'en vérifier de lui-même le contenu pour le traiter.

On est bien loin de la programmation sur PC où une API (OS) gère d'elle-même tous les évènements (clavier, souris, HDD...) et en mémorise les actions dans un buffer afin qu'elles ne soient pas perdues, ainsi qu'un multitâche virtuel, car les processeurs en eux même ne font pas de multitâche, au mieux, ils sont optimisés matériellement pour. Dans un simple processeur (comme l'ATMEGA), il n'y a pas d'API, car l'API est le programme en lui-même. De plus, les ressources d'un µP dans un PC sont des millions de fois plus larges que dans un arduino, permettant à l'API d'être presque invisible au programmeur.

Dans mon exemple, le Serial.available() n'est qu'un exemple, en aucun cas il ne sera vérifié si on ne rajoute pas quelque lignes de configuration dans son programme... (pas facile à expliquer)

quand je parle de mes programmes à interruption, en général, il y a plusieurs fonctions (ISR) associées à des évènements (INT externes, timers, réception séries...) qui peuvent être appelées à tout moment, et dans loop(), on ne trouve qu'une simple lecture de registres, comme un clavier, gestion d'affichage LCD... Il est évident que si mes timers s'affolent ou que le port série est en pleine ébullition, alors le CPU sera appelé très souvent pour traiter mes ISR et le traitement du loop() sera considérablement ralenti, d'où mon utilisation d'une pin en sortie qui est à 0 pendant le traitement de loop() et qui passe à 1 dès qu'on l'interrompt par une interruption. C'est à partir de là que j'optimise mes programmes (minimalisation des variables, éviter les digitalWrite ou Read...)

M'est-je fait comprendu? sinon, il y a le datasheet de l'ATMEL qui explique tout ça, mais ça demande plusieurs jours de lecture...

On est bien loin de la programmation sur PC où une API (OS) gère d'elle-même tous les évènements (clavier, souris, HDD...) et en mémorise les actions dans un buffer afin qu'elles ne soient pas perdues,

Dans le cas du PC c'est toujours un morceau de programme qui se charge de surveiller les entrées/sorties. Seulement comme le processeur va 100x plus vite il risque moins de louper les événements. L'API c'est juste un moyen de standardiser l'interface et de la rendre visible de toutes les applications.

Super_Cinci:
Ah... nous voilà dans le vif d'un sujet auquel il faut faire super attention, car ça plante bien des gens :

Le fonctionnement d'un arduino n'a absolument rien à voir avec une plateforme ou OS (windows, linux...) d'un PC!

...

M'est-je fait comprendu? sinon, il y a le datasheet de l'ATMEL qui explique tout ça, mais ça demande plusieurs jours de lecture...

C'est agréable de voir un forum avec des gens capable de résumer à ce point un problème complexe. Serte, il y a les datasheet, mais expliquer aussi clairement rendra la lecture du datasheet beaucoup plus efficace.
Merci à vous tous.

L'allocation mémoires pour les variables locales lors de l'appel d'une fonction prend toujours le même temps car c'est juste un déplacement du pointeur de pile.

Par contre l'écriture des valeurs passées en paramètre peut prendre du temps. Si les valeurs doivent êtres modifiées par la fonction il est intéressant de passer simplement un pointeur sur une structure.

JLB

Petite précision pour agrément l'excellente explication de Super_Cinci :
Contrairement à un PC sur arduino (et plus généralement sur toute plateformes utilisant des micro-contrôleurs) il n'y as pas de notion de "charge CPU".

Sur un PC on peut ouvrir le gestionnaire de taches et ce dire "ya cette bip d'application qui me bouche 100% du CPU !", avec un micro-contrôleur ce n'est pas possible.
Le micro-contrôleur tourne sur un signal d'horloge (8MHz ou 16MHz en général avec les cartes arduino), à chaque "tick" de l'horloge le CPU prend une instruction depuis la mémoire flash, la décode et agit en conséquence.
Le CPU est donc toujours "à 100%" en train d'effectuer une tache, même faire une série de "nop" (instruction assembleur "no operation") revient à occuper le CPU.

En gros il n'y as pas de notion de "idle process" comme c'est le cas sur un PC.

La seul notion de "temps cpu" que l'on peut avoir est par exemple le % de cycles cpu occupé par une interruption / fonction par rapport au reste du programme.
Mais il n'existe aucune fonction pour connaitre ce "temps cpu" en temps réel, la meilleur façon de le déduire et de le mesurer avec un oscilloscope et une broche que l'on fait commuter.

JLB a déjà répondu : l'allocation est immédiate puisqu'elle se fait par le déplacement d'un pointeur dans la pile.
Et la taille de ce déplacement est connu à la compilation
MAIS LE C ne vérifie pas si la pile est assez grande.

Puisque Super_Cinci en parle, sur un processeur évolué disposant de gestion avancée de la mémoire (MMU) et surtout de mémoire paginée, la taille de la pile peut grandir automatiquement au fur et a mesure. Au risque de "bouffer" tout si tu fait une fonction récursive qui s'appelle sans fin genre :

int boufe_tout( int in )
{
  return bouffe_tout( in + 1 );
}

De même sur PC/Windows/Linux ou Mac/OSX, pas de problème pour allouer une variable globale de grande taille :

void grosse_variable()
{
  int gros_tableau[1024*1024]; // même pas mal même si tu n'as que 2MO de RAM, Windows alloue de la mémoire sur le disque...
}

Par contre sur un micro embarqué, la taille mémoire est fixe et toutes les tailles sont connues à l'avance.
Une taille fixe est allouée à la pile par l'éditeur de lien (pas facilement changeable dans l'environnement Arduino je pense) et on peut très rapidement exploser la limite si on ne fait pas attention.

Comme je le disait, par défaut le C ne vérifie pas cette possibilité (ou du moins il n'est pas spécifié qu'il doive le faire). Il existe dans certains compilateur une option de compilation que l'on peut activer si on le souhaite pendant la phase de mise au point qui va vérifier a l'entrée d'une fonction que l'on a pas dépassé la taille de la pile. Si c'est le cas : blocage de sécurité. Cette fonctionnalité est désactivée par défaut pour des raisons de performance.

Au contraire, le langage Pascal effectue généralement plus de vérification et génère un code plus sûr. Ce qui explique le grand succès de Borland et TurboPascal puis Delphi dans les années 90 et début 2000.

Pour ce qui est du "défragmenteur" (en anglais on parle de Garbagge collection (ramassage des poubelles)) ceci n'existe pas en C mais uniquement dans des environnements qui n'autorisent pas un accès par pointeurs directs mais il a surtout été popularisé dans les systèmes de machines virtuelles telles que Java et DotNet. Dans ces environnements on ne n'accède jamais à la mémoire directement par un pointeur mais par une référence qui elle contient l'adresse en mémoire. Cela autorise l'environnement à défragmenter la mémoire en tâche de fond en mettant à jour les références.

Pour ce qui est de la charge CPU, comme le dit Skywodd ce n'est pas aussi immédiat.
Mais normalement, une application Arduino tourne avec loop()
Donc on peut considérer que plus souvent on fait des tour de loop(), moins le CPU est chargé.
Plus longtemps dure l'exécution de loop() (et donc moins de tour on effectue) plus le CPU est chargé.

Dans une application j'ai fait une estimation de la charge CPU à l'aide de la LED de la carte (PIN13) en éteignant la LED à l'entrée de loop() et en l'allumant sur la dernière ligne.
Plus l'exécution de loop() est courte, plus la LED est allumée.
Plus l'éxécution de loop() s'allonge, plus la LED diminue d'intensité.

On pourrait aussi faire une estimation en mesurant le nombre de tour de loop() par unité de temps.

Ca ne reste qu'une estimation et considère de programmer l'Arduino d'un façon absolument non bloquante (pas de delay, pas de boucle bloquante ou d'attente).
Mais c'est de toute façon la seule manière de programmer efficacement une plateforme telle que l'Arduino sans ordonnanceur de tâches.

Je n'ai même pas résumé, car il y a autant de choses à décrire qu'il y a de transistors dans le µP (en gros...)

Pour les histoires d'allocation des variables locales, je viens d'apprendre un truc, merci les gars!

Selon le nombre de fonctions de mon programme, j'utilise au max les interruptions. Par exemple, j'ai trouvé un moyen d'utiliser un clavier matriciel sans aucun appel de fonction : je branche les colonnes sur un port (prenons le port X par exemple) et les lignes sur le port Y. le port Y contient l'interruption PCINT1 (Pin Change INTerrupt). cette interruption est déclenchée dès qu'une des pin du port change, ce qui est très pratique dans mon cas, la lecture du clavier dans mon programme se résume alors à la lecture de la variable key :

#define NO_KEY 0xFF;  // code "pas de touche"
volatile byte lignes, colonnes, key;

void KeybInit(){       // initialisation du clavier
  PORTX en sortie;  // Préparation des colonnes
  PORTX = 0x00;

  PORTY en entrée;  // Préparation des lignes
  PORTY = 0xFF;  // pullup sur les lignes

  Autoriser PCINT1 sur PORTY;  // démarrer la lecture sur interruption
}

ISR(PCINT1-Vect)(){        // routine d'interruption appelée dès qu'une pin du port Y change d'état (appui sur une touche ou relâchement)
  Désactiver PCINT1 sur PORTY;
  colonnes = PINY;     // lecture de la ligne qui a changé d'état

  PORTX en entrée;  // Préparation des colonnes
  PORTX = 0xFF;  // pullup sur les colonnes

  PORTY en sortie;  // Préparation des lignes
  PORTY = 0x00;

  lignes = PINY;  // lecture de la ligne de la touche enfoncée
  key = decodeKey(lignes, colonnes);  // décodage du code de la touche
  keybInit();  // réinitialisation du clavier
}

void setup(){
  key = NO_KEY;
  keybInit();
}

void loop(){
 if (key != NO_KEY){
  switch (key){  // ne fait appel à aucune fonction (donc très rapide)
    case 0x00 :
      //traitement touche code 0x00
      break;
    case 0x01 :
      //traitement touche code 0x01
      while (key == 0x01);  // attente du relâchement de la touche
      // faire quelque chose
      while (key == NO_KEY);  // attente de l'appui sur une nouvelle touche
      // faire encore quelque chose
      while (key != NO_KEY);  // attente du relâchement de toutes les touches
      break;
  }
 }
}

Ici, j'ai grandement simplifié, la fonction decodeKey n'est pas là, elle transforme le couple (lignes, colonnes) en un code sur un octet (utile pour un clavier à touches numérotées... la touche "1" donne la valeur 1 etc etc.

J'avoue qu'un arduino Mega sera bien plus adapté à ce genre de code car il possède beaucoup de PCINT, permettant de gérer ainsi des claviers de 256 touches voire plus, sans avoir besoin de balayer les lignes et colonnes en continu, le gain de temps est bien plus que considérable, car si on n'appuie sur aucune touche, alors on peut résumer la loop ainsi :

void loop(){
 if (key != NO_KEY){
    // peu importe ce qu'il se passe ici, car on n'appuie sur aucune touche : key = NO_KEY...
 }
}

et donc loop boucle uniquement sur un simple if. Ce n'est qu'un exemple, mais de cette même manière, on peut gérer le clavier quand et comme on veut!

Dans mes programmes utilisant ce genre de manip d'int pour un clavier, la loop contient un menu (dans lequel on navigue via le clavier).

si ça peut donner des idées, prenez, prenez! faudrait que j'en fasse une lib (je sais toujours pas comment on fait, j'ai essayé une fois sans résultat), ça serait grandement pratique pour beaucoup, car la lib Keypad est vraiment très lente (du digitalWrite et Read à foison avec je ne sais combien de if()...) et bug de temps en temps...

barbudor:
Pour ce qui est du "défragmenteur" (en anglais on parle de Garbagge collection (ramassage des poubelles)) ceci n'existe pas en C mais uniquement dans des environnements qui n'autorisent pas un accès par pointeurs directs mais il a surtout été popularisé dans les systèmes de machines virtuelles telles que Java et DotNet. Dans ces environnements on ne n'accède jamais à la mémoire directement par un pointeur mais par une référence qui elle contient l'adresse en mémoire. Cela autorise l'environnement à défragmenter la mémoire en tâche de fond en mettant à jour les références.

Barbudor, la fonction du garbage collector Java n'est pas exactement un défragmentage. Plus exactement c'est la récupération des instances d'objets qui ne sont plus référencées. Contrairement au C++ le programmeur java ne s'occupe pas de désinstancier les objets créés alors qu'en C++ le moindre oubli provoque des "fuites de mémoire".

JLB

Le garbage collector Java récupère la mémoire des objets non référencés puis défragmente ensuite l'espace mémoire.
Il y a d'ailleurs plusieurs algorithmes disponibles.

Je crois me souvenirs que la défragmentation mémoire peut aussi être transparente même en manipulant des pointeurs directement pour peu que le système dispose d'une MMU (Memory Managemen Unit), mais c'est pas le cas d'un micro-contrôleur tel que l'AVR :grin:

patg_:
Le garbage collector Java récupère la mémoire des objets non référencés puis défragmente ensuite l'espace mémoire.
Il y a d'ailleurs plusieurs algorithmes disponibles.

L'activation du garbage collector en java (et dérivé, android, etc) est le truc chiant par excellence quand on code un jeu ou une application graphique ...
La 1er chose à faire est de le désactiver, et de lancer le garbage collector à la main pendant un temps "libre", sinon -> lags.
(En gros vive System.gc(); \o/)