Table des matières

MANIPULER UN TABLEAU EN C

Présentation

Il est très utile de manipuler des tableaux (ou listes) en langage C (celui d'Arduino). Cela permet d'automatisation de la manipulation des données. Par exemple, si on souhaite manipuler une grille de LED, ou plusieurs servomoteurs, etc.

Références et tutoriels

Les tutoriels suivants sont très explicite sur la déclaration, l'initialisation et la manipulatioon des tableaux en C :

Exemples d'utilisation

Création et initialisation d'un tableau

Méthode directe :

int tableau[4];

tableau[0] = 10;
tableau[1] = 23;
tableau[2] = 505;
tableau[3] = 8;

autre méthode:

int taille = 5;
int tableau[taille];

Initialiser un tableau - méthode directe:

int tableau[4], i = 0;

    // Initialisation du tableau
    for (i = 0 ; i < 4 ; i++)
    {
        tableau[i] = 0;
    }

Initialiser un tableau - méthode rapide :

    int tableau[4] = {0, 0, 0, 0};

ou

    int tableau[4] = {10, 23}; // Valeurs insérées : 10, 23, 0, 0

ou

    int tableau[4] = {0}; // Toutes les cases du tableau seront initialisées à 0

Allocation dynamique d'un tableau

Toutes les explications sur l'allocation dynamique d'un tableau sont sur le lien suivant : Openclassrooms.

Il faut utiliser la fonction:

En résumé :

Déclaration du tableau (entiers):

int* tableau = NULL; // Ce pointeur va servir de tableau après l'appel du malloc

Création du tableau (entiers): En supposant que tailleDuTableau ait été défini :

tableau = = malloc(tailleDuTableau * sizeof(int)); // On alloue de la mémoire pour le tableau; 

Initialisation du tableau (entiers):

for (i = 0 ; i < nombreDAmis ; i++){
   tableau[i] = 0;
}

Le programme total est le suivant :

int main(int argc, char *argv[])
{
    int nombreDAmis = 0, i = 0;
    int* ageAmis = NULL; // Ce pointeur va servir de tableau après l'appel du malloc

    // On demande le nombre d'amis à l'utilisateur
    printf("Combien d'amis avez-vous ? ");
    scanf("%d", &nombreDAmis);

    if (nombreDAmis > 0) // Il faut qu'il ait au moins un ami (je le plains un peu sinon :p)
    {
        ageAmis = malloc(nombreDAmis * sizeof(int)); // On alloue de la mémoire pour le tableau
        if (ageAmis == NULL) // On vérifie si l'allocation a marché ou non
        {
            exit(0); // On arrête tout
        }

        // On demande l'âge des amis un à un
        for (i = 0 ; i < nombreDAmis ; i++)
        {
            printf("Quel age a l'ami numero %d ? ", i + 1);
            scanf("%d", &ageAmis[i]);
        }

        // On affiche les âges stockés un à un
        printf("\n\nVos amis ont les ages suivants :\n");
        for (i = 0 ; i < nombreDAmis ; i++)
        {
            printf("%d ans\n", ageAmis[i]);
        }

        // On libère la mémoire allouée avec malloc, on n'en a plus besoin
        free(ageAmis);
    }

    return 0;
}

Passage de tableaux à une fonction

Pour envoyer en paramètre d'entrée un tableau en C, il faut envoyer l'adresse du tableau (&tableau) et sa taille (sizeof(tableau)).

La fonction doit être capable d'initialiser un tableau de n'importe quelle taille. Or, dans cette fonction, on ne connaît, a priori, pas la taille du tableau en paramètre d'entrée. C'est pour cela qu'il faut envoyer en plus une variable qu'on appelle par “exempletailleTableau”.

La variable tableau peut en fait être considérée comme un pointeur (une référence sur une adresse en mémoire de l'ordinateur), on peut donc le passer en paramètre d'entrée de la fonction comme un pointeur.

// Prototype de la fonction d'affichage
void affiche(int *tableau, int tailleTableau);
 
int main(int argc, char *argv[])
{
    int tableau[4] = {10, 15, 3};
 
    // On affiche le contenu du tableau
    affiche(tableau, 4);
 
    return 0;
}
 
void affiche(int *tableau, int tailleTableau)
{
    int i;
 
    for (i = 0 ; i < tailleTableau ; i++)
    {
        printf("%d\n", tableau[i]);
    }
}

On peut aussi utiliser la syntaxe suivante :

void affiche(int tableau[], int tailleTableau)

Enregistrement de données et calcul de moyenne

La mesure d'une valeur issue d'un capteur peut être affectée par du bruit, ce qui entraîne des variations significatives autour d'une valeur moyenne. Pour atténuer cet effet, il est utile de conserver les N dernières mesures et d'en calculer la moyenne.

Lorsqu'une valeur est mesurée par un capteur, il est nécessaire de mettre à jour le tableau de mesures en décalant les valeurs des indices 0 à N-1 vers les indices 1 à N, afin de libérer la case à l'indice 0. La nouvelle valeur est ensuite enregistrée dans cette case d’indice 0.

Version 1

float *tab_mesures;
#define NB_MOYENNE 20
float moy = 1.0;

void mettreAJourTableau(float nouvelleValeur) {
  // Décaler les valeurs vers la gauche
  for (int i = 0; i < NB_MOYENNE - 1; i++) {
    tab_mesures[i] = tab_mesures[i + 1];
  }
  // Ajouter la nouvelle valeur à la fin du tableau
  tab_mesures[NB_MOYENNE - 1] = nouvelleValeur;
}

float calculerMoyenne() {
  float somme = 0;
  for (int i = 0; i < NB_MOYENNE; i++) {
    somme += tab_mesures[i];
  }
  float moyenne = somme / NB_MOYENNE;
  return moyenne;
}


void setup(){
  // création du tableau de mesures des volumes du micros (volts)
  tab_mesures = (float *)malloc(sizeof(float) * NB_MOYENNE);
  // initialisation du tableau de moyenne
  for (int i = 0; i < NB_MOYENNE; i++) {
    tab_mesures[i] = 1.5;
  }
}

void loop(){
  int mesure = analogRead(A0);
  mettreAJourTableau(mesure);
  float moy = calculerMoyenne();
  Serial.println(moy);

}

Version 2

const int N_moy = 10;  // Taille du tableau
int valeurs[N_moy];    // Tableau pour stocker les valeurs

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < N_moy; i++) {
    valeurs[i] = 0;  // Initialisation du tableau à 0
  }
}

