Aller au contenu


Photo
- - - - -

Utiliser un tableau 2 dimensions en paramètre d'une fonction


  • Veuillez vous connecter pour répondre
25 réponses à ce sujet

#1 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 732 messages
  • Gender:Male

Posté 16 janvier 2019 - 11:21

Bon suite à ce sujet j'ai modifié mon programme en suivant les conseils de Cocothebo.
Dans ma fonction Kinematic(), je passe donc les paramètres par valeur à l'exception des tableaux, mais c'est légèrement plus lisible.
Pour le tableau SrvAdjust[], l'écriture est nettement plus lisible, mais je n'ai pas réussi le passage par valeur.
Par contre pour le tableau Tab1[][] qui est en 2 dimensions, là je dois avouer que je n'ai pas réussi du tout.
J'ai un peu chercher et visiblement, je ne suis pas le seul à avoir ce problème.

Spoiler


#2 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 9 934 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 17 janvier 2019 - 03:56

Est ce que le code ci dessous peut t'inspirer ? 

 

int tab[2][2]= {{0, 1},
                {2, 3}};

void readtab (int tab[2][2]) {
  for(uint8_t i = 0; i < 2; i++) {
    for(uint8_t j = 0; j < 2; j++) {
      Serial.println(tab[i][j]);
    }
  }
}

void setup() { 
  Serial.begin(115200); 
  readtab(tab);
}

void loop() {

}

Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 
Si vous souhaitez un robot pilotable par internet n'hésitez pas à visiter www.vigibot.com et à lire le sous forum dédié à vigibot!

 

Les réalisations de Mike118  

 

 

 


#3 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 732 messages
  • Gender:Male

Posté 17 janvier 2019 - 08:40

Merci Mike !

Je dois avouer que la simplicité de ton exemple m'a perturbé l'esprit.
J'ai cherché pourquoi je n'avais pas tester cela.
Et j'ai trouvé !
Dans ma fonction, je ne suis pas censé connaitre les tailles du tableau que je dois donc passer en paramètres.
Exemple : void readtab (int tab[][], int l, int c ) { }
Malheureusement, cela ne fonctionne pas.
Cherche sur le web et tu verras que je ne suis pas le premier à avoir ce problème qui revient de manière récurrente.

#4 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 732 messages
  • Gender:Male

Posté 17 janvier 2019 - 11:43

Voici quelque chose que j'avais testé et qui ressemble à ton exemple, mais qui malheureusement ne fonctionne pas.
 
const int lg=2; const int cl=2;
int tab[lg][cl]= {{0, 1},
                  {2, 3}};

void setup() { 
  Serial.begin(9600);
  readtab(tab[0][0],lg,cl);
}

void loop() {}

void readtab (int tab[0][0], int l, int c) {
  for(int i = 0; i < l; i++) {
    for(int j = 0; j < c; j++) {
      Serial.println(tab[i][j]);
    }
  }
}



#5 cocothebo

cocothebo

    Membre passionné

  • Membres
  • PipPipPip
  • 341 messages
  • Gender:Male

Posté 17 janvier 2019 - 05:28

Salut,

 

Et si tu changes

