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

Questions sur les déclarateurs de fonctions

12 réponses
Avatar
Taurre
Bonjour =E0 tous,

Je m'excuse d'avance pour le titre un peu flou, mais j'avoue ne pas
avoir trouv=E9 mieux.
Je viens vers vous car j'ai deux questions en rapport avec les
"d=E9clarateurs" de fonctions, plus pr=E9cis=E9mment au sujet des param=E8t=
res
de type tableaux:

- j'ai lu qu'il =E9tait possible d'indiquer qu'une fonction recevait un
tableau de longueur variable comme ceci:

void fonction(int tab[*]);

Cependant, je ne comprend pas l'int=E9r=EAt de cette =E9criture qui est
limit=E9e aux prototypes de fonctions (6.7.5.3 =A7 12 p 119). En effet,
dans le cas o=F9 je passes un tableau de longueur variable en argument
d'une fonction, ce dernier est implicitement converti en un pointeur
sur son premier =E9l=E9ment (6.3.2.1 =A7 3 p 46). J'aurais donc tr=E8s bien=
pu
me contenter de sp=E9cifier tab comme de type "pointeur sur int". D=E8s
lors, o=F9 est l'int=E9r=EAt de l'=E9criture ci-dessus?

- j'ai =E9galement vu qu'il =E9tait possible d'utiliser le mot-cl=E9 static
juste avant la taille d'un param=E8tre de type tableau, par exemple
comme ceci:

void fonction(int tab[static 10]);

=C0 ce sujet, la Norme nous dit (6.7.5.3 =A7 7 p 119):

> If the keyword static also appears within the [ and ] of the
> array type derivation, then for each call to the function, the value of t=
he corresponding
> actual argument shall provide access to the first element of an array wit=
h at least as many
> elements as specified by the size expression.

Je comprends cette phrase comme une obligation pour le tableau fourni
en argument d'avoir une taille minimale. Cependant, je constate que
violer cette interdiction n'entra=EEne aucune erreur/avertissement du
c=F4t=E9 de GCC (4.4.5). D=E8s lors, de nouveau, quel est l'int=E9r=EAt de =
cette
=E9criture si elle est purement indicative puisque dans ce cas, indiquer
la taille sans le mot-cl=E9 static suffit?

Merci d'avance pour vos r=E9ponses.

10 réponses

1 2
Avatar
Antoine Leca
Taurre écrivit :
- j'ai lu qu'il était possible d'indiquer qu'une fonction recevait un
tableau de longueur variable comme ceci:

void fonction(int tab[*]);

Cependant, je ne comprend pas l'intérêt de cette écriture qui est
limitée aux prototypes de fonctions (6.7.5.3 § 12 p 119).



Permettre de savoir qu'il s'agit d'un tableau (« à taille variable »),
ce qui permet à la fonction de le manipuler comme un tableau (par
exemple en utilisant sizeof dessus), plutôt que comme un pointeur vers
le premier élément comme c'est habituel en C.

En effet, dans le cas où je passes un tableau de longueur variable en
argument d'une fonction, ce dernier est implicitement converti en un
pointeur sur son premier élément (6.3.2.1 § 3 p 46).



"Except when it is the operand of the sizeof operator, or the
unary & operator"

Dans le cas d'un paramètre tableau « normal » (type_t toto[]), de par
6.7.5.3 alinéa 7, l'objet (ici toto) est vu dans le corps de la fonction
comme un pointeur ; en dehors des cas ci-dessus il n'y a donc pas de
différences au final ; mais si tu écris sizeof(toto) tu va récupérer la
taille d'un pointeur, ce qui n'a pas grand intérêt ; par contre
sizeof(tab) va te donner la taille du tableau passé comme argument à la
fonction.


void fonction(int tab[static 10]);

Je comprends cette phrase comme une obligation pour le tableau fourni
en argument d'avoir une taille minimale. Cependant, je constate que
violer cette interdiction n'entraîne aucune erreur/avertissement du
côté de GCC (4.4.5).



Remarque tout d'abord que http://gcc.gnu.org/gcc-4.4/c99status.html
spécifie que le support pour les tableaux à taille dynamique est
"broken" (cassé), les résultats ne sont donc pas très probants.

