2010-01-07 35 views
12

I tienen el siguiente fragmento de código:diferencia entre (* ++ argv) [0] y mientras (c = * ++ argv [0])

int main(int argc, char *argv[]) 
{ 

    char line[MAXLINE]; 
    long lineno = 0; 
    int c, except = 0, number = 0, found = 0; 

    while(--argc > 0 && (*++argv)[0] == '-') //These two lines 
     while(c = *++argv[0])     //These two lines 
      switch(c) { 
      case 'x': 
        except = 1; 
        break; 
      case 'n': 
        number = 1; 
        break; 
      default: 
        printf("find: illegal option %c\n", c); 
        argc = 0; 
        found = -1; 
        break; 
      } 

    ... 
} 

que contienen las siguientes expresiones:

while(--argc > 0 && (*++argv)[0] == '-') 

hace esto la expresión en el paréntesis (*++argv)[0] difieren de while(c = *++argv[0]) sin paréntesis?

Si es así, ¿cómo? ¿Significa (*++argv) un puntero al siguiente argumento y *++argv[0] apunta al puntero del siguiente carácter de la matriz de caracteres actual a la que se está apuntando?

+0

También estoy interesado en una cosa: while (c = * ++ argv [0]) esta expresión. ¿Significa esto realmente: while (c = * ++ argv [0]! = 0), quiero decir que * ++ argv [0] devuelve un puntero nulo a c si no ha encontrado un carácter? – Tool

+0

sí - responde a continuación. –

+0

Como mencioné en mi respuesta, vea la entrada de erratas de K & R en este código: http://cm.bell-labs.com/cm/cs/cbook/2ediffs.html –

Respuesta

36

En primer lugar, K & R tienen una errata en este fragmento en particular:

117 (§5.10): En el encontrar ejemplo, los incrementos de programa argv[0]. Esto no está específicamente prohibido, pero tampoco está específicamente permitido.

Ahora para la explicación.

Digamos que su programa se llama prog, y lo ejecuta con: prog -ab -c Hello World. Desea poder analizar los argumentos para decir que se especificaron las opciones a, b y c, y Hello y World son los argumentos que no tienen opción.

argv es del tipo char ** — recuerde que un parámetro de matriz en una función es lo mismo que un puntero. En la invocación del programa, las cosas se ven de esta manera:

    +---+   +---+---+---+---+---+ 
argv ---------->| 0 |-------->| p | r | o | g | 0 | 
       +---+   +---+---+---+---+---+ 
       | 1 |-------->| - | a | b | 0 | 
       +---+   +---+---+---+---+ 
       | 2 |-------->| - | c | 0 | 
       +---+   +---+---+---+---+---+---+ 
       | 3 |-------->| H | e | l | l | o | 0 | 
       +---+   +---+---+---+---+---+---+ 
       | 4 |-------->| W | o | r | l | d | 0 | 
       +---+   +---+---+---+---+---+---+ 
       | 5 |-------->NULL 
       +---+ 

Aquí, argc es 5, y es argv[argc]NULL. Al principio, argv[0] es un char * que contiene la cadena "prog".

En (*++argv)[0], debido a los paréntesis, argv se incrementa primero y luego se quita la referencia. El efecto del incremento es mover esa flecha argv ----------> "un bloque hacia abajo", para apuntar al 1. El efecto de la desreferenciación es obtener un puntero al primer argumento de línea de comando, -ab. Finalmente, tomamos el primer caracter ([0] en (*++argv)[0]) de esta cadena, y lo probamos para ver si es '-', porque eso denota el inicio de una opción.

Para la segunda construcción, realmente queremos caminar por la cuerda apuntada por el puntero actual argv[0]. Por lo tanto, tenemos que tratar argv[0] como un puntero, ignorar su primer carácter (es decir '-' como acabamos de prueba), y mirar a los otros personajes:

++(argv[0]) incrementará argv[0], para obtener un puntero a la primera no - personaje, y quitarle la referencia nos dará el valor de ese personaje. Entonces obtenemos *++(argv[0]).Pero como en C, [] se une más estrictamente que ++, podemos deshacernos de los paréntesis y obtener nuestra expresión como *++argv[0]. Queremos seguir procesando este personaje hasta que sea 0 (el último cuadro de caracteres en cada una de las filas de la imagen de arriba).

La expresión

