Perché questa funzione restituisce la lunghezza corretta di una stringa? (Incrementare un puntatore a carattere)


12

Questa è una funzione che conta il numero di caratteri in una stringa:

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

Perché questo restituisce la lunghezza corretta?

Diciamo che chiamo questa funzione con una semplice stringa "a". Quindi sviene incrementato nel ciclo while, quindi il valore di se isono entrambi 0.

Risposte:


10

Il valore di s++è il valore originale di s, prima dell'incremento, l'incremento si verifica in un momento non specificato prima del successivo punto di sequenza.

Quindi *s++e *(s++)sono equivalenti: entrambi determinano il valore originale di s. Un'altra espressione equivalente è *(0, s++)e, non per i deboli di cuore, tale è questa:0[s++]

Si noti tuttavia che la funzione deve utilizzare type size_tper ie il tipo restituito:

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

Ecco una versione potenzialmente più efficiente con un singolo incremento per ciclo:

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

Per coloro che si chiedono le strane espressioni nel secondo paragrafo:

  • 0, s++è un'istanza dell'operatore virgola ,che valuta la sua parte sinistra, quindi la sua parte destra che costituisce il suo valore. quindi (0, s++)è equivalente a (s++).

  • 0[s++]è equivalente (s++)[0]e *(0 + s++)o *(s++ + 0)che semplifica come *(s++). Trasporre il puntatore e le espressioni dell'indice nelle []espressioni non è molto comune né particolarmente utile, ma è conforme allo standard C.


Sicuramente spero che l'operatore virgola fosse chiaro. Porta via le , s++cose brutte e accadranno:)
David C. Rankin il

6

Diciamo che chiamo questa funzione con una semplice stringa "a". Quindi s viene incrementato nel ciclo while, quindi il valore di s è 0 e i è anche 0.

In questo esempio, spunta a 'a'in "a". Quindi viene incrementato e ianche incrementato. Ora spunta al terminatore null, ed iè 1. Quindi nella prossima corsa attraverso il ciclo, *(s++)è '\0'(che è 0), quindi il ciclo termina e viene restituito il valore corrente di i(cioè 1).

Generalmente, il ciclo viene eseguito una volta per ogni carattere nella stringa, quindi si interrompe sul terminatore null, quindi è così che conta i caratteri.


Poiché s è tra parentesi, ho pensato che sarebbe stato incrementato per primo (quindi ora punta a '/ 0'). Pertanto il ciclo while è falso e non viene mai incrementato.
Lor

2
@lor, ricorda cosa fanno gli operatori di postincremento: valuta qualsiasi cosa strattenuta prima dell'incremento. Quello che stai descrivendo è il comportamento di ++s(che in effetti sarebbe sottostimato da uno, e invocherebbe UB se passasse una stringa vuota).
Toby Speight,

2

Ha perfettamente senso:

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

"Ma sè tra parentesi. Ecco perché ho pensato che sarebbe stato incrementato prima"

Questo è esattamente il motivo per cui il puntatore viene incrementato e non il carattere, supponiamo di avere (*s)++, in questo caso il carattere verrà incrementato e non il puntatore. La dereferenziazione significa che stai lavorando con il valore a cui fa riferimento il puntatore, non con il puntatore stesso.

Poiché entrambi gli operatori hanno la stessa precendenza ma associatività da destra a sinistra, puoi persino utilizzare semplicemente *s++senza parentesi per aumentare il puntatore.


Ma s è tra parentesi. Ecco perché ho pensato che sarebbe stato incrementato per primo. (Se abbiamo una semplice stringa come "a" s ora punta a "/ 0"). Poiché la condizione è now while (0), il ciclo while non viene mai inserito.
Lor

2

L'operatore post incremento aumenta il valore dell'operando di 1 ma il valore dell'espressione è il valore originale dell'operando prima dell'operazione di incremento.

Supponiamo che l'argomento passato str_len()sia "a". Nel str_len(), il puntatore spunta al primo carattere della stringa "a". Nel whileciclo:

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

sebbene la sviene incrementato ma il valore sdella espressione sarà puntatore al carattere che sta puntando prima incremento, che è un puntatore alla prima carattere 'a'. Quando il puntatore sè dereferenziato, darà carattere 'a'. Nella prossima iterazione, il spuntatore indicherà il carattere successivo che è il carattere null \0. Quando sviene dereferenziato, darà 0e il loop uscirà. Nota che sora indicherà un elemento oltre il carattere null della stringa "a".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.