¿Por qué esta función devuelve la longitud correcta de una cadena? (Incrementando un puntero de caracteres)

12

Esta es una función que cuenta el número de caracteres en una cadena:

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

¿Por qué esto devuelve la longitud correcta?

Digamos que llamo a esta función con una cadena simple "a". Luego sse incrementa en el ciclo while, por lo tanto, el valor de sy ison ambos 0.

lor
fuente

Respuestas:

10

El valor de s++es el valor original de s, antes del incremento, el incremento ocurre en un tiempo no especificado antes del siguiente punto de secuencia.

Por lo tanto *s++y *(s++)son equivalentes: ambos desreferencian el valor original de s. Otra expresión equivalente es *(0, s++)y, no para los débiles de corazón, tal es esta:0[s++]

Sin embargo, tenga en cuenta que su función debe usar type size_tfor iy su 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;
}

Aquí hay una versión potencialmente más eficiente con un solo incremento por ciclo:

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

Para aquellos que se preguntan sobre las expresiones extrañas en el segundo párrafo:

  • 0, s++es una instancia del operador de coma ,que evalúa su parte izquierda, luego su parte derecha que constituye su valor. por lo tanto, (0, s++)es equivalente a (s++).

  • 0[s++]es equivalente a (s++)[0]y *(0 + s++)o *(s++ + 0)que se simplifica como *(s++). La transposición del puntero y las expresiones de índice en []expresiones no es muy común ni particularmente útil, pero se ajusta al estándar C.

chqrlie
fuente
Seguro espero que el operador de coma fuera claro. Quiten , s++y sucederán cosas malas:)
David C. Rankin
6

Digamos que llamo a esta función con una cadena simple "a". Entonces s se incrementa en el ciclo while, por lo tanto, el valor de s es 0 e i también es 0.

En ese ejemplo, sapunta a 'a'in "a". Luego se incrementa y itambién se incrementa. Ahora sseñale el terminador nulo, y ies 1. Entonces, en la próxima ejecución a través del ciclo, *(s++)es '\0'(que es 0), entonces el ciclo termina y se devuelve el valor actual de i(eso es 1).

Generalmente, el ciclo se ejecuta una vez para cada carácter en la cadena, y luego se detiene en el terminador nulo, así es como cuenta los caracteres.

Resplandor
fuente
Como s está entre paréntesis, pensé que se incrementaría primero (por lo que ahora apunta a '/ 0'). Por lo tanto, el ciclo while es falso y nunca se incrementa.
lor
2
@lor, recuerda lo que los operadores postincremento: se evalúa a lo srealizado antes del incremento. Lo que está describiendo es el comportamiento de ++s(que de hecho sería menos que uno e invocaría a UB si se pasa una cadena vacía).
Toby Speight
2

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

"Pero sestá entre paréntesis. Por eso pensé que se incrementaría primero"

Es exactamente por eso que el puntero se incrementa y no el carácter, digamos que sí (*s)++, en este caso el carácter se incrementará y no el puntero. La desreferenciación significa que ahora está trabajando con el valor referido por el puntero, no el puntero en sí.

Dado que ambos operadores tienen la misma precedencia pero asociatividad de derecha a izquierda, incluso puede usar simplemente *s++sin corchetes para incrementar el puntero.

anastaciu
fuente
Pero s está entre paréntesis. Por eso pensé que se incrementaría primero. (Si tenemos una cadena simple como "a" s ahora apuntaría a "/ 0"). Como la condición ahora es while (0), el ciclo while nunca se ingresa.
lor
2

El operador posterior al incremento aumenta el valor del operando en 1, pero el valor de la expresión es el valor original del operando antes de la operación de incremento.

Suponga que el argumento pasado a str_len()es "a". En el str_len(), el puntero sapunta al primer carácter de la cadena "a". En el whilebucle:

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

aunque sse incrementará, pero el valor de sen la expresión será el puntero al carácter al que apunta antes del incremento, que es el puntero al primer carácter 'a'. Cuando el puntero sse desreferencia, dará carácter 'a'. En la próxima iteración, el spuntero apuntará al siguiente carácter, que es el carácter nulo \0. Cuando sse desreferencia, se dará 0y el bucle se cerrará. Tenga en cuenta que sahora apuntará a un elemento más allá del carácter nulo de la cadena "a".

HS
fuente