OVH Cloud OVH Cloud

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
-ed-
On 3 juin, 01:25, candide wrote:

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 à d es tableaux
statiques comme suggéré par LL.



Confusion classique. Un define ne signifie pas du tout "constante"
mais "remplacement d'un texte par un autre". Rien n'empêche que le
texte remplacé soit le nom d'une variable...
Avatar
-ed-
On 3 juin, 10:12, Jean-Claude Arbaut
wrote:

Il suffit d'allouer en dehors de la fonction pour ne plus
avoir de problème.



Horrible. Ça signifie mémoire statique avec tous les inconvénients qu i
s'y rattachent. Le sujet a été traité des zillion de fois. Pas de
statiques. (sauf dans le main(, éventuellement)).
Avatar
Lucas Levrel
Le 3 juin 2009, Jean-Claude Arbaut a écrit :

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.



1) N'est-ce pas justement le sens du warning ?

2) Si j'ai bien compris, tu voudrais qu'il ne râle pas sur la déclaration
double *t[][] mais qu'il t'interdise de l'utiliser directement. Le
problème est que si tu déclares double *t, tu ne peux pas le passer en
paramètre de la fonction f(int n, int p, int q, t[n][p][q]), c'est ça ?
Et void *t non plus ? Parce que j'ai essayé et ça ne râle pas (avec gcc
-W -Wall -stdÉ9 -pedantic -c).


--
LL
Avatar
Jean-Claude Arbaut
-ed- wrote:

On 3 juin, 10:12, Jean-Claude Arbaut
wrote:

Il suffit d'allouer en dehors de la fonction pour ne plus
avoir de problème.



Horrible. Ça signifie mémoire statique avec tous les inconvénients qui
s'y rattachent. Le sujet a été traité des zillion de fois. Pas de
statiques. (sauf dans le main(, éventuellement)).



Ca dépend ce qu'on veut faire. Si on programme un serveur web,
on va éviter. Si en revanche on souhaite seulement écrire
un programme réalisant des calculs matriciels intensifs sur
un problème mathématique précis, et qu'on n'a pas envie de
s'em*** avec la gestion de la mémoire, ça semble cohérent
d'allouer statiquement. Du reste, c'est évidemment dans
le main qu'elles vont se retrouver dans ce cas.

Petit parallèle: en fortran 77 c'est comme ça que ça fonctionne
souvent. Et j'ai vu un programme qui passait au fortran 90
à exactement un endroit : dans le main, pour allouer dynamiquement
suivant la taille du problème, tout le reste du proramme
étant encore codé en f77. Pour les curieux, il s'agit de MT3D.

Deuxième petit parallèle: toujours en f77, la bibliothèque PORT3
de Lucent réalise des allocations en passant par des pointeurs
dans une "mémoire de travail" (un tableau statique bien sûr).
Sur un système comme Linux, on peut _réserver_ statiquement une
place énorme pour un tableau (*), elle ne sera
réellement allouée par l'OS que si elle est utilisée.
C'est pas très joli, mais qu'est-ce que ça peut faire si
le principal intérêt du programme est de donner un résultat,
là, tout de suite ?
Et surtout, qu'est-ce que ça change profondément, si au lieu
de faire ça on alloue avec un mmap massif dans le main ?


(*) Pour aller jusqu'au bout de l'horreur, sur ma machine,
je peux allouer 7G avec malloc, ou déclarer un tableau
statique de 7G, ça compile et ça tourne.
En revanche, je peux compiler avec un tableau statique
de 2^63-1 octets, mais ça plante au lancement :o)
Avatar
Lucas Levrel
Le 3 juin 2009, -ed- a écrit :

On 3 juin, 01:25, candide wrote:

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

Confusion classique. Un define ne signifie pas du tout "constante"
mais "remplacement d'un texte par un autre". Rien n'empêche que le
texte remplacé soit le nom d'une variable...



Confusion classique. *suggère* ne signifie pas « prouve », *typique* ne
signifie pas « systématique ».

:-p
--
LL
Avatar
Jean-Claude Arbaut
Lucas Levrel wrote:
Le 3 juin 2009, Jean-Claude Arbaut a écrit :

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.



1) N'est-ce pas justement le sens du warning ?



Ouaip, mais je n'aime pas les warnings : en plus
il a raison, passer un double * au lieu d'un
"double (*)[][]", c'est pas très sain.

2) Si j'ai bien compris, tu voudrais qu'il ne râle pas sur la déclaration
double *t[][] mais qu'il t'interdise de l'utiliser directement. Le
problème est que si tu déclares double *t, tu ne peux pas le passer en
paramètre de la fonction f(int n, int p, int q, t[n][p][q]), c'est ça ?
Et void *t non plus ? Parce que j'ai essayé et ça ne râle pas (avec gcc
-W -Wall -stdÉ9 -pedantic -c).



