Je m'interroge sur une construction peu orthodoxe que je dois utiliser
en C (=E0 savoir un pointeur de tableaux).
J'ai une fonction dont le prototype est =E0 peu pr=E8s le suivant :
void f(float (*tab)[N], /* reste des arguments */);
Jusque l=E0, tout va bien. Dans le main(), j'alloue "tab" =E0 l'aide des
lignes suivantes :
#define M ...
#define N ...
int main(void)
{
/* d=E9clarations ... */
float (*t)[N];
t =3D malloc(sizeof(float) * M * N);
if (t =3D=3D NULL) { /* gestion d'erreur */ }
/* ... */
f(t, ...);
return 0;
}
J'ai cherch=E9 un peu partout, demand=E9 =E0 des personnes qui =E0 ma
connaissance connaissent bien la norme, et ce code semble l=E9gal.
J'aimerais d'une part avoir confirmation, et d'autre part savoir si
quelqu'un pourrait me dire o=F9 dans la norme je peux confirmer cela.
Tu introduis une variable, ce n'est plus du tout le même code.
du code initial suggère qu'on n'alloue pas à l'exécution.
Ah bon ? tu trouves qu'il est plus facile d'aller chercher ... = malloc(2500 * 345 * sizeof(truc) ) et de changer le 2500 par 3500, parce que les résultats du code Navier-Stockes ne sont pas probants, ou parce que on peut se permettre de passer plus de temps dans les calculs matriciels ?
Et de recommencer à l'envers lorsque les résultats ne s'améliorent pas...
Comprends pas ta réaction, j'ai rien contre les macro-constantes, bien au contraire. Dans le code du PO, tout suggère que les choix de taille se font à la compilation (typique d'un #define) et non à l'exécution, et mise à part le problème d'allocation sur la pile, il est assez naturel de songer à des tableaux statiques comme suggéré par LL.
Pour élargir, je pense qu'on doit pouvoir enseigner le C presque seulement avec des exemples mais c'est un art que de savoir construire les exemples. Pas d'affolement, cette remarque ne s'adresse ni à toi ni au PO d'ailleurs.
Antoine Leca a écrit :
Le 02/06/2009 18:03, candide écrivit :
Certes mais la directive
#define M
... peut être modifiée en
int m;
#define M m
en un instant,
Tu introduis une variable, ce n'est plus du tout le même code.
du code initial suggère qu'on n'alloue pas à l'exécution.
Ah bon ? tu trouves qu'il est plus facile d'aller chercher
... = malloc(2500 * 345 * sizeof(truc) )
et de changer le 2500 par 3500, parce que les résultats du code
Navier-Stockes ne sont pas probants, ou parce que on peut se permettre
de passer plus de temps dans les calculs matriciels ?
Et de recommencer à l'envers lorsque les résultats ne s'améliorent pas...
Comprends pas ta réaction, j'ai rien contre les macro-constantes, bien au
contraire. Dans le code du PO, tout suggère que les choix de taille se font à la
compilation (typique d'un #define) et non à l'exécution, et mise à part le
problème d'allocation sur la pile, il est assez naturel de songer à des tableaux
statiques comme suggéré par LL.
Pour élargir, je pense qu'on doit pouvoir enseigner le C presque seulement avec
des exemples mais c'est un art que de savoir construire les exemples. Pas
d'affolement, cette remarque ne s'adresse ni à toi ni au PO d'ailleurs.
Tu introduis une variable, ce n'est plus du tout le même code.
du code initial suggère qu'on n'alloue pas à l'exécution.
Ah bon ? tu trouves qu'il est plus facile d'aller chercher ... = malloc(2500 * 345 * sizeof(truc) ) et de changer le 2500 par 3500, parce que les résultats du code Navier-Stockes ne sont pas probants, ou parce que on peut se permettre de passer plus de temps dans les calculs matriciels ?
Et de recommencer à l'envers lorsque les résultats ne s'améliorent pas...
Comprends pas ta réaction, j'ai rien contre les macro-constantes, bien au contraire. Dans le code du PO, tout suggère que les choix de taille se font à la compilation (typique d'un #define) et non à l'exécution, et mise à part le problème d'allocation sur la pile, il est assez naturel de songer à des tableaux statiques comme suggéré par LL.
Pour élargir, je pense qu'on doit pouvoir enseigner le C presque seulement avec des exemples mais c'est un art que de savoir construire les exemples. Pas d'affolement, cette remarque ne s'adresse ni à toi ni au PO d'ailleurs.
Jean-Claude Arbaut
candide wrote:
Jean-Claude Arbaut a écrit :
Pour éviter de coder les dimensions en dur, ne peut-on pas faire la chose suivante ? (en tout cas "gcc -ansi -Wall" ne crie pas)
double get(int n, int p, double t[n][p], int i, int j) { return t[i][j]; }
C'est dans le nouveau standard: -stdÉ9 et ça passe ;-)
L'allocation peut se faire avec double (*t)[];
Tiens, j'imaginais pas qu'on pouvait déclarer un pointeur vers un tableau incomplet.
On peut garder la dernière dimension incomplète. C'est pour ça que je galère avec le tableau à trois dimensions: il y en a une de trop, il veut pour celle-là une taille constante. Je suppose que le coup a été prévu par le C99: si on peut déclarer des fonctions à tableaux variables, il faut bien pouvoir déclarer les arguments qu'on leur passe... j'espère :o)
t = (double (*)[])calloc(n*p, sizeof(double));
Tu castes le retour de calloc() ?
Bonne remarque :-) Je crois que je suis tombé un jour sur un compilateur qui fait des warnings sur les malloc/calloc non castés, du coup c'est resté. Je ne me rappelle pas les circonstances exactes (peut-être un gcc 2.x sous macosx, mais je le jurerais pas).
Tiens, pour me simplifier la vie j'utilisais ça :
#define new(n,t) ((t *)calloc(n, sizeof(t)))
candide wrote:
Jean-Claude Arbaut a écrit :
Pour éviter de coder les dimensions en dur, ne peut-on pas faire
la chose suivante ? (en tout cas "gcc -ansi -Wall" ne crie pas)
double get(int n, int p, double t[n][p], int i, int j) {
return t[i][j];
}
C'est dans le nouveau standard: -stdÉ9 et ça passe ;-)
L'allocation peut se faire avec
double (*t)[];
Tiens, j'imaginais pas qu'on pouvait déclarer un pointeur vers un tableau incomplet.
On peut garder la dernière dimension incomplète. C'est pour ça
que je galère avec le tableau à trois dimensions: il y en a une
de trop, il veut pour celle-là une taille constante. Je suppose
que le coup a été prévu par le C99: si on peut déclarer des
fonctions à tableaux variables, il faut bien pouvoir
déclarer les arguments qu'on leur passe... j'espère :o)
t = (double (*)[])calloc(n*p, sizeof(double));
Tu castes le retour de calloc() ?
Bonne remarque :-) Je crois que je suis tombé un jour sur un compilateur
qui fait des warnings sur les malloc/calloc non castés, du coup c'est
resté. Je ne me rappelle pas les circonstances exactes (peut-être un
gcc 2.x sous macosx, mais je le jurerais pas).
C'est dans le nouveau standard: -stdÉ9 et ça passe ;-)
L'allocation peut se faire avec double (*t)[];
Tiens, j'imaginais pas qu'on pouvait déclarer un pointeur vers un tableau incomplet.
On peut garder la dernière dimension incomplète. C'est pour ça que je galère avec le tableau à trois dimensions: il y en a une de trop, il veut pour celle-là une taille constante. Je suppose que le coup a été prévu par le C99: si on peut déclarer des fonctions à tableaux variables, il faut bien pouvoir déclarer les arguments qu'on leur passe... j'espère :o)
t = (double (*)[])calloc(n*p, sizeof(double));
Tu castes le retour de calloc() ?
Bonne remarque :-) Je crois que je suis tombé un jour sur un compilateur qui fait des warnings sur les malloc/calloc non castés, du coup c'est resté. Je ne me rappelle pas les circonstances exactes (peut-être un gcc 2.x sous macosx, mais je le jurerais pas).
Comprends pas ta réaction, j'ai rien contre les macro-constantes, bien au contraire. Dans le code du PO, tout suggère que les choix de taille se font à la compilation (typique d'un #define) et non à l'exécution,
Oui, c'est pour ça que je demandais pourquoi pas float t[M][N]. Dans le cas général je connais les avantages de l'allocation dynamique, mais là ça n'avait pas l'air d'être le cas.
et mise à part le problème d'allocation sur la pile,
Là j'ai appris un truc ! Je ne savais pas du tout (ou j'avais oublié) que ma suggestion et malloc n'utilisaient pas la même partie de la mémoire. (Sans doute un inconvénient du C par l'exemple.) Qu'est-ce qui détermine l'espace libre pour la pile ?
-- LL
Le 3 juin 2009, candide a écrit :
Comprends pas ta réaction, j'ai rien contre les macro-constantes, bien au
contraire. Dans le code du PO, tout suggère que les choix de taille se font à la
compilation (typique d'un #define) et non à l'exécution,
Oui, c'est pour ça que je demandais pourquoi pas float t[M][N]. Dans le
cas général je connais les avantages de l'allocation dynamique, mais là ça
n'avait pas l'air d'être le cas.
et mise à part le problème d'allocation sur la pile,
Là j'ai appris un truc ! Je ne savais pas du tout (ou j'avais oublié) que
ma suggestion et malloc n'utilisaient pas la même partie de la mémoire.
(Sans doute un inconvénient du C par l'exemple.) Qu'est-ce qui détermine
l'espace libre pour la pile ?
Comprends pas ta réaction, j'ai rien contre les macro-constantes, bien au contraire. Dans le code du PO, tout suggère que les choix de taille se font à la compilation (typique d'un #define) et non à l'exécution,
Oui, c'est pour ça que je demandais pourquoi pas float t[M][N]. Dans le cas général je connais les avantages de l'allocation dynamique, mais là ça n'avait pas l'air d'être le cas.
et mise à part le problème d'allocation sur la pile,
Là j'ai appris un truc ! Je ne savais pas du tout (ou j'avais oublié) que ma suggestion et malloc n'utilisaient pas la même partie de la mémoire. (Sans doute un inconvénient du C par l'exemple.) Qu'est-ce qui détermine l'espace libre pour la pile ?
-- LL
Jean-Claude Arbaut
Lucas Levrel wrote:
Le 3 juin 2009, candide a écrit :
Comprends pas ta réaction, j'ai rien contre les macro-constantes, bien au contraire. Dans le code du PO, tout suggère que les choix de taille se font à la compilation (typique d'un #define) et non à l'exécution,
Oui, c'est pour ça que je demandais pourquoi pas float t[M][N]. Dans le cas général je connais les avantages de l'allocation dynamique, mais là ça n'avait pas l'air d'être le cas.
et mise à part le problème d'allocation sur la pile,
Là j'ai appris un truc ! Je ne savais pas du tout (ou j'avais oublié) que ma suggestion et malloc n'utilisaient pas la même partie de la mémoire.
Il suffit d'allouer en dehors de la fonction pour ne plus avoir de problème. La variable est alors en dur dans l'exécutable, dans le .bss en principe (ou un équivalent). Pour des gros tableaux, ça ne va pas changer grand chose, puisque malloc ferait de toute façon un mmap (sous linux au moins).
(Sans doute un inconvénient du C par l'exemple.) Qu'est-ce qui détermine l'espace libre pour la pile ?
Il me semble que c'est codé dans l'exécutable (modifiable par le lieur ?), et que l'OS a aussi une limite.
Lucas Levrel wrote:
Le 3 juin 2009, candide a écrit :
Comprends pas ta réaction, j'ai rien contre les macro-constantes, bien au
contraire. Dans le code du PO, tout suggère que les choix de taille se font à la
compilation (typique d'un #define) et non à l'exécution,
Oui, c'est pour ça que je demandais pourquoi pas float t[M][N]. Dans le
cas général je connais les avantages de l'allocation dynamique, mais là ça
n'avait pas l'air d'être le cas.
et mise à part le problème d'allocation sur la pile,
Là j'ai appris un truc ! Je ne savais pas du tout (ou j'avais oublié) que
ma suggestion et malloc n'utilisaient pas la même partie de la mémoire.
Il suffit d'allouer en dehors de la fonction pour ne plus
avoir de problème. La variable est alors en dur dans l'exécutable,
dans le .bss en principe (ou un équivalent). Pour des gros
tableaux, ça ne va pas changer grand chose, puisque malloc
ferait de toute façon un mmap (sous linux au moins).
(Sans doute un inconvénient du C par l'exemple.) Qu'est-ce qui détermine
l'espace libre pour la pile ?
Il me semble que c'est codé dans l'exécutable (modifiable par le lieur
?), et que l'OS a aussi une limite.
Comprends pas ta réaction, j'ai rien contre les macro-constantes, bien au contraire. Dans le code du PO, tout suggère que les choix de taille se font à la compilation (typique d'un #define) et non à l'exécution,
Oui, c'est pour ça que je demandais pourquoi pas float t[M][N]. Dans le cas général je connais les avantages de l'allocation dynamique, mais là ça n'avait pas l'air d'être le cas.
et mise à part le problème d'allocation sur la pile,
Là j'ai appris un truc ! Je ne savais pas du tout (ou j'avais oublié) que ma suggestion et malloc n'utilisaient pas la même partie de la mémoire.
Il suffit d'allouer en dehors de la fonction pour ne plus avoir de problème. La variable est alors en dur dans l'exécutable, dans le .bss en principe (ou un équivalent). Pour des gros tableaux, ça ne va pas changer grand chose, puisque malloc ferait de toute façon un mmap (sous linux au moins).
(Sans doute un inconvénient du C par l'exemple.) Qu'est-ce qui détermine l'espace libre pour la pile ?
Il me semble que c'est codé dans l'exécutable (modifiable par le lieur ?), et que l'OS a aussi une limite.
Lucas Levrel
Le 3 juin 2009, Jean-Claude Arbaut a écrit :
> > L'allocation peut se faire avec > > double (*t)[];
On peut garder la dernière dimension incomplète. C'est pour ça que je galère avec le tableau à trois dimensions: il y en a une de trop, il veut pour celle-là une taille constante. Je suppose que le coup a été prévu par le C99: si on peut déclarer des fonctions à tableaux variables, il faut bien pouvoir déclarer les arguments qu'on leur passe... j'espère :o)
Pour faire strictement ce que tu sembles vouloir, je vois deux possibilités à priori : - double *t[][] : ne marche pas car comment faire de l'arithmétique sur le pointeur en ne connaissant pas la taille de la dernière dimension ? - double **t[] : marche mais c'est à toi de construire les *t[][x].
Comme je ne suis probablement pas très clair ;-), voilà ce que je fais pour 4 dimensions par exemple (c'est du C++ mais probablement pas dur à traduire en C) : float ****table; table=new float***[a]; for(int ia=0;ia<a;ia++){ table[ia]=new float**[b]; for(int ib=0;ib<b;ib++){ table[ia][ib]=new float*[c]; for(int ic=0;ic<c;ic++){ table[ia][ib][ic]=new float[d]; } } }
Bien sûr là les éléments ne sont pas forcément contigus en mémoire. On doit pouvoir aussi faire malloc(sizeof(float)*a*b*c*d), malloc(sizeof(float*)*a*b*c), etc., puis définir les pointeurs vers les éléments adéquats.
-- LL
Le 3 juin 2009, Jean-Claude Arbaut a écrit :
> > L'allocation peut se faire avec
> > double (*t)[];
On peut garder la dernière dimension incomplète. C'est pour ça
que je galère avec le tableau à trois dimensions: il y en a une
de trop, il veut pour celle-là une taille constante. Je suppose
que le coup a été prévu par le C99: si on peut déclarer des
fonctions à tableaux variables, il faut bien pouvoir
déclarer les arguments qu'on leur passe... j'espère :o)
Pour faire strictement ce que tu sembles vouloir, je vois deux
possibilités à priori :
- double *t[][] : ne marche pas car comment faire de l'arithmétique sur le
pointeur en ne connaissant pas la taille de la dernière dimension ?
- double **t[] : marche mais c'est à toi de construire les *t[][x].
Comme je ne suis probablement pas très clair ;-), voilà ce que je fais
pour 4 dimensions par exemple (c'est du C++ mais probablement pas dur à
traduire en C) :
float ****table;
table=new float***[a];
for(int ia=0;ia<a;ia++){
table[ia]=new float**[b];
for(int ib=0;ib<b;ib++){
table[ia][ib]=new float*[c];
for(int ic=0;ic<c;ic++){
table[ia][ib][ic]=new float[d];
}
}
}
Bien sûr là les éléments ne sont pas forcément contigus en mémoire. On
doit pouvoir aussi faire malloc(sizeof(float)*a*b*c*d),
malloc(sizeof(float*)*a*b*c), etc., puis définir les pointeurs vers les
éléments adéquats.
> > L'allocation peut se faire avec > > double (*t)[];
On peut garder la dernière dimension incomplète. C'est pour ça que je galère avec le tableau à trois dimensions: il y en a une de trop, il veut pour celle-là une taille constante. Je suppose que le coup a été prévu par le C99: si on peut déclarer des fonctions à tableaux variables, il faut bien pouvoir déclarer les arguments qu'on leur passe... j'espère :o)
Pour faire strictement ce que tu sembles vouloir, je vois deux possibilités à priori : - double *t[][] : ne marche pas car comment faire de l'arithmétique sur le pointeur en ne connaissant pas la taille de la dernière dimension ? - double **t[] : marche mais c'est à toi de construire les *t[][x].
Comme je ne suis probablement pas très clair ;-), voilà ce que je fais pour 4 dimensions par exemple (c'est du C++ mais probablement pas dur à traduire en C) : float ****table; table=new float***[a]; for(int ia=0;ia<a;ia++){ table[ia]=new float**[b]; for(int ib=0;ib<b;ib++){ table[ia][ib]=new float*[c]; for(int ic=0;ic<c;ic++){ table[ia][ib][ic]=new float[d]; } } }
Bien sûr là les éléments ne sont pas forcément contigus en mémoire. On doit pouvoir aussi faire malloc(sizeof(float)*a*b*c*d), malloc(sizeof(float*)*a*b*c), etc., puis définir les pointeurs vers les éléments adéquats.
-- LL
Jean-Claude Arbaut
Lucas Levrel wrote:
Le 3 juin 2009, Jean-Claude Arbaut a écrit :
L'allocation peut se faire avec double (*t)[];
On peut garder la dernière dimension incomplète. C'est pour ça que je galère avec le tableau à trois dimensions: il y en a une de trop, il veut pour celle-là une taille constante. Je suppose que le coup a été prévu par le C99: si on peut déclarer des fonctions à tableaux variables, il faut bien pouvoir déclarer les arguments qu'on leur passe... j'espère :o)
Pour faire strictement ce que tu sembles vouloir, je vois deux possibilités à priori : - double *t[][] : ne marche pas car comment faire de l'arithmétique sur le pointeur en ne connaissant pas la taille de la dernière dimension ? - double **t[] : marche mais c'est à toi de construire les *t[][x].
Comme je ne suis probablement pas très clair ;-), voilà ce que je fais pour 4 dimensions par exemple (c'est du C++ mais probablement pas dur à traduire en C) : float ****table; table=new float***[a]; for(int ia=0;ia<a;ia++){ table[ia]=new float**[b]; for(int ib=0;ib<b;ib++){ table[ia][ib]=new float*[c]; for(int ic=0;ic<c;ic++){ table[ia][ib][ic]=new float[d]; } } }
Bien sûr là les éléments ne sont pas forcément contigus en mémoire. On doit pouvoir aussi faire malloc(sizeof(float)*a*b*c*d), malloc(sizeof(float*)*a*b*c), etc., puis définir les pointeurs vers les éléments adéquats.
Oui oui, je connais des alternatives ;-) Ce qui me semblait bizarre, c'est qu'on peut déclarer
double get(int n, int p, int q, double t[n][p][q], int i, int j, int k) { return t[i][j][k]; }
Mais on ne peut pas déclarer un pointeur sur une telle variable tableau, qui serait alloué par malloc/calloc. Je peut très bien faire
Ca fera un warning mais ça passe. Par contre, je ne vois pas comment le déclarer sans warning.
Il n'y a pourtant aucun calcul mal défini là-dedans il me semble. En fait j'aimerais pouvoir faire ce qu'on fait dans les prototypes:
double get(int n, int p, int q, double t[*][*][*], int i, int j, int k);
et déclarer double (*t)[*][*]; ou un truc équivalent. Mais le compilateur refuse évidemment.
Ca ne serait pas un problème majeur qu'il ne puisse pas calculer les indices sans connaître les dimensions, dans la fonction appelante puisqu'il lui suffirait de dire: c'est un tableau déclaré sans dimension connue, je ne sais pas quoi faire avec, point. Dans la fonction appelée il saurait. On pourrait aussi imaginer faire un cast dans la fonction appelante vers un double (*)[x][y] pour pouvoir indicer.
C'est exactement ce qu'on fait quand on passe de void* à qqch*: avec void *, le compilateur ne peut pas déréférencer, mais il est tout de même capable d'allouer, quitte à caster ensuite vers qqch* pour indicer.
Peut-être que j'en demande beaucoup :o)
Lucas Levrel wrote:
Le 3 juin 2009, Jean-Claude Arbaut a écrit :
L'allocation peut se faire avec
double (*t)[];
On peut garder la dernière dimension incomplète. C'est pour ça
que je galère avec le tableau à trois dimensions: il y en a une
de trop, il veut pour celle-là une taille constante. Je suppose
que le coup a été prévu par le C99: si on peut déclarer des
fonctions à tableaux variables, il faut bien pouvoir
déclarer les arguments qu'on leur passe... j'espère :o)
Pour faire strictement ce que tu sembles vouloir, je vois deux
possibilités à priori :
- double *t[][] : ne marche pas car comment faire de l'arithmétique sur le
pointeur en ne connaissant pas la taille de la dernière dimension ?
- double **t[] : marche mais c'est à toi de construire les *t[][x].
Comme je ne suis probablement pas très clair ;-), voilà ce que je fais
pour 4 dimensions par exemple (c'est du C++ mais probablement pas dur à
traduire en C) :
float ****table;
table=new float***[a];
for(int ia=0;ia<a;ia++){
table[ia]=new float**[b];
for(int ib=0;ib<b;ib++){
table[ia][ib]=new float*[c];
for(int ic=0;ic<c;ic++){
table[ia][ib][ic]=new float[d];
}
}
}
Bien sûr là les éléments ne sont pas forcément contigus en mémoire. On
doit pouvoir aussi faire malloc(sizeof(float)*a*b*c*d),
malloc(sizeof(float*)*a*b*c), etc., puis définir les pointeurs vers les
éléments adéquats.
Oui oui, je connais des alternatives ;-) Ce qui me semblait bizarre,
c'est qu'on peut déclarer
double get(int n, int p, int q, double t[n][p][q], int i, int j, int k)
{
return t[i][j][k];
}
Mais on ne peut pas déclarer un pointeur sur une telle variable
tableau, qui serait alloué par malloc/calloc.
Je peut très bien faire
Ca fera un warning mais ça passe.
Par contre, je ne vois pas comment le déclarer sans warning.
Il n'y a pourtant aucun calcul mal défini là-dedans il
me semble. En fait j'aimerais pouvoir faire ce qu'on fait
dans les prototypes:
double get(int n, int p, int q, double t[*][*][*], int i, int j, int k);
et déclarer double (*t)[*][*];
ou un truc équivalent.
Mais le compilateur refuse évidemment.
Ca ne serait pas un problème majeur qu'il ne puisse
pas calculer les indices sans connaître les dimensions,
dans la fonction appelante puisqu'il lui suffirait de dire:
c'est un tableau déclaré sans dimension connue, je ne sais
pas quoi faire avec, point. Dans la fonction appelée
il saurait.
On pourrait aussi imaginer faire un cast dans la fonction
appelante vers un double (*)[x][y] pour pouvoir indicer.
C'est exactement ce qu'on fait quand on passe de void* à qqch*:
avec void *, le compilateur ne peut pas déréférencer,
mais il est tout de même capable d'allouer, quitte à
caster ensuite vers qqch* pour indicer.
On peut garder la dernière dimension incomplète. C'est pour ça que je galère avec le tableau à trois dimensions: il y en a une de trop, il veut pour celle-là une taille constante. Je suppose que le coup a été prévu par le C99: si on peut déclarer des fonctions à tableaux variables, il faut bien pouvoir déclarer les arguments qu'on leur passe... j'espère :o)
Pour faire strictement ce que tu sembles vouloir, je vois deux possibilités à priori : - double *t[][] : ne marche pas car comment faire de l'arithmétique sur le pointeur en ne connaissant pas la taille de la dernière dimension ? - double **t[] : marche mais c'est à toi de construire les *t[][x].
Comme je ne suis probablement pas très clair ;-), voilà ce que je fais pour 4 dimensions par exemple (c'est du C++ mais probablement pas dur à traduire en C) : float ****table; table=new float***[a]; for(int ia=0;ia<a;ia++){ table[ia]=new float**[b]; for(int ib=0;ib<b;ib++){ table[ia][ib]=new float*[c]; for(int ic=0;ic<c;ic++){ table[ia][ib][ic]=new float[d]; } } }
Bien sûr là les éléments ne sont pas forcément contigus en mémoire. On doit pouvoir aussi faire malloc(sizeof(float)*a*b*c*d), malloc(sizeof(float*)*a*b*c), etc., puis définir les pointeurs vers les éléments adéquats.
Oui oui, je connais des alternatives ;-) Ce qui me semblait bizarre, c'est qu'on peut déclarer
double get(int n, int p, int q, double t[n][p][q], int i, int j, int k) { return t[i][j][k]; }
Mais on ne peut pas déclarer un pointeur sur une telle variable tableau, qui serait alloué par malloc/calloc. Je peut très bien faire
Ca fera un warning mais ça passe. Par contre, je ne vois pas comment le déclarer sans warning.
Il n'y a pourtant aucun calcul mal défini là-dedans il me semble. En fait j'aimerais pouvoir faire ce qu'on fait dans les prototypes:
double get(int n, int p, int q, double t[*][*][*], int i, int j, int k);
et déclarer double (*t)[*][*]; ou un truc équivalent. Mais le compilateur refuse évidemment.
Ca ne serait pas un problème majeur qu'il ne puisse pas calculer les indices sans connaître les dimensions, dans la fonction appelante puisqu'il lui suffirait de dire: c'est un tableau déclaré sans dimension connue, je ne sais pas quoi faire avec, point. Dans la fonction appelée il saurait. On pourrait aussi imaginer faire un cast dans la fonction appelante vers un double (*)[x][y] pour pouvoir indicer.
C'est exactement ce qu'on fait quand on passe de void* à qqch*: avec void *, le compilateur ne peut pas déréférencer, mais il est tout de même capable d'allouer, quitte à caster ensuite vers qqch* pour indicer.
Peut-être que j'en demande beaucoup :o)
candide
Jean-Claude Arbaut a écrit :
candide wrote:
Jean-Claude Arbaut a écrit :
Pour éviter de coder les dimensions en dur, ne peut-on pas faire la chose suivante ? (en tout cas "gcc -ansi -Wall" ne crie pas)
double get(int n, int p, double t[n][p], int i, int j) { return t[i][j]; }
C'est dans le nouveau standard: -stdÉ9 et ça passe ;-)
Oui, je sais mais je croyais que gcc -ansi (cf. ce que tu écris ci-dessus) référait au standard ansi de 1989 (ie C iso 90) ce qui n'est finalement pas le cas. La logique des options de gcc resteront toujours un mystère pour moi.
L'allocation peut se faire avec double (*t)[];
Tiens, j'imaginais pas qu'on pouvait déclarer un pointeur vers un tableau incomplet.
On peut garder la dernière dimension incomplète
En effet, il s'avère qu'il est bien légal de déclarer des pointeurs vers des tableaux incomplets (en fait vers des types incomplets, au hasard void) :
§6.2.5 A pointer type may be derived from a function type, an object type, or an incomplete type, called the referenced type.
mais je ne vois pas l'intérêt de ce genre de pointeur, en particulier on ne dispose pas d'arithmétique des pointeurs.
Jean-Claude Arbaut a écrit :
candide wrote:
Jean-Claude Arbaut a écrit :
Pour éviter de coder les dimensions en dur, ne peut-on pas faire
la chose suivante ? (en tout cas "gcc -ansi -Wall" ne crie pas)
double get(int n, int p, double t[n][p], int i, int j) {
return t[i][j];
}
C'est dans le nouveau standard: -stdÉ9 et ça passe ;-)
Oui, je sais mais je croyais que gcc -ansi (cf. ce que tu écris ci-dessus)
référait au standard ansi de 1989 (ie C iso 90) ce qui n'est finalement pas le
cas. La logique des options de gcc resteront toujours un mystère pour moi.
L'allocation peut se faire avec
double (*t)[];
Tiens, j'imaginais pas qu'on pouvait déclarer un pointeur vers un
tableau incomplet.
On peut garder la dernière dimension incomplète
En effet, il s'avère qu'il est bien légal de déclarer des pointeurs vers des
tableaux incomplets (en fait vers des types incomplets, au hasard void) :
§6.2.5
A pointer type may be derived from a function type, an object
type, or an incomplete type, called the referenced type.
mais je ne vois pas l'intérêt de ce genre de pointeur, en particulier on ne
dispose pas d'arithmétique des pointeurs.
C'est dans le nouveau standard: -stdÉ9 et ça passe ;-)
Oui, je sais mais je croyais que gcc -ansi (cf. ce que tu écris ci-dessus) référait au standard ansi de 1989 (ie C iso 90) ce qui n'est finalement pas le cas. La logique des options de gcc resteront toujours un mystère pour moi.
L'allocation peut se faire avec double (*t)[];
Tiens, j'imaginais pas qu'on pouvait déclarer un pointeur vers un tableau incomplet.
On peut garder la dernière dimension incomplète
En effet, il s'avère qu'il est bien légal de déclarer des pointeurs vers des tableaux incomplets (en fait vers des types incomplets, au hasard void) :
§6.2.5 A pointer type may be derived from a function type, an object type, or an incomplete type, called the referenced type.
mais je ne vois pas l'intérêt de ce genre de pointeur, en particulier on ne dispose pas d'arithmétique des pointeurs.
Jean-Claude Arbaut
candide wrote:
Oui, je sais mais je croyais que gcc -ansi (cf. ce que tu écris ci-dessus) référait au standard ansi de 1989 (ie C iso 90) ce qui n'est finalement pas le cas. La logique des options de gcc resteront toujours un mystère pour moi.
Ah, oui, j'avais zappé ça. En prime ça a dû changer suivant les versions de gcc...
En effet, il s'avère qu'il est bien légal de déclarer des pointeurs vers des tableaux incomplets (en fait vers des types incomplets, au hasard void) :
§6.2.5 A pointer type may be derived from a function type, an object type, or an incomplete type, called the referenced type.
mais je ne vois pas l'intérêt de ce genre de pointeur, en particulier on ne dispose pas d'arithmétique des pointeurs.
On n'en dispose pas non plus avec un void*, pourtant un void* ça sert beaucoup ;-)
Cela dit, je suis sot: en pratique, pour pouvoir allouer, il faut bien connaître les dimensions, et il suffit donc faire
double f(int n, double a[*][*][*]);
double g() { int n; double (*t)[n][n]; t = malloc(8*n*n*n); return f(n, t); }
J'étais fixé sur l'idée de déclarer un tableau sans dimension pour l'allouer :o)
Par contre, on peut aussi faire mumuse:
double f(int n, int p, int q, double a[n][p][q]);
double g() { int n, p , q0; double a, b; double (*t)[p][q]; t = malloc(8*n*p*q); a = f(n,p,q,t); b = f(q,n,p,t); return a+b; }
Et le tableau qu'on déclare change de dimension comme on veut, la déclaration d'origine n'a aucune importance. Ce serait quand même plus propre de le déclarer (*)[*][*] ;-)
candide wrote:
Oui, je sais mais je croyais que gcc -ansi (cf. ce que tu écris ci-dessus)
référait au standard ansi de 1989 (ie C iso 90) ce qui n'est finalement pas le
cas. La logique des options de gcc resteront toujours un mystère pour moi.
Ah, oui, j'avais zappé ça. En prime ça a dû changer suivant les
versions de gcc...
En effet, il s'avère qu'il est bien légal de déclarer des pointeurs vers des
tableaux incomplets (en fait vers des types incomplets, au hasard void) :
§6.2.5
A pointer type may be derived from a function type, an object
type, or an incomplete type, called the referenced type.
mais je ne vois pas l'intérêt de ce genre de pointeur, en particulier on ne
dispose pas d'arithmétique des pointeurs.
On n'en dispose pas non plus avec un void*, pourtant un void* ça sert
beaucoup ;-)
Cela dit, je suis sot: en pratique, pour pouvoir allouer, il
faut bien connaître les dimensions, et il suffit donc faire
double f(int n, double a[*][*][*]);
double g() {
int n;
double (*t)[n][n];
t = malloc(8*n*n*n);
return f(n, t);
}
J'étais fixé sur l'idée de déclarer un tableau
sans dimension pour l'allouer :o)
Par contre, on peut aussi faire mumuse:
double f(int n, int p, int q, double a[n][p][q]);
double g() {
int n, p , q0;
double a, b;
double (*t)[p][q];
t = malloc(8*n*p*q);
a = f(n,p,q,t);
b = f(q,n,p,t);
return a+b;
}
Et le tableau qu'on déclare change de dimension
comme on veut, la déclaration d'origine
n'a aucune importance. Ce serait quand
même plus propre de le déclarer (*)[*][*] ;-)
Oui, je sais mais je croyais que gcc -ansi (cf. ce que tu écris ci-dessus) référait au standard ansi de 1989 (ie C iso 90) ce qui n'est finalement pas le cas. La logique des options de gcc resteront toujours un mystère pour moi.
Ah, oui, j'avais zappé ça. En prime ça a dû changer suivant les versions de gcc...
En effet, il s'avère qu'il est bien légal de déclarer des pointeurs vers des tableaux incomplets (en fait vers des types incomplets, au hasard void) :
§6.2.5 A pointer type may be derived from a function type, an object type, or an incomplete type, called the referenced type.
mais je ne vois pas l'intérêt de ce genre de pointeur, en particulier on ne dispose pas d'arithmétique des pointeurs.
On n'en dispose pas non plus avec un void*, pourtant un void* ça sert beaucoup ;-)
Cela dit, je suis sot: en pratique, pour pouvoir allouer, il faut bien connaître les dimensions, et il suffit donc faire
double f(int n, double a[*][*][*]);
double g() { int n; double (*t)[n][n]; t = malloc(8*n*n*n); return f(n, t); }
J'étais fixé sur l'idée de déclarer un tableau sans dimension pour l'allouer :o)
Par contre, on peut aussi faire mumuse:
double f(int n, int p, int q, double a[n][p][q]);
double g() { int n, p , q0; double a, b; double (*t)[p][q]; t = malloc(8*n*p*q); a = f(n,p,q,t); b = f(q,n,p,t); return a+b; }
Et le tableau qu'on déclare change de dimension comme on veut, la déclaration d'origine n'a aucune importance. Ce serait quand même plus propre de le déclarer (*)[*][*] ;-)