Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Légallité d'un pointeur de tableaux

44 réponses
Avatar
Stéphane Zuckerman
Bonjour,

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.

Merci d'avance ! :-)

--
St=E9phane Zuckerman

10 réponses

1 2 3 4 5
Avatar
Marc Boyer
On 2009-05-28, Stéphane Zuckerman wrote:
Bonjour,

Je m'interroge sur une construction peu orthodoxe que je dois utiliser
en C (à savoir un pointeur de tableaux).

J'ai une fonction dont le prototype est à peu près le suivant :

void f(float (*tab)[N], /* reste des arguments */);

Jusque là, tout va bien. Dans le main(), j'alloue "tab" à l'aide des
lignes suivantes :

#define M ...
#define N ...

int main(void)
{
/* déclarations ... */
float (*t)[N];

t = malloc(sizeof(float) * M * N);



C'est là que je tique un peu: le *M, c'est exprès ou c'est une
erreur de frappe.

if (t == NULL) { /* gestion d'erreur */ }

/* ... */
f(t, ...);

return 0;
}

J'ai cherché un peu partout, demandé à des personnes qui à ma
connaissance connaissent bien la norme, et ce code semble légal.



Il me semble aussi.

J'aimerais d'une part avoir confirmation, et d'autre part savoir si
quelqu'un pourrait me dire où dans la norme je peux confirmer cela.



J'ai donné mon exemplaire de la norme avec le cours qui allait avec.

Marc Boyer

--
Au XXIème siècle, notre projet de société s'est réduit
à un projet économique...
Avatar
Stéphane Zuckerman
On May 28, 3:15 pm, Marc Boyer
wrote:
> #define M ...
> #define N ...

> int main(void)
> {
>     /* déclarations ... */
>     float (*t)[N];

>     t = malloc(sizeof(float) * M  * N);

  C'est là que je tique un peu: le  *M, c'est exprès ou c'est une
erreur de frappe.



Non non, pas de typo ici, je veux bien un tableau de floats de M * N
cases. Mais je veux aussi que le compilateur sache qu'il doit
considérer que t[i] signifie « avancer de sizeof(float)*i*N cases ». À
la base il s'agit d'un code FORTRAN transposé en C (pour faire des
choses peu avouables ici, et qui plus est totalement non conformes à
la norme...).

> J'ai cherché un peu partout, demandé à des personnes qui à ma
> connaissance connaissent bien la norme, et ce code semble légal.

  Il me semble aussi.

> J'aimerais d'une part avoir confirmation, et d'autre part savoir si
> quelqu'un pourrait me dire où dans la norme je peux confirmer cela.

  J'ai donné mon exemplaire de la norme avec le cours qui allait avec .


Bon, tant pis. J'ai cherché, mais je suis un peu perdu dedans ...

Merci pour la (prompte) réponse en tout cas ! :-)

--
Stéphane Zuckerman
Avatar
Marc Boyer
On 2009-05-28, Stéphane Zuckerman wrote:
On May 28, 3:15 pm, Marc Boyer
wrote:
> #define M ...
> #define N ...