Pas bête. Merci.
En prime, on pourra effectivement faire un cast dans la fonction
appelante vers un (*)[n][n], et on pourra indicer si on y tient.
Comment n'y ai-je pas pensé ! :o)
Avatar
Antoine Leca
Le 03/06/2009 10:26Z, -ed- <emmanuel.delahaye> écrivit :
On 3 juin, 10:12, Jean-Claude Arbaut
wrote:

Il suffit d'allouer en dehors de la fonction pour ne plus
avoir de problème.



Horrible. Ça signifie mémoire statique avec tous les inconvénients qui
s'y rattachent.



Oui, et tous les avantages aussi. Par exemple, le programme n'est pas
lancé s'il n'y a pas assez de mémoire pourqu'il s'exécute, donc pas
besoin d'avoir à gérer les codes d'erreur de malloc. Oubliés aussi les
bogues introduits par le code (caché) de la bibliothèque standard.

Évidemment, cela n'est pas adapté à des jeux de données dynamiques en
taille, comme un programme de tri ou un butineur web. Mais c'est souvent
bien adapté aux programmes où on peut déterminer (ou majorer) la taille
des données à traiter, par exemple un code de calcul... ou un serveur !


Antoine
Avatar
Antoine Leca
Le 02/06/2009 23:25Z, candide écrivit :
Tu introduis une variable, ce n'est plus du tout le même code.



Certes. Mais le fait d'allouer en dynamique ou d'utiliser une variable
auto donne aussi deux codes différents ; et static en donne encore un autre.


Antoine Leca a écrit :
Le 02/06/2009 18:03, candide écrivit :
Certes mais la directive
#define M
du code initial suggère qu'on n'alloue pas à l'exécution.







Comprends pas ta réaction,



Moi je ne comprends pas pourquoi #define implique allocation statique
(ou plus exactement excluerait allocation dynamique).

Mais bon, si c'est ton style de programmation, il n'y a pas de problème
pour moi.
Tant que l'on ne m'oblige pas à le suivre.

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,



Voilà. Et moi, je n'ai pas vu toutes ces sugestions.

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.



C'est certes naturel, et comme tu le fais remarquer il y a peu de
différences.
Comme souvent, les verres sont à moitié vides ou au contraire à moitié
remplis ; j'ai choisi de souligner la différence en réponse au message
initial de LL ; à voir sa réaction, ce ne fut pas totalement inutile,
cf. <news:.


Antoine
Avatar
candide
-ed- a écrit :
On 3 juin, 01:25, candide wrote:

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.



Confusion classique.



Il te faudra un petit peu plus que ton air supérieur pour te donner de la hauteur.

Un define ne signifie pas du tout "constante"



Ai-je dis cela ? Je commence à connaître mon -ed- et si tu dis ça, c'est
probablement que toi-même tu l'as cru longtemps. Tu méprises les lectures et
c'est un tort. Mon livre de C m'en avait averti assez tôt, extrait de K.N. King,
1ère édition, page 278-9 :

#define BEGIN {
#define END }

#define LOOP for (;;)

#define BOOL int

etc.

mais "remplacement d'un texte par un autre". Rien n'empêche que le
texte remplacé soit le nom d'une variable...




Transformer des macro-constantes en variables c'est un gros bidouillage, par
exemple pour rattraper désespérément un code dont on hérite. Si tu fais ça sur
le code du PO avec la constante N, il ne compile pas en C90.

Vu que bien souvent une macro simple est déclarée en dehors de tout bloc, si tu
veux que ta variable remplacement de constante soit reconnue, tu devras la
déclarer en globale. Je croyais que les variables te faisait "gerber" (sic) ?

Tu prends au hasard du code C reconnu, tu regardes la liste de remplacement d'un
#define sans argument pris au hasard et tu me diras dans combien de cas sur
10000 tu trouves un nom de variable. Maintenant dans ta vie privée de
programmeur tu fais ce que tu veux ...
Avatar
candide
-ed- a écrit :
On 3 juin, 10:12, Jean-Claude Arbaut
wrote:

Il suffit d'allouer en dehors de la fonction pour ne plus
avoir de problème.



Horrible. Ça signifie mémoire statique avec tous les inconvénients qui
s'y rattachent. Le sujet a été traité des zillion de fois. Pas de
statiques. (sauf dans le main(, éventuellement)).




Tu prends le C pour une secte et tu crois en être le gourou.

Si je regarde le code source C de Python par exemple, je trouve plein de
variable en mémoire statique y compris des variables locales, au hasard en
faisant une rapide recherche :


else {
static PyObject *mro_str; /* <-- Au bucher */
checkit = 1;
mro = lookup_method((PyObject *)type, "mro", &mro_str);
if (mro == NULL)
return -1;
result = PyObject_CallObject(mro, NULL);
Py_DECREF(mro);
}
1 2 3 4 5