asigna a c el valor de la opción actual, y tiene el valor c. while(c) es una abreviatura de while(c != 0), por lo que la línea while(c = *++argv[0]) básicamente asigna el valor de la opción actual a c y lo prueba para ver si hemos llegado al final del argumento actual de la línea de comandos.

Al final de este bucle, argv apuntará al primer argumento no es una opción:

    +---+   +---+---+---+---+---+ 
       | 0 |-------->| p | r | o | g | 0 | 
       +---+   +---+---+---+---+---+ 
       | 1 |-------->| - | a | b | 0 | 
       +---+   +---+---+---+---+ 
       | 2 |-------->| - | c | 0 | 
       +---+   +---+---+---+---+---+---+ 
argv ---------->| 3 |-------->| H | e | l | l | o | 0 | 
       +---+   +---+---+---+---+---+---+ 
       | 4 |-------->| W | o | r | l | d | 0 | 
       +---+   +---+---+---+---+---+---+ 
       | 5 |-------->NULL 
       +---+ 

¿Esto ayuda?

+0

Muy buena explicación. –

+0

+1 para imágenes bonitas. – dreamlax

+0

@Alok podría explicar este paso :: if ((strstr (línea, * argv)! = NULL)! = Excepto)? –

5

sí, estás en lo cierto.

while(--argc > 0 && (*++argv)[0] == '-') 

está escaneando el array (de longitud argc) de comando argumentos de la línea de uno en uno en busca de los que empiezan con un prefijo - opción. Para cada uno de los:

while(c = *++argv[0]) 

es escaneado a través del conjunto de caracteres de conmutación que siguen a la primera - en el argumento actual (es decir t y n en -tn, hasta que llega a la cadena de terminador nulo \0, que termina el mientras bucle, puesto que se evalúa como falsa.

Este diseño permite tanto

myApp -t -n 

y

myApp -tn 

tanto para trabajar como para tener las opciones t y n.

+1

Este diseño es simple y en su mayoría razonable, aparte del hecho de que modifica argc y el contenido de la matriz argv, que es un diseño deficiente, ya que impide un mayor uso de estas variables. –

5

Incrementar argv es una muy mala idea, ya que una vez que lo ha hecho es difícil recuperar el valor original. Es más simple, más claro y mejor usar un índice entero - ¡después de todo, argv IS es una matriz!

Para responder a su pregunta ++ argv incrementa el puntero. A continuación, se aplica indirectamente para obtener el primer carácter.

+0

En realidad, la indirección comienza con el primer carácter después de -, y cada ciclo pasa al siguiente, para admitir grupos de indicadores de opción después de un solo carácter. –

+0

Me refería a (* ++ argv) [0] == '-' –

2

Sí, las dos expresiones difieren (aunque solo ligeramente). OMI, este código es un poco excesivamente inteligente. Usted sería mejor con algo como esto:

for (int i=1; i<argc; i++) 
    if (argv[i][0] == '-') { 
     size_t len = strlen(argv[i]); 
     for (int j=0; j<len; ++j) 
      switch(argv[i][j]) { 
       case 'x': 
       // ... 

Esto es más o menos equivalente al código anterior, pero dudo que nadie (que sabe C en absoluto) tendría ninguna dificultad para averiguar lo que realmente hace.

+0

pero este código no detectaría cadenas de opciones; necesita otro iterador para recorrer la cadena de opciones -tn –

+0

@Alex Brown: creo que Lo he arreglado, aunque no estoy seguro de que sea necesariamente una mejora real. Permitir '-tn' en lugar de' -t -n' hubiera significado una cantidad justa cuando una terminal típica era un Teletipo, pero ya no vale la pena. –

+0

@Jerry Vale la pena por completo. Cada usuario de línea de comandos espera poder proporcionar opciones de una sola letra en un grupo, y ser flojo con el código significa violar esas expectativas muy arraigadas. El problema más profundo aquí es la codificación personalizada de esta funcionalidad, en lugar del uso de getopt o similar. – Novelocrat

4

Los paréntesis cambian el orden en que se evalúan las expresiones.

Sin paréntesis *++argv[0]:

  1. argv[0] obtiene el puntero a los datos de caracteres actualmente apuntada por argv.
  2. ++ incrementa ese puntero al siguiente carácter en la matriz de caracteres.
  3. * obtiene el personaje.

con paréntesis (*++argv)[0]:

  1. ++argv incrementa el puntero argv para que apunte al siguiente argumento.
  2. * lo reemplaza para obtener un puntero a los datos del caracter.
  3. [0] obtiene el primer carácter en la matriz de caracteres.