c function gcc

¿Por qué el primer argumento de getline es un puntero al puntero "char**" en lugar de "char*"?



function gcc (5)

Uso la función getline para leer una línea de STDIN .

El prototipo de getline es:

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

Utilizo esto como un programa de prueba que se obtiene de http://www.crasseux.com/books/ctutorial/getline.html#getline

#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int atgc, char *argv[]) { int bytes_read = 1; int nbytes = 10; char *my_string; my_string = (char *)malloc(nbytes+1); puts("Please enter a line of text"); bytes_read = getline(&my_string, &nbytes, stdin); if (bytes_read == -1) { puts ("ERROR!"); } else { puts ("You typed:"); puts (my_string); } return 0; }

Esto funciona bien

Mis dudas son?

  1. ¿Por qué usar char **lineptr lugar de char *lineptr como un parámetro de la función getline ?

  2. Por qué está mal cuando uso el siguiente código:

    char **my_string; bytes_read = getline(my_string, &nbytes, stdin);

  3. Estoy confundido con * y & .

Aquí hay una parte de las advertencias:

testGetline.c: In function ‘main’: testGetline.c:34: warning: pointer targets in passing argument 2 of ‘getline’ differ in signedness /usr/include/stdio.h:671: note: expected ‘size_t * __restrict__’ but argument is of type ‘int *’ testGetline.c:40: warning: passing argument 1 of ‘putchar’ makes integer from pointer without a cast /usr/include/stdio.h:582: note: expected ‘int’ but argument is of type ‘char *’

Uso la versión 4.4.5 de GCC (Ubuntu / Linaro 4.4.4-14ubuntu5).


¿Por qué usar char **lineptr lugar de char *lineptr como parámetro de la función getline ?

Imagina que el prototipo de getline veía así:

ssize_t getline(char *line, size_t n, FILE *stream);

Y lo llamaste así:

char *buffer = NULL; size_t len = 0; ssize_t read = getline(buffer, len, stdin);

Antes de llamar a getline , el buffer es nulo:

+------+ |buffer+-------> NULL +------+

Cuando se getline , la line obtiene una copia del buffer porque los argumentos de la función se pasan por valor en C. Dentro de getline , ya no tenemos acceso al buffer :

+------+ |buffer+-------> NULL +------+ ^ | +------+ | | line +----------+ +------+

getline asigna algo de memoria con malloc y apunta al final del bloque:

+------+ |buffer+-------> NULL +------+ +------+ +---+---+---+---+---+ | line +------->+ | | | | | +------+ +---+---+---+---+---+

Después de que getline regrese, ya no tenemos acceso a la line :

+------+ |buffer+-------> NULL +------+

Y estamos de vuelta donde comenzamos. No podemos reajustar el buffer a la memoria recién asignada dentro de getline porque solo tenemos una copia de buffer .

El prototipo de getline es en realidad:

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

Y lo llamas así:

char *buffer = NULL; size_t len = 0; ssize_t read = getline(&buffer, &len, stdin);

&foo devuelve un puntero a foo , por lo que tenemos:

+-------+ +------+ |&buffer+------->+buffer+-------> NULL +-------+ +---+--+

Cuando se llama a getline , lineptr obtiene una copia de &buffer porque C es call-by-value. lineptr apunta al mismo lugar que &buffer :

+-------+ +------+ |&buffer+------->+buffer+-------> NULL +-------+ +---+--+ ^ +-------+ | |lineptr+------------+ +-------+

getline asigna algo de memoria con malloc y apunta el punto de lineptr (es decir, los puntos a los que apunta la línea) al comienzo del bloque:

+-------+ +------+ +---+---+---+---+---+ |&buffer+------->+buffer+------->+ | | | | | +-------+ +---+--+ +---+---+---+---+---+ ^ +-------+ | |lineptr+------------+ +-------+

Después de que getline regrese, ya no tenemos acceso a lineptr , pero aún podemos acceder a la memoria recientemente asignada a través del buffer :

+-------+ +------+ +---+---+---+---+---+ |&buffer+------->+buffer+------->+ | | | | | +-------+ +---+--+ +---+---+---+---+---+


¿Por qué usar char ** lineptr en lugar de char * lineptr como un parámetro de la función getline?

char **lineptr se usa porque getline() solicita la dirección del puntero que apunta a dónde se almacenará la cadena.
Utilizaría char *lineptr si getline() esperaba el puntero en sí (que no funcionaría, vea por qué en la respuesta de ThisSuitIsBlackNot)

Por qué está mal cuando uso el siguiente código:
char **my_string; bytes_read = getline(my_string, &nbytes, stdin);

Lo siguiente funcionaría:

char *my_string; char **pointer_to_my_string = &my_string; bytes_read = getline(my_string, &nbytes, stdin);

Estoy confundido con * y &.

El * tiene un doble significado.
Cuando se utiliza en una declaración de un puntero, por ejemplo, un puntero a char, significa que quiere un puntero a char en lugar de a char.
Cuando se usa en otro lugar, obtiene la variable a la que apunta un puntero.

