Bu işlev neden bir dizenin doğru uzunluğunu döndürüyor? (Karakter işaretçisini artırma)


12

Bu, bir dizedeki karakter sayısını sayan bir işlevdir:

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

Bu neden doğru uzunluğu döndürüyor?

Diyelim ki bu işlevi basit bir String ile çağırıyorum "a". Daha sonra swhile döngüsünde artırılır, bu nedenle sve iöğelerinin her ikisi de 0 olur.

Yanıtlar:


10

Değeri, artıştan önce, artışın bir sonraki dizi noktasından önce belirtilmemiş bir zamanda gerçekleştiği s++orijinal değerdir s.

Bu nedenle *s++ve *(s++)eşdeğerdir: her ikisi de orijinal değerinin atılmasını gerektirir s. Başka bir eşdeğer ifade, *(0, s++)kalbin zayıflığı için değil, şu şekildedir:0[s++]

Fonksiyonun türünü kullanması gerektiğini ancak unutmayın size_tiçin ive döndürme türü:

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

İşte döngü başına tek bir artışla potansiyel olarak daha verimli bir sürüm:

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

İkinci paragraftaki garip ifadeleri merak edenler için:

  • 0, s++virgül operatörünün ,sol kısmını, ardından değerini oluşturan sağ kısmını değerlendiren bir örneğidir . dolayısıyla (0, s++)eşittir (s++).

  • 0[s++]eşdeğerdir (s++)[0]ve *(0 + s++)ya *(s++ + 0)kadar basitleştirmek olan *(s++). İmlecin ve indeks ifadelerinin ifadelerde aktarılması []çok yaygın veya özellikle yararlı değildir, ancak C standardına uygundur.


Eminim virgül operatörü açıktı. Almak , s++ve kötü şeyler olacak:)
David C. Rankin

6

Diyelim ki bu işlevi basit bir String "a" ile çağırıyorum. Daha sonra, while döngüsü içinde s artırılır, bu nedenle s değeri 0 ve i de 0'dır.

Bu örnekte, sişaret 'a'in "a". Sonra artırılır ve iartırılır. Şimdi sboş sonlandırıcıya işaret edin ve iöyle 1. Böylece döngüden sonraki çalışmada *(s++), '\0'(yani 0), döngü sona erer ve i(that 1) 'un geçerli değeri döndürülür.

Genellikle, döngü dizedeki her karakter için bir kez çalışır ve sonra boş sonlandırıcıda durur, böylece karakterleri sayar.


S köşeli parantez içinde olduğu için, önce artırılacağını düşündüm (şimdi '/ 0'ı gösteriyor). Bu nedenle while döngüsü yanlıştır ve i hiçbir zaman artırılmaz.
lor

2
@lor, arttırma sonrası operatörlerinin ne olduğunu hatırlayın: arttırmadan önces tutulan her şeyi değerlendirir . Açıkladığınız şey (gerçekten bir tane sayılmaz ve boş bir dize geçirilirse UB'yi çağırır) davranışıdır . ++s
Toby Speight

2

Mükemmel mantıklı:

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

"Ama sköşeli parantez içinde. Bu yüzden önce artırılacağını düşündüm"

İşaretçinin karakteri değil tam olarak bu yüzden arttığını söyleyelim, diyelim ki (*s)++, bu durumda işaretçi değil, karakter artırılacaktır. Dereferencing, şimdi işaretçinin kendisi değil, işaretçinin başvurduğu değerle çalıştığınız anlamına gelir.

Her iki işleç de aynı önceliğe ancak sağdan sola ilişkisine *s++sahip olduğundan, işaretçiyi artırmak için köşeli ayraç olmadan bile kullanabilirsiniz .


Ama s parantez içinde. Bu yüzden öncelikle artırılacağını düşündüm. (Eğer "a" s gibi basit bir Stringimiz varsa "/ 0" 'a işaret eder). Koşul şimdi (0) olduğu için while döngüsü hiçbir zaman girilmez.
lor

2

Artış sonrası operatörü işlenen değerini 1 arttırır, ancak ifadenin değeri arttırma işleminden önce işlenenin orijinal değeridir.

Kabul edilen argümanın str_len()is olduğunu varsayın "a". 'De, str_len()işaretçi sdizenin ilk karakterini gösteriyor "a". In whiledöngü:

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

Her ne kadar sartırılır olacak ama değeri solarak ifade ilk karakteri işaretçidir bu artış önceki işaret karakterine gösterici olacaktır 'a'. İşaretçi skaydı kaldırıldığında, karakter verecektir 'a'. Sonraki yinelemede, sişaretçi boş karakter olan bir sonraki karakteri gösterecektir \0. Ne zaman sduruma gelmiş, bu verecek 0ve loop çıkış olacaktır. sŞimdi, dizenin boş karakterini geçen bir öğeye işaret edeceğini unutmayın "a".

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.