Diferencia entre * ptr + = 1 y * ptr ++ en C

122

Recién comencé a estudiar C, y cuando hice un ejemplo sobre pasar puntero a puntero como parámetro de una función, encontré un problema.

Este es mi código de muestra:

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

El problema ocurre en la línea 16, cuando modifico *ptr+=1a *ptr++. El resultado esperado debería ser toda la matriz y el número 1, pero cuando uso*ptr++ el resultado es 0.

¿Hay alguna diferencia entre +=1y ++? Pensé que los dos son lo mismo.

huy nguyen
fuente
2
Tenga en cuenta que el código dado no se compilará como no ha declarado string.
Spikatrix
66
Otras notas: 1) allocateIntArrayes un mal nombre, ya que parece ser mallocla matriz de la función, pero no lo hace. Sugiero en su fillIntArraylugar. 2) No utiliza el valor de retorno de allocateIntArray. Le sugiero que cambie el tipo de retorno a void. 3) No debe if (ptr != NULL)en función de increasePointerser if (*ptr != NULL)? 4) El reparto malloces innecesario. Ver el comentario de Sourav arriba. 5) Esto: for (int i = 0; i < 10; i++){ printf("%d\n", p1[i]); }y printf("%d\n", *p1); p1--;debe incluirse if(p1 != NULL). 6) string.hno se usa.
Spikatrix
9
p+=1es como ++p, no comop++
Kos
55
Esta pregunta se hizo hace 4 años: ¿++ es lo mismo que + = 1 para los punteros
Ren
3
@ren Casi, pero no del todo. La pregunta vinculada no involucra al operador de desreferenciación, que es el quid de la cuestión del OP aquí.
Jason C

Respuestas:

289

La diferencia se debe a la precedencia del operador.

El operador posterior al incremento ++tiene mayor prioridad que el operador de desreferencia *. Entonces *ptr++es equivalente a*(ptr++) . En otras palabras, el incremento posterior modifica el puntero, no lo que apunta.

El operador de asignación +=tiene menor prioridad que el operador de desreferencia *, por lo que *ptr+=1es equivalente a (*ptr)+=1. En otras palabras, el operador de asignación modifica el valor al que apunta el puntero y no cambia el puntero en sí.

usuario3386109
fuente
3
Para principiantes, un mnemotécnico es la similitud entre *p++y *++p. La precedencia del operador de este último es clara, sigue la del primero.
Walter Tross
21

El orden de precedencia para los 3 operadores involucrados en su pregunta es el siguiente:

post-incremento ++> desreferencia *> asignación+=

Puede consultar esta página para obtener más detalles sobre el tema.

Al analizar una expresión, un operador que aparece en una fila estará más ajustado (como entre paréntesis) a sus argumentos que cualquier operador que aparezca en una fila más abajo. Por ejemplo, la expresión *p++se analiza como *(p++)y no como (*p)++.

En pocas palabras, para expresar esta asignación *ptr+=1utilizando el operador posterior al incremento, debe agregar paréntesis al operador de desreferencia para dar prioridad a esa operación ++como en este(*ptr)++

Younes Regaieg
fuente
3
Curiosamente, esta es actualmente la única respuesta que contiene la solución ... (* ptr) ++
hyde
7

Apliquemos paréntesis para mostrar el orden de las operaciones.

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

Hagámoslo de nuevo con

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

Y de nuevo con

*ptr++
*(ptr++)
  • En *ptr += 1, incrementamos el valor de la variable que apunta nuestro puntero .
  • En *ptr++, incrementamos el puntero después de que se completa toda nuestra declaración (línea de código) y devolvemos una referencia a la variable a la que apunta nuestro puntero .

Este último te permite hacer cosas como:

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

Este es un método común utilizado para copiar una srcmatriz en otra destmatriz.

Mateen Ulhaq
fuente
"y devolver una referencia a la variable a la que apunta nuestro puntero". C no tiene referencias.
Miles Rout
@MilesRout ¿Quizás llamarlo lvalue podría ser más preciso? Pero no estoy seguro de cómo ponerlo sin agregar jerga.
Mateen Ulhaq
3

Muy buena pregunta

En K&R "Lenguaje de programación C" "5.1 Punteros y Direcciones", podemos obtener una respuesta para esto.

"Los operadores unarios * y & se unen más estrechamente que los operadores aritméticos"

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

"Los operadores unarios como * y ++ se asocian de derecha a izquierda ".

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

// Funciona como * (ptr ++).

La forma correcta es:

(*ptr)++      //This will work.
Nick.Sang
fuente
Esta es la primera vez que comento sobre Stack Overflow. He actualizado el formato del código. ^^ Gracias por su sugerencia.
Nick.Sang
2

* ptr + = 1: aumenta los datos a los que apunta ptr. * ptr ++: Incrementa el puntero que apunta a la siguiente ubicación de memoria en lugar de los datos a los que apunta el puntero.

usuario5787482
fuente