[C] Envoyer des tableaux multidimensionnels à des fonctions
Posté le 18/08/2014 11:53
Qui n'as jamais eu besoin d'envoyer des tableaux multidimensionnels à des fonctions pour pouvoir les modifier ? Malheureusement, il n'est pas possible d'envoyer un pointeur sur ce tableau puis d'y accéder comme ceci : mon_tableau[y][x]. Toutefois, des astuces existent, je vais vous en présenter quelques unes ici, à vous de faire votre choix.
Prérequis : avoir une bonne connaissance des pointeurs et des structures.
Méthode 1 : accès direct
Un tableau à 2 dimensions n'est rien d'autre qu'un regroupement d'un certain nombre (nombre de lignes) de tableaux 1D qui se suivent dans la mémoire. De la même manière, les tableaux 3D sont des regroupements de tableaux 2D, et donc de manière générale un tableau nD est un regroupement de tableaux (n-1)D. On peut alors acceder manuellement à une case en multipliant chaque indice par le nombre de case de sa dimension.
Exemple : je veux créer une fonction qui initialise un tableau de dimensions
dim_x et
dim_y en matrice diagonale (des 1 dans la diagonale, 0 ailleurs).
>>> Accéder au code <<<
Avantages :
- Le tableau peut être de taille variable, il suffit d'envoyer les dimensions à la fonction, celle-ci s'adapte automatiquement.
Inconvénients :
- Le nombre de dimensions ne peut pas être variable.
- Plus il y a de dimensions, plus la déclaration de la fonction est lourde.
On peut toutefois utiliser un tableau 1D contenant les dimensions du tableau.
- Le code d'accès aux cases du tableau peut vite devenir incompréhensible.
Méthode 2 : accès par pointeur sur structure
Une autre méthode consiste à créer une structure contenant un tableau puis à envoyer un pointeur sur cette structure aux fonctions en ayant besoin. Les dimensions du tableau étant contenues dans la déclaration de la structure, on peut alors y accéder facilement.
Exemple de la matrice diagonale avec cette méthode :
>>> Accéder au code <<<
Avantages :
- La lisibilité du code n'est que peut perturbée (il ne faut que penser à accéder au tableau dans la structure avec l'opérateur –>)
- On peut ajouter des informations dans la structure "Tableau" : un nom, etc., l'accès aux données ne changera pas.
- Un seul argument suffit pour passer le tableau.
Inconvénients :
- Les dimensions du tableaux ne sont pas variables. Pour créer des tableaux de dimensions différentes, il faut plusieurs structures différentes.
- On ne peut se passer de l'opérateur –> pour accéder aux données du tableau.
Méthode 3 : accès par pointeur sur union.
Cette dernière méthode, qui utilise
les unions, permet de palier à certains désavantages de la méthode 2. On créé une union "Tableau" dans laquelle on défini tout les types de tableaux que l'on va utiliser. On peut alors créer des tableaux de tailles diverses et y accéder comme avec les structures. Exemple :
>>> Accéder au code <<<
Avantages :
- La lisibilité du code n'est que peut perturbée (il ne faut que penser à accéder au tableau dans la structure avec l'opérateur –>)
- On peut utiliser beaucoup de types différents de tableaux.
- Deux arguments suffisent pour passer le tableau et son type de données.
Inconvénients :
- L'union fait le poids du tableau le plus lourd, et ce quel que soit le type utilisé.
- On ne peut se passer de l'opérateur –> pour accéder aux données du tableau.
Je pense avoir fait le tour, si vous avez d'autres techniques, faites m'en part, je me ferais un plaisir de les ajouter ici. De même, n'hésitez pas à signaler d'éventuelles erreurs.
A bientôt sur Planète Casio !
Citer : Posté le 18/08/2014 11:57 | #
Bon, ça, c'est fait.
Peut-être que ça pourra nous éviter quelques problèmes...
Citer : Posté le 18/08/2014 12:57 | #
πEn allouant dynamiquement un tableau à deux dimensions, on peut l'envoyer à une fonction sans problème.
Une petite class que j'avais programmé (en C++, il y a très peu de méthode, libre à vous d'en rajouter):
template <class T>
class Matrix
/** class permettant de définir un tableau à deux dimensions **/
{
private :
unsigned int _h;
unsigned int _l;
T** _t;
public :
Matrix(){
_h = 0;
_l = 0;
_t = NULL;
}
Matrix( unsigned int h, unsigned int l ) {
_t = (T**) malloc ( sizeof (T*) * h) ;
for (unsigned int i = 0 ; i < l ; i++)
_t[i] = (T*) malloc ( sizeof (T) * l ) ;
_h = h;
_l = l;
}
virtual ~Matrix(){
this->free();
}
unsigned int get_h() const{
return _h;
}
unsigned int get_l() const{
return _l;
}
void free() {
for (unsigned int i = 0 ; i < _h ; i++)
free(_t[i]);
free(_t);
_h = 0;
_l = 0;
}
void init(T obj){
for (unsigned int i = 0 ; i < _h ; i++)
for (unsigned int j = 0 ; j < _l ; j++)
_t[i][j] = obj;
}
T* operator[] (int i) {
return _t[i];
}
};
Exemple d'utilisation :
Matrix <int> tableau(5,5); //On créé un tableau de taille 5*5
tableau[2][1] = 2;
La même chose en C
int** Matrix_new( int h, int l)
{
int** tab = NULL;
tab = (int**) malloc ( sizeof (int*) * h) ;
for (int i = 0 ; i < l ; i++)
tab[i] = (int*) malloc ( sizeof (int) * l );
return tab;
}
void Matrix_free(int** tab, int h, int l) {
for (int i = 0 ; i < h ; i++)
free(tab[i]);
free(tab);
}
void Matrix_init(int** tab, int h, int l, int val){
for (int i = 0 ; i < h ; i++)
for (int j = 0 ; j < l ; j++)
tab[i][j] = val;
}
L'utilisation est différent :
int**tab = Matrix_new(10,10);
tab[9][9] = 5;
Matrix_free(tab,10,10);
Etant donné que les templates n'existent pas en C, on est obligé de modifier les fonction si on veut que la fonction Matrix_new() retourne un long** ou un struct t** par exemple.
Citer : Posté le 18/08/2014 13:41 | #
Oui mais là c'est un vrai tableau de pointeurs, alors que dans le cas habituel c'est juste un unique bloc de donnees.