Diferença entre * ptr + = 1 e * ptr ++ em C

122

Comecei a estudar C e, ao fazer um exemplo sobre a passagem de ponteiro para ponteiro como parâmetro de uma função, encontrei um problema.

Este é o meu código de exemplo:

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

O problema ocorre na linha 16, quando modifico *ptr+=1para *ptr++. O resultado esperado deve ser toda a matriz e o número 1, mas quando eu uso *ptr++o resultado é 0.

Existe alguma diferença entre +=1e ++? Eu pensei que os dois são iguais.

huy nguyen
fonte
2
Observe que o código fornecido não será compilado porque você não declarou string.
Spikatrix
6
Outras notas: 1) allocateIntArrayé um nome ruim, pois parece que você é malloco array da função, mas não o faz. Eu sugiro em fillIntArrayvez disso. 2) Você não utiliza o valor de retorno de allocateIntArray. Eu sugiro que você altere o tipo de retorno para void. 3) Não deve if (ptr != NULL), em função de increasePointerser if (*ptr != NULL)? 4) O elenco não mallocé necessário. Veja o comentário de Sourav acima. 5) Isto: for (int i = 0; i < 10; i++){ printf("%d\n", p1[i]); }e printf("%d\n", *p1); p1--;precisa ser incluído if(p1 != NULL). 6) string.hnão está sendo usado.
Spikatrix
9
p+=1é como ++p, não é comop++
Kos
5
esta pergunta foi feita há 4 anos: Is ++ o mesmo que + = 1 para ponteiros
ren
3
@ren Quase, mas não exatamente. A questão vinculada não envolve o operador de desreferência, que é o cerne da questão do OP aqui.
Jason C

Respostas:

289

A diferença é devido à precedência do operador.

O operador pós-incremento ++tem precedência mais alta que o operador de desreferência *. Então *ptr++é equivalente a *(ptr++). Em outras palavras, o incremento de postagem modifica o ponteiro, não o que ele aponta.

O operador de atribuição +=tem precedência mais baixa que o operador de desreferência *, portanto *ptr+=1é equivalente a (*ptr)+=1. Em outras palavras, o operador de atribuição modifica o valor que o ponteiro aponta e não altera o ponteiro em si.

user3386109
fonte
3
Para iniciantes, um mnemônico é a semelhança entre *p++e *++p. A precedência do operador para este último é clara, a do primeiro.
Walter Tross
21

A ordem de precedência para os três operadores envolvidos na sua pergunta é a seguinte:

pós-incremento ++> desreferência *> atribuição+=

Você pode verificar esta página para obter mais detalhes sobre o assunto.

Ao analisar uma expressão, um operador listado em alguma linha será mais restrito (como entre parênteses) aos seus argumentos do que qualquer operador listado em uma linha mais abaixo. Por exemplo, a expressão *p++é analisada como *(p++)e não como (*p)++.

Para encurtar a história, para expressar essa atribuição *ptr+=1usando o operador pós-incremento, é necessário adicionar parênteses ao operador de desreferência para dar precedência à operação, ++como neste exemplo.(*ptr)++

Younes Regaieg
fonte
3
De maneira interessante, atualmente é a única resposta que contém a solução ... (* ptr) ++ #
hyde
7

Vamos aplicar parênteses para mostrar a ordem das operações

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

Vamos fazer de novo com

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

E novamente com

*ptr++
*(ptr++)
  • Em *ptr += 1, incrementamos o valor da variável para a qual o ponteiro aponta .
  • Em *ptr++, incrementamos o ponteiro após a conclusão de toda a nossa declaração (linha de código) e retornamos uma referência à variável para a qual o ponteiro aponta .

O último permite que você faça coisas 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 é um método comum usado para copiar uma srcmatriz para outra destmatriz.

Mateen Ulhaq
fonte
"e retorne uma referência à variável para a qual nosso ponteiro aponta." C não tem referências.
Route de milhas
@MilesRout Talvez chamá-lo de lvalue possa ser mais preciso? Mas não sei como colocá-lo sem adicionar jargão.
Mateen Ulhaq 15/02
3

Muito boa pergunta.

Na K&R "linguagem de programação C" "5.1 Ponteiros e endereços", podemos obter uma resposta para isso.

"Os operadores unários * e & se ligam mais firmemente que os operadores aritméticos"

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

"Operadores unários como * e ++ associam da direita para a esquerda ."

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

// Funciona como * (ptr ++).

A maneira correta é:

(*ptr)++      //This will work.
Nick.Sang
fonte
Esta é a primeira vez que comento o Stack Overflow. Atualizei o formato do código. ^^ Obrigado pela sua sugestão.
Nick.Sang
2

* ptr + = 1: incrementa os dados para os quais ptr aponta. * ptr ++: Incrementa o ponteiro que está apontado para o próximo local da memória, em vez dos dados que o apontador aponta.

user5787482
fonte