void loop() {
  int valeur_lue = lireCapteur();  // Lire la valeur du capteur digital
  mettreAJourTableau(valeur_lue, valeurs);  // Mettre à jour le tableau avec la nouvelle valeur
  float moyenne = calculerMoyenne(valeurs);  // Calculer la moyenne des valeurs
  Serial.print("Moyenne: ");
  Serial.println(moyenne);
  
  delay(1000);  // Attendre 1 seconde avant la prochaine mesure
}

int lireCapteur() {
  // Remplacez ceci par le code pour lire votre capteur digital
  // Exemple : int valeur = digitalRead(pinDuCapteur);
  int valeur = random(0, 1023);  // Générer une valeur aléatoire pour l'exemple
  return valeur;
}

void mettreAJourTableau(int nouvelleValeur, int tableau[]) {
  // Décaler les valeurs vers la gauche
  for (int i = 0; i < N_moy - 1; i++) {
    tableau[i] = tableau[i + 1];
  }
  // Ajouter la nouvelle valeur à la fin du tableau
  tableau[N_moy - 1] = nouvelleValeur;
}

float calculerMoyenne(int tableau[]) {
  int somme = 0;
  for (int i = 0; i < N_moy; i++) {
    somme += tableau[i];
  }
  return (float)somme / N_moy;
}

Version 3

const int N_moy = 10;  // Taille du tableau
int valeurs[N_moy];    // Tableau pour stocker les valeurs

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < N_moy; i++) {
    valeurs[i] = 0;  // Initialisation du tableau à 0
  }
}

void loop() {
  int valeur_lue = lireCapteur();  // Lire la valeur du capteur digital
  mettreAJourTableau(valeur_lue, valeurs);  // Mettre à jour le tableau avec la nouvelle valeur
  float moyenne = calculerMoyenne(valeurs);  // Calculer la moyenne des valeurs
  Serial.print("Moyenne: ");
  Serial.println(moyenne);
  
  delay(1000);  // Attendre 1 seconde avant la prochaine mesure
}

int lireCapteur() {
  // Remplacez ceci par le code pour lire votre capteur digital
  // Exemple : int valeur = digitalRead(pinDuCapteur);
  int valeur = random(0, 1023);  // Générer une valeur aléatoire pour l'exemple
  return valeur;
}

void mettreAJourTableau(int nouvelleValeur, int *tableau) {
  // Décaler les valeurs vers la gauche
  for (int i = 0; i < N_moy - 1; i++) {
    tableau[i] = tableau[i + 1];
  }
  // Ajouter la nouvelle valeur à la fin du tableau
  tableau[N_moy - 1] = nouvelleValeur;
}

float calculerMoyenne(int *tableau) {
  int somme = 0;
  for (int i = 0; i < N_moy; i++) {
    somme += tableau[i];
  }
  return (float)somme / N_moy;
}

Détails Techniques

Tableaux et Pointeurs en C/C++ : Lorsque vous passez un tableau à une fonction, vous passez en fait un pointeur vers le premier élément du tableau. Ainsi, int tableau[] et int *tableau sont équivalents dans le contexte des paramètres de fonction. Les deux notations permettent à la fonction de modifier les éléments du tableau original.

Accès aux éléments : Vous pouvez accéder aux éléments du tableau en utilisant l'indexation habituelle (tableau[i]) dans les deux cas.

En résumé, vous pouvez utiliser l'une ou l'autre notation, celle avec le pointeur (int *tableau) est souvent préférée pour des raisons de clarté dans le contexte de la programmation en C/C++, bien que les deux soient correctes.