Відповіді:
Значення s++
є початковим значенням s
до збільшення приросту, який відбувається за не визначений час до наступної точки послідовності.
Звідси *s++
і *(s++)
рівнозначні: вони обидва відкидають початкове значення s
. Ще один еквівалентний вираз - це *(0, s++)
, не для слабкого серця, такий:0[s++]
Однак зауважте, що ваша функція повинна використовувати тип size_t
для i
та тип повернення:
size_t str_len(const char *s) {
size_t i = 0;
while (*s++) {
i++;
}
/* s points after the null terminator */
return i;
}
Ось потенційно більш ефективна версія з одним збільшенням на цикл:
size_t str_len(const char *s) {
const char *s0 = s;
while (*s++) {
/* nothing */
}
return s - 1 - s0;
}
Для тих, хто цікавиться дивними виразами у другому абзаці:
0, s++
є екземпляром оператора комами, ,
який оцінює його ліву частину, а потім його праву частину, яка становить його значення. отже (0, s++)
, еквівалентно (s++)
.
0[s++]
еквівалентно (s++)[0]
і *(0 + s++)
чи *(s++ + 0)
які спрощують , як *(s++)
. Транспонування вказівників та покажчиків у []
виразах не є дуже поширеним і особливо корисним, але відповідає стандарту C.
Скажімо, я називаю цю функцію простим рядком "a". Тоді s збільшується в циклі while, тому значення s дорівнює 0 a i також дорівнює 0.
У цьому прикладі, s
вказує на 'a'
ін "a"
. Потім він збільшується і i
також збільшується. Тепер s
вкажіть на нульовий термінатор і i
є 1
. Отже, при наступному пробігу через цикл *(s++)
є '\0'
(що є 0
), тому цикл закінчується, і поточне значення i
(that 1
) повертається.
Як правило, цикл працює один раз для кожного символу в рядку, а потім зупиняється на нульовому термінаторі, тож саме так він рахує символи.
s
відбулося перед збільшенням. Те, що ви описуєте, - це поведінка ++s
(яка б насправді була недооцінена на одиницю, і викликати UB, якщо він пропустив порожню рядок).
Це має ідеальний сенс:
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;
}
"Але
s
в дужках. Тому я думав, що спочатку це буде збільшено"
Саме тому вказівник збільшується, а не символ, скажімо, у вас є (*s)++
, в цьому випадку персонаж буде нарощений, а не вказівник. Перенаправлення означає, що ви зараз працюєте зі значенням, на яке посилається вказівник, а не з самим вказівником.
Оскільки обидва оператори мають однаковий пріоритет, але асоціативність справа наліво, ви можете навіть просто використовувати *s++
без дужок для збільшення покажчика.
Оператор після збільшення приросту збільшує значення операнда на 1, але значення виразу є початковим значенням операнду перед операцією збільшення.
Припустимо, аргумент переданий str_len()
є "a"
. У полі str_len()
вказівник s
вказує на перший символ рядка "a"
. У while
циклі:
while(*(s++)) {
.....
.....
хоча s
воля буде нарощена, але значення s
в виразі буде вказувати на символ, на який він вказує перед збільшенням, який вказує на перший символ 'a'
. Коли вказівник s
буде відмеженим, він надасть характер 'a'
. У наступній ітерації s
вказівник буде вказувати на наступний символ, який є нульовим символом \0
. Коли s
dereferenced, він видасть 0
і цикл буде виходом. Зауважте, що s
тепер буде вказувати на один елемент повз нульовий символ рядка "a"
.
, s++
і погані речі трапляться:)