void readtab (int tab[0][0], int l, int c) {

en

void readtab (int** tab, int l, int c) {

Ça devrait marcher, et la de toute façon pour moi tu es dans le cas ou il faut passer par des pointeurs (façon int tab[8] ou int* tab, du point de vue du compilo c'est en gros la même chose, un pointeur vers une zone de données avec des int dedans ).

Comme j'avais expliqué au dessus, le passage par valeur, on le fait surtout pour des types primitifs, pour le reste (tableau, structures, etc), vu que ça risque d'être gros c'est mieux par référence comme ça on risque pas de faire exploser la pile.



#6 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 732 messages
  • Gender:Male

Posté 17 janvier 2019 - 05:47

Ça devrait marcher...

Non, cela ne marche pas. Cela passe à la compilation, on obtient quelque chose comme avec ma version, mais c'est n'importe quoi.

#7 cocothebo

cocothebo

    Membre passionné

  • Membres
  • PipPipPip
  • 341 messages
  • Gender:Male

Posté 17 janvier 2019 - 05:58

Tu appelles bien la fonction avec juste le nom de la variable sans crochet * ou &?

#8 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 732 messages
  • Gender:Male

Posté 17 janvier 2019 - 06:10

Tu appelles bien la fonction avec juste le nom de la variable sans crochet * ou &?

Comme ça, cela ne passe pas à la compilation.
const int lg=2; const int cl=2;
int tab[lg][cl]= {{0, 1},
                  {2, 3}};

void setup() { 
  Serial.begin(9600);
  readtab(tab, lg, cl);
}

void loop() {}

void readtab (int **tab, int l, int c) {
  for(int i = 0; i < l; i++) {
    for(int j = 0; j < c; j++) {
      Serial.println(tab[i][j]);
    }
  }
}



#9 cocothebo

cocothebo

    Membre passionné

  • Membres
  • PipPipPip
  • 341 messages
  • Gender:Male

Posté 17 janvier 2019 - 07:20

Ah oui tu n'alloues pas le tableau dynamiquement, dans ton cas ton type est un int(*)[2] et non un int** donc ça va pas vouloir compiler effectivement.

Juste sans alourdir le tout, un int** c'est un pointeur de pointeur, int(*)[2] c'est un tableau de 2 pointeurs sur des int* (quand on lit un type de variable on part de la droite pas de la gauche).

Et int(*)[2], ça s'écrit aussi int[][2]. 

 

Sans être sur sur, si tu cast a l'appel de la fonction avec un (int**), ça devrait être bon (programmatiquement j'suis sur, pour le résultat je pense que ça marche).

 

Sinon tu peux aussi allouer dynamiquement ton tableau avec un malloc et la tu auras bien un type int**, donc le typage sera homogène.

(Quoi un malloc renvoie en vrai un "void*", qui est un pointeur générique sur n'importe qu'el type, donc c'est n'importe quoi, comme le int**, mais aussi n'importe quell pointeur en fait ;))

 

 

Quand on alloue en "dur" un tableau à deux dim (ce que tu fais vu que tu utilises des const pour sa taille), il faut au moins passer la taille de la deuxième dim (souvent dite colonne) pour que le compilo soit cool.



#10 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 732 messages
  • Gender:Male

Posté 17 janvier 2019 - 07:37

Cela compile, mais ça donne n'importe quoi.
Sinon, Cocothebo et Mike, ma dernière version de programme plus haut, il ne vous convient pas ?
const int lg=2; 
const int cl=2;
int tab[lg][cl]= {{0, 1},
                  {2, 3}};

void setup() { 
  Serial.begin(9600);
  readtab((int**)tab, lg, cl);
}

void loop() {}

void readtab (int **tab, int l, int c) {
  for(int i = 0; i < l; i++) {
    for(int j = 0; j < c; j++) {
      Serial.println(tab[i][j]);
    }
  }
}



#11 cocothebo

cocothebo

    Membre passionné

  • Membres
  • PipPipPip
  • 341 messages
  • Gender:Male

Posté 17 janvier 2019 - 09:00

Moi tout me va :P

 

Mais sinon, je trouve toujours tes notations moins compréhensibles, je parle des notations du genre

*(tb+(srv*lg)+i)

qui peuvent plus facilement s’écrire

 tb[srv][i] (ou un truc du genre)

J'ai toujours trouvé que l'écriture par pointeur pour accéder à une case de tableau peu compréhensible. Surtout quand on commence à utiliser un tableau en deux dimensions (ou plus).

 

Si on reprend la ligne complète ça donne:

if ( *(tb+(srv*lg)+i) >= *(tb+(srv*lg)+i+1)

VS

if ( tb[srv][i] >= tb[srv][i+1] )

Ben moi je trouve la deuxième version plus explicite, c'est bien un tableau qu’on utilise pas juste un entier dans un espace mémoire.

 

De même pour alléger l'écriture et faciliter la lecture/maintenance (et les performances), ton "*(tb+(srv*lg)+i)", comme tu l'utilises plusieurs fois, autant le mettre dans une variable locale, certes ça prendra de l'espace mémoire, mais ça sera plus rapide vu qu'on évite 2 additions et une multiplication (c'est long les multiplications).

 

 

Tout cela c'est pas forcément mieux, surtout la forme d'écriture suivant les habitudes.



#12 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 732 messages
  • Gender:Male

Posté 17 janvier 2019 - 09:21

Ben moi je trouve la deuxième version plus explicite...

Je suis d'accord avec toi. Malheureusement, cela ne fonctionne pas.
Tant que l'on aura pas résolu le problème de passage d'un tableau 2D, je serai obligé d'utiliser cette notation qui n'est pas intuitive, même pour moi.
J'ai cherché sur internet et visiblement, il n'y a pas de solution simple pour un tableau 2D.
Je suis loin d'être le premier à avoir ce problème.

#13 cocothebo

cocothebo

    Membre passionné

  • Membres
  • PipPipPip
  • 341 messages
  • Gender:Male

Posté 17 janvier 2019 - 09:34

Ben tu as dans tous les cas la possibilité de déclarer un peu plus finement to tableau dans la fonction:

void  Kinematic(int lg, int tb[][nbFeet], int nsrv, int vel, int foot, int *srvadjust){

Normalement ça c'est valide et comme ton tableau est déclaré en utilisant cette constante, ça me semble être ce que tu veux.

 

J'ai pas de quoi tester sous la main, je regarderai demain pour quoi les autres façons ne fonctionnent pas...

 

 

Ya aussi la solution du malloc pour ton tableau, la le passage par int** devrait fonctionner avec [][].



#14 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 732 messages
  • Gender:Male

Posté 18 janvier 2019 - 08:04

Cocothebo, j'ai testé ta solution, mais sans succès.

Je pense avoir trouvé une solution qui est un bon compromis.
L'idée, c'est de passé un tableau en 2 dimensions, mais de le traiter comme un tableau en une seule dimension.
Ce n'est pas de moi, l'idée est archi connu, et je la connaissais déjà, mais jusqu'à là, je n'avais pas réussi à faire quelque chose d'aussi lisible.
Qu'en pensez-vous ?
 
// Passage d'un tableau à 2 dimensions (fonctionne !)
const int l=3;
const int c=2;
int t[l][c]= {{0, 1},
              {2, 3},
              {4, 5}};
void setup() { 
  Serial.begin(9600); 
  readtab(t[0], l, c);
}
void loop() {}
 
void readtab(int *t, int lg, int cl){
  for(int i=0; i<lg; i++){
    for(int j=0; j<cl; j++){
      Serial.print(t[i*cl+j]);Serial.print(" ");
    }
    Serial.println();
  }
}



#15 cocothebo

cocothebo

    Membre passionné

  • Membres
  • PipPipPip
  • 341 messages
  • Gender:Male

Posté 18 janvier 2019 - 10:10

C'est effectivement une solution, mais moi j'aime pas trop le fait de conceptuellement passer le tableau de 2 dimensions à 1, même si ça devrait fonctionner partout.

 

Dans ce cas on sous entend que la représentation mémoire du tableau à deux dimensions est linéaire (en fait une dimension), mais je ne suis pas sur que ce soit obligatoire en C (ie que le langage C impose de représenter un tableau multi dimension ainsi).

 

 

Sinon je viens de tester:

const int l=3;
const int c=2;
int t[l][c]= {{0, 1},
              {2, 3},
              {4, 5}};
void setup() { 
  Serial.begin(9600); 
  readtab(t, l, c);
}
void loop() {}
 
void readtab(int (*t)[c], int lg, int cl){
  for(int i=0; i<lg; i++){
    for(int j=0; j<cl; j++){
      Serial.print(t[i][j]);Serial.print(" ");
    }
    Serial.println();
  }
}

fonctionne bien comme prévu. et on peut remplacer le "int (*t)[c]" par "int t[][c]" qui est identique.

 

 

En fait j'ai regardé rapidemment, mais tout doit venir d'un problème de typage, même si un tableau à la fin c'est du pointeur, int** c'est un pointeur de pointeur sur int alors que int[][] c'est bien un tableau à deux dimensions de int.

Et ce problème n'est présent que parce que le tableau est alloué statiquement en varibale "globale". Avec un malloc on aurait pas de soucis (bon après pour remplir le tableau c'est moins joli je sais).

 

J'essaye de voir comment en castant ou autre on peut faire une vraie utilisation de tableau à partir d'un double pointeur mais ce n'est pe pas possible.

 

Dans tous les cas pour moi la solution la plus propre est de donner la deuxième dimension en entrée de la fonction, c'est la bonne sémantique en C  qui demande que toutes les dimensions sauf la premières doivent être données pour l'initialisation d'un tableau statique.



#16 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 732 messages
  • Gender:Male

Posté 18 janvier 2019 - 10:32

T'es le meilleur !
 

on peut remplacer le "int (*t)[c]" par "int t[][c]" qui est identique.

Encore mieux, "int t[l][c]"

Voici donc une version très lisible. Cela semble évident, une fois terminé, mais le diable était dans le détail.
 
// Passage d'un tableau à 2 dimensions (fonctionne !)
const int l=3;
const int c=2;
int t[l][c]= {{0, 1},
              {2, 3},
              {4, 5}};
void setup() { 
  Serial.begin(9600); 
  readtab(t, l, c);
}
void loop() {}
 
void readtab(int t[l][c], int lg, int cl){
  for(int i=0; i<lg; i++){
    for(int j=0; j<cl; j++){
      Serial.print(t[i][j]);Serial.print(" ");
    }
    Serial.println();
  }
}



#17 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 732 messages
  • Gender:Male

Posté 18 janvier 2019 - 10:39

Ah zut ! Ce n'est pas bon !
On a bouclé. On est revenu au même problème que j'avais souligné sur la version de Mike.
Dans la description de la fonction, on ne connait pas 'l' et 'c'.

#18 Path

Path

    Made By Humans

  • Modérateur
  • PipPipPipPipPip
  • 2 504 messages
  • Gender:Male
  • Location:Paris

Posté 18 janvier 2019 - 10:55

Dans le cas présent, t'es pas obligé de passer le tableau et les 2 int en paramètre de la procédure readtab. Je crois que ça fonctionne si tu ne mets pas d'arguments void readtab() { ... Et que tu utilise directement t, l et c dans la procédure.

#19 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 732 messages
  • Gender:Male

Posté 18 janvier 2019 - 03:23

Et que tu utilise directement t, l et c dans la procédure.

Oui, bien entendu que cela fonctionne, mais c'est précisément ce que je ne veux pas faire.
L'idée, c'est que la fonction Kinematic() soit une boite noire réutilisable dans un autre programme.
Néanmoins, le code étant peu lisible, on m'a suggéré, à juste titre, de le modifier.
C'est ce que j'ai fait dans une certaine mesure que l'on essaye depuis quelques jours de dépasser.
L'un des intérêt à la lisibilité serait, par exemple, de pouvoir écrire le même code dans un autre langage.
Chose impossible, tel quel.

#20 cocothebo

cocothebo

    Membre passionné

  • Membres
  • PipPipPip
  • 341 messages
  • Gender:Male

Posté 18 janvier 2019 - 05:15

Un malloc et on en parle plus hein ;)

 

mais même sans on peut faire relativement propre, même si le paramètre sera un int * et non int**:

// Passage d'un tableau à 2 dimensions (fonctionne !)
const int l=3;
const int c=2;
int t[l][c]= {{0, 1},
              {2, 3},
              {4, 5}};
void setup() { 
  Serial.begin(9600);
  readtab(&t[0][0], l, c);
  Serial.println();Serial.println();Serial.println();Serial.println();
  readtab((int *)t, l, c);
}
void loop() {}
 
void readtab(int *tab, int lg, int cl){
  for(int i=0; i<lg*cl; i++){
      Serial.print(tab[i]);
      Serial.print(" ");
  }
}

La version avec malloc et l'utilisation d'un int** (on peut faire avec un int* comme au dessus, mais faut changer les malloc):

// Passage d'un tableau à 2 dimensions (fonctionne !)
const int l = 3;
const int c = 2;
int t[l][c] = {{0, 1},
  {2, 3},
  {4, 50}
};
void setup() {
  Serial.begin(9600);
  int** myTab = malloc(l * sizeof(int));
  for (int i = 0 ; i < l ; i++) {
    myTab[i] = malloc(c * sizeof(double));
  }

  for (int i = 0; i < l; i++) {
    for (int j = 0; j < c; j++) {
      myTab[i][j] = t[i][j];
    }
  }

  readtab(myTab, l, c);
}
void loop() {}

void readtab(int **tab, int lg, int cl) {
  for (int i = 0; i < lg; i++) {
    for (int j = 0; j < cl; j++) {
      Serial.print(tab[i][j]); Serial.print(" ");
    }
    Serial.println();
  }
}

 


  • Path aime ceci




0 utilisateur(s) li(sen)t ce sujet

0 members, 0 guests, 0 anonymous users