Por que essa função retorna o comprimento correto de uma string? (Incrementando um ponteiro de caractere)

12

Esta é uma função que conta o número de caracteres em uma string:

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

Por que isso retorna o comprimento correto?

Digamos que eu chame essa função com uma String simples "a". Então sé incrementado no loop while, portanto, o valor de se isão ambos 0.

lor
fonte

Respostas:

10

O valor de s++é o valor original de s, antes do incremento, o incremento ocorre em um tempo não especificado antes do próximo ponto de sequência.

Daí *s++e *(s++)são equivalentes: ambos desreferenciam o valor original de s. Outra expressão equivalente é *(0, s++)e, não para os fracos de coração, é esta:0[s++]

Observe, no entanto, que sua função deve usar type size_tfor ie seu tipo de retorno:

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

Aqui está uma versão potencialmente mais eficiente com um único incremento por loop:

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

Para aqueles que se perguntam sobre as expressões estranhas no segundo parágrafo:

  • 0, s++é uma instância do operador de vírgula ,que avalia sua parte esquerda e depois a parte direita, que constitui seu valor. portanto, (0, s++)é equivalente a (s++).

  • 0[s++]é equivalente a (s++)[0]e *(0 + s++)ou *(s++ + 0)que simplifica como *(s++). Transpor o ponteiro e as expressões de índice em []expressões não é muito comum nem particularmente útil, mas está em conformidade com o padrão C.

chqrlie
fonte
Espero que o operador de vírgula esteja claro. Tire as , s++coisas ruins e acontecerá:)
David C. Rankin
6

Digamos que eu chamo essa função com uma string simples "a". Então s é incrementado no loop while, portanto, o valor de s é 0 e i também é 0.

Nesse exemplo, saponta para o 'a'in "a". Então é incrementado e itambém é incrementado. Agora saponte para o terminador nulo e ié 1. Portanto, na próxima execução do loop, *(s++)é '\0'(is 0), então o loop termina e o valor atual de i(that 1) é retornado.

Geralmente, o loop é executado uma vez para cada caractere na string e, em seguida, para no terminador nulo, e é assim que conta os caracteres.

Chama
fonte
Como s está entre colchetes, pensei que seria incrementado primeiro (agora aponta para '/ 0'). Portanto, o loop while é falso e i nunca é incrementado.
lor
2
@lor, lembre-se do que os operadores pós- acréscimo avaliam para o que for smantido antes de incrementar. O que você está descrevendo é o comportamento de ++s(que de fato subconta por um e invoca o UB se for passado uma string vazia).
Toby Speight
2

Faz todo o sentido:

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

"Mas sestá entre parênteses. Por isso pensei que seria incrementado primeiro"

É exatamente por isso que o ponteiro é incrementado e não o caractere, digamos que você tenha (*s)++, nesse caso, o caractere será incrementado e não o ponteiro. A desreferenciação significa que agora você está trabalhando com o valor referido pelo ponteiro, não o ponteiro em si.

Como os dois operadores têm a mesma precedência, mas associatividade da direita para a esquerda, você pode até usar simplesmente *s++sem colchetes para aumentar o ponteiro.

anastaciu
fonte
Mas s está entre parênteses. Por isso achei que seria incrementado primeiro. (Se tivermos uma String simples como "a" s agora aponte para "/ 0"). Como a condição agora é while (0), o loop while nunca é inserido.
lor
2

O operador pós-incremento aumenta o valor do operando em 1, mas o valor da expressão é o valor original do operando antes da operação de incremento.

Suponha que o argumento passado para str_len()é "a". No str_len(), o ponteiro sestá apontando para o primeiro caractere da string "a". No whileloop:

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

embora o sseja incrementado, mas o valor de sna expressão será o ponteiro para o caractere para o qual está apontando antes do incremento, que é o ponteiro para o primeiro caractere 'a'. Quando o ponteiro sé desreferenciado, ele fornece um caractere 'a'. Na próxima iteração, o sponteiro estará apontando para o próximo caractere, que é o caractere nulo \0. Quando sfor desreferenciado, ele dará 0e o loop será encerrado. Observe que sagora apontará para um elemento além do caractere nulo da string "a".

HS
fonte