Tout d'abord quelques précisions sur la gestion des tableaux en C et surtout en C99. Le terme tableau dynamique est ambigüe dans le sens ou la taille des tableaux ne change pas. Elle est juste fixée à l'éxecution et non pas à la compilation.
Ce qui veut dire que si tu déclare : int tbl[3][5]; Le compilateur va produire du code pour allouer 15 emplacements sur la pile, et quand tu va indicer ton tableau, il va produire du code qui fait x * 5 + y. 5 est donc ici une constante qu'il peut stocker dans la pile, dans un registre ou dans l'instruction elle-même.
Quand tu fait : int tbl[X][Y]; Il va produire une allocation sur la pile qui commence par calculer la taille et ensuite réserve l'espace sur la pile. Pour l'indicage, c'est pareil sauf que cette fois ci la taille est forcement dans la pile ou dans un registre et ne peut pas être dans l'instruction elle-même.
Dans les deux cas, une fois la mémoire allouée sur la pile, il n'est plus possible de changer la taille du tableau; et ce n'est pas lié au C mais au principe même des allocation sur la pile.
Si l'on utilise la gestion interne des tableau par C99, on a un programme simple et lisible, aux prix de devoir tout mettre dans la même fonction.
Les solution alternative sont (ne pas oublier que l'on travail ici avec plusieurs tableaux) :
1/ continuer d'utiliser la gestion classique mais avec de multiple fonctions qui passe en plus des tableaux, leur dimension. On obtient des prototypes à rallonge et on augmente les risque d'erreurs au moment du passage de paramètres. Si l'on passe 4 tableaux à 2 dimension, ça fait 12 paramètres.
2/ on fait une grosse structure dans laquelle on passe le tout, ce qui nous donne :
typedef struct {
int *t1, x1, y1;
int *t2, x2, y2;
int *t3, x3, y3;
} state_t;
void etape1(state_t *st) {
...
st->t1[x * st->y1 + y] = val;
...
}
void algo(void) {
int x1 = ...;
....
int t1[...], t2[...], t3[...];
state_t st = {t1, x1, x1, t2, x2, x2, t3, x3, x3};
etape1(&st);
...
}
C'est un schéma très classique en C mais c'est tout sauf lisible. L'accès à un élément dans les sous-fonction est ien moins simple car le calcul que le compilo peut faire tout seul doit ici être fait de manière explicite.
La création et l'initialisation de la structure montre que l'on retrouve le même problème que lors du passage de paramètre: Une grosse liste de valeur à passer dans le bon ordre sans ce tromper.
3/ Faire une petite bibliothèque à côter pour gérer les tableaux. On note tout d'abord que même si elle est parfaite elle ne résout pas le problème. Dans le cas de l'algo de CRF, tu a 7 tableaux et 5 variables à passer aux différentes fonction, donc même si tes tableaux sont réduit à une structure, ça fait encore 13 variables donc tu est obligé de passer par une autre structure qui englobe le tout.
Ensuite, il devient difficile d'allouer cette structure dans la pile sans faire des acrobaties, imaginons la structure suivante :
typedef struct {
int *d, sx, sy;
} table_t;
Pour créer un tableau sur la pile il faut que tu ai quelquechose allouer sur la pile à mettre dans *d, ce qui te donne un code d'initialisation du genre :
int raw_d[x * y];
table_t tbl = {raw_d, x, y};
Ce qui n'est pas vraiment lisible, et tu as toujours le problème de l'indiçage.
Pour l'indiçage, une seule solution : une macro ce qui implique une syntaxe qui n'est plus celle des tableaux, donc moins lisible.
Pour la création des tables, tu ne peux pas utiliser directement une macro car la variable raw_d doit avoir un nom différent à chaque fois.
Donc la seule solution qui reste est de passer par une fonction qui fait un malloc et il ne faudra donc pas oublier de faire un free à la fin...
Comme tu peut le voir, c'est un truc que j'ai déjà pas mal potassé, et avant de faire des fonctions de cette taille je réfléchit toujours plusieurs fois et je cherche d'autre solutions.
Pour être honnête il y a une éventuelle solution au sujet de l'initialisation, tu peut faire une macro qui crée ta structure avec un "alloca" pour allouer les donnée sur la pile, mais ce n'est plus portable et ça ne résout pas le problème de l'indiçage qui est illisible.
Le problème ici n'est pas lié au langage mais au calcul que l'on souhaite faire. Ce calcul nécessite de construire de nombreux résultats intermédiaire qui sont utilisés tout au long des différentes étapes du calcul. Si l'on souhaite découper ce calcul en sous fonctions il est donc nécessaire de pouvoir faire passer un grand nombre de paramètre d'une fonction à l'autre sans que cela deviennent illisible.
Dans le cas du C ça demanderais deux modifications :
- pouvoir passer un tableaux dynamique à plusieurs dimensions dans une structure ;
- l'ajout d'une directive du style du "with" en pascal qui permet d'importer dans l'espace lexical local les nom d'une structure. ça permettrait de ce passer du "st->".
Cela ne résoudrais pas complètement le problème car il faudrait toujours construire et détruire cette structure, mais les sous-fonctions pourrait être lisibles.
L'aspect gestion de la mémoire était plus un élément anecdotique dans mon argumentaire au début, quand je listait les avantages que l'on a parfois faire de grosse fonctions ou gros blocs indentés.
[^] # Re: Langage idéal...
Posté par beagf . En réponse au journal Nimrod, ça se rapproche du langage idéal. Évalué à 2.
Ce qui veut dire que si tu déclare : int tbl[3][5]; Le compilateur va produire du code pour allouer 15 emplacements sur la pile, et quand tu va indicer ton tableau, il va produire du code qui fait x * 5 + y. 5 est donc ici une constante qu'il peut stocker dans la pile, dans un registre ou dans l'instruction elle-même.
Quand tu fait : int tbl[X][Y]; Il va produire une allocation sur la pile qui commence par calculer la taille et ensuite réserve l'espace sur la pile. Pour l'indicage, c'est pareil sauf que cette fois ci la taille est forcement dans la pile ou dans un registre et ne peut pas être dans l'instruction elle-même.
Dans les deux cas, une fois la mémoire allouée sur la pile, il n'est plus possible de changer la taille du tableau; et ce n'est pas lié au C mais au principe même des allocation sur la pile.
Si l'on utilise la gestion interne des tableau par C99, on a un programme simple et lisible, aux prix de devoir tout mettre dans la même fonction.
Les solution alternative sont (ne pas oublier que l'on travail ici avec plusieurs tableaux) :
1/ continuer d'utiliser la gestion classique mais avec de multiple fonctions qui passe en plus des tableaux, leur dimension. On obtient des prototypes à rallonge et on augmente les risque d'erreurs au moment du passage de paramètres. Si l'on passe 4 tableaux à 2 dimension, ça fait 12 paramètres.
2/ on fait une grosse structure dans laquelle on passe le tout, ce qui nous donne :
typedef struct {
int *t1, x1, y1;
int *t2, x2, y2;
int *t3, x3, y3;
} state_t;
void etape1(state_t *st) {
...
st->t1[x * st->y1 + y] = val;
...
}
void algo(void) {
int x1 = ...;
....
int t1[...], t2[...], t3[...];
state_t st = {t1, x1, x1, t2, x2, x2, t3, x3, x3};
etape1(&st);
...
}
C'est un schéma très classique en C mais c'est tout sauf lisible. L'accès à un élément dans les sous-fonction est ien moins simple car le calcul que le compilo peut faire tout seul doit ici être fait de manière explicite.
La création et l'initialisation de la structure montre que l'on retrouve le même problème que lors du passage de paramètre: Une grosse liste de valeur à passer dans le bon ordre sans ce tromper.
3/ Faire une petite bibliothèque à côter pour gérer les tableaux. On note tout d'abord que même si elle est parfaite elle ne résout pas le problème. Dans le cas de l'algo de CRF, tu a 7 tableaux et 5 variables à passer aux différentes fonction, donc même si tes tableaux sont réduit à une structure, ça fait encore 13 variables donc tu est obligé de passer par une autre structure qui englobe le tout.
Ensuite, il devient difficile d'allouer cette structure dans la pile sans faire des acrobaties, imaginons la structure suivante :
typedef struct {
int *d, sx, sy;
} table_t;
Pour créer un tableau sur la pile il faut que tu ai quelquechose allouer sur la pile à mettre dans *d, ce qui te donne un code d'initialisation du genre :
int raw_d[x * y];
table_t tbl = {raw_d, x, y};
Ce qui n'est pas vraiment lisible, et tu as toujours le problème de l'indiçage.
Pour l'indiçage, une seule solution : une macro ce qui implique une syntaxe qui n'est plus celle des tableaux, donc moins lisible.
Pour la création des tables, tu ne peux pas utiliser directement une macro car la variable raw_d doit avoir un nom différent à chaque fois.
Donc la seule solution qui reste est de passer par une fonction qui fait un malloc et il ne faudra donc pas oublier de faire un free à la fin...
Comme tu peut le voir, c'est un truc que j'ai déjà pas mal potassé, et avant de faire des fonctions de cette taille je réfléchit toujours plusieurs fois et je cherche d'autre solutions.
Pour être honnête il y a une éventuelle solution au sujet de l'initialisation, tu peut faire une macro qui crée ta structure avec un "alloca" pour allouer les donnée sur la pile, mais ce n'est plus portable et ça ne résout pas le problème de l'indiçage qui est illisible.
Le problème ici n'est pas lié au langage mais au calcul que l'on souhaite faire. Ce calcul nécessite de construire de nombreux résultats intermédiaire qui sont utilisés tout au long des différentes étapes du calcul. Si l'on souhaite découper ce calcul en sous fonctions il est donc nécessaire de pouvoir faire passer un grand nombre de paramètre d'une fonction à l'autre sans que cela deviennent illisible.
Dans le cas du C ça demanderais deux modifications :
- pouvoir passer un tableaux dynamique à plusieurs dimensions dans une structure ;
- l'ajout d'une directive du style du "with" en pascal qui permet d'importer dans l'espace lexical local les nom d'une structure. ça permettrait de ce passer du "st->".
Cela ne résoudrais pas complètement le problème car il faudrait toujours construire et détruire cette structure, mais les sous-fonctions pourrait être lisibles.
L'aspect gestion de la mémoire était plus un élément anecdotique dans mon argumentaire au début, quand je listait les avantages que l'on a parfois faire de grosse fonctions ou gros blocs indentés.