Wyrażenia wskaźnikowe: * ptr ++, * ++ ptr i ++ * ptr

128

Niedawno natknąłem się na ten problem, którego sam nie potrafię zrozumieć.

Co naprawdę oznaczają te trzy wyrażenia ?

*ptr++
*++ptr
++*ptr

Próbowałem Ritchie. Ale niestety nie był w stanie nadążyć za tym, co powiedział o tych 3 operacjach.

Wiem, że wszystkie są wykonywane w celu zwiększenia wskaźnika / wskazanej wartości. Mogę również zgadywać, że może być wiele rzeczy dotyczących pierwszeństwa i kolejności ocen. Podobnie jak inkrementuje się wskaźnik, a potem pobiera zawartość tego wskaźnika, po prostu pobiera się zawartość, a następnie zwiększa wskaźnik itd. Itd. Jak widać, nie mam jasnego zrozumienia ich faktycznych operacji, które chciałbym jak najszybciej usunąć. Ale jestem naprawdę zagubiony, kiedy mam szansę zastosować je w programach. Na przykład:

int main()
{
    const char *p = "Hello";
    while(*p++)
         printf("%c",*p);
    return 0;
}

daje mi to wyjście:

ello

Ale spodziewałem się, że to się wydrukuje Hello. Ostatnia prośba - proszę podać przykłady działania każdego wyrażenia w danym fragmencie kodu. Ponieważ przez większość czasu nad moją głową przelatuje tylko jeden akapit teorii.

asygnowany
źródło
6
Przegapiłeś czwarty: (*ptr)++(nawiasy potrzebne do ujednoznacznienia *ptr++)
user4815162342
15
Ponieważ zwiększyłeś wskaźnik przed wydrukowaniem. Chciałeś while (* p) i printf ("% c", * p ++);
dcaswell
Świetne pytania do rozmowy kwalifikacyjnej. Ograniczone praktyczne zastosowanie. Żałuję, że C nie ma tych wskazówek :)
Himanshu
5
@Himanshu Jeśli to piecze makaron twojego rozmówcy, spróbuj tego: Miej globalny wskaźnik char* p, wskazujący na poprawny zakończony ciąg unikalnych znaków. Następnie mają funkcję fn(char ch), która drukuje zarównoch parametr i prąd wskazywany przez char p. Teraz wywołaj fn(*p++);P: Czy wyświetla fnten sam znak dwa razy ? Zdziwiłbyś się, ilu profesorów źle zrozumiało to pytanie.
WhozCraig
1
ponieważ p wskazuje na literał łańcuchowy, który należy napisaćconst char* p = "Hello";
hetepeperfan

Odpowiedzi:

275

Oto szczegółowe wyjaśnienie, które, mam nadzieję, będzie pomocne. Zacznijmy od twojego programu, ponieważ jest on najłatwiejszy do wyjaśnienia.

int main()
{
    const char *p = "Hello";
    while(*p++)
        printf("%c",*p);
    return 0;
}

Pierwsze stwierdzenie:

const char* p = "Hello";

deklaruje pjako wskaźnik do char. Kiedy mówimy „wskaźnik do a char”, co to oznacza? Oznacza to, że wartością pjest adres a char; pmówi nam, gdzie w pamięci jest trochę miejsca na przechowywanie char.

Instrukcja jest również inicjowana, paby wskazać pierwszy znak w literale ciągu "Hello". Dla dobra tego ćwiczenia, ważne jest, aby zrozumieć, pjak nie wskazując na cały ciąg, ale tylko do pierwszego znaku, 'H'. W końcu pjest wskaźnikiem do jednego char, a nie do całego ciągu. Wartością pjest adres 'H'in "Hello".

Następnie konfigurujesz pętlę:

while (*p++)

