Je ne suis pas capable de comprendre comment la dernière instruction incrémente le pointeur. Est-ce que quelqu'un peut m'expliquer avec quelques exemples?Incrément de tampon circulaire utilisant une autre méthode
Répondre
Le code, comme le montre:
aptr = (aptr + 1) & (void *)(BUFFERSIZE - 1);
// |________| incremented here
Comme il est un tampon circulaire et la taille de la mémoire tampon est une puissance de 2, le &
est un moyen facile et rapide à rouler en masquant simplement. En supposant que le BUFFERSIZE
est 256
, puis:
num & (256 - 1) == num % 256
num & (0x100 - 1) == num % 0x100
num & (0x0ff) == num % 0x100
Lorsque le numéro est une puissance de 2, alors vous ne pouvez pas utiliser la technique de masquage:
num & (257 - 1) != num % 257
num & (0x101 - 1) != num % 0x101
num & 0x100 != num % 0x101
Le (void *)
permet au compilateur de choisir une largeur appropriée pour la constante BUFFERSIZE
en fonction de la largeur de votre pointeur ... bien qu'il soit généralement préférable de savoir - et d'utiliser! - la largeur avant une déclaration comme celle-ci.
J'ai ajouté la notation hexadécimale afin de rendre plus clair pourquoi le résultat &
dans un événement de survol émulé. Notez que 0xff
est binaire 0x11111111
, de sorte que l'opération AND masque simplement les bits supérieurs.
Mon doute est le num obtient ANDED avec (void *) (BUFFERSIZE-1), comment se fait-il que vous pouvez directement ET avec (256-1), car il en fait ETING num avec type de pointeur et pas simplement entier. Mon hypothèse est, si elle avait été num & (BUFFERSIZE-1), cela aurait donné la réponse finale dont vous parlez. –
Le 'aptr' a probablement été déclaré comme un type de pointeur, c'est-à-dire' uint16_t * aptr', auquel cas c'est valide. – slightlynybbled
Passer une valeur de pointeur vers le bit et est [une violation de contrainte] (http://port70.net/~nsz/c/c11/n1570.html#6.5.10p2) et devrait donc produire des diagnostics. –
2 problèmes avec cette approche.
A) L'utilisation d'un pointeur avec un fonctionnement par bits n'est pas un code portable. @Ilja Everilä
char *aptr;
// error: invalid operands to binary & (have 'char *' and 'void *')
// The following increments the index: (not really)
// aptr = (aptr + 1) & (void *)(BUFFERSIZE-1);
B) avec des compilateurs qui prennent en charge les calculs non-standard sur un void *
semblable à un char *
, le calcul est erroné si aptr
le point sur un objet plus large que char
et BUFFERSIZE
est le nombre d'éléments dans la mémoire tampon et pas la taille de l'octet. Bien sûr, cela dépend de la façon dont le complément non standard implémente some_type * & void *
. Pourquoi s'embêter à coder inutilement pour utiliser un comportement spécifique à l'implémentation?
Utilisez plutôt i % BUFFERSIZE
. Cette approche portable fonctionne lorsque BUFFERSIZE
est une puissance de 2 et ainsi que quand il ne l'est pas. Quand un compilateur voit i % power-of-2
et i
est un certain type non signé, alors le même code est certainement émis comme i & (power-of-2 - 1)
.
Pour les compilateurs qui ne reconnaissent pas cette optimisation, il faut alors envisager un meilleur compilateur.
#define BUFFERSIZE 256
int main(void) {
char buf[BUFFERSIZE];
// pointer solution
char *aptr = buf;
aptr = &buf[(aptr - buf + 1) % BUFFERSIZE];
// index solution
size_t index = 0;
index = (index + 1) % BUFFERSIZE;
}
Ceci est un très mauvais code. –
En raison de la nature power-of-two, il peut utiliser un masque de bits (BUFFERSIZE-1) pour ramener l'index à zéro lorsqu'il déborde. L'auteur du code essaye apparemment d'éviter le coup de performance de l'utilisation de l'opérateur modulo ou d'une branche conditionnelle. –
ne devrait pas il directement et il avec [BUFFERSIZE-1] au lieu de ANDING avec (void *) [BUFFERSIZE-1]? –