Odpowiedzi:
Wartość s++
jest pierwotną wartością s
przyrostu, przed przyrostem, występuje on w nieokreślonym czasie przed następnym punktem sekwencji.
Stąd *s++
i *(s++)
są równoważne: obaj odwołują się do pierwotnej wartości s
. Innym równoważnym wyrażeniem jest *(0, s++)
i, nie dla osób o słabym sercu, takie jest:0[s++]
Zauważ jednak, że twoja funkcja powinna używać type size_t
for i
i jej typu zwracanego:
size_t str_len(const char *s) {
size_t i = 0;
while (*s++) {
i++;
}
/* s points after the null terminator */
return i;
}
Oto potencjalnie bardziej wydajna wersja z jednym przyrostem na pętlę:
size_t str_len(const char *s) {
const char *s0 = s;
while (*s++) {
/* nothing */
}
return s - 1 - s0;
}
Dla tych, którzy zastanawiają się nad dziwnymi wyrażeniami w drugim akapicie:
0, s++
jest przykładem operatora przecinka, ,
który ocenia jego lewą część, a następnie jej prawą część, która stanowi jej wartość. stąd (0, s++)
jest równoważne z (s++)
.
0[s++]
jest równoważne (s++)[0]
i *(0 + s++)
lub *(s++ + 0)
które upraszczają jako *(s++)
. Przeniesienie wskaźnika i wyrażeń indeksowych w []
wyrażeniach nie jest zbyt powszechne ani szczególnie przydatne, ale jest zgodne ze standardem C.
Powiedzmy, że nazywam tę funkcję prostym ciągiem „a”. Następnie s jest zwiększane w pętli while, dlatego wartość s wynosi 0, a i również wynosi 0.
W tym przykładzie s
wskazuje na 'a'
in "a"
. Następnie jest zwiększany, a i
także zwiększany. Teraz s
wskaż na terminator zerowy i i
jest 1
. Tak więc w następnym przebiegu przez pętlę *(s++)
jest '\0'
(która jest 0
), więc pętla kończy się i zwracana jest bieżąca wartość i
(to 1
).
Zasadniczo pętla jest uruchamiana raz dla każdego znaku w ciągu, a następnie zatrzymuje się na zakończeniu zerowym, więc w ten sposób liczy znaki.
s
utrzymywało przed inkrementacją. To, co opisujesz, to zachowanie ++s
(które w rzeczywistości byłoby niedoszacowane o jeden i wywołanie UB, jeśli przejdzie pusty ciąg).
Ma to sens:
int str_len(const char* s) {
int i = 0;
while(*(s++)) { //<-- increments the pointer to char till the end of the string
//till it finds '\0', that is, if s = "a" then s is 'a'
// followed by '\0' so it increments one time
i++; //counts the number of times the pointer moves forward
}
return i;
}
„Ale
s
jest w nawiasach. Dlatego pomyślałem, że najpierw zostanie zwiększony”
Właśnie dlatego wskaźnik jest zwiększany, a nie znak, powiedzmy, że masz (*s)++
, w tym przypadku znak będzie zwiększany, a nie wskaźnik. Dereferencje oznaczają, że pracujesz teraz z wartością wskazywaną przez wskaźnik, a nie sam wskaźnik.
Ponieważ obaj operatorzy mają tę samą przewagę, ale skojarzenie od prawej do lewej, możesz nawet użyć po prostu *s++
bez nawiasów, aby zwiększyć wskaźnik.
Operator postkrementacji zwiększa wartość operandu o 1, ale wartość wyrażenia jest pierwotną wartością operandu przed operacją inkrementacji.
Załóżmy, że przekazany argument str_len()
to "a"
. W str_len()
, wskaźnik s
jest skierowany do pierwszego znaku łańcucha "a"
. W while
pętli:
while(*(s++)) {
.....
.....
chociaż wartość s
będzie zwiększana, ale wartość s
w wyrażeniu będzie wskaźnikiem do znaku, na który wskazuje przed przyrostem, czyli wskaźnika do pierwszego znaku 'a'
. Po s
odsunięciu wskaźnika nada charakter 'a'
. W następnej iteracji s
wskaźnik będzie wskazywał na następny znak, który jest znakiem pustym \0
. Gdy s
zostanie odwołany, poda się, 0
a pętla zostanie zakończona. Zauważ, s
że teraz będzie wskazywał jeden element poza pustym znakiem łańcucha "a"
.
, s++
i złe rzeczy się zdarzą:)