Devinette C/C++

Petite chose amusante que je viens de découvrir dans du code C/C++ . Selon vous, comment faire en sorte qu’une structure contienne un champ noté pData ( de type char par exemple) sans qu’il soit comptabilisé dans l’évaluation de la taille de ladite structure (via sizeof typiquement) ? ce champ doit néanmoins rester accessible sous la forme MyStruct.pData.

Je ne suis pas sûr que le code soit vraiment portable mais il compile et tourne sous MSDEV (Windows XP) et GNU C++ V2.95.2 (Solaris 7).

:stuck_out_tongue:

La vraie question n’est pas de savoir comment, mais plutôt pourquoi ? :stuck_out_tongue:

Sinon je réponds sûrement à côté, mais si tu codes avec une directive #pragma pack 4, les structures:

struct s1 {   char c1;   char c2;   int i; }
et

struct s2 {   char c1;   int i; }

donneront sizeof(s1) == sizeof(s2) à cause de l’alignement non ?

Mais j’imagine que tu voulais nous montrer une bidouille/bouse/pas de commentaire/pas documenté/accroche-toi au pinceau…

En effet, j’aurai dû préciser que l’astuce ne portait pas sur une bidouille lié à l’alignement car là, ce n’est pas portable du tout. Donc non, ce n’est pas le truc. Bien joué d’y avoir pensé quand même.

Tout le monde est à fond sur le sujet…

j’aimerais bien savoir moi.

C’est clair … j’ai un peu regarder dans les tréfonds des opérateurs qu’on connait pas … j’ai rien trouvé de tel.

Heuuu oui pardon, je l’avais oublié celui-ci blush

Bon alors je le répète, je pense que ce n’est pas du tout portable donc à ne pas utiliser à priori. Pourtant les compilos sus-mentionnés se comportent de la même façon, il y a donc un doute.

[code]struct MyStruct_
{
int a;
int b;
int c;
char pData[];
};

using namespace std;

int main(void)
{
MyStruct_ sValue;

	// Taille de la structure compte-tenu d'un alignement sur 4 octets on devrait 
	// avoir au moins 16
	cout << "size MyStruct_ == " << sizeof(MyStruct_) << endl;

	// Taille de "l'instanciation" de la structure (on ne sait jamais)
	// Même chiose que ci-dessus
	cout << "size sValue == " << sizeof(sValue) << endl;

	// Et pourtant j'ai accès au champ pData
	// SAIMAL ici car je ne tape pas dans une zone allouée mais
	/// c'est pour l'exemple.
	sValue.pData[0] = '1';

	return( 0 );

}[/code]

Mon exemple n’a de sens que pour l’illustration.

Ben euh oui, ça fait 16… Car pData[] est un pointeur, donc prend 32 bits sur un cpu 32 bits. Donc ca prend la même place qu’un int, sauf que ça risque de faire une violation mémoire :stuck_out_tongue:

ben chez moi ça fait 12 :stuck_out_tongue:

EDIT : Je viens de faire l’essai sur un Solaris 8 + GCC 3.3.2 et un Linux RedHat 7 G++ 2.96 et j’obtiens toujours 12 :stuck_out_tongue:

Drealmer> pData n’est pas un pointeur, ça serait plutôt un tableau à proprement parlé, c’est à dire dont le contenu est à l’endroit de la déclaration.

:stuck_out_tongue: : « Moi j’dis qu’il bleuffe »

[quote name=‹ Moktar › date=’ 2 May 2005, 16:13’]ben chez moi ça fait 12 :stuck_out_tongue:

EDIT : Je viens de faire l’essai sur un Solaris 8 + GCC 3.3.2 et un Linux RedHat 7 G++ 2.96 et j’obtiens toujours 12  :stuck_out_tongue:

Drealmer> pData n’est pas un pointeur, ça serait plutôt un tableau à proprement parlé, c’est à dire dont le contenu est à l’endroit de la déclaration.
[right][post=« 355505 »]<{POST_SNAPBACK}>[/post][/right][/quote]

Heu, je veux pas dire de conneries, mais il me semble bien, au moins en C, que pdata[] et *pData sont totalement equivalents.

Tu aurais mis pData[10], là, effectivement, il s’agirait d’un tableau (et pData « pointerais » sur l’offset du tableau dans la structure).

[quote name=‹ Tzim › date=’ 2 May 2005, 17:39’]Heu, je veux pas dire de conneries, mais il me semble bien, au moins en C, que pdata[] et *pData sont totalement equivalents.

Tu aurais mis pData[10], là, effectivement, il s’agirait d’un tableau (et pData « pointerais » sur l’offset du tableau dans la structure).
[right][post=« 355522 »]<{POST_SNAPBACK}>[/post][/right][/quote]

Je pense que c’est une demi bêtise :stuck_out_tongue:

Tu as raison, pData[] et *pData sont équivalent dans l’usage mais pas dans l’implentation mémoire.

Uh d’accord… étrange en effet. Je précise aussi que j’avais mal compris le commentaire dans le code qui dit « on devrait avoir au moins 16 », je pensais que le résultat était 16 :stuck_out_tongue: Et que du coup la bidouille n’avait aucun effet.

Bon alors je suis pas d’accord.

[quote name=‹ Moktar › date=’ 2 May 2005, 15:49’][code]struct MyStruct_
{
       int     a;
       int     b;
       int     c;
       char    pData[];
};
using namespace std;

int main(void)
{
       MyStruct_ sValue;
       // Taille de la structure compte-tenu d’un alignement sur 4 octets on devrait
       // avoir au moins 16
       cout << "size MyStruct_ == " << sizeof(MyStruct_) << endl;

       // Taille de « l’instanciation » de la structure (on ne sait jamais)
       // Même chiose que ci-dessus
       cout << "size sValue == " << sizeof(sValue) << endl;

       // Et pourtant j’ai accès au champ pData
       // SAIMAL ici car je ne tape pas dans une zone allouée mais
       /// c’est pour l’exemple.
       sValue.pData[0] = ‹ 1 ›;

       return( 0 );
}[/code][/quote]
Le code n’a de sens qu’en C++. En C, tu te touches. Avec « char pData[]; », gcc me dit:
sizeof.c:8: field `pData’ has incomplete type
Et ne link pas le merdier.
Le seul code qui compile, c’est ca:

[code]struct MyStruct
{
  int     a;
  int     b;
  int     c;
  char    *pData;
};

int main(void)
{
   struct MyStruct sValue;
   printf(« size MyStruct == %d\n »,sizeof(struct MyStruct));
   printf("size sValue ==%d\n ",sizeof(sValue));
   sValue.pData[0] = ‹ 1 ›;
   return( 0 );
}[/code]
Etonnament, le code ne segfault pas mais il devrait (c’est juste du bol que pdata ne pointe pas sur une zone memoire reserve), et ca donne:

[quote]size MyStruct == 16
size sValue ==16[/quote]
Bref c’est crade ton truc, moktar :stuck_out_tongue:
Teste sur:
Reading specs from /usr/lib/gcc-lib/i386-linux/2.95.4/specs
gcc version 2.95.4 20011002 (Debian prerelease)
(Debian Woody a jour)

edit: En C et a ma connaissance, char foo[]; n’a pas de sens en lui meme, il n’a d’interet que pour definir des const string:
char foo[]=« Hello World »;
Quand, en C, on fait ca, ca marche super bien et en theorie, on a pas le droit de modifier la chaine, meme si rien en pratique nous l’interdit.

LoneWolf
Jamais vu du C avec un cout :stuck_out_tongue:

Oui c’est du C++ (enfin façon de parler).

En effet, la syntaxe

à ma connaissance ne pouvait s’écrire que telle que, charge au compilo de réserver la taille nécessaire à stocker la chaine (dans cette exemple).

Si les compilos C++ sont passés outre cette restriction, c’est peut-être qu’il y a une raison et derrière ça un usage potentiel, pourquoi pas. La démonstration par l’exemple n’est pas rigoureuse, c’est évident, mais n’ayant jamais vu une telle syntaxe auparavant je vous sollicitais pour avoir éventuellement des infos (que je n’ai pas).

L’exemple que tu prends LoneWolf, avec la déclaration d’un pointeur, n’est pas en rapport avec la déclaration première. Ce n’est pas pareil un pointeur et un tableau :P.

Bref, si quelqu’un a des infos sur ce type de syntaxe, ça m’intéresse. Je ne fais que constater le comportement de plusieurs compilo C++ .

[quote name=‹ Moktar › date=’ 2 May 2005, 18:02’]Oui c’est du C++ (enfin façon de parler).

En effet, la syntaxe

char pData[] = "gnagna";

à ma connaissance ne pouvait s’écrire que telle que, charge au compilo de réserver la taille nécessaire à stocker la chaine (dans cette exemple).[/quote]
En fait, si je me souviens bien, la chaine est stocke tel quel dans le code et est alloue par le programme a l’initialisation (dans un coin de la ram) et pdata prend pour valeur le pointeur vers cette zone allouee.

Ca me dit rien et j’aimerais bien savoir d’ou tu sors ca :stuck_out_tongue:

Semantiquement, c’est different, mais dans le codage derriere, c’est pareil. Sauf que ton cas ne compile pas en C pur. Soit c’est tab (pointeur vers un tableau alloue) soit c’est *tab (pointeur vers un truc) soit c’est tab=« bla » (pointeur vers un tableau predefini). (on doit pouvoir faire *tab=« blah » d’ailleurs)

LoneWolf
Merde je vais rater le Zapping :stuck_out_tongue:

Si je ne m’abuse, la syntaxe [] avec rien entre les crochets est licite pour les arguments de fonctions, c’est souvent utilisé pour argv[] par exemple. Je pense aussi pouvoir dire que c’est équivalent à [0] (sans en être sûr cependant, et la flemme de tester). Par contre, déclarer un tableau quelconque avec [] ou [0] est illicite, mais supporté par certains compilers, avec un warning cependant.

Ca doit être une histoire de compatibilité ascendante bien tordue.

[quote name=‹ Drealmer › date=’ 2 May 2005, 18:49’]Si je ne m’abuse, la syntaxe [] avec rien entre les crochets est licite pour les arguments de fonctions, c’est souvent utilisé pour argv[] par exemple. Je pense aussi pouvoir dire que c’est équivalent à [0] (sans en être sûr cependant, et la flemme de tester). Par contre, déclarer un tableau quelconque avec [] ou [0] est illicite, mais supporté par certains compilers, avec un warning cependant.

Ca doit être une histoire de compatibilité ascendante bien tordue.
[right][post=« 355577 »]<{POST_SNAPBACK}>[/post][/right][/quote]
Bon alors c’est vieux, mais en C, AFAIK:
On a le droit de faire ca:
function foo(char str[])
qui est equivalent a:
function foo(char *str)
C’est simplement une maniere de dire « je veux un tableau » plutot que « je veux un pointeur ».

Pour []=[0] c’est faux. Je viens de tester l’exemple de Moktar, sizeof me renvoie « 12 » si je mets pData[0] a la place de *pData (c’est amusant d’ailleurs).
Pas de Warning a la compil d’ailleurs

LoneWolf
Au coeur du langage :stuck_out_tongue:

Je susi tombé sur cette syntaxe par hasard dans du code que je reprends et lorsque j’ai vu ça (déclaration d’un tab sans contenu initialisé - et pas en extern-) je me suis dit que ça ne pouvait pas compiler, selon mon XP. A ma grande, surprise ça compile avec des compilos C++ (cf. les différentes version sus-citées). J’essaie de trouver une info qui permettrait de retomber sur mes pattes :P.