2010-11-22 41 views
9

He leído varias discusiones de pasar char * en C.char * vs char ** como parámetros a una función en C

stackoverflow: passing-an-array-of-strings-as-parameter-to-a-function-in-c
stackoverflow: how-does-an-array-of-pointers-to-pointers-work
stackoverflow: whats-your-favorite-programmer-ignorance-pet-peeve
drexel.edu: Character arrays

Muchos de ellos incluyen discusiones de matrices, pero quiero evitar eso.

Estoy escribiendo un programa de ejemplo para enseñarme sobre el paso de char * y char ** en C. Este es un ejercicio para pasar char *, sin usar matrices (punteros a). También no hay preocupaciones por la eficiencia de ejecución. :-)

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 

void get_args_works(int, char **, char **); 
void get_args_broken(int, char **, char *); 
char *get_string(int, char **); 

int main(int argc, char **argv) 
{ 
    char *string_works; 
    char *string_broken; 

    get_args_works(argc, argv, &string_works); 
    get_args_broken(argc, argv, string_broken); 

    printf("in main string_works (%p) = %s\n",string_works,string_works); 
    free(string_works); 

    printf("in main string_broken (%p) = %s\n",string_broken,string_broken); 
    free(string_broken); 
} 

void get_args_works(int argc, char **argv, char **string) 
{ 
    *string = get_string(argc, argv); 
    printf("in get_args_works %p string %s\n",*string,*string); 
} 

void get_args_broken(int argc, char **argv, char *string) 
{ 
    string = get_string(argc, argv); 
    printf("in get_args_broken %p string %s\n",string,string); 
} 

char * get_string(int argc, char **argv) 
{ 
    int i; 
    char *string; 
    string = malloc(40); 

    // placeholder in case -s switch not found below 
    strcpy(string,"-s switch not found below"); 

    for(i = 0; i < argc; i++) 
    { 
     if(argv[i][0] == '-') 
     { 
      switch(argv[i][1]) 
      { 
      case 's': 
       // release above malloc(40) for "-s switch not found below" 
       free(string); 
       // make room for storing variable 
       string = malloc(strlen(argv[++i]) + 1); 
       // the argv just after -s 
       strcpy (string,argv[i]); 
       break; 
      } 
     } 
    } 
    return string; 
} 

También puede ver el same code on github

El código anterior es algo autodocumentado. main() declara dos variables char * y las pasa como parámetros a sus respectivas funciones get_args().

Cada función de get_args() llama al char * get_string(int, char **), utilizando exactamente la misma llamada (pero una forma diferente de recopilar el valor devuelto).

get_string() funciona bien; hace un malloc() y devuelve el puntero a la función de llamada. Ese código funciona, y cada función get_args() recibe el valor de retorno como espero.

Pero entonces, cuando los get_args() devuelven a main(), ¿por qué el valor de puntero sin referencia a volver a principal (de get_args_works(), pero no el valor del puntero (de get_args_broken())?

(es decir, puedo ver que si eliminar la referencia al puntero (&string_works) al enviar como parámetro, funciona. Pero ¿por qué? ¿no es char * string_broken ya un puntero? ¿Por qué necesita el eliminar la referencia de "extra" cuando se envía como parámetro?)

I Estoy esperando una respuesta ganadora que explique cómo usted (sí, usted) conceptualiza el envío de char * como parámetro frente a recibirlo como el fu valor de retorno de nction.

Respuesta

4
int get_args_broken(int argc, char **argv, char *string) 
{ 
    string = get_string(argc, argv); 
    printf("in get_args_broken %p string %s\n",string,string); 
} 

Sólo se está modificando la variable string local (automático). Eso no es visible para la persona que llama de ninguna manera. Tenga en cuenta que esto significa que está liberando un puntero salvaje en main.

Está mal por la misma razón:

int get_sum(int sum, int a, int b) 
{ 
    sum = a + b; 
} 

es; el parámetro es copiado por valor. Además, no está devolviendo un int (como declaró que lo haría).

int get_args_works(int argc, char **argv, char **string) 
{ 
    *string = get_string(argc, argv); 
    printf("in get_args_works %p string %s\n",*string,*string); 
} 

es correcto (excepto el retorno falta). No está modificando string, lo cual sería inútil. Está modificando el objeto en la ubicación en string, que en este caso es un char *.

EDITAR: Necesitaría triplicar * el argv si había una función que llamaba main, y deseaba establecer la variable de esa función en un char diferente **. P.EJ.

void trip_main(int *argc, char ***argv) 
{ 
    *argc = 10; 
    *argv = malloc(*argc * sizeof(char *)); 
} 

void caller() 
{ 
    char **argv; 
    int argc; 
    trip_main(&argc, &argv); 
} 
+0

hmmm. Supongo que puede ser una buena manera simple de entenderlo. No habla de char * de ninguna manera. pero entonces ¿por qué no tenemos que triplicar * la argv? hmmmm –

+0

¡¡¡OMG !!! ¿Cómo pasé por alto el tipo de devolución no válida? ? He estado componiendo esta pregunta por * días * ... !!!! d'oh !!!! –

+2

@ThunderRabbit: es hora de aprender a activar más advertencias de su compilador u obtener un mejor compilador. Si su compilador no se queja de las funciones declaradas para devolver un valor que retorna sin devolver un valor, es ... bueno, no precisamente roto, pero no tan útil como debería ser. Las advertencias del compilador son su amigo, escúchelo hablar y asegúrese de que sea detallado. Con GCC, un buen punto de partida es '-Wall'; Usualmente uso '-Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition' (el último porque trabajo en código arcaico). '-Wshadow' también es bueno ... –

1

Una de las necesidades de usar puntero a un puntero (aquí get_args_works()) es modificar (o retorno) más que en la variable de una función, como en C que no es posible volver más de una variable.

get_args_works() funciona 'coz, está pasando el puntero a un puntero & una referencia está en su main().

Pero en get_args_broken() está pasando solo un puntero. No hay nada mal aquí, ahora lo hace malloc() & devolver la cadena de memoria asignada a get_args_broken(), todavía nada de nada aquí. Pero ahora, esta cadena asignada es local & main() no tiene una referencia a esta var. Por lo tanto, cuando desreferencia char *string_broken; en main() podría causar un comportamiento indefinido.

Espero que esto sea claro.