> int main(void)
> {
>     /* déclarations ... */
>     float (*t)[N];

>     t = malloc(sizeof(float) * M * N);

  C'est là que je tique un peu: le  *M, c'est exprès ou c'est une
erreur de frappe.



Non non, pas de typo ici, je veux bien un tableau de floats de M * N
cases. Mais je veux aussi que le compilateur sache qu'il doit
considérer que t[i] signifie « avancer de sizeof(float)*i*N cases ».



En fait, t est un pointeur sur un tableau de N float
qui pointeur sur un tableau de M tableau de N float,
de même que
int*p = malloc( sizeof(int) * M)
est un pointeur sur un int qui pointe sur un tableau.

Après, t[i], ce sera un pointeur sur un tableau de N float.

À
la base il s'agit d'un code FORTRAN transposé en C (pour faire des
choses peu avouables ici, et qui plus est totalement non conformes à
la norme...).



Plein de bonheur j'imagine.

Marc Boyer
--
Au XXIème siècle, notre projet de société s'est réduit
à un projet économique...
Avatar
Antoine Leca
Le 28/05/2009 11:23Z, Stéphane Zuckerman écrivit :
Je m'interroge sur une construction peu orthodoxe que je dois utiliser
en C (à savoir un pointeur de tableaux).



Où cela ? Dans ton code, je ne vois que des matrices à 2 deux
dimensions, passées comme tableaux de tableaux (et le tableau extérieur
est passé comme pointeur sur le premier élément, on est en C).


J'ai une fonction dont le prototype est à peu près le suivant :
void f(float (*tab)[N], /* reste des arguments */);

Jusque là, tout va bien. Dans le main(), j'alloue "tab" à l'aide des
lignes suivantes :
float (*t)[N];
t = malloc(sizeof(float) * M * N);

J'ai cherché un peu partout, demandé à des personnes qui à ma
connaissance connaissent bien la norme, et ce code semble légal.



Ne vois pas non plus de problèmes en dehors du cas où N*M provoque un
débordement non signalé, l'exemple classique étant N==M=%6 en 16 bits.

J'aimerais d'une part avoir confirmation, et d'autre part savoir si
quelqu'un pourrait me dire où dans la norme je peux confirmer cela.



6.2.5p20 (et 6.5.6) te garantit que les éléments sont tous addressables
séquentiellement en mémoire, sans aucun intervalle entre eux (exactement
comme en Fortran); à partir de là, un tableau de N×M float peut être
considéré comme un tableau de M bleurps, un bleurp étant évidemment un
tableau de N floats.

Une manière de se faciliter la vie est d'écrire
typedef float bleurp[N];
void f(bleurp*tab, ...
main(){
bleurp *t;
t = malloc(sizeof(bleurp)*M);
dont on voit bien que c'est la même chose que le code ci-dessus, mais
aussi où on reconnaît bien les constructions classiques du C.


Antoine
Avatar
Lucas Levrel
Le 28 mai 2009, Stéphane Zuckerman a écrit :

#define M ...
#define N ...

int main(void)
{
/* déclarations ... */
float (*t)[N];

t = malloc(sizeof(float) * M * N);



Pourquoi pas directement float t[M][N] ?


--
LL
Avatar
Antoine Leca
Le 02/06/2009 10:17, Lucas Levrel écrivit :
Le 28 mai 2009, Stéphane Zuckerman a écrit :

#define M ...
#define N ...

int main(void)
{
/* déclarations ... */
float (*t)[N];

t = malloc(sizeof(float) * M * N);



Pourquoi pas directement float t[M][N] ?



C'est très différent : tu demandes à allouer N*M float en pile.

A contrario, Stéphane alloue 1 pointeur en pile, et le reste est
dynamique ; donc on peut changer M, passer outre les limitations sur la
taille de la pile, ou déplacer le code dans une fonction annexe chargée
de l'initialisation, le tout sans souci.


Antoine
Avatar
candide
Antoine Leca a écrit :

Pourquoi pas directement float t[M][N] ?



C'est très différent : tu demandes à allouer N*M float en pile.

A contrario, Stéphane alloue 1 pointeur en pile, et le reste est
dynamique ; donc on peut changer M,



Certes mais la directive

#define M

du code initial suggère qu'on n'alloue pas à l'exécution.

Au demeurant, je n'ai pas précisément vu à quel niveau Stéphane (qui n'est pas
vraiment un débutant en C vu ses fils sur fclc) trouvait son code problématique
(un problème d'allocation dynamique ou un problème d'arithmétique des pointeurs ?).
Avatar
Antoine Leca
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, et le reste du code devient à géométrie variable.
C'est en fait tout l'intérêt du préprocesseur, ce genre de truc...


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...


Antoine
Avatar
Jean-Claude Arbaut
Antoine Leca wrote:
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, et le reste du code devient à géométrie variable.
C'est en fait tout l'intérêt du préprocesseur, ce genre de truc...


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...


Antoine



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];
}

L'allocation peut se faire avec
double (*t)[];
t = (double (*)[])calloc(n*p, sizeof(double));


PS: j'ai un doute, ça donnerait quoi dans la
déclaration de t pour un tableau à 3 dimensions ?
Avatar
candide
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];
}




:~$ gcc -W -Wall -stdÈ9 -pedantic -c fclc.c
fclc.c:3: attention : ISO C90 forbids variable-size array «t»
fclc.c:3: attention : ISO C90 forbids variable-size array «t»




L'allocation peut se faire avec
double (*t)[];



Tiens, j'imaginais pas qu'on pouvait déclarer un pointeur vers un tableau incomplet.

t = (double (*)[])calloc(n*p, sizeof(double));




Tu castes le retour de calloc() ?
1 2 3 4 5