Par ailleurs, on est dans le cas typique de « il ne faut pas faire
cela ! » Le texte spécifie (par l'écriture "shall") que l'obligation de
s'assurer de la conformité relève de la responsabilité du programmeur,
et que s'il ne le fait pas on tombe dans le royaume insondable du
comportement indéfini...
Même si le cas que tu as testé paraît évident (et de fait un bon
compilateur /devrait/ émettre un diagnostic), si tu te places dans le
cas suivant, la détection devient plus compliquée :

------8<------
void f(int tab[static 20]);

void g(int inter[*])
{
f(inter);
}

------8<------

void g(int inter[*]);

void h(void)
{
int tableau[12];
g(tableau);
}
------8<------


Le fond de toutes ces syntaxes apparemment bizarres si tu apprends le C
en 2011 après avoir peut-être rencontré un autre langage, est à
rechercher dans l'histoire et la nécessité de compatibilité ascendante.
La formulation sans static ni * était la seule permise (et reconnue)
avant 1998 ; pour implémenter ces extensions, il a fallu inventer des
nouvelles constructions ; parfois baroques, c'est vrai.


Antoine
Avatar
espie
De toutes facons, personne n'utilise ces nouvelles syntaxes.

Entre autres, parce que le support est plus ou moins casse, et donc
que ca ferait du code "moins portable", ce qui est quand meme toujours
un gros interet du C.

Aussi, parce que de toutes facons, c'est juste du sucre syntaxique.
Passer du tableau dynamique a base de f(int t[*]), ca sera de toutes
facons exactement aussi efficace que le classique f(int t[], size_t n)
qui lui a l'avantage de fonctionner depuis C89.

Et si f(static int t[10]) ne "fonctionne" pas, c'est pas si grave...
ou plus exactement, je soupconne fort que, le jour ou il fonctionnera, le
compilo aura suffisamment de jugeotte pour faire les verifications
correspondantes dans des cas qui ne sont pas annotes...
Avatar
Taurre
Tout d'abord merci pour ces réponses :)

Le fond de toutes ces syntaxes apparemment bizarres si tu apprends le C
en 2011 après avoir peut-être rencontré un autre langage, est à
rechercher dans l'histoire et la nécessité de compatibilité ascenda nte.
La formulation sans static ni * était la seule permise (et reconnue)
avant 1998 ; pour implémenter ces extensions, il a fallu inventer des
nouvelles constructions ; parfois baroques, c'est vrai.



Je comprends tout à fait le besoin d'inventer de nouvelles syntaxes
afin de conserver celles existantes, c'est d'ailleurs un point que
j'apprécie en C ;)
Maintenant, pour reprendre l'exemple de static, c'est son intérêt que
je ne comprends pas... En effet, si le but est purement indicatif, que
les compilateurs ne sont pas obligés d'émettre un diagnostic et qu'en
plus il est impossible/difficile d'effectuer des vérificaions dans
certains cas, je ne comprends pas pourquoi cela a été inventé... Le
programmeur pourrait tout aussi bien se contenter d'indiquer la taille
nécessaire sans ce mot-clé, cela reviendra au même.

> - j'ai lu qu'il était possible d'indiquer qu'une fonction recevait un
> tableau de longueur variable comme ceci:

> void fonction(int tab[*]);

> Cependant, je ne comprend pas l'intérêt de cette écriture qui est
> limitée aux prototypes de fonctions (6.7.5.3 § 12 p 119).

Permettre de savoir qu'il s'agit d'un tableau (« à taille variable »),
ce qui permet à la fonction de le manipuler comme un tableau (par
exemple en utilisant sizeof dessus), plutôt que comme un pointeur vers
le premier élément comme c'est habituel en C.



Apparemment non, car au point 6.7.5.3 § 12, la norme précise que cela
ne peut être utilisé que dans les prototypes et non dans les
définitions de fonctions:

If the function declarator is not part of a definition of that function, parameters may have
incomplete type and may use the [*] notation in their sequences of declar ator specifiers
to specify variable length array types.



D'ailleurs, si je compile ce code d'exemple avec GCC et Clang,
j'obtiens:

#include <stdio.h>
#include <stdlib.h>


void
affiche_taille(int tab[*])
{
printf("%zun", sizeof tab);
}


int
main(void)
{
unsigned n;

if (scanf("%u", &n) == 1) {
int tab[n];
affiche_taille(tab);
}

return EXIT_SUCCESS;
}

GCC:
main.c:7: error: ‘[*]’ not allowed in other than function prototype s cope



Clang:
main.c:6:20: error: variable length array must be bound in function defin ition
affiche_taille(int tab[*])



Dès lors, de nouveau, je ne comprends pas l'utilité de cette syntaxe.

Aussi, parce que de toutes facons, c'est juste du sucre syntaxique.
Passer du tableau dynamique a base de f(int t[*]), ca sera de toutes
facons exactement aussi efficace que le classique f(int t[], size_t n)
qui lui a l'avantage de fonctionner depuis C89.

Et si f(static int t[10]) ne "fonctionne" pas, c'est pas si grave...
ou plus exactement, je soupconne fort que, le jour ou il fonctionnera, le
compilo aura suffisamment de jugeotte pour faire les verifications
correspondantes dans des cas qui ne sont pas annotes...



Je suis entièrement d'accord, ce ne sont visiblement pas des éléments
importants et on peut franchement sans passer. Cependant, c'est
surtout par curiosité que je pose la question, j'aimerais juste savoir
à quoi serve concrètement ces syntaxes.
Avatar
espie
In article ,
Taurre wrote:

Je suis entièrement d'accord, ce ne sont visiblement pas des éléments
importants et on peut franchement sans passer. Cependant, c'est
surtout par curiosité que je pose la question, j'aimerais juste savoir
à quoi serve concrètement ces syntaxes.



Ben, a rien !
Avatar
Taurre
On 19 déc, 12:22, (Marc Espie) wrote:
In article .com>,

Taurre   wrote:

>Je suis entièrement d'accord, ce ne sont visiblement pas des éléme nts
>importants et on peut franchement sans passer. Cependant, c'est
>surtout par curiosité que je pose la question, j'aimerais juste savoir
>à quoi serve concrètement ces syntaxes.

Ben, a rien !



J'ai quand même un peu de mal à avaler qu'un comité de normalisation
composé (normalement) de professionnels et de gens compétents, ajoute
de nouvelle syntaxes au langage juste pour faire joli. Il doit quand
même bien y avoir un petite idée ou une petit raison derrière ces
ajouts non (autre que de faire joli)?
Avatar
espie
In article ,
Taurre wrote:
On 19 déc, 12:22, (Marc Espie) wrote:
In article


,

Taurre   wrote:

>Je suis entièrement d'accord, ce ne sont visiblement pas des éléments
>importants et on peut franchement sans passer. Cependant, c'est
>surtout par curiosité que je pose la question, j'aimerais juste savoir
>à quoi serve concrètement ces syntaxes.

Ben, a rien !



J'ai quand même un peu de mal à avaler qu'un comité de normalisation
composé (normalement) de professionnels et de gens compétents, ajoute
de nouvelle syntaxes au langage juste pour faire joli. Il doit quand
même bien y avoir un petite idée ou une petit raison derrière ces
ajouts non (autre que de faire joli)?



Il y a parfois des ratages dans les ajouts, genre des trucs qui ont l'air sympa
sur le papier, mais qui au final posent quelques gros soucis. Tres souvent,
c'est les memes trucs sur lesquels on voit apparaitre de grosses clarifications
dans la norme suivante.

(Il faut bien voir aussi que ca prend parfois du temps pour que les choses
soient implementees. Ca c'est bien vu en C99, ou tout le monde s'est depeche
d'implementer stdint.h et inttypes.h, tellement c'etait bienvenu par rapport
a ce qui existait avant. Et par contre d'autres choses plus complexes et
d'une utilite moins immediate sont restees un peu dans les cartons).


Par exemple, restrict, pour rester avec le C, a la semantique quelque peu
douteuse.

Cote C++, il a la grande histoire de extern template. Et dans les trucs plus
simples, auto_ptr<>, apparu pour C++98, et deprecie en C++2011....


Dans une norme, tu vas avoir des bouts qui cautionnent juste une pratique
deja existante. Parfois, un peu dur de trancher cote syntaxe, si tu as deux
compilos payants qui implementent des syntaxes divergentes, il y a fort a
parier que le comite va faire son consensus sur une 3e syntaxe, histoire de
mettre tout le monde d'accord. Et tu as des bouts qui vont etre ajoutes
parce que ca parait une bonne idee, en esperant que les compilos et les
developpeurs vont suivre... j'ai un peu l'impression que ces declarations
de tableau ameliores font partie de cette categorie.

Ah si, ca a eu un effet interessant: gcc s'est mis a verifier certains
prototypes avec un peu plus de precision, et a raler si tu declares
extern void f(int k[10]);

et puis si tu definis
void f(int k[15])
{}

comme toujours avec ce genre de precision accru, tu vois que certaines
personnes ont fait des trucs immondes... du coup, tu commences par corriger
le prototype fautif, tu lis un peu plus loin, et tu passes ton apres-midi
a remplacer du code bien pourri par quelque chose d'a peu pres correct.
Avatar
espie
In article <jcnbeu$2q0a$, Marc Espie wrote:
(Il faut bien voir aussi que ca prend parfois du temps pour que les choses
soient implementees. Ca c'est bien vu en C99, ou tout le monde s'est depeche


Ca s'est bien vu

mes excuses aux familles, tout ca...
d'implementer stdint.h et inttypes.h, tellement c'etait bienvenu par rapport
a ce qui existait avant. Et par contre d'autres choses plus complexes et
d'une utilite moins immediate sont restees un peu dans les cartons).
Avatar
Taurre
Ok, merci pour ces précisions.
Voilà qui répond à ma question, même si ce n'est pas vraiment le ty pe
de réponse auquel je m'attendais :p
Merci à vous ;)
Avatar
Pascal J. Bourguignon
Taurre writes:

On 19 déc, 12:22, (Marc Espie) wrote:
In article ,

Taurre   wrote:

>Je suis entièrement d'accord, ce ne sont visiblement pas des éléments
>importants et on peut franchement sans passer. Cependant, c'est
>surtout par curiosité que je pose la question, j'aimerais juste savoir
>à quoi serve concrètement ces syntaxes.

Ben, a rien !



J'ai quand même un peu de mal à avaler qu'un comité de normalisation
composé (normalement) de professionnels et de gens compétents, ajoute
de nouvelle syntaxes au langage juste pour faire joli. Il doit quand
même bien y avoir un petite idée ou une petit raison derrière ces
ajouts non (autre que de faire joli)?



Puisqu'il s'agit d'une question de conception de langage et de comités
de normalisation, je me permettrai de répondre en donnant l'exemple de
Common Lisp.

La différence entre lisp et les autres langages, c'est qui est autorisé
de changer le langage.

Dans le cas de lisp, grâce à son homoiconicité et à ses macros (qui
n'ont rien à voir avec les macros du préprocesseur C, mais ce n'est pas
la place de faire un cours), le programmeur lambda peut changer la
syntaxe du langage lui même.

Dans le cas des autres langages, tout changement, même le plus bénin
relève du comité de standardisation. (D'où nécessité d'une nouvelle
norme chaque paire d'année, et de changements triviaux ou esthétiques,
apparement inutiles comme celui discuté ici).



Ajouter une syntaxe pour faire joli peut avoir pour intérêt
d'uniformiser un concept, et donc de permettre de le manipuler de
manière plus abstraite. Ça permet de rendre indépendante la notation du
concept de l'implémentation de ce concept. Cette abstration syntactique
est un élement aussi important que les autres types d'abstration dont le
programmeur peut disposer ou non.

Il est malheureux que dans la plupart des langages l'abstration
syntactique ne soit pas accessible au programmeur, mais réservée aux
comités de normalisation. Lisp donne accès au programmeur cet outil
d'abstraction en plus des autres, ce qui est bénéfique.


--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.
Avatar
espie
In article ,
Pascal J. Bourguignon wrote:
[ vend du lisp en ligne depuis pas mal d'annees ]

Oh ben ca faisait longtemps, tiens !
1 2