IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Arduino Discussion :

Comment estimer la RAM requise pour les variables locales ?


Sujet :

Arduino

  1. #1
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut Comment estimer la RAM requise pour les variables locales ?
    Bonjour,

    Le compilateur AVR donne toujours la mémoire utilisée par les variables globales.

    Comment estimer la RAM nécessaire pour les variables locales ?

    C'est difficile voire impossible si le code utilise la récursivité ou des chaines de longueur variables alimentées par des données extérieures (ce qui n'est pas mon cas).

    Il me reste 486 octets de RAM pour les variables locales, et le programme est stable.

    J'aimerais augmenter la taille d'un tampon en RAM pour permettre plus de fonctionnalités.

    Il y a toujours la méthode bourrin (augmenter jusqu'à ce que ça plante, puis ensuite redescendre avec une marge de sécurité)

    Ca serait mieux de pouvoir le calculer proprement.

    Et ça serait un vrai plus si justement, le calcul pourrait dire qu'il n'est pas possible, signifiant qu'on a de la récursivité à supprimer ou à conserver mais en y prêtant beaucoup d'attention.

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  2. #2
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    Par défaut
    il y a l'approche qui consiste à calculer ce qui reste entre la pile et le tas --> cf cet article

    Bon ça va vous manger un peu de flash et de ram pour l'appel de fonction freeMemory(), mais si vous l'appelez dans chaque fonction (après les allocations de variables locales) ça vous donnera une idée assez précise surtout si vous n'avez pas d'allocation dynamique (donc pas de trous dans le tas).

    vous faites tourner un moment votre code, vous capturez le min et le max et toutes les 5 minutes vous affichez ces valeurs. (si elle divergent c'est que vous avez une fuite mémoire sans doute)

  3. #3
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut
    Bonjour,

    C'est pas mal du tout comme idée !

    Le sens de ma question était plus "théorique" (c'est à dire en examinant le code et/ou le code ASM compilé) mais cette "mesure" m'intéresse, ça reste empirique mais c'est moins bourrin.

    Le truc c'est de bien appeler freeMemory() aux endroits judicieux, c'est à dire à l'intérieur des functions les plus profondément "imbriquées" (donc aux moments où la pile et le tas sont le plus fortement sollicités)

    Il suffit de mémoriser dans une variable globale la valeur minimum de RAM disponible.

    Je vais tester ça, merci.

    Je pense même que je vais laisser freeMemory() en place et inclure dans mon projet d'interface l’envoi par l'Arduino de la valeur minimale de mémoire disponible au server à chaque échange.

    De cette façon en cas de bug on peut voir si c'est lié à la mémoire ou pas.

    Et ça sera intéressant de voir quelles fonctionnalités sollicitent le plus la RAM.

    A bientôt !
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  4. #4
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    Par défaut
    Comme vous l'avez dit, s'il y a des recursions qui dépendent de données externes alors c'est impossible à calculer. De même si vous avez poussé un peu l'usage de C++ et vous avez des classes avec des virtual methods alors c'est aussi impossible à déterminer statiquement (la fonction appelée est décidée au run time) et l'allocation dynamique (appel du constructeur et destructeur) va bien sûr aussi poser problème si elle dépend de facteurs externes.

    En C c'est plus simple, et il existe même des outils comme GNU cflow

  5. #5
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut
    Citation Envoyé par Jay M Voir le message
    De même si vous avez poussé un peu l'usage de C++ et vous avez des classes avec des virtual methods alors c'est aussi impossible à déterminer statiquement (la fonction appelée est décidée au run time) et l'allocation dynamique (appel du constructeur et destructeur) va bien sûr aussi poser problème si elle dépend de facteurs externes.

    En C c'est plus simple, et il existe même des outils comme GNU cflow
    Cela va dans le sens des remarques du modo du forum Délias : se débarrasser d'Arduino et passer du C++ au C
    En effet, j'ai beaucoup de code C++ alambiqué liée aux bibliothèques que j'ai forké, sans pour autant les réécrire à 100%

    Dilemme... je souhaite que le projet puisse être compilé et modifié avec l'IDE Arduino pour qu'il soit accessible au plus grand nombre.
    C'est aussi pour ça que les petits utilitaires annexes qui vont avec sont faits en Visual Basic

    Même si je modifiait mes librairies pour avoir du code en pur C, j'aurais toujours quelques fonctions de base de l'Arduino.
    Ou alors je fait une version "extrême" sans faire d'include en dehors des fichiers de mon projet.
    Mais cela va rendre plus compliqué le portage du projet vers une autre carte que UNO.

    On va déjà voir ce que donne ces mesures.
    En particulier, vérifier qu'au fur et à mesure de l'utilisation, il n'y a pas de fuite de mémoire.
    Cette vérification est la plus simple, un seul appel de freeMemory() à l'envoit de chaque requête est suffisant.

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  6. #6
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    Par défaut
    Avoir des classes c'est quand même bien pratique quand il s'agit d'instance plusieurs fois le même "objet" (port Série, capteur, ...) donc je pense que c'est pas mal de rester en C++...

  7. #7
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 189
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 189
    Points : 11 571
    Points
    11 571
    Par défaut
    Bonjour,
    Citation Envoyé par electroremy Voir le message
    [...] cela va rendre plus compliqué le portage du projet vers une autre carte que UNO.
    Ce qui est compliqué avec les microcontrôleurs, et la remarque de Delias vient de là, c'est que la notion de portabilité est très relative en l'absence d'OS surtout avec des différences très fortes entres les cibles. La notion de code générique pour un micro qui a un DMA et un autre qui n'en n'a pas est très compliqué.

    En informatique la notion de portabilité concerne l'OS et moins l'application ; imaginons que le langage PHP soit devenu un langage de programmation générique, que l'on utilise et qu'on l'adapte pour n'importe quelle application (web, compta, calcul scientifique, industrielle, IA, ...) je pense que vous me diriez que ce n'est pas ça la "portabilité" et qu'on choisit un langage de programmation par rapport à l'application finale. Vous choisissez un langage de programmation pour qu'il se fonde à l'application afin de maximiser les performances.

    Dans l'industrie électronique on fait la même chose, on fait un code pour une seule cible, pour une application donnée et avec du hardware dédié afin de privilégié l'efficacité et la fiabilité.

    A+
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  8. #8
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    Par défaut
    Citation Envoyé par Vincent PETIT Voir le message
    Dans l'industrie électronique on fait la même chose, on fait un code pour une seule cible, pour une application donnée et avec du hardware dédié afin de privilégié l'efficacité et la fiabilité.
    et la maintenabilité

  9. #9
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 189
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 189
    Points : 11 571
    Points
    11 571
    Par défaut
    Citation Envoyé par Jay M Voir le message
    et la maintenabilité
    Exacte
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  10. #10
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut
    Bonjour,

    j'ai donc ajouté ce bout de code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #ifdef __arm__
    // should use uinstd.h to define sbrk but Due causes a conflict
    extern "C" char* sbrk(int incr);
    #else  // __ARM__
    extern char *__brkval;
    #endif  // __arm__
    int freeMemory() {
      char top;
    #ifdef __arm__
      return &top - reinterpret_cast<char*>(sbrk(0));
    #elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
      return &top - __brkval;
    #else  // __arm__
      return __brkval ? &top - __brkval : &top - __malloc_heap_start;
    #endif  // __arm__
    }
    J'ai ajouté un appel à freeMemory() et un affichage dans le menu principal

    J'ai en permanence une valeur fixe de... 2098

    Sachant que l'arduino UNO a une RAM de 2048 octets

    Du coup je ne comprend pas le sens de cette valeur.

    A noter que cette valeur change lorsque je modifie le code, en optimisant une fonction, je suis passé de 2096 à 2098.

    Le fait que la valeur ne change pas semble indiquer que mon code n'occasionne pas de fuite de mémoire.

    Mais la valeur est bizarre, elle devrait être inférieure à la quantité de RAM disponible pour les variables locales (sur mon projet, 482 octets)

    D'après le site qui a publié ce code (https://learn.adafruit.com/memories-...ng-free-memory) :

    What freeMemory() is actually reporting is the space between the heap and the stack.

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  11. #11
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    Par défaut
    si vous regardez ce site sur avr-libc on voit l'organisation de la mémoire

    quand dans la fonction vous allouez un char, il est mis sur le sommet de la pile tandis que __brkval est le pointeur en haut du tas

    donc si on calcule on devrait bien avoir l'espace entre le bout de la pile et le sommet du tas

    par contre si votre code ne fait pas grand chose, notamment aucune allocation dynamique alors __brkval n'est pas initialisé, il faut tenir compte de __malloc_heap_start
    et il se peut que l'optimiseur fasse des siennes...

    essayez ce code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    extern char *__brkval;
     
    int freeMemory() {
      char top;
      return __brkval ? &top - __brkval : &top - __malloc_heap_start;
    }
     
    void setup() {
      Serial.begin(115200);
      Serial.println(F("----- EXTENSION DU TAS -----"));
      Serial.println(freeMemory());
    }
     
    void loop() {
      char * ptr = (char*) malloc(128); // memory leak (128 + 2 octets)
      if (ptr == NULL) {
        Serial.println(F("MALLOC FAILED"));
        while (true);
      } else {
        Serial.println(freeMemory());
      }
      delay(1000);
    }

  12. #12
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut
    Oui c'est vrai, tout est compilé en "Os" donc les optimisations peuvent être assez violentes

    Il n'y a pas moyen de forcer le compilateur à ne pas toucher une fonction ?

    Ou alors la traduire en assembleur et la placer dans le c++ en ASM ?

    J'ai placé l'appel de freeMemory() dans une petite fonction qui me sert à renvoyer des valeurs de capteurs au serveur, qui réalise ensuite un affichage :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    void ReadInput(byte mode) {
      byte pin;
      Status = STATUS_4_Read_Pin;
      if (ReadedPinIndex<MaxReadedPin-1) { // ! AHT20 use 2 values
        pin = ReadUInt8();
    	if (pin == READ_PIN_AHT20_Humi_Temp) { 
    		ATH20_getSensor_B();
    	} else if (pin == READ_PIN_FreeMemory) {
    		ReadedPin[ReadedPinIndex] = READ_PIN_FreeMemory;
    		ReadedPinValue[ReadedPinIndex] = freeMemory(); 
    		ReadedPinIndex++;		
    	} else {
    		if (!PinAvailable(pin)) return;
    		pinMode(pin, INPUT);
    		ReadedPin[ReadedPinIndex] = pin;
    		if (mode==ReadDigitalInput) {
    		  ReadedPinValue[ReadedPinIndex] = digitalRead(pin);
    		} else {
    		  ReadedPinValue[ReadedPinIndex] = analogRead(pin);
    		}
    		ReadedPinIndex++;
    	}
      }
    }
    Cette fonction ReadInput() a une variable locale 'pin'.
    Et elle est appelée à l'intérieur d'une une grosse fonction qui utilise plusieurs variables locales.

    Donc le tas est forcément utilisé.

    Les autres variables de ReadInput() sont globales, car utilisées à plusieurs niveau dans le code pour traiter la réponse et pour écrire la requêtes au serveur, voici leur déclaration :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // Var for managing I/O operation asked by server :
    #define ReadAnalogInput 1
    #define ReadDigitalInput 2
    #define AmbiantLightReadedPinIndex 0
    const byte MaxReadedPin = 10;
    byte ReadedPin[MaxReadedPin];
    int ReadedPinValue[MaxReadedPin];
    byte ReadedPinIndex;
    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  13. #13
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut
    Je ne suis pas le seul a avoir du mal avec freeMemory() : https://forum.arduino.cc/index.php?topic=464785.0

    J'ai essayé de modifier la fonction comme ça, mais sans succès (affichage = 2093):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #ifdef __arm__
    // should use uinstd.h to define sbrk but Due causes a conflict
    extern "C" char* sbrk(int incr);
    #else  // __ARM__
    extern char *__brkval;
    #endif  // __arm__
    int __attribute__((noinline)) freeMemory() {
      volatile char top;
      top = digitalRead(PIN_SETUP);
      digitalWrite(A0,top);
    #ifdef __arm__
      return &top - reinterpret_cast<char*>(sbrk(0));
    #elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
      return &top - __brkval;
    #else  // __arm__
      return __brkval ? &top - __brkval : &top - __malloc_heap_start;
    #endif  // __arm__
    }
    Il faut trouver une façon fiable de lire la position du tas et de la pile

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  14. #14
    Membre éprouvé Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    934
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 934
    Points : 1 274
    Points
    1 274
    Par défaut
    Bonjour,

    j'ai utilisé cette fonction, ça marche très bien :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int freeRam () 
    {
      extern int __heap_start, *__brkval; 
      int v; 
      return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
    }
    Trouvée sur ce site : http://jheyman.github.io/blog/pages/...ilation-traces

    J'ai une valeur stable de 282 octets, c'est cohérent avec mon programme.

    Il reste à appeler cette fonction freeRam() à différents endroit du code pour mieux évaluer l'utilisation de la mémoire

    A bientôt
    Quand deux personnes échangent un euro, chacun repart avec un euro.
    Quand deux personnes échangent une idée, chacun repart avec deux idées.

  15. #15
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    Par défaut
    Elle est équivalente à celle que je vous ai postée ci dessous dans mon exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     extern char *__brkval;
     
    int freeMemory() {
      char top;
      return __brkval ? &top - __brkval : &top - __malloc_heap_start;
    }
    sauf qu’elle alloue deux octets sur la pile au lieu d’un seul en mettant un int. Autant ne consommer qu’un seul octet

    Si l’information pertinente qui vous intéresse c’est êtes vous au dessus d’un seuil vous pouvez aussi juste retourner un bool plutôt qu’un entier qui là encore prend un octet de plus sur la pile lors de l’appel de fonction

    Cette fonction ReadInput() a une variable locale 'pin'.
    Et elle est appelée à l'intérieur d'une une grosse fonction qui utilise plusieurs variables locales.
    Donc le tas est forcément utilisé.
    non pas le tas, la pile -> le tas c’est pour tout ce qui est allocation persistante dynamique (non connue à la compilation) (que vous obtenez or malloc() new etc). La pile c’est pour toutes les variables locales de vos fonctions (et le contexte d’appel)

    Si vous regardez le code de mon petit exemple vous verrez que j’alloue à chaque tour de loop un peu de mémoire du tas (et le tas mange 2 octets de plus pour conserver l’info) et on voit donc la mémoire dispo descendre petit à petit.

    EDIT: j'ai rejoué un peu avec différentes versions des fonctions et il semble que suivant ce que l'on fait et ce que fait l'optimiser on a des résultats qui peuvent varier... donc à utiliser quand même un peu avec prudence et pas aveuglément.

Discussions similaires

  1. Réponses: 6
    Dernier message: 20/12/2006, 10h12
  2. [Tout langages] Comment Générer un ID alphanumérique pour les champs
    Par digital prophecy dans le forum Général Dotnet
    Réponses: 2
    Dernier message: 04/12/2006, 18h47
  3. Réponses: 4
    Dernier message: 29/09/2006, 11h53
  4. unset() pour les variables de session
    Par ozzmax dans le forum Langage
    Réponses: 2
    Dernier message: 05/05/2006, 15h56
  5. Problème pour les variables de session
    Par brotelle dans le forum Langage
    Réponses: 11
    Dernier message: 20/04/2006, 11h22

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo