Różnica między * ptr + = 1 i * ptr ++ w C

123

Właśnie zacząłem uczyć się C i kiedy robiłem jeden przykład dotyczący przekazywania wskaźnika do wskaźnika jako parametru funkcji, znalazłem problem.

Oto mój przykładowy kod:

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

Problem pojawia się w linii 16, kiedy modyfikuję *ptr+=1do *ptr++. Oczekiwanym wynikiem powinna być cała tablica i liczba 1, ale kiedy używam *ptr++wyniku, to 0.

Czy jest jakaś różnica między +=1a ++? Myślałem, że obaj są tacy sami.

huy nguyen
źródło
2
Zauważ, że podany kod nie skompiluje się, ponieważ nie został zadeklarowany string.
Spikatrix
6
Inne uwagi: 1) allocateIntArrayto zła nazwa, ponieważ wydaje ci mallocsię tablica z funkcji, ale tak nie jest. Proponuję fillIntArrayzamiast tego. 2) Nie używasz zwracanej wartości allocateIntArray. Proponuję zmienić typ zwrotu na void. 3) nie powinna if (ptr != NULL)w funkcję increasePointerbyć if (*ptr != NULL)? 4) Wrzucenie mallocjest zbędne. Zobacz komentarz Sourav powyżej. 5) To: for (int i = 0; i < 10; i++){ printf("%d\n", p1[i]); }i printf("%d\n", *p1); p1--;musi być zawarte w if(p1 != NULL). 6) string.hjest nieużywany.
Spikatrix
9
p+=1jest jak ++p, a nie jakp++
Kos
5
to pytanie zadano 4 lata temu: Czy ++ to to samo co + = 1 dla wskaźników
ren
3
@ren Prawie, ale niezupełnie. Powiązane pytanie nie dotyczy operatora wyłuskiwania, który jest sednem problemu PO.
Jason C

Odpowiedzi:

290

Różnica wynika z pierwszeństwa operatorów.

Operator post-inkrementacji ++ma wyższy priorytet niż operator wyłuskiwania *. Więc *ptr++jest równoważne *(ptr++). Innymi słowy, przyrost postu modyfikuje wskaźnik, a nie to, na co wskazuje.

Operator przypisania +=ma niższy priorytet niż operator wyłuskiwania *, więc *ptr+=1jest równoważny (*ptr)+=1. Innymi słowy, operator przypisania modyfikuje wartość wskazywaną przez wskaźnik i nie zmienia samego wskaźnika.

user3386109
źródło
3
Dla początkujących mnemonik to podobieństwo między *p++i *++p. Pierwszeństwo operatorów tego drugiego jest jasne, następuje pierwszy z pierwszych.
Walter Tross
21

Kolejność pierwszeństwa dla 3 operatorów, których dotyczy Twoje pytanie, jest następująca:

post-inkrementacja ++> dereference *> przypisanie+=

Możesz sprawdzić tę stronę, aby uzyskać więcej informacji na ten temat.

Podczas analizowania wyrażenia operator, który jest wymieniony w jakimś wierszu, będzie ściślej powiązany (jak w nawiasach) z jego argumentami niż jakikolwiek operator wymieniony w wierszu dalej pod nim. Na przykład wyrażenie *p++jest analizowane jako *(p++), a nie jako (*p)++.

Krótko mówiąc, aby wyrazić to przypisanie *ptr+=1za pomocą operatora post-inkrementacji, musisz dodać nawiasy do operatora wyłuskiwania, aby nadać tej operacji pierwszeństwo przed ++jak w tym(*ptr)++

Younes Regaieg
źródło
3
Co ciekawe, jest to obecnie jedyna odpowiedź, która zawiera rozwiązanie ... (* ptr) ++
hyde
7

Zastosujmy nawiasy, aby pokazać kolejność operacji

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

Zróbmy to jeszcze raz

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

I znowu z

*ptr++
*(ptr++)
  • W programie *ptr += 1zwiększamy wartość zmiennej, na którą wskazuje nasz wskaźnik .
  • W programie *ptr++zwiększamy wskaźnik po wykonaniu całej instrukcji (linii kodu) i zwracamy odniesienie do zmiennej, na którą wskazuje nasz wskaźnik .

Ta ostatnia pozwala na:

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

Jest to typowa metoda kopiowania srctablicy do innej desttablicy.

Mateen Ulhaq
źródło
"i zwróć odniesienie do zmiennej, na którą wskazuje nasz wskaźnik." C nie ma odniesień.
Miles Rout
@MilesRout Być może nazwanie tego lwartością byłoby dokładniejsze? Ale nie jestem pewien, jak to ująć bez dodawania żargonu.
Mateen Ulhaq
3

Bardzo dobre pytanie.

W K&R „języku programowania C” „5.1 Wskaźniki i adresy” możemy uzyskać odpowiedź na to pytanie.

„Operatory jednoargumentowe * i & wiążą ściślej niż operatory arytmetyczne”

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

„Operatory jednoargumentowe, takie jak * i ++, kojarzą się od prawej do lewej ”.

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

// Działa jak * (ptr ++).

Prawidłowy sposób to:

(*ptr)++      //This will work.
Nick.Sang
źródło
Po raz pierwszy komentuję przepełnienie stosu. Zaktualizowałem format kodu. ^^ Dziękujemy za sugestię.
Nick. Śpiewał
2

* ptr + = 1: przyrost danych, na które wskazuje ptr. * ptr ++: Wskaźnik przyrostowy, który wskazuje następną lokalizację w pamięci zamiast danych, na które wskazuje wskaźnik.

user5787482
źródło