Quels sont des exemples convaincants où l’arithmétique de pointeur est préférable à un indice de tableau?

Je prépare des diapositives pour un cours d’introduction au C et j’essaie de présenter de bons exemples (et la motivation) pour utiliser l’arithmétique de pointeur sur un indice en tableau.

Beaucoup d’exemples que je vois dans les livres sont assez équivalents. Par exemple, de nombreux livres montrent comment inverser la casse de toutes les valeurs d’une chaîne, mais à l’exception du remplacement d’un a [i] par un * p, le code est identique.

Je suis à la recherche d’un bon (et bref) exemple avec des tableaux unidimensionnels où l’arithmétique de pointeur peut produire un code nettement plus élégant. Des idées?

Obtenir à nouveau un pointeur au lieu d’une valeur:

On utilise généralement l’arithmétique de pointeur quand ils veulent obtenir un pointeur à nouveau. Pour obtenir un pointeur lorsque vous utilisez un index de tableau: vous devez 1) calculer le décalage du pointeur, puis 2) obtenir la valeur à cet emplacement de mémoire, puis 3) vous devez utiliser & pour obtenir à nouveau l’adresse. C’est plus de frappe et une syntaxe moins propre.

Exemple 1: Supposons que vous ayez besoin d’un pointeur sur le 512ème octet dans un tampon.

char buffer[1024] char *p = buffer + 512; 

Est plus propre que:

 char buffer[1024]; char *p = &buffer[512]; 

Exemple 2: strcat plus efficace

 char buffer[1024]; strcpy(buffer, "hello "); strcpy(buffer + 6, "world!"); 

C’est plus propre que:

 char buffer[1024]; strcpy(buffer, "hello "); strcpy(&buffer[6], "world!"); 

Utilisation de l’arithmétique de pointeur ++ en tant qu’iterator:

Incrémenter des pointeurs avec ++ et décrémenter avec – est utile lors de l’itération de chaque élément d’un tableau d’éléments. C’est plus propre que d’utiliser une variable distincte utilisée pour suivre le décalage.


Soustraction de pointeur:

Vous pouvez utiliser la soustraction de pointeur avec l’arithmétique de pointeur. Cela peut être utile dans certains cas pour obtenir l’élément avant celui que vous pointez. Cela peut également être fait avec des indices de tableau, mais cela semble vraiment mauvais et déroutant. Surtout pour un programmeur python où un indice négatif est donné pour indexer quelque chose à partir de la fin de la liste.

 char *my_strcpy(const char *s, char *t) { char *u = t; while (*t++ = *s++); return u; } 

Pourquoi voudriez-vous gâcher une telle beauté avec un index? (Voir K & R et comment ils se basent sur ce style.) Il y a une raison pour laquelle j’ai utilisé la signature ci-dessus telle qu’elle est. Arrêtez l’édition sans demander une clarification au préalable. Pour ceux qui pensent le savoir, recherchez la signature actuelle – vous avez manqué quelques qualifications ressortingct .

Test de l’alignement de la structure et de la mise en œuvre de la macro offsetof

L’arithmétique de pointeur peut sembler sophistiquée et “hackerique”, mais je n’ai jamais rencontré de cas plus rapide que l’indexation standard. Au contraire, j’ai souvent rencontré des cas où cela ralentissait considérablement le code.

Par exemple, la boucle séquentielle typique dans un tableau avec un pointeur peut être moins efficace que la boucle avec un index classique sur des processeurs modernes prenant en charge les extensions SSE. L’arithmétique de pointeur dans une boucle empêche suffisamment les compilateurs de réaliser la vectorisation de boucle, ce qui peut donner une amélioration typique des performances 2x à 4x. De plus, l’utilisation de pointeurs au lieu de simples variables entières peut entraîner des opérations de stockage de mémoire inutiles en raison de la création d’alias de pointeur.

Donc, généralement, l’arithmétique de pointeur au lieu d’un access indexé standard ne devrait JAMAIS être recommandée.

itérer dans un tableau à 2 dimensions où la position d’une donnée n’a pas vraiment d’importance
si vous n’utilisez pas de pointeurs, vous devrez garder trace de deux indices
avec des pointeurs, vous pouvez pointer vers le haut de votre tableau, et avec une seule boucle, zipper le tout

Si vous utilisiez un ancien compilateur ou un compilateur de systèmes embarqués spécialisé, il pourrait y avoir de légères différences de performances, mais la plupart des compilateurs modernes optimiseraient probablement ces différences (minuscules).

L’article suivant pourrait vous intéresser – dépend du niveau de vos étudiants:

http://geeks.netindonesia.net/blogs/risman/archive/2007/06/25/Pointer-Arithmetic-and-Array-Indexing.aspx

Vous parlez spécifiquement de C, mais C ++ s’appuie également sur ceci:

La plupart des calculs arithmétiques se généralisent naturellement au concept Forward Iterator. Parcourir la mémoire avec *p++ peut être utilisé pour n’importe quel conteneur séquencé (liste liée, liste à sauter, vecteur, arbre binary, arbre B, etc.), grâce à la surcharge de l’opérateur.

Quelque chose d’amusant, j’espère que vous n’aurez jamais à vous en occuper: les pointeurs peuvent être pseudonymes, alors que les tableaux ne le peuvent pas. Le crénelage peut entraîner toutes sortes de génération de code non idéal, la plus courante consistant à utiliser un pointeur comme paramètre de sortie pour une autre fonction. Fondamentalement, le compilateur ne peut pas supposer que le pointeur utilisé par la fonction ne se nomme pas lui-même ni quoi que ce soit d’autre dans le cadre de la stack. Il doit donc recharger la valeur à partir du pointeur à chaque utilisation. Ou plutôt, pour être sûr, il le fait.

Souvent, le choix n’est qu’un choix de style: l’un se présente ou se sent plus naturel que l’autre pour un cas particulier.

Il existe également l’argument selon lequel l’utilisation des index peut obliger le compilateur à recalculer de manière répétée les décalages à l’intérieur d’une boucle – je ne suis pas sûr de la fréquence (sauf dans les versions non optimisées), mais j’imagine que cela se produit, mais c’est probablement rarement un problème.

Un domaine qui, à mon avis, est important à long terme (ce qui pourrait ne pas s’appliquer à une classe d’introduction au C – mais apprenez-le tôt, dis-je) est l’utilisation de l’arithmétique de pointeur s’applique aux idiomes utilisés dans la STL C ++. Si vous leur faites comprendre l’arithmétique de pointeur et l’utilisez, ils découvriront ensuite, lorsqu’ils passeront à la STL, comment utiliser correctement les iterators.

 #include ctype.h void skip_spaces( const char **ppsz ) { const char *psz = *ppsz; while( isspace(*psz) ) psz++; *ppsz = psz; } void fn(void) { char a[]=" Hello World!"; const char *psz = a; skip_spaces( &psz ); printf("\n%s", psz); }