Co oznacza stan pętli *p++? Działają tu trzy rzeczy, które sprawiają, że jest to zagadkowe (przynajmniej do czasu, gdy się zażywa):

  1. Pierwszeństwo dwóch operatorów, przyrostka ++i pośrednia*
  2. Wartość wyrażenia przyrostu przyrostka
  3. Efekt uboczny wyrażenia przyrostu przyrostka

1. Pierwszeństwo . Szybkie spojrzenie na tabelę pierwszeństwa dla operatorów powie, że przyrost przyrostka ma wyższy priorytet (16) niż wyłuskiwanie / pośrednictwo (15). Oznacza to, że złożone wyrażenie *p++ma być grupowane jako: *(p++). Oznacza to, że *część zostanie zastosowana do wartości p++części. Więc weźmy p++najpierw część.

2. Wartość wyrażenia Postfix . Wartość p++jest wartością p przed przyrostem . Jeśli masz:

int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);

wynik będzie następujący:

7
8

ponieważ i++szacuje się do iprzed przyrostem. Podobnie p++będzie oceniać bieżącą wartość p. Jak wiemy, aktualna wartość pto adres 'H'.

Więc teraz p++część *p++została oszacowana; to aktualna wartość p. Wtedy *część się dzieje. *(current value of p)oznacza: dostęp do wartości pod adresem przechowywanym przez p. Wiemy, że wartość pod tym adresem to 'H'. Więc wyrażenie *p++oblicza 'H'.

Teraz poczekaj chwilę, mówisz. Jeśli ma *p++wartość 'H', dlaczego nie pojawia się to 'H'w powyższym kodzie? Tutaj pojawiają się efekty uboczne .

3. Efekty uboczne wyrażenia Postfix . Postfix ++ma wartość bieżącego operandu, ale ma efekt uboczny zwiększania wartości tego operandu. Co? Spójrz intponownie na ten kod:

int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);

Jak wspomniano wcześniej, wynik będzie następujący:

7
8

Gdy i++ocenia się w pierwszym printf(), to ocenia się 7. Ale C Standard gwarantuje, że w pewnym momencie przed drugim printf()zaczyna wykonującego efektem ubocznym tego ++operatora będą miały miejsce. Oznacza to, że zanim printf()nastąpi drugie , izostanie zwiększone w wyniku działania ++operatora w pierwszym printf(). Nawiasem mówiąc, jest to jedna z nielicznych gwarancji, jakie norma daje co do czasu wystąpienia skutków ubocznych.

Następnie w kodzie *p++obliczone wyrażenie oblicza 'H'. Ale zanim do tego dojdziesz:

printf ("%c", *p)

pojawił się ten nieznośny efekt uboczny. pzostał zwiększony. Whoa! Nie wskazuje już 'H'na jedną postać z przeszłości 'H': 'e'innymi słowy. To wyjaśnia twoje cockneyfied wyjście:

ello

Stąd chór pomocnych (i dokładnych) sugestii w innych odpowiedziach: aby wydrukować wymowę otrzymaną, "Hello"a nie jej odpowiednik cockney, potrzebujesz czegoś takiego jak

while (*p)
    printf ("%c", *p++);

Tyle na ten temat. Co z resztą? Pytasz o znaczenie tych:

*ptr++
*++ptr
++*ptr

Po prostu rozmawialiśmy o pierwszej, więc spójrzmy na sekundę: *++ptr.

Widzieliśmy w naszym wcześniejszym wyjaśnieniu, że przyrost przyrostka p++ma pewien priorytet , wartość i efekt uboczny . Inkrementacja przedrostka ++pma ten sam efekt uboczny, co jego odpowiednik po przyrostku: zwiększa swój operand o 1. Jednakże ma inny priorytet i inną wartość .

Przyrost przedrostka ma niższy priorytet niż przyrostek; ma pierwszeństwo 15. Innymi słowy, ma taki sam priorytet jak operator wyłuskiwania / pośredniczenia *. W wyrażeniu takim jak