El & obtiene la dirección en memoria de una variable (qué punteros se crearon para contener como valor)

char letter = ''c''; char *ptr_to_letter = &letter; char letter2 = *ptr_to_letter; char *ptr2 = &*ptr_to_letter; //ptr2 will also point to letter

&*ptr_to_letter significa darme la dirección ( & ) de la variable en la que ptr_to_letter apunta ( * ), y es lo mismo que ptr_to_letter
Puedes pensar en * como el opuesto de & , y que se cancelan uno al otro.


De ahí que la respuesta sea correcta para su primera pregunta. Verifique la página de manual en el futuro, tiene la información que necesita.

Su segunda línea no funciona porque el puntero no está inicializado. Si quieres hacer eso, necesitarías escribir:

char **my_string = malloc(sizeof(char**))

Esencialmente, cuando se crea una variable, * significa un puntero, cuando se hace referencia a una variable, significa desreferenciar el puntero (obtener lo que señala el puntero). & significa "El puntero que apunta a esto".


Habiendo asumido el control de algún código heredado en mi nuevo concierto, creo que debería ofrecer una advertencia contra llamar a calloc y devolver un puntero-puntero. Debería funcionar, pero oscurece cómo opera getline (). El operador & deja en claro que está pasando la dirección del puntero que obtuvo de malloc (), calloc (). Aunque técnicamente idéntico, declarar foo como char ** foo, en lugar de char * foo, y luego llamar a getline (foo ,,) en lugar de getline (& foo ,,) oscurece este importante punto.

  1. getline () le permite asignar almacenamiento y pasar getline () un puntero al puntero que le devuelve malloc (), calloc (), que usted asigna a su puntero. P.ej:

    char *foo = calloc(size_t arbitrarily_large, 1);

  2. es posible pasarlo & foo = NULL, en cuyo caso hará una asignación de almacenamiento a ciegas llamando calladamente a malloc (), calloc (), oculto a la vista.

  3. char * foo, ** p_foo = & foo también funcionaría. Luego llame a foo = calloc (size_t, size_t), y luego llame a getline (p_foo ,,); Creo que getline (y foo ,,) es mejor.

Las asignaciones de ciegas son muy malas, y una invitación a la memoria problemática se filtra, porque en ninguna parte de SU código está llamando malloc (), calloc (), por lo que usted o alguien que luego se encargará de mantener su código no sabrá liberar () el puntero a ese almacenamiento, porque alguna función a la que llamas asigna memoria sin que lo sepas (excepto para leer la descripción de la función y entender que está haciendo una asignación ciega).

Como getline () reasignará () la memoria que tu llamada a malloc (), calloc () proporcionó si es demasiado pequeña, lo mejor es asignar tu mejor estimación al almacenamiento requerido con una llamada a calloc () y hacerlo aclare lo que está haciendo el puntero char * foo. No creo que getline () haga nada con el almacenamiento, siempre y cuando tengas calloc () d es suficiente.

Tenga en cuenta que el valor de su puntero puede cambiarse si getline () tiene que llamar a realloc () para asignar más almacenamiento, ya que el nuevo almacenamiento probablemente SERÁ de una ubicación diferente en el montón. IE: si pasa & foo, y la dirección de foo es 12345, y getline () realloc () s su almacenamiento, y en una nueva ubicación, la nueva dirección de foo podría ser 45678.

Esto no es un argumento en contra de hacer su propia llamada a calloc (), porque si establece foo = NULL, se le garantiza que getline () tendrá que llamar a realloc ().

En resumen, haga una llamada a calloc () con algunas buenas suposiciones en cuanto al tamaño, lo que hará obvio para cualquiera que lea su código que la memoria ESTÁ ASIGNADA, que debe ser libre () d, sin importar lo que getline () haga o no haga no hacer más tarde.

if(NULL == line) { // getline() will realloc() if too small line = (char *)calloc(512, sizeof(char)); } getline((char**)&line, (size_t *)&len, (FILE *)stdin);


Porque getline() asignará la memoria por usted si pasa un puntero a un puntero nulo.

Desde la página man :

getline () lee una línea completa de la ruta, almacenando la dirección del búfer que contiene el texto en * lineptr. El búfer tiene terminación nula e incluye el carácter de nueva línea, si se encontró uno.

Si * lineptr es NULL, getline () asignará un buffer para almacenar la línea, que debe ser liberada por el programa de usuario. (En este caso, el valor en * n se ignora).

Necesita pasar un char** (es decir, un puntero a un puntero a un carácter) para que la función pueda actualizar el valor del char* que apunta.

Podrías haber usado:

char *my_string = NULL; // getline will alloc puts("Please enter a line of text"); bytes_read = getline(&my_string, &nbytes, stdin);

No olvide que si hace esto, usted es responsable de free() la memoria asignada por getline() .