2017-10-17 2 views
-1

Circular buffer incrementation 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

+3

Ceci est un très mauvais code. –

+0

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. –

+0

ne devrait pas il directement et il avec [BUFFERSIZE-1] au lieu de ANDING avec (void *) [BUFFERSIZE-1]? –

Répondre

1

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.

+0

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. –

+0

Le 'aptr' a probablement été déclaré comme un type de pointeur, c'est-à-dire' uint16_t * aptr', auquel cas c'est valide. – slightlynybbled

+1

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. –

1

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; 
}