2009-05-08 19 views
5

En la pág. 109 K & R, vemos:K & R: matriz de punteros de caracteres

void writelines(char *lineptr[], int nlines) 
{ 
    while (nlines -- > 0) printf("%s\n", *lineptr++); 
} 

estoy confundido acerca de lo que * lineptr ++ hace exactamente. Desde mi punto de vista, printf requiere un puntero de char, por eso lo proporcionamos con * lineptr. Entonces incrementamos lineptr hasta el próximo puntero char en la matriz? ¿No es esto ilegal?

En la página 99, K & R escribe que "un nombre de matriz no es una variable; construcciones como a = pa [donde a es una matriz, pa es un puntero a una matriz] y a ++ son ilegales."

Respuesta

4

La respuesta seleccionada de Adam Rosenfield es incorrecta. La respuesta de Coobird también. Por esa razón, he votado ambas respuestas.

La interpretación de Adam Markowitz de *lineptr++ es correcta, pero no responde la pregunta principal si este es el código legal C99. Solo Tom Future lo hace; desafortunadamente él no explica *lineptr++. Les concedí un punto cada uno.

Así que para abreviar, lineptr es una variable y se puede manipular como un puntero. Por lo tanto, es legal incrementar el puntero.

lineptr es un puntero en una secuencia de punteros a secuencias de caracteres. En otras palabras, es un puntero a la primera cadena de una matriz de cadenas. Según el código, podemos suponer que las cadenas son secuencias de caracteres terminadas nulas ('\ 0'). nlines es el número de cadenas en la matriz.

La expresión de prueba while es nlines-- > 0. nlines-- es una disminución posterior (porque -- está a la derecha de la variable). Por lo tanto, se ejecuta después de la prueba se ha realizado e independientemente del resultado de la prueba, por lo que en cualquier caso.

Por lo tanto, si el valor nlines dado como argumento fue 0, la prueba se realiza primero y devuelve false; las instrucciones en el ciclo no se ejecutan. Tenga en cuenta que dado que nlines se decrementa de todos modos, el valor de nlines después del while será -1.

Si nlines == 1, la prueba devolverá true y nlines se reducirá; las instrucciones en el ciclo se ejecutarán una vez. Tenga en cuenta que mientras se ejecutan estas instrucciones, el valor de nlines es 0. Cuando la prueba se realiza nuevamente, volvemos al caso cuando nlines == 0.

La instrucción printf utiliza la expresión *lineptr++. Es un incremento posterior del puntero (++ está a la derecha de la variable). Esto significa que la expresión se evalúa primero y el incremento se realiza después de su uso. Por lo tanto, en la primera ejecución, printf recibe una copia del primer elemento de la matriz de cadenas, que es un puntero al primer carácter de las cadenas. El lineptr se incrementa solo después de eso. La próxima vez que se ejecute printf, lineptr apunta al segundo elemento y se moverá al tercero cuando se haya impreso la segunda cadena. Esto tiene sentido porque obviamente queremos imprimir la primera cadena. Si Adam Rosenfield estaba en lo cierto, la primera cuerda se habría omitido y al final trataríamos de imprimir la cuerda más allá de la última, lo que obviamente es algo malo de hacer.

Por lo tanto, la instrucción printf es una forma concisa de las dos instrucciones siguientes

printf("%s\n", *lineptr); 
++lineptr; // or lineptr++, which is equivalent but not as good. lineptr += 1; is ok too. 

señalar, como regla general, que cuando pre- y post-incremento son equivalentes en su acción, la pre el incremento es preferible por razones de rendimiento. Los compiladores se encargarán de cambiarlo por usted. bueno, la mayoría del tiempo. Es mejor para el operador previo, siempre que sea posible, por lo que se usa siempre. La razón se vuelve más explícita una vez que implemente un post- y un incremento previo en C++.

+0

Gracias por aclarar ... Me preguntaba si me estaba volviendo loco o no :) –

6

lineptr no es realmente una matriz; es un puntero a un puntero. Tenga en cuenta que el operador postincremento ++ tiene mayor precedencia que el operador eliminar la referencia *, por lo que lo que ocurre en lineptr++ es la siguiente:

  1. lineptr consigue incrementa para apuntar al siguiente elemento de la matriz
  2. El resultado de lineptr++ es la antiguo valor de lineptr, a saber, un puntero al elemento actual en la matriz
  3. *lineptr++ elimina la referencia de que, por lo que es el valor del elemento actual en la matriz

Por lo tanto, las iteraciones de bucle mientras que más de todos los elementos de la matriz apuntada por lineptr (cada elemento es un char*) y las imprime.

+0

Ah, esto tiene sentido. ¡Gracias a todos por responder mi pregunta! –

5

Podría ser más fácil de imaginar el *lineptr++ como los siguientes:

*(lineptr++) 

Allí, el puntero a la matriz de char* s se mueve al siguiente elemento de la matriz, es decir, de lineptr[0], nos pasar al lineptr[1]. (El incremento del puntero se produce después de que el desreferenciar debido al incremento de sufijo del puntero.)

Así que, básicamente, las siguientes cosas:

  1. lineptr[0] (de tipo char*) se recupera por deferencing.
  2. El puntero se incrementa al siguiente elemento de la matriz. (Por el incremento de sufijo en puntero lineptr.)
  3. El puntero apunta ahora a lineptr[1], repetir el proceso desde el paso 1 de nuevo.
+0

Creo que * (lineptr ++) no es muy útil aquí. Eso implica que lineptr se incrementa * antes * de que se desreferencia. Lo que realmente está sucediendo es más como dos tareas separadas: * lineptr, ++ lineptr. – Naaff

+0

Si usa (* lineptr) ++ desreferenciará y luego incrementará. El lineptr es un puntero a una matriz que selecciona la primera fila desreferenciando y luego incrementa el puntero para apuntar al siguiente carácter. Entonces, si el puntero de línea es una matriz de 3 líneas, su horquillado imprimirá la primera línea 3 veces a partir del primer, segundo y tercer carácter. El * (lineptr ++) imprimirá cada una de las tres líneas. – stefanB

+0

* (lineptr ++) sigue siendo una declaración, por lo que la desreferenciación del puntero ocurrirá antes de que tenga lugar el incremento posterior. Agregar un paréntesis solo cambia la precedencia del operador. En este caso, como señala la respuesta de Adam Rosenfield, ++ tiene una precedencia mayor que *, por lo que el paréntesis solo sirve como una indicación visual de las dos cosas que suceden en esa declaración única. – coobird

7

sigue leyendo! En la parte inferior de p. 99

Como parámetros formales en una definición de función,

char s[]; 

y

char *s; 

son equivalentes; preferimos este último porque dice más explícitamente que el parámetro es un puntero.

I.e. nunca se puede pasar una matriz (que no es una variable) a una función. Si declara una función que parece que necesita una matriz, realmente necesita un puntero (que es una variable). Esto tiene sentido. Sería extraño que un argumento de función no sea una variable; puede tener un valor diferente cada vez que llame a la función, ¡así que seguro no es una constante!

+0

Bueno, puede pasar un nombre de matriz a una función, hay solo una acción entre bastidores que hace que parezca que realmente pasó en un puntero. Parece más como enfrentar que tener sentido. –

+0

Oh, cierto, recuerdo ahora. La matriz no se copia en el marco de la pila de la función, solo un puntero a esa matriz. ¡Gracias por el recordatorio! –