*++ptr

nie ma znaczenia pierwszeństwo: te dwa operatory mają takie same pierwszeństwo. Tak więc asocjatywność zaczyna działać. Przyrost przedrostka i operator pośredni mają łączność prawa-lewa. Z powodu tego skojarzeń, operand ptrma być zgrupowane ze skrajnej prawej operatora ++przed operatorem bardziej na lewo *. Innymi słowy, wyrażenie zostanie zgrupowane *(++ptr). Tak więc, jak w przypadku, *ptr++ale z innego powodu, również tutaj *część zostanie zastosowana do wartości ++ptrczęści.

Więc co to za wartość? Wartość wyrażenia zwiększania przedrostka jest wartością argumentu po inkrementacji . To sprawia, że ​​jest to zupełnie inna bestia od operatora przyrostu przyrostka. Powiedzmy, że masz:

int i = 7;
printf ("%d\n", ++i);
printf ("%d\n", i);

Wynik będzie:

8
8

... różni się od tego, co widzieliśmy z operatorem postfiksowym. Podobnie, jeśli masz:

const char* p = "Hello";
printf ("%c ", *p);    // note space in format string
printf ("%c ", *++p);  // value of ++p is p after the increment
printf ("%c ", *p++);  // value of p++ is p before the increment
printf ("%c ", *p);    // value of p has been incremented as a side effect of p++

wynik będzie następujący:

H e e l                // good dog

Czy rozumiesz, dlaczego?

Teraz mamy do trzeciej wypowiedzi pytał pan o, ++*ptr. Właściwie to najtrudniejsza z wielu. Oba operatory mają ten sam priorytet i łączność prawa-lewa. Oznacza to, że wyrażenie zostanie zgrupowane ++(*ptr). ++Część zostanie zastosowana do wartości *ptrczęści.

Więc jeśli mamy:

char q[] = "Hello";
char* p = q;
printf ("%c", ++*p);

zaskakująco egoistyczny wynik będzie następujący:

I

Co?! OK, więc *pczęść będzie oceniana do 'H'. Wtedy ++wchodzi do gry, w którym momencie zostanie zastosowany do wskaźnika 'H', a nie do wskaźnika! Co się stanie, gdy dodasz 1 do 'H'? Otrzymasz 1 plus wartość ASCII 'H', 72; masz 73. Oświadczamy, że jako char, i masz charo wartości ASCII 73: 'I'.

To rozwiązuje problem z trzema wyrażeniami, o które zapytałeś w swoim pytaniu. Oto kolejny, wspomniany w pierwszym komentarzu do Twojego pytania:

(*ptr)++ 

Ten też jest interesujący. Jeśli masz:

char q[] = "Hello";
char* p = q;
printf ("%c", (*p)++);
printf ("%c\n", *p);

da ci to entuzjastyczne wyjście:

HI

Co się dzieje? Ponownie, jest to kwestia pierwszeństwa , wartości wyrażenia i skutków ubocznych . Ze względu na nawiasy *pczęść jest traktowana jako wyrażenie podstawowe. Podstawowe wyrażenia są ważniejsze od wszystkiego innego; są oceniane jako pierwsze. I *p, jak wiesz, ocenia 'H'. Reszta wyrażenia, czyli ++część, jest stosowana do tej wartości. Tak więc w tym przypadku (*p)++staje się 'H'++.

Jaka jest wartość 'H'++? Jeśli powiedziałeś 'I', zapomniałeś (już!) Naszej dyskusji o wartości vs. efekt uboczny z przyrostem przyrostka. Pamiętaj, 'H'++zwraca bieżącą wartość 'H' . Więc to najpierw printf()zostanie wydrukowane 'H'. Następnie, jako efekt uboczny , 'H'zostanie zwiększony do 'I'. Drugi to printf()drukuje 'I'. I masz twoje wesołe powitanie.

W porządku, ale w tych dwóch ostatnich przypadkach, dlaczego tego potrzebuję

char q[] = "Hello";
char* p = q;

Dlaczego nie mogę po prostu mieć czegoś takiego

/*const*/ char* p = "Hello";
printf ("%c", ++*p);   // attempting to change string literal!

Ponieważ "Hello"jest to literał ciągu. Jeśli spróbujesz ++*p, próbujesz zmienić 'H'ciąg na 'I', tworząc cały ciąg "Iello". W języku C literały łańcuchowe są tylko do odczytu; próba ich zmodyfikowania wywołuje niezdefiniowane zachowanie. "Iello"jest niezdefiniowany również w języku angielskim, ale to tylko zbieg okoliczności.

I odwrotnie, nie możesz tego mieć

char p[] = "Hello";
printf ("%c", *++p);  // attempting to modify value of array identifier!

Dlaczego nie? Ponieważ w tym przypadku pjest tablicą. Tablica nie jest modyfikowalną wartością l; nie możesz zmienić pmiejsca, w którym wskazuje przed lub po inkrementacji lub dekrementacji, ponieważ nazwa tablicy działa tak, jakby była stałym wskaźnikiem. (Tak naprawdę nie jest; to po prostu wygodny sposób patrzenia na to).

Podsumowując, oto trzy rzeczy, o które pytałeś:

*ptr++   // effectively dereferences the pointer, then increments the pointer
*++ptr   // effectively increments the pointer, then dereferences the pointer
++*ptr   // effectively dereferences the pointer, then increments dereferenced value

A oto czwarta, równie zabawna jak pozostałe trzy:

(*ptr)++ // effectively forces a dereference, then increments dereferenced value

Pierwsza i druga ulegną awarii, jeśli w ptrrzeczywistości jest to identyfikator tablicy. Trzeci i czwarty ulegną awarii, jeśli ptrwskażą literał ciągu.

Masz to. Mam nadzieję, że teraz wszystko jest kryształowe. Byłeś świetną publicznością i będę tu przez cały tydzień.

gadatliwy
źródło
22
Przed wejściem na to forum przeszukałem 3 książki "C", których jestem właścicielem. Wypróbowałem także kilka godnych uwagi samouczków online. Ale żaden z nich nie zbliża się do twojego wyjaśnienia (zwłaszcza sposobu, w jaki to wszystko złożyłeś). Nie tylko odpowiedziałeś na zadane przeze mnie pytanie, ale także omówiłeś o wiele więcej rzeczy z poziomu podstawowego. Właściwie nauczyłeś mnie dzisiaj wielu podstawowych rzeczy, których wcześniej mi brakowało. Nie mogłem się powstrzymać przed przełączeniem zaakceptowanej odpowiedzi. :) Dzięki jeszcze raz.
przydzielono
26
+1 Myślę, że to najlepsza długa odpowiedź, jaką przeczytałem na SO. Myślę, że każdy mógłby się wiele nauczyć z tej odpowiedzi.
Shafik Yaghmour
9
Proszę pana, powinien pan napisać książkę o C.
Dillon Burton,
1
Cóż za piękna odpowiedź na dobre pytanie! Dobra robota @verbose!
benka
7
@verbose ty panie, zasłużyłeś na twoje imię .. :)
sleeping_dragon
44

Załóżmy, że ptrwskazuje i-ty element tablicy arr.

  1. *ptr++oblicza arr[i]i ustawia ptrwskazanie na (i + 1) -ty element arr. Jest odpowiednikiem *(ptr++).

  2. *++ptrustawia ptrwskazanie (i + 1) -tego elementu arri oblicza do arr[i+1]. Jest odpowiednikiem *(++ptr).

  3. ++*ptrwzrasta arr[i]o jeden i ocenia się do swojej zwiększonej wartości; wskaźnik ptrpozostaje nietknięty. Jest odpowiednikiem ++(*ptr).

Jest jeszcze jeden, ale aby go napisać, potrzebujesz nawiasów:

  1. (*ptr)++rośnie arr[i]o jeden i szacuje się do swojej wartości przed zwiększeniem; wskaźnik ptrponownie pozostaje nietknięty.

Resztę możesz dowiedzieć się sam; odpowiedział na nie również @Jaguar.

miłe
źródło
13

*ptr++ : post increment a pointer ptr

*++ptr : Pre Increment a pointer ptr

++*ptr : preincrement the value at ptr location

Przeczytaj tutaj o operatorach preinkrementacji i postinkrementacji


To da Hellojako wyjście

int main()
{
    const char *p = "Hello";
    while(*p)
         printf("%c",*p++);//Increment the pointer here 
    return 0;
}
Jainendra
źródło
@ Nik-Lz Tak, wyjście byłobyHello
Jainendra
7

Stan twojej pętli jest zły:

while(*p++)
    printf("%c",*p);

Jest taki sam jak

while(*p)
{
    p++;
    printf("%c",*p);
}

I to źle, powinno to być:

while(*p)
{
    printf("%c",*p);
    p++;
} 

*ptr++jest taki sam jak *(ptr++), czyli:

const char  *ptr = "example";
char  value;

value = *ptr;
++ptr;
printf("%c", value); // will print 'e'

*++ptrjest taki sam jak *(++ptr), czyli:

const char  *ptr = "example";
char  value;

++ptr;
value = *ptr;
printf("%c", value); // will print 'x'

++*ptrjest taki sam jak ++(*ptr), czyli:

const char  *ptr = "example";
char  value;

value = *ptr;
++value;
printf("%c", value); // will print 'f' ('e' + 1)
nouney
źródło
Całkowicie zgodzę się z pierwszą częścią odpowiedzi. W drugiej części inicjalizacja wskaźników (do liczb całkowitych!) Za pomocą liczb całkowitych jest myląca dla kogoś, kto ma trudności ze zrozumieniem użycia wskaźnika.
nickie
4

Masz rację co do pierwszeństwa, pamiętaj, że *ma pierwszeństwo przed przyrostem przedrostka, ale nie przed przyrostem przyrostka. Oto jak ten podział:

*ptr++ - przechodzenie od lewej do prawej, wyłuskiwanie wskaźnika, a następnie zwiększanie wartości wskaźnika (nie tego, na co wskazuje, ze względu na pierwszeństwo przyrostka nad dereferencją)

*++ptr - zwiększ wskaźnik, a następnie wyłuskuj go, dzieje się tak, ponieważ przedrostek i wyłuskiwanie mają ten sam priorytet i są oceniane w kolejności od prawej do lewej

++*ptr- podobny do powyższego pod względem pierwszeństwa, ponownie przechodząc od prawej do lewej w celu wyłuskiwania wskaźnika, a następnie zwiększania tego, na co wskazuje wskaźnik. Zwróć uwagę, że w twoim przypadku spowoduje to niezdefiniowane zachowanie, ponieważ próbujesz zmodyfikować zmienną tylko do odczytu ( char* p = "Hello";).

Nobilis
źródło
3

Dodam moje zdanie, ponieważ chociaż inne odpowiedzi są poprawne, myślę, że czegoś brakuje.

 v = *ptr++

znaczy

 temp = ptr;
 ptr  = ptr + 1
 v    = *temp;

Natomiast

 v = *++ptr

znaczy

 ptr = ptr + 1
 v   = *ptr

Ważne jest, aby zrozumieć, że po zwiększeniu (i po zmniejszeniu) oznacza

 temp = ptr       // Temp created here!!!
 ptr  = ptr + 1   // or - 1 if decrement)
 v    = *temp     // Temp destroyed here!!!

Dlaczego to ma znaczenie? Cóż, w C to nie jest takie ważne. Jednak w C ++ ptrmoże to być typ złożony, taki jak iterator. Na przykład

 for (std::set<int>::iterator it = someSet.begin(); it != someSet.end(); it++)

W tym przypadku, ponieważ itjest to złożony typ, it++może mieć skutki uboczne z powodu temptworzenia. Oczywiście, jeśli masz szczęście, kompilator spróbuje wyrzucić kod, który nie jest potrzebny, ale jeśli konstruktor lub destruktor iteratora coś it++zrobi, pokaże te efekty, gdy utworzytemp .

Krótko o tym, co próbuję powiedzieć, brzmi: Napisz, co masz na myśli . Jeśli masz na myśli przyrost ptr, to ++ptrnie pisz ptr++. Jeśli masz na myśli temp = ptr, ptr += 1, tempto napiszptr++

gman
źródło
0
*ptr++    // 1

To jest to samo, co:

    tmp = *ptr;
    ptr++;

Zatem ptrpobierana jest wartość obiektu wskazywanego przez , a następnie ptrjest zwiększana.

*++ptr    // 2

To jest to samo, co:

    ++ptr;
    tmp = *ptr;

Tak więc wskaźnik ptrjest zwiększany, a następnie ptrodczytywany jest obiekt wskazywany przez .

++*ptr    // 3

To jest to samo, co:

    ++(*ptr);

Zatem obiekt wskazywany przez ptrjest zwiększany; ptrsama w sobie jest niezmieniona.

David R. Tribble
źródło
0

Postfix i prefix mają wyższy priorytet niż dereference, więc

* ptr ++ w tym miejscu podaje przyrost ptr, a następnie wskazuje na nową wartość ptr

* ++ ptr tutaj PreInkrementacja pięści, a następnie wskazanie nowej wartości ptr

++ * ptr tutaj najpierw pobierz wartość ptr wskazującą na i zwiększ tę wartość

Kiran Padwal
źródło
1
To jest niepoprawne. Postfix ma wyższy priorytet, ale prefiks ma taki sam priorytet jak dereference.
gadatliwy
0

Wyrażenia wskaźników: * ptr ++, * ++ ptr i ++ * ptr:

Uwaga : wskaźniki muszą zostać zainicjowane i muszą mieć prawidłowy adres. Ponieważ w pamięci RAM oprócz naszego programu (a.out) jest dużo więcej programów uruchomionych jednocześnie, tj. Jeśli spróbujesz uzyskać dostęp do pamięci, która nie została zarezerwowana dla Ciebie, system operacyjny przejdzie przez błąd Segmentacji.

Zanim to wyjaśnimy, rozważmy prosty przykład.

#include<stdio.h>
int main()
{
        int num = 300;
        int *ptr;//uninitialized pointer.. must be initialized
        ptr = &num;
        printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
        *ptr = *ptr + 1;//*ptr means value/data on the address.. so here value gets incremented
        printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
        /** observe here that "num" got changed but manually we didn't change, it got modified by pointer **/
        ptr = ptr + 1;//ptr means address.. so here address got incremented
        /**     char pointer gets incremented by 1 bytes
          Integer pointer gets incremented by 4 bytes
         **/
        printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
}

przeanalizuj dane wyjściowe powyższego kodu, mam nadzieję, że otrzymałeś dane wyjściowe powyższego kodu. Z powyższego kodu jasno wynika, że ​​nazwa wskaźnika ( ptr ) oznacza, że ​​mówimy o adresie, a * ptr oznacza, że ​​mówimy o wartości / danych.

PRZYPADEK 1 : * ptr ++, * ++ ptr, * (ptr ++) i * (++ ptr):

wyżej wspomniana składnia wszystkich 4 jest podobna, address gets incrementedale sposób , w jaki adres jest zwiększany, jest inny.

Uwaga : aby rozwiązać dowolne wyrażenie, dowiedz się, ile operatorów występuje w wyrażeniu, a następnie znajdź priorytety operatora. I wielu operatorów mających ten sam priorytet, następnie sprawdź kolejność ewolucji lub asocjatywności, która może przebiegać od prawej (R) do lewej (L) lub od lewej do prawej.

* ptr ++ : Tutaj są 2 operatory, mianowicie de-reference (*) i ++ (inkrementacja). Oba mają ten sam priorytet, a następnie sprawdź skojarzenie, które jest od R do L. Więc rozpoczyna rozwiązywanie od prawej do lewej, niezależnie od operatorów, które pojawią się jako pierwsze.

* ptr ++ : pierwsza ++ pojawiła się podczas rozwiązywania z R do L, więc adres jest zwiększany, ale przyrost postu.

* ++ ptr : Tak samo jak pierwszy tutaj również adres jest zwiększany, ale jego wartość wstępna.

* (ptr ++) : Tutaj są 3 operatory, wśród nich grouping () o najwyższym priorytecie, więc pierwszy ptr ++ rozwiązany, tj. adres jest zwiększany ale post.

* (++ ptr) : Tak samo jak w powyższym przypadku adres również jest zwiększany, ale wstępnie inkrementowany.

PRZYPADEK 2 : ++ * ptr, ++ (* ptr), (* ptr) ++:

wyżej wspomniana składnia wszystkich 4 jest podobna, we wszystkich wartościach / danych są zwiększane, ale sposób zmiany wartości jest inny.

++ * ptr : first * pojawił się podczas rozwiązywania od R do L, więc wartość ulega zmianie, ale jej początkowy inkrement.

++ (* ptr) : Tak samo jak powyżej, wartość zostanie zmodyfikowana.

(* ptr) ++ : Tutaj są 3 operatory, wśród nich grouping () o najwyższym priorytecie, Inside () * ptr, więc pierwszy * ptr jest rozwiązany, tj. wartość jest zwiększana, ale post.

Uwaga : ++ * ptr i * ptr = * ptr + 1 są takie same, w obu przypadkach wartość ulega zmianie. ++ * ptr: używana jest tylko 1 instrukcja (INC), bezpośrednio wartość jest zmieniana w jednym strzale. * ptr = * ptr + 1: tutaj pierwsza wartość jest zwiększana (INC), a następnie przypisywana (MOV).

Aby zrozumieć powyższą różną składnię inkrementacji wskaźnika, rozważmy prosty kod:

#include<stdio.h>
int main()
{
        int num = 300;
        int *ptr;
        ptr = &num;
        printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
        *ptr++;//address changed(post increment), value remains un-changed
//      *++ptr;//address changed(post increment), value remains un-changed
//      *(ptr)++;//address changed(post increment), value remains un-changed
//      *(++ptr);//address changed(post increment), value remains un-changed

//      ++*ptr;//value changed(pre increment), address remains un-changed
//      (*ptr)++;//value changed(pre increment), address remains un-changed
//      ++(*ptr);//value changed(post increment), address remains un-changed

        printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
}

W powyższym kodzie spróbuj komentować / usuwać komentarze i analizować wyniki.

Wskaźniki jako stałe : nie ma sposobów, za pomocą których można uczynić wskaźniki stałymi, o kilku wspominam tutaj.

1) const int * p OR int const * p : Tutaj valuejest stała , adres nie jest stały, tj. Gdzie wskazuje p? Jakiś adres? Jaka jest wartość pod tym adresem? Jakaś wartość, prawda? Ta wartość jest stała, nie możesz jej modyfikować, ale gdzie wskazuje wskaźnik? Jakiś adres, prawda? Może również wskazywać na inny adres.

Aby to zrozumieć, rozważmy poniższy kod:

#include<stdio.h>
int main()
{
        int num = 300;
        const int *ptr;//constant value, address is modifible
        ptr = &num;
        printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
        *ptr++;//
//      *++ptr;//possible bcz you are trying to change address which is possible
//      *(ptr)++;//possible
//      *(++ptr);//possible

//      ++*ptr;//not possible bcz you trying to change value which is not allowed
//      (*ptr)++;//not possible
//      ++(*ptr);//not possible

        printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
}

Spróbuj przeanalizować wynik powyższego kodu

2) int const * p : nazywa się ' **constant pointe**r' ie address is constant but value is not constant. Tutaj nie możesz zmienić adresu, ale możesz zmodyfikować wartość.

Uwaga : stały wskaźnik (powyższy przypadek) musi zostać zainicjowany podczas deklaracji.

Aby to zrozumieć, sprawdźmy prosty kod.

#include<stdio.h>
int main()
{
        int x = 300;
        int* const p;
        p = &x;
        printf("x = %d p =%p and *p = %d\n",num,p,*p);
}

W powyższym kodzie, jeśli zauważysz, że nie ma ++ * p lub * p ++, więc możesz pomyśleć, że jest to prosty przypadek, ponieważ nie zmieniamy adresu ani wartości, ale spowoduje to błąd. Czemu ? Powód, o którym wspominam w komentarzach.

#include<stdio.h>
int main()
{
        int x = 300;
        /** constant pointer must initialize while decaring itself **/
        int* const p;//constant pointer i.e its pointing to some address(here its pointing to garbage), it should point to same address(i.e garbage ad
dress only 
        p = &x;// but here what we are doing ? we are changing address. we are making p to point to address of x instead of garbage address.
        printf("x = %d p =%p and *p = %d\n",num,p,*p);
}

Więc jakie jest rozwiązanie tego problemu?

     int* const p = &x;

aby uzyskać więcej informacji na ten temat, rozważmy poniższy przykład.

#include<stdio.h>
int main()
{
        int num = 300;
        int *const ptr = &num;//constant value, address is modifible
        printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
        *ptr++;//not possible
//      *++ptr;//not possible bcz you are trying to change address which is not possible
//      *(ptr)++;//not possible
//      *(++ptr);//not possible

//      ++*ptr;// possible bcz you trying to change value which is allowed
//      (*ptr)++;// possible
//      ++(*ptr);// possible
        printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
}

3) const int * const p : Tutaj zarówno adres, jak i wartość są stałe .

Aby to zrozumieć, sprawdźmy poniższy kod

#include<stdio.h>
int main()
{
        int num = 300;
        const int* const ptr = &num;//constant value,constant address 
        printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
        *ptr++;//not possible
        ++*ptr;//not possible
        printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
}
Achal
źródło
-1
const char *p = "Hello";   

*p means "Hello"
          ^
          | 
          p

*p++ means "Hello"
             ^
             | 
             p

*++p means "Hello"
            ^
            |     (WHILE THE STATEMENT IS EXECUTED)
            p

*++p means "Hello"
             ^
             |     (AFTER THE STATEMENT IS EXECUTED)
             p

++*pśrodki, które starają się zwiększyć wartość ASCII *p, które

   is "Hello"
       ^
       | 
       p

nie możesz zwiększyć wartości, ponieważ jest ona stała, więc wystąpiłby błąd

tak jak w przypadku pętli while, pętla działa aż *p++do końca łańcucha, w którym znajduje się znak '\0'(NULL).

Odkąd *p++pomija pierwszy znak, wynik będzie wyświetlany tylko od drugiego znaku.

Poniższy kod nie wyświetli niczego, ponieważ pętla while ma '\0'

const char *p = "Hello";
    while('\0') 
         printf("%c",*p);

Poniższy kod daje takie same wyniki, jak następny kod, tj. Ello.

const char *p = "Hello";
    while(*++p)
         printf("%c",*p);

...................................

const char *p = "Hello";
    while(*p++)
         printf("%c",*p);
iammosespaulr
źródło