Différence entre * ptr + = 1 et * ptr ++ en C

123

Je viens de commencer à étudier C, et en faisant un exemple de passage d'un pointeur à un pointeur en tant que paramètre d'une fonction, j'ai trouvé un problème.

Voici mon exemple de code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int* allocateIntArray(int* ptr, int size){
    if (ptr != NULL){
        for (int i = 0; i < size; i++){
            ptr[i] = i;
        }
    }
    return ptr;
}

void increasePointer(int** ptr){
    if (ptr != NULL){
        *ptr += 1; /* <----------------------------- This is line 16 */
    }
}

int main()
{
    int* p1 = (int*)malloc(sizeof(int)* 10);
    allocateIntArray(p1, 10);

    for (int i = 0; i < 10; i++){
        printf("%d\n", p1[i]);
    }

    increasePointer(&p1);
    printf("%d\n", *p1);
    p1--;
    free(p1);
    fgets(string, sizeof(string), stdin);
    return 0;
}

Le problème se produit à la ligne 16, lorsque je modifie *ptr+=1en *ptr++. Le résultat attendu doit être le tableau entier et le numéro 1, mais lorsque j'utilise *ptr++le résultat est 0.

Y a-t-il une différence entre +=1et ++? Je pensais que les deux étaient identiques.

huy nguyen
la source
2
Notez que le code donné ne sera pas compilé car vous ne l'avez pas déclaré string.
Spikatrix
6
Autres notes: 1) allocateIntArrayest un mauvais nom car il semble que vous soyez mallocle tableau de la fonction, mais vous ne le faites pas. Je suggère à la fillIntArrayplace. 2) Vous n'utilisez pas la valeur de retour de allocateIntArray. Je vous suggère de changer le type de retour en void. 3) La if (ptr != NULL)fonction ne devrait-elle pas l' increasePointerêtre if (*ptr != NULL)? 4) Le casting mallocest inutile. Voir le commentaire de Sourav ci-dessus. 5) Ceci: for (int i = 0; i < 10; i++){ printf("%d\n", p1[i]); }et printf("%d\n", *p1); p1--;doit être inclus dans if(p1 != NULL). 6) string.hest inutilisé.
Spikatrix
9
p+=1est comme ++p, pas commep++
Kos
5
cette question a été posée il y a 4 ans: Is ++ the same as + = 1 for pointers
ren
3
@ren Presque, mais pas tout à fait. La question liée n'implique pas l'opérateur de déréférencement, qui est au cœur du problème du PO ici.
Jason C

Réponses:

290

La différence est due à la priorité des opérateurs.

L'opérateur de post-incrémentation ++a une priorité plus élevée que l'opérateur de déréférencement *. Donc *ptr++équivaut à *(ptr++). En d'autres termes, l'incrément de poste modifie le pointeur, pas ce qu'il pointe.

L'opérateur d'affectation +=a une priorité inférieure à l'opérateur de déréférencement *, il *ptr+=1est donc équivalent à (*ptr)+=1. En d'autres termes, l'opérateur d'affectation modifie la valeur vers laquelle pointe le pointeur et ne change pas le pointeur lui-même.

user3386109
la source
3
Pour les débutants, un mnémonique est la similitude entre *p++et *++p. La priorité de l'opérateur de ce dernier est claire, celle du premier suit.
Walter Tross
21

L'ordre de priorité des 3 opérateurs impliqués dans votre question est le suivant:

post-incrément ++> déréférencement *> affectation+=

Vous pouvez consulter cette page pour plus de détails sur le sujet.

Lors de l'analyse d'une expression, un opérateur qui est répertorié sur une ligne sera lié plus étroitement (comme par des parenthèses) à ses arguments que tout opérateur répertorié sur une ligne plus en dessous. Par exemple, l'expression *p++est analysée en tant que *(p++)et non en tant que (*p)++.

En bref, pour exprimer cette affectation *ptr+=1à l'aide de l'opérateur de post-incrémentation, vous devez ajouter des parenthèses à l'opérateur de déréférencement pour donner à cette opération la priorité sur ++comme dans ce(*ptr)++

Younes Regaieg
la source
3
Fait intéressant, c'est actuellement la seule réponse qui contient une solution ... (* ptr) ++
hyde
7

Appliquons des parenthèses pour montrer l' ordre des opérations

a + b / c
a + (b/c)

Faisons-le encore avec

*ptr   += 1
(*ptr) += 1

Et encore avec

*ptr++
*(ptr++)
  • Dans *ptr += 1, nous incrémentons la valeur de la variable vers laquelle pointe le pointeur .
  • Dans *ptr++, nous incrémentons le pointeur après la fin de notre instruction (ligne de code) et renvoyons une référence à la variable vers laquelle pointe le pointeur .

Ce dernier vous permet de faire des choses comme:

for(int i = 0; i < length; i++)
{
    // Copy value from *src and store it in *dest
    *dest++ = *src++;

    // Keep in mind that the above is equivalent to
    *(dest++) = *(src++);
}

Il s'agit d'une méthode courante utilisée pour copier un srctableau dans un autre desttableau.

Mateen Ulhaq
la source
"et renvoie une référence à la variable vers laquelle pointe le pointeur." C n'a pas de références.
Miles Rout
@MilesRout Peut-être que l'appeler lvalue pourrait être plus précis? Mais je ne sais pas comment le dire sans ajouter de jargon.
Mateen Ulhaq
3

Très bonne question.

Dans K&R "langage de programmation C" "5.1 pointeurs et adresses", nous pouvons obtenir une réponse à cela.

"Les opérateurs unaires * et & se lient plus étroitement que les opérateurs arithmétiques"

*ptr += 1      //Increment what ptr points to.

"Les opérateurs unaires comme * et ++ s'associent de droite à gauche ."

*ptr++        //Increment prt instead of what ptr point to.

// Cela fonctionne comme * (ptr ++).

La bonne manière est:

(*ptr)++      //This will work.
Nick.Sang
la source
C'est la première fois que je commente Stack Overflow. J'ai mis à jour le format du code. ^^ Merci pour votre suggestion.
Nick.Sang
2

* ptr + = 1: Incrémente les données vers lesquelles pointe ptr. * ptr ++: pointeur d'incrémentation qui pointe vers l'emplacement mémoire suivant au lieu des données vers lesquelles pointe le pointeur.

user5787482
la source