Ce qui est plus lisible (C ++ =)

int valueToWrite = 0xFFFFFFFF; static char buffer2[256]; int* writePosition = (int* ) &buffer2[5]; *writePosition = valueToWrite; //OR * ((int*) &buffer2[10] ) = valueToWrite; 

Maintenant, je vous demande qui trouvez-vous le plus lisible? La technique en 2 étapes impliquant une variable temporaire ou la technique en une étape?

Ne vous inquiétez pas pour l’optimisation, ils optimisent tous les deux la même chose, comme vous pouvez le voir ici. Dites-moi simplement lequel est le plus lisible pour vous.

 or DWORD PTR ?buffer2@?1??main@@9@4PADA+5, -1 or DWORD PTR ?buffer2@?1??main@@9@4PADA+10, -1 

 int* writePosition = (int* ) &buffer2[5] 

Ou

 *((int*) &buffer2[10] ) = valueToWrite; 

Sont tous les deux incorrects car, sur certaines plates-formes, l’access à des valeurs non alignées (+5 + 10) peut coûter des centaines de cycles de processeur et, sur certains (comme l’ancien ARM), cela entraînerait une opération non conforme.

La manière correcte est:

 memcpy( buffer+5, &valueToWrite, sizeof(valueToWrite)); 

Et c’est plus lisible.

Une fois que vous l’encapsulez dans une classe, peu importe la technique que vous utilisez. Le nom de la méthode fournira la description de ce que fait le code. Ainsi, dans la plupart des cas, vous ne devrez pas vous plonger dans l’impl. pour voir ce qui se passe.

 class Buffer { char buffer2[256]; public: void write(int pos, int value) { int* writePosition = (int*) &buffer2[pos]; *writePosition = value; } } 

Si j’étais obligé de choisir, je dirais 1. Cependant, je noterai que le code tel qu’il est présenté est très similaire à C; Je craignais l’un ou l’autre et réexaminais le problème. En voici un simple qui est plus C ++ – y

 const char * begin = static_cast(static_cast(&valueToWrite)); std::copy(begin, begin+sizeof(int), &buffer2[5]); 

Le premier exemple est plus lisible simplement parce que votre cerveau n’a pas à déchiffrer les opérations de pointeur globales.

Cela réduira le temps nécessaire à un développeur pour consulter le code pour comprendre ce qui se passe réellement. D’après mon expérience, cela correspond vaguement à la réduction de la probabilité d’introduction de nouveaux bogues.

Je trouve le deuxième, plus court, plus facile à lire.

Je suppose cependant que cela dépend plutôt de savoir si vous êtes le type de personne qui peut facilement «obtenir» des indicateurs.

Le typage de char * à int * est toutefois un peu gênant. Je présume qu’il y a une bonne raison pour que cela soit fait.

Attention, ce code ne fonctionnera probablement pas à cause de problèmes d’alignement! Pourquoi ne pas simplement utiliser memset ?

 #include  memset(buffer2+10, 0xFF, 4); 

Si vous pouvez vous permettre de vous lier à un seul compilateur (ou faire des piratages de préprocesseur autour de problèmes de compatibilité), vous pouvez utiliser une option de structures condensées pour obtenir des noms symboliques pour les valeurs que vous écrivez. Par exemple, sur GCC:

 struct __atsortingbute__ ((__packed__)) packed_struct { char stuff_before[5] int some_value; } /* .... */ static char buffer2[256]; struct packed_struct *ps = buffer2; ps->some_value = valueToWrite; 

Cela présente de nombreux avantages:

  • Votre code reflète plus clairement ce que vous faites si vous nommez bien vos champs.
  • Comme le compilateur sait si la plate-forme sur laquelle vous vous trouvez prend en charge un access non aligné efficace, il peut automatiquement choisir entre un access natif non aligné, ou des solutions de contournement appropriées sur les plates-formes ne prenant pas en charge l’access non aligné.

Mais là encore, l’inconvénient majeur est de ne pas avoir de syntaxe normalisée.

Le plus lisible serait une variante avec un commentaire ajouté sur ce que vous faites là.

Cela étant dit, je méprise les variables introduites simplement dans le but d’utiliser une fois deux lignes plus tard. Travailler la majeure partie de mon travail dans le domaine de la maintenance, recevoir des dizaines de noms de variables qui me font mal au visage, ce qui représente de médiocres efforts, le fait de ne pas avoir à rédiger de commentaire explicatif me met sur les nerfs.

Absolument:

 * ((int*) &buffer2[10] ) = valueToWrite; 

Je ne l’parsing pas en une mais en quelques étapes, c’est pourquoi il est plus lisible: j’ai toutes les étapes dans la même ligne.

Du sharepoint vue de la lisibilité, le comportement de votre code doit être clair, mais “clair” n’est pas la façon dont je décrirais l’une ou l’autre de ces alternatives. En fait, ils sont le contraire de “clair”, car ils ne sont pas portables .

En plus des problèmes d’alignement, il existe une représentation entière (la taille varie d’un système à l’autre, de même que la représentation des signes, l’endianisme et le remplissage à jeter dans la soupe). Ainsi, le comportement de votre code d’un système à l’autre est erratique .

Si vous voulez être clair sur ce que votre algorithme est censé faire, vous devez explicitement mettre chaque octet à sa place correcte . Par exemple:

 void serialise_uint_lsb(unsigned char *destination, unsigned source) { destination[0] = source & 0xff; source >>= 8; destination[1] = source & 0xff; source >>= 8; assert(source == 0); } void deserialise_uint_lsb(unsigned *destination, unsigned char *source) { *destination = 0; *destination <<= 8; *destination += source[1]; *destination <<= 8; *destination += source[0]; } 

La sérialisation et la désérialisation sont des concepts idiomatiques pour les programmeurs ... *printf et *scanf sont des formes de sérialisation / désérialisation, par exemple, sauf que le chiffre le plus significatif (décimal) passe en premier ... ce qui est le problème avec votre code; votre code n'indique pas à votre système la direction de l'entier, le nombre d'octets, etc ... mauvaises nouvelles.

Utilisez une fonction de sérialisation / désérialisation. Les programmeurs comprendront mieux cela.