[C] Décaler un début de tableau - défendable ou pas ?

Décaler un début de tableau - défendable ou pas ? [C] - C - Programmation

Marsh Posté le 10-05-2005 à 09:38:31    

Plop, j'aimerais votre avis sur une pratique en C, possible mais pas forcément bonne, puisqu'elle casse ( pour la personne qui va réutiliser le code )
les habitudes, et fait commencer un tableau en 1 au lieu de 0, ceci en décrémentant le pointeur (cf. le gras (et la tache )) avant tout usage.
Inversement il faut par contre penser à incrémenter le pointeur avant de freeter la variable ...
Je suis partagé :
+ La dimention 1 est en case 1 et etc, c'est pratique pour la relecture des algos
+ je peut faire dans mes for des test "lala>0" pour aller jusqu'à la dernière dimension, alors qu'avec un tableau normal je devrais faire "lala>=0", ce qui implique d'avoir un type dims_t signé, et donc de gérér 2x moins de dimensions, (et sémantiquement d'employer une variable signée pour quelque chose qui n'a pas de sens au négatif.).
 
- ca change de l'habitude, ca casse un peu la "norme"
- ca fait un pointeur qui naturellement ne pointe pas au bon endroit.
 
bien que l'emploi de con/destructeurs et d'un type particulier ( cvect_t ) tende a circonscrire le problème et avertir le lecteur de code...
 
Je reste encore partagé, avec tout plein de remords (j'aime pas faire un code crade), cette idée me simplifie grandement les algos (dumoins en terme de clarté) mais le "hors-norme" me fait peur, comment évaluriez-vous la balance avantages/inconvénients d'un truc comme ca ? existe t'il une autre solution plus propre, ou plus établie ?
 
( y'a certes aussi la solution d'alouer une case en plus, de ne pas décaler le pointeur, et de ne pas utiliser la case [0], mais quel gaspillage, surtout que je vais en avoir bcp de ces vecteurs ... )

Code :
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include "wetrust.h"
  4. /* type de base d'une "cellule" */
  5. typedef int cell_t;
  6. /* vecteur en n dimensions de cellules */
  7. /* ( des coordonnées quoi ... ) */
  8. typedef cell_t* cvect_t;
  9. /* type stockant la dimention */
  10. typedef unsigned dims_t;
  11. int main (void)
  12. {
  13.   cvect_t coin;
  14.   dims_t lala;
  15.   lala = 3;
  16.   coin = calloc(lala,sizeof(*coin));
  17.   if (coin == NULL) exit(0xBAD);
  18.   coin--;   /* <-- C'est la que ca se passe ! */
  19.   while ( lala > 0 )
  20.   {
  21.     printf("La coordonnées sur la dimention %u du vecteur coin est : %i\n", lala, coin[lala]);
  22.     lala--;
  23.   }
  24.   coin++;
  25.   free(coin);
  26.   return 0;
  27. }


 
Nota : dans l'example j'ai pas remplit les valeurs, elles sont toutes a 0, le mécanisme de la boucle avait rien de différent du while déja présent, c'est pas un noubli ;)


Message édité par 0x90 le 10-05-2005 à 09:41:47
Reply

Marsh Posté le 10-05-2005 à 09:38:31   

Reply

Marsh Posté le 10-05-2005 à 10:03:52    

Beaucoup de soucis pour un gain mineur.
 
D'autant plus que la vitesse de compréhension de relecture de code, c'est subjectif. Tu trouves peut-être ça plus simple, mais si quelqu'un d'autre te relit, sa première expression risque d'être "WTF ?!".
 
Et tes typedef ne contribuent pas à la clarté du code non plus.
Je préfère, et de loin, lire un bon vieux "int*", plutôt que de voir des cvect_t, qui est un typedef de cell_t*, qui lui-même est un typedef de int.
 
Au final, dès que le code deviendra compliqué, tu risques de t'y perdre toi-même.


Message édité par Elmoricq le 10-05-2005 à 10:05:02
Reply

Marsh Posté le 10-05-2005 à 10:20:59    

pour les typedef, j'ai pas trop le choix, dans cet exemple simplifié cell_t est un int, dans le programme final, le cell_t peut varier selon les compils, je me vois mal maintenir des versions différentes suivant les types que je veut :/
 
A noter que la j'ai utilisé un décalage de seulement 1, j'ai un autre cas ou je voulais être plus radical et faire un décalage de 'A' (65),  pour ne pas avoir à faire à chaque fois que je veut consulter ma table une soustraction .
A chaque lettre en majuscule j'associe une fonction, c'est beaucoup plus lisible il me semble :  
fonction[letter](coin);
que :
fonction[letter-'A'](coin);
 
( avec fonction le tableau de fonctions "décalé" ).

Reply

Marsh Posté le 10-05-2005 à 10:56:02    

0x90 a écrit :


+ je peut faire dans mes for des test "lala>0" pour aller jusqu'à la dernière dimension, alors qu'avec un tableau normal je devrais faire "lala>=0", ce qui implique d'avoir un type dims_t signé, et donc de gérér 2x moins de dimensions, (et sémantiquement d'employer une variable signée pour quelque chose qui n'a pas de sens au négatif.).


 
tu peux aussi exprimer ta boucle dans le sens croissant, et utiliser un type non signé.
 

Citation :

- ca change de l'habitude, ca casse un peu la "norme"
- ca fait un pointeur qui naturellement ne pointe pas au bon endroit.


 
rédibitoire !
 

Citation :

bien que l'emploi de con/destructeurs et d'un type particulier ( cvect_t ) tende a circonscrire le problème et avertir le lecteur de code...


 
A la rigueur ...
 

Citation :

Je reste encore partagé, avec tout plein de remords (j'aime pas faire un code crade), cette idée me simplifie grandement les algos (dumoins en terme de clarté) mais le "hors-norme" me fait peur, comment évaluriez-vous la balance avantages/inconvénients d'un truc comme ca ? existe t'il une autre solution plus propre, ou plus établie ?


 
Oui, faire du fortran. Houla, qu'est ce que j'ai dit moi :o
En C, l'indiçage commence à 0, je pense qu'il faut s'y tenir. Un relecteur plus expérimenté en C sera perplexe devant cet affront envers les us et coutumes !

Reply

Marsh Posté le 10-05-2005 à 12:02:13    

Les tableaux commençant à 1, c'est une habitude du Basic ça, non  :whistle:  ?
 
Je te déconseille de faire ce que tu fais là :
- 1 : c'est pas lisible pour les autres développeur  
- 2 : c'est pas la norme, donc pour appeler une autre fonction avec ton tableau tu devras faire fct( coin+1 ) => pas vraiment lisible
- 3 : prend des bonnes habitudes, comence les tableaux à 0, parceque quand tu devras faire du Java ou autres et que tu essayeras coin-- ça va pas le faire :D
 
 
 

0x90 a écrit :


A chaque lettre en majuscule j'associe une fonction, c'est beaucoup plus lisible il me semble :  
fonction[letter](coin);
que :
fonction[letter-'A'](coin);


 
Si tu ne trouves pas ça lisible, suffit de rajouter des commentaires:

Code :
  1. int indice = letter-'A' ; /* Calcul de l'indice de la lettre dans le tableau */
  2.   fonction[indice](coin);


Message édité par pascal_ le 10-05-2005 à 12:03:09
Reply

Marsh Posté le 10-05-2005 à 12:56:01    

"et fait commencer un tableau en 1 au lieu de 0, ceci en décrémentant le pointeur "
 
je vais pas plus loin ...

Reply

Marsh Posté le 10-05-2005 à 13:21:23    

Perso j'ai eu a reutiliser un code ou il y avait ce genre de fantaisie sur certains pointeurs ... et ben j'en pleure encore  :sweat:  
Pour moi c est vraiment a eviter si tu tiens a la sante mentale de la personne qui utilise le code apres toi.

Reply

Marsh Posté le 10-05-2005 à 14:05:11    

pas défendable.

Reply

Marsh Posté le 10-05-2005 à 14:15:39    

Indéfendable : ça me rappelle le turbo pascal :D

Reply

Marsh Posté le 10-05-2005 à 14:57:30    

Ce genre de chose ammène imanquablement la cohabitation de tableaux commencent à 0 et de tableaux commencent à 1. La cohabitation des 2 va faire apparaitre un paquet de bugs à cause des +1 / -1 difficiles à comprendre et à deboguer.
 
En plus, utiliser un type non signé pour représenter la taille d'un tableau et la position dans celui-ci présente un avantage indéniable : cela permet de faire des opération arithmétiques sur les indices sans douleur. Va faire la difference entre 2 indices sur un tableau quand ce resultat peut-être négatif ( et il à bien le droit d'ailleurs ! ) si les indices sont non signés.

Reply

Marsh Posté le 10-05-2005 à 14:57:30   

Reply

Marsh Posté le 10-05-2005 à 15:41:46    

Bon vu l'avis de la majorité, je pense pas que je vais l'utiliser, mais cependant je tient a préciser quelques points :
 
- Je ne "commence pas à prendre des habitudes", mon habitude c'est de démarrer a 0, 99.9% du temps je trouve ca mieux, plus clair, et ca m'avantage dans mes algos (de plus, Vive les normes!).
- Si je le fais, je ne mélange pas, c'est uniquement pour un type de donnée précis ( mes cvect_t ), pour une application précise, avec ses propres fonctions de traitements
- Je suis POUR l'utilisation d'un non-signé dans ce cas la, c'était justement entre autres pour ca que je préférais cette méthode décalée, pour mes tests en boucle.  
- Ce type de données ( cvect_t ), dans l'application sera extrèmement utilisé, avec de grosses contraintes de rapidité d'exécution, si je peut gratter une opération par dimension (certes minime, une soustraction), ca m'intéresse beaucoup. (c'est une genre de VM, et le cvect_t équivaut à un pointeur, mais dans une espace à n-dimentions ( language ésotérique ...), il est donc très-très utilisé.
 
A priori jvais pouvoir m'en sortir avec des boucles ascendantes quasiment sans surcout (ni temps, ni espace), et si je peut, tant mieux :) par contre pour ma table d'instructions, me passer de la soustraction en la précalculant (il s'agit de ca en somme) et en la balisant bien dans le code /* Attention, Code Méchant !! */
 
 
Ptit détail entre la boucle ascendante et la descendante cependant :
 
Dans celle qui descent, on initialise à une valeur "borne" qu'on a récupéré kkpart ( une autre variable ), et on la compare ensuite à une constante numérique ( 0 ).
Dans celle qui monte, on fait l'inverse, on initialise une fois à une constante, et on compare à une variable.
J'avais donc initialement peur que la solution qui monte soit plus lente, si le prog doit aller la chercher à chaque fois, c'est plus lourd qu'une constante qui fait partie du code. ( ok c'est de l'encu... de mouches ).
Finalement je viens de réaliser que si le compilo est logique, si dans le corps de la boucle la variable à laquelle on compare n'est pas affectée, et si elle n'a pas été déclarée volatile, il sait qu'il n'a pas à la relire et donc la garde dans un registre tout le temps de la boucle.
Si c'est vraiment le cas, alors il n'y a pas perte, le monde est merveilleux et les compilateurs malins ( et je fais relativement confiance à gcc de ce point de vue la ), est-ce que c'est le cas ?
 
( ouais je pourrais aller voir dans l'asm et regarder ce qu'il fout c'est vrai ...)

Reply

Marsh Posté le 11-05-2005 à 12:13:55    

0x90 a écrit :

Finalement je viens de réaliser que si le compilo est logique, si dans le corps de la boucle la variable à laquelle on compare n'est pas affectée, et si elle n'a pas été déclarée volatile, il sait qu'il n'a pas à la relire et donc la garde dans un registre tout le temps de la boucle.
Si c'est vraiment le cas, alors il n'y a pas perte, le monde est merveilleux et les compilateurs malins ( et je fais relativement confiance à gcc de ce point de vue la ), est-ce que c'est le cas ?


oui, mais pas toujours. Si la borne sup de la boucle est une rvalue, le compilateur a de très bonne oportunité de réalisé l'optimisation. Dans le cas contraire, il agira avec prudence. Pour plus de détails, voir le thread récent sur FCLC (usenet) qui évoque le sujet.

Reply

Marsh Posté le 11-05-2005 à 12:19:40    

rvalue, constante, ou variable locale non modifiée dans la boucle, ...

Reply

Marsh Posté le 11-05-2005 à 13:52:53    

J'viens de m'inscrire sur FCLC , j'ai fait quelques recherches ( optim, gcc, boucle ... ) j'ai pas trouvé le thread, pourrait tu me l'indiquer s'il te plait ( le titre par exemple, ou un endroit ou le voir si il s'est déja fait écraser ) ?


Message édité par 0x90 le 11-05-2005 à 14:12:09
Reply

Marsh Posté le 11-05-2005 à 14:01:03    

merde, FCLC++ au temps pour moi.

Reply

Marsh Posté le 11-05-2005 à 14:23:03    

oki thx :)
 
( ca part un peu loin dans le thread, mais en somme pour des cas aussi simples, oui :) ( j'avais pas pensé au cas ou y'a une fonction dans la boucle cela dit ... ) )

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed