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

sizeof d'un tableau typedef-é

35 réponses
Avatar
Pierre Maurette
Bonjour,

A partir de ça:

typedef uint8_t pixelRGBA_word[4];
typedef uint8_t pixelRGB_word[3];
typedef uint8_t pixelNG_word[1];

typedef pixelNG_word pixel_word;

je voulais avoir automatiquement le sizeof de ma donnée de type
pixel_word. Sur Google, j'ai dû mal chercher, je tombe uniquement sur
des trucs bateaux concernant les tableaux une fois passés à une
fonction.

Après avoir constaté qu'évidemment il me suffisait de créer un
pixel_word bidon là ou je voulais le sizeof, puis passé du bidon au
temporaire, j'ai de fil en aiguille trouvé ça à mettre dans le .h qui
fonctionne (gcc 3.4):

static const size_t MPI_MUL = sizeof(pixel_word){};
ou
#define MPI_MUL (sizeof(pixel_word){})

Est-ce la bonne pratique ?

Merci et bonne journée...

--
Pierre Maurette

10 réponses

1 2 3 4
Avatar
Marc Boyer
Le 11-06-2007, Thierry B a écrit :
--{ Marc Boyer a plopé ceci: }--
C'est aussi mon sentiment, et ça semble idiomatique. Mais
Marc Espie semblait émettre des réserves.

Oui, j'ai vu. Il faut bien entendu que le nom soit bien choisi,

et là, c'est affaire de conventions personnelles (dans mon cas)
ou de règles clairement prédéfinies dans le cas d'un projet.
Il me semble bien qu'à la lecture de ce nom, il soit immédiat
de se dire "c'est un typedef pour ce projet/cette lib".


Et pui on a exactement le même problème de collision de
noms sans le typedef.

Et _surtout_, de ne pas passer à la déclaration
d'un typedef pour les pointeurs sur la structure "typedéfée"
à l'étape précedente. Pour moi, un pointeur doit *toujours*
rester explicite. Bon, je ne suis pas une référence non
plus, c'est juste que je fonctionne comme ça :-(


A mon sens, si on fait un typedef, c'est pour définir un nouveau
type, un nouvel usage. En effet, si on a juste besoin d'un pointeur,
je conseillerais de faire une struc avec un unique pointeur dedans.


Euh ? mettre un unique pointeur dans une structure, j'ai du mal
à voir, un petit exemple ?


Prenons un arbre:
struct Node {
double data;
struct Noeud* lesser;
struct Noeud* greater;
};

typedef struct Node* Tree; // Pas beau
typedef struct {
struct Node* root;
} Tree; // Beau ;-)


En fait ce que je voulais dire, c'est
#v+
/* exemple rapide */
#define T_NOM 42
typedef struct
{
char nom[T_NOM+1];
int valeur;
} Mon_Machin;
typedef Mon_Machin *Mon_Machin_Ptr; /* <----- ici */
int main(int argc, char *argv[])
{
Mon_Machin machin;
Mon_Machin_Ptr truc; /* <----- et la */
machin.valeur = 51;
truc = &machin;
return 0;
}
#v-

C'est ce genre de typedef que je n'aime pas, puisque plus loin dans
le code, il n'est pas immédiat que c'est réellement un pointeur C,
l'interprétation est plus "subjective".


C'est surtout execivement moche puisqu'on cache un pointeur dans un
type, mais qu'on va l'utiliser comme un pointeur. Ensuite, on va faire
void foo(Mon_Machin_Ptr x);
pour simuler un passage par référence de Mon_Machin (mode in/out en
algo), alors que l'idiomatique en C, c'est
void foo(Mon_Machin* x);


Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)



Avatar
Jean-Marc Bourguet
(Marc Espie) writes:

Les erreurs courantes de debutants sont de mettre des typedef sur des
struct ou des tableaux (oui, je n'aime pas).


J'ai aucun probleme avec les typedef sur les struct (ni meme sur les
pointeurs quand la cible est une struct declaree mais non definie).

Entre:
typedef struct foo foo; // qui ne compile pas en C++


Ca devrait.

typedef struct foo * foo;

par contre ne devrait pas.

typedef struct foo *machin; // et hop, un pointeur tout ca.


Tant que la seule utilisation de machin c'est de les copier et de les
passer a des fonctions, ca ne me gene pas.

Il semblerait que le programmeur moyen ait beaucoup de mal a comprendre
a quel point un typedef est quelque chose de violent, puisque sa simple
presence suffit a rendre invalide tout le code C qui suit qui a le malheur
d'utiliser le nouveau nom de type pour n'importe quoi, y compris comme
nom de variable locale.


Je ne me rends pas compte de ca. J'ai meme la conviction que c'est faux.

typedef struct x { int x; } x;

int foo() {
x a;
int x;
a.x = 1;
x = 2;
return a.x == x;
}

doit compiler (et compile en C comme en C++ d'ailleurs avec tout les
compilateurs que j'ai essaye)

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Antoine Leca
En news:,
Thierry B va escriure:
--{ Marc Boyer a plopé ceci: }--

Un commentaire sur le commentaire. Lorsque j'enseigne le C, je
deconseille toujours l'usage de typedef et l'usage de macros (*)
aux debutants.

*: pour autre chose que de betes #define VALUE 42


Même un typedef sur des structs ?


Si, pour moi, celui-ci peut être bien, et même souvent,
comment dire, "clarifiant", à condition que le nom soit
bien choisi.


Euh, j'ai du mal à suivre. En quoi

typedef struct {
. . .
} montype;

est plus, ou moins, clair que

struct montype {
. . .
};


Et _surtout_, de ne pas passer à la déclaration
d'un typedef pour les pointeurs sur la structure "typedéfée"
à l'étape précedente. Pour moi, un pointeur doit *toujours*
rester explicite.


Désolé, mais en C ce n'est pas comme cela que le langage est organisé. D'où
les paramètres « passés par référence », les chaînes de caractères, les
tableaux, argv, localconv(), gmtime() et une palanquée d'autres choses qui
sont en fait des pointeurs mais qui ne le disent pas toujours...


Antoine



Avatar
Charlie Gordon
"Jean-Marc Bourguet" a écrit dans le message de news:

(Marc Espie) writes:

Les erreurs courantes de debutants sont de mettre des typedef sur des
struct ou des tableaux (oui, je n'aime pas).


J'ai aucun probleme avec les typedef sur les struct (ni meme sur les
pointeurs quand la cible est une struct declaree mais non definie).

Entre:
typedef struct foo foo; // qui ne compile pas en C++


Ca devrait.

typedef struct foo * foo;

par contre ne devrait pas.

typedef struct foo *machin; // et hop, un pointeur tout ca.


Tant que la seule utilisation de machin c'est de les copier et de les
passer a des fonctions, ca ne me gene pas.

Il semblerait que le programmeur moyen ait beaucoup de mal a comprendre
a quel point un typedef est quelque chose de violent, puisque sa simple
presence suffit a rendre invalide tout le code C qui suit qui a le
malheur
d'utiliser le nouveau nom de type pour n'importe quoi, y compris comme
nom de variable locale.


Je ne me rends pas compte de ca. J'ai meme la conviction que c'est faux.

typedef struct x { int x; } x;

int foo() {
x a;
int x;
a.x = 1;
x = 2;
return a.x == x;
}

doit compiler (et compile en C comme en C++ d'ailleurs avec tout les
compilateurs que j'ai essaye)


Trop fort !
Ca compile en effet sans le moindre warning et si je rajoute la ligne

printf("sizeof(x)=%dn", (int)sizeof(x));

Cela me donne 4 (en 32 bits) même après modification de la structure pour
rendre réellement ambiguë cette expression.

En revanche, il n'est pas possible d'inverser l'ordre des déclarations de a
et x dans la fonction foo;
Pas possible non plus de nommer une fonction "x" ce qui est un vrai
problème de portabilité.

Chqrlie.


Avatar
Jean-Marc Bourguet
"Charlie Gordon" writes:

En revanche, il n'est pas possible d'inverser l'ordre des déclarations de a
et x dans la fonction foo;
Pas possible non plus de nommer une fonction "x"


Les deux ont une meme cause, dans une meme portee, un identificateur ne
peut avoir qu'une definition par espace de nommage et les identificateurs
de fonctions, variables et typedef partage le meme espace de nommage.

ce qui est un vrai problème de portabilité.


La maniere dont je choisi les identificateurs fait qu'il y a peut de risque
de conflits meme sans jouer avec des conventions imposant des structures
majuscule/minuscule/underscore/prefixe/suffixe differentes pour les types,
variables et fonctions.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Thierry B
--{ Marc Boyer a plopé ceci: }--

Programmer en C, c'est aussi se prémunir de sa propre bétise.


Ah ben ça oui...

Thierry, coredumpeur professionnel.

--
D'un côté, les appels au bios et de l'autre, ceux au noyau Linux.
Si une application a été mal portée dans Linux, il se peut qu'il reste des
appels au bios, quoique ce ne soit pas très habituel sans doute.
--{ DB, in fcol.configuration }--

Avatar
Thierry B
--{ Marc Boyer a plopé ceci: }--

C'est ce genre de typedef que je n'aime pas, puisque plus loin dans
le code, il n'est pas immédiat que c'est réellement un pointeur C,
l'interprétation est plus "subjective".


C'est surtout execivement moche puisqu'on cache un pointeur dans un
type, mais qu'on va l'utiliser comme un pointeur. Ensuite, on va faire
void foo(Mon_Machin_Ptr x);
pour simuler un passage par référence de Mon_Machin (mode in/out en
algo), alors que l'idiomatique en C, c'est
void foo(Mon_Machin* x);


Voilà, c'est ça. Je dois être idiot, mais j'aime bien les
idiomatismes...


--
How much does a slrn weigh?
42.

42 in African or European units?

British units.




Avatar
espie
In article ,
Jean-Marc Bourguet wrote:
Marc Espie a ecrit:
Il semblerait que le programmeur moyen ait beaucoup de mal a comprendre
a quel point un typedef est quelque chose de violent, puisque sa simple
presence suffit a rendre invalide tout le code C qui suit qui a le malheur
d'utiliser le nouveau nom de type pour n'importe quoi, y compris comme
nom de variable locale.


Je ne me rends pas compte de ca. J'ai meme la conviction que c'est faux.

typedef struct x { int x; } x;

int foo() {
x a;
int x;
a.x = 1;
x = 2;
return a.x == x;
}

doit compiler (et compile en C comme en C++ d'ailleurs avec tout les
compilateurs que j'ai essaye)


Tiens, j'avais des souvenirs plus croquignolesques, mais tu as raison,
pour ce point... le typedef n'entre en collision qu'avec les objets
globaux, ce qui est deja embetant.

Bon, l'autre bonne raison de ne pas aimer les typedefs est d'ordre
stylistique, comme les macros: la presence de typedef oblige a lire
des fragments de code situes ailleurs, pour comprendre ce qui se passe.
En particulier, ca change un peu la syntaxe du code, puisqu'on peut
faire des typedefs sur des structures, des pointeurs, etc.

Lorsque je vois une fonction de prototype:
void f(struct machin *a);

je sais ce que la fonction prend comme parametre, et a quoi je peux
m'attendre comme semantique.

Lorsque je vois une fonction de prototype
void f(machin a);

eh bien, je ne sais pas si machin est un type scalaire, un type pointeur
(de quel niveau ?) ou autre chose. Faudra que j'aille chercher sa definition
pour comprendre ce qui se passe, meme au niveau syntaxique.

Tu me diras, je peux choisir des conventions de nommage propre, mais c'est
pas tres facile.

Ajoute a ca le fait que le typedef ne fait que des equivalences de type (pas
de vrais types abstraits), et c'est tres facile de se retrouver avec
des constructions equivalentes qui ne font pas la meme chose...

je prefere le code un peu plus explicite, c'est plus facile pour un
exterieur de rentrer dedans, et j'ai rarement vu des conventions de
nommage suffisamment carrees pour que les typedef aident a rendre les
choses claires (bien au contraire, j'ai vu des tonnes de gens se vautrer
avec des conventions de nommage incorrectes...)

Dans le cadre tres particulier de l'audit de code, tout ce que font les
typedef, c'est rendre le code plus difficile a lire, et masquer les
problemes...

Les deux seuls cas ou typedef se justifient pour moi:
- les types de portabilite, a la size_t et uint32_t (et encore... ils
sont a moitie buggues a certains points de vue... le coup du size_t unsigned
et du ptrdiff_t signed occasionnent souvent quelques maux de tete).
- les pointeurs de fonction, le cas ou on gagne en lisibilite a passer
par un typedef intermediaire.

La situation est differente en C++, qui possede de vrais types abstraits,
et des references, ce qui fait qu'on peut precisement controler la
semantique d'un type A pour quasiment toutes les operations (mais on n'utilise
pas non plus trop de typedef en C, a part pour les traits).


Avatar
Pascal Bourguignon
(Marc Espie) writes:

In article ,
Jean-Marc Bourguet wrote:
Marc Espie a ecrit:
Il semblerait que le programmeur moyen ait beaucoup de mal a comprendre
a quel point un typedef est quelque chose de violent, puisque sa simple
presence suffit a rendre invalide tout le code C qui suit qui a le malheur
d'utiliser le nouveau nom de type pour n'importe quoi, y compris comme
nom de variable locale.


Je ne me rends pas compte de ca. J'ai meme la conviction que c'est faux.

typedef struct x { int x; } x;

int foo() {
x a;
int x;
a.x = 1;
x = 2;
return a.x == x;
}

doit compiler (et compile en C comme en C++ d'ailleurs avec tout les
compilateurs que j'ai essaye)
[...]

je prefere le code un peu plus explicite, c'est plus facile pour un
exterieur de rentrer dedans, et j'ai rarement vu des conventions de
nommage suffisamment carrees pour que les typedef aident a rendre les
choses claires (bien au contraire, j'ai vu des tonnes de gens se vautrer
avec des conventions de nommage incorrectes...)


Oui, moi aussi je préfère que le code soit plus explicite. D'ailleurs,

void foo(struct s* a){
a->un=1;
a->deux="deux";
}


n'est pas assez explicite. Il faudrait l'écrire:

foo:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
movl $1, 4(%eax)
movl 8(%ebp), %eax
movl $.LC0, 8(%eax)
popl %ebp
ret

pour que ce soit encore plus explicite. Ainsi on verrait
explicitement que un est le champ à un offset de 4, et deux est le
champ à un offset de 8. Avec la forme précédence, ça reste trop
implicite.


Dans le cadre tres particulier de l'audit de code, tout ce que font les
typedef, c'est rendre le code plus difficile a lire, et masquer les
problemes...


En effet. On se demande comment on peut même écrire des programmes
avec des struct s* qui masquent les problèmes. Vive les 4(%eax) et
autres 8(%eax).



--
__Pascal Bourguignon__ http://www.informatimago.com/

NOTE: The most fundamental particles in this product are held
together by a "gluing" force about which little is currently known
and whose adhesive power can therefore not be permanently
guaranteed.



Avatar
Jean-Marc Bourguet
(Marc Espie) writes:

Lorsque je vois une fonction de prototype:
void f(struct machin *a);

je sais ce que la fonction prend comme parametre, et a quoi je peux
m'attendre comme semantique.

Lorsque je vois une fonction de prototype
void f(machin a);

eh bien, je ne sais pas si machin est un type scalaire, un type pointeur
(de quel niveau ?) ou autre chose. Faudra que j'aille chercher sa definition
pour comprendre ce qui se passe, meme au niveau syntaxique.


Je ne vois pas la difference.

Soit tu sais comment te servir correctement de machin, et donc les
operations autorisees. Soit tu ne le sais pas et tu vas voir la doc qui va
te les indiquer.

Savoir que machin est un unsigned int ne va pas t'aider a savoir ce que tu
peux passer quand une fonction demande un machin (est-ce que la somme de
deux machins a du sens? et un ou bitwise? ou uniquement ce qui a ete
retourne precedemment) Savoir que machin est un pointeur ne va pas t'aider
a savoir comment allouer ou deallouer un machin. Savoir que machin est une
struct ne va pas t'aider a connaitre les invariants que les champs doivent
respecter.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org

1 2 3 4