Mengapa fungsi ini mengembalikan panjang string yang benar? (Menambah pointer char)

12

Ini adalah fungsi yang menghitung jumlah karakter dalam string:

int str_len(const char* s) {
    int i = 0;
    while(*(s++)) {
        i++;
    }
    return i;
}

Mengapa ini mengembalikan panjang yang benar?

Katakanlah saya memanggil fungsi ini dengan String sederhana "a". Kemudian sditambahkan dalam loop sementara, oleh karena itu nilai sdan ikeduanya 0.

lor
sumber

Jawaban:

10

Nilai s++adalah nilai asli dari s, sebelum kenaikan, kenaikan terjadi pada waktu yang tidak ditentukan sebelum titik urutan berikutnya.

Karenanya *s++dan *(s++)setara: mereka berdua dereferensi nilai asli s. Ungkapan lain yang sepadan adalah *(0, s++)dan, bukan untuk orang yang lemah hati, seperti ini:0[s++]

Perhatikan bahwa fungsi Anda harus menggunakan tipe size_tuntuk idan tipe pengembaliannya:

size_t str_len(const char *s) {
    size_t i = 0;
    while (*s++) {
        i++;
    }
    /* s points after the null terminator */
    return i;
}

Berikut adalah versi yang berpotensi lebih efisien dengan kenaikan satu per loop:

size_t str_len(const char *s) {
    const char *s0 = s;
    while (*s++) {
        /* nothing */
    }
    return s - 1 - s0;
}

Bagi mereka yang bertanya-tanya tentang ekspresi aneh di paragraf kedua:

  • 0, s++adalah turunan dari operator koma ,yang mengevaluasi bagian kirinya, kemudian bagian kanannya yang merupakan nilainya. karenanya (0, s++)setara dengan (s++).

  • 0[s++]sama dengan (s++)[0]dan *(0 + s++)atau *(s++ + 0)yang disederhanakan sebagai *(s++). Transposing pointer dan ekspresi indeks dalam []ekspresi tidak sangat umum atau sangat berguna tetapi sesuai dengan standar C.

chqrlie
sumber
Tentu berharap koma-operatornya jelas. Singkirkan , s++hal-hal buruk yang akan terjadi:)
David C. Rankin
6

Katakanlah saya memanggil fungsi ini dengan String sederhana "a". Kemudian s bertambah dalam loop while oleh karena itu nilai s adalah 0 dan i juga 0.

Dalam contoh itu, smenunjuk ke 'a'dalam "a". Kemudian bertambah dan ijuga bertambah. Sekarang smenunjuk ke nol terminator, dan iadalah 1. Jadi dalam menjalankan selanjutnya melalui loop, *(s++)adalah '\0'(yang 0), sehingga loop berakhir, dan nilai saat ini i(yang 1) dikembalikan.

Secara umum, loop dijalankan sekali untuk setiap karakter dalam string, dan kemudian berhenti di terminator nol, jadi begitulah ia menghitung karakter.

Api
sumber
Karena s ada dalam tanda kurung, saya pikir akan bertambah pertama (jadi sekarang menunjuk ke '/ 0'). Oleh karena itu loop sementara salah dan saya tidak pernah bertambah.
lor
2
@atau, ingat apa yang dilakukan operator postincrement: itu mengevaluasi apa pun yang sdimiliki sebelum bertambah. Apa yang Anda gambarkan adalah perilaku ++s(yang memang akan di-hitung oleh satu, dan memanggil UB jika melewati string kosong).
Toby Speight
2

Masuk akal:

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

"Tapi sada dalam kurung. Itu sebabnya saya pikir itu akan bertambah dulu"

Itulah mengapa pointer bertambah dan bukan karakter, misalkan Anda miliki (*s)++, dalam hal ini karakter akan bertambah dan bukan pointer. Dereferencing berarti bahwa Anda sekarang bekerja dengan nilai yang dirujuk oleh pointer, bukan pointer itu sendiri.

Karena kedua operator memiliki prioritas yang sama tetapi asosiatifitas kanan-ke-kiri, Anda bahkan dapat menggunakan hanya *s++tanpa tanda kurung untuk menambah pointer.

anastaciu
sumber
Tapi s ada di dalam kurung. Itu sebabnya saya pikir itu akan bertambah dulu. (Jika kita memiliki String sederhana seperti "a" sekarang akan menunjuk ke "/ 0"). Karena kondisinya sekarang while (0), loop while tidak pernah dimasukkan.
lor
2

Operator kenaikan pos meningkatkan nilai operan sebesar 1 tetapi nilai ekspresi adalah nilai asli operan sebelum operasi kenaikan.

Anggap argumen yang diteruskan str_len()adalah "a". Dalam str_len(), pointer smenunjuk ke karakter pertama dari string "a". Dalam whilelingkaran:

while(*(s++)) {
.....
.....

meskipun sakan bertambah tetapi nilai sdalam ekspresi akan menjadi penunjuk ke karakter yang ditunjuk sebelum kenaikan, yang merupakan penunjuk ke karakter pertama 'a'. Ketika pointer sdireferensikan, itu akan memberikan karakter 'a'. Dalam iterasi berikutnya, spointer akan menunjuk ke karakter berikutnya yang merupakan karakter nol \0. Ketika sdereferenced, ia akan memberi 0dan loop akan keluar. Perhatikan bahwa, ssekarang akan menunjuk ke satu elemen melewati karakter nol dari string "a".

HS
sumber