Pourquoi le premier argument de getline est-il un pointeur à pointer «char **» au lieu de «char *»?

getline fonction getline pour lire une ligne de STDIN .

Le prototype de getline est:

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

J’utilise ceci comme programme de test fourni par http://www.crasseux.com/books/ctutorial/getline.html#getline

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

Cela fonctionne bien.

Mes doutes sont?

  1. Pourquoi utiliser char **lineptr place de char *lineptr tant que paramètre de la fonction getline ?

  2. Pourquoi c’est faux quand j’utilise le code suivant:

     char **my_ssortingng; bytes_read = getline(my_ssortingng, &nbytes, stdin); 
  3. Je suis confondu avec * et & .

Voici une partie des avertissements:

 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 * __ressortingct__' 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 *' 

J’utilise GCC version 4.4.5 (Ubuntu / Linaro 4.4.4-14ubuntu5).

Pourquoi utiliser char **lineptr au lieu de char *lineptr tant que paramètre de la fonction getline ?

Imaginez que le prototype de getline ressemble à ceci:

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

Et vous l’avez appelé comme ça:

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

Avant d’appeler getline , la buffer est nulle:

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

Lorsque getline est appelée, line obtient une copie du buffer car les arguments de la fonction sont passés par valeur en C. Dans getline , nous n’avons plus access au buffer :

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

getline alloue de la mémoire avec malloc et une line points au début du bloc:

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

Après le retour de getline , nous n’avons plus access à la line :

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

Et nous sums de retour là où nous avons commencé. Nous ne pouvons pas re-pointer le buffer vers la mémoire nouvellement allouée à l’intérieur de getline car nous n’en avons qu’une copie.


Le prototype de getline est en réalité:

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

Et vous l’appelez comme ça:

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

&foo renvoie un pointeur sur foo , nous avons donc:

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

Lorsque getline est appelé, lineptr obtient une copie de &buffer car C est un appel valeur par valeur. lineptr pointe au même endroit que &buffer :

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

getline alloue un peu de mémoire avec malloc et pointe la pointe de lineptr (c’est-à-dire le point que lineptr pointe vers) au début du bloc:

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

Après le retour de getline , nous n’avons plus access à lineptr , mais nous pouvons toujours accéder à la mémoire récemment allouée via le buffer :

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

Parce que getline() allouera la mémoire pour vous si vous passez un pointeur à un pointeur nul.

De la page de manuel :

getline () lit une ligne entière dans stream, stockant l’adresse du tampon contenant le texte dans * lineptr. Le tampon est terminé à zéro et inclut le caractère de nouvelle ligne, s’il en a été trouvé.

Si * lineptr est NULL, alors getline () allouera un tampon pour stocker la ligne, qui devrait être libéré par le programme utilisateur. (Dans ce cas, la valeur dans * n est ignorée.)

Vous devez passer un caractère char** (c’est-à-dire un pointeur vers un pointeur vers un caractère) afin que la fonction puisse mettre à jour la valeur du caractère char* qu’elle désigne.

Vous auriez pu utiliser:

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

N’oubliez pas que si vous faites cela, vous êtes responsable de free() la mémoire allouée par getline() .

La réponse est donc correcte pour votre première question. Consultez la page de manuel à l’avenir, elle contient les informations dont vous avez besoin.

Votre deuxième ligne ne fonctionne pas car le pointeur n’est pas initialisé. Si vous voulez faire cela, vous devez écrire:

 char **my_ssortingng = malloc(sizeof(char**)) 

En gros, lorsque vous créez une variable, * signifie un pointeur, lorsque vous référencez une variable, cela signifie déréférencer le pointeur (obtenir ce à quoi le pointeur se réfère ). & signifie “Le pointeur qui pointe vers ceci”.

Ayant pris en charge certains codes hérités lors de mon nouveau travail, je pense que je devrais mettre en garde contre le fait d’appeler calloc et de renvoyer un pointeur pointeur. Cela devrait fonctionner, mais cela obscurcit le fonctionnement de getline (). L’opérateur & indique clairement que vous transmettez l’adresse du pointeur que vous avez reçu de malloc (), calloc (). Bien que techniquement identique, déclarer foo en tant que char ** foo, au lieu de char * foo, puis appeler getline (foo ,,) au lieu de getline (& foo ,,) occultent ce point important.

  1. getline () vous permet d’allouer de la mémoire et de transmettre à getline () un pointeur sur le pointeur que malloc (), calloc () vous renvoie, que vous affectez à votre pointeur. Par exemple:

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

  2. il est possible de passer & foo = NULL, auquel cas il effectuera une allocation aveugle de stockage pour vous en appelant discrètement malloc (), calloc (), masqué de la vue.

  3. char * foo, ** p_foo = & foo fonctionnerait aussi. Ensuite, appelez foo = calloc (size_t, size_t), puis appelez getline (p_foo ,,); Je pense que getline (& foo ,,) est meilleur.

Les allocations aveugles sont très mauvaises et constituent une invitation aux memory leaks problématiques, car nulle part dans VOTRE code, vous n’appelez malloc (), calloc (), de sorte que vous-même ou une personne chargée ultérieurement de la maintenance de votre code ne saura pas libérer () le pointeur sur ce stockage, car une fonction que vous avez appelée alloue de la mémoire sans que vous le sachiez (à l’exception de la lecture de la description de la fonction et de la compréhension qu’il s’agit d’une allocation aveugle).

Puisque getline () va réalloc () la mémoire de votre appel à malloc (), calloc () fournie si elle est trop petite, il est préférable d’allouer votre meilleure estimation du stockage requirejs avec un appel à calloc (), et de la définir. clarifie ce que fait le pointeur char * foo. Je ne crois pas que getline () fasse quoi que ce soit avec le stockage tant que calloc () d est suffisant.

Gardez à l’esprit que la valeur de votre pointeur peut être modifiée si getline () doit appeler realloc () pour allouer plus de stockage, car le nouveau stockage SERA probablement à partir d’un emplacement différent sur le tas. IE: si vous passez & foo, et que l’adresse de foo est 12345 et que getline () realloc () est votre espace de stockage, et dans un nouvel emplacement, la nouvelle adresse de foo pourrait être 45678.

Ce n’est pas un argument contre le fait de faire votre propre appel à calloc (), car si vous définissez foo = NULL, vous êtes assuré que getline () devra appeler realloc ().

En résumé, appelez calloc () avec une bonne estimation de la taille, ce qui laissera évident à toute personne qui lit votre code que la mémoire est allouée, ce qui doit être libre (), peu importe ce que getline () fait ou ne fait pas. fais pas plus tard.

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

Pourquoi utiliser char ** lineptr à la place de char * lineptr en tant que paramètre de la fonction getline?

char **lineptr est utilisé parce que getline() demande l’adresse du pointeur qui pointe vers l’endroit où la chaîne sera stockée.
Vous utiliseriez char *lineptr si getline() attend le pointeur lui-même (ce qui ne fonctionnerait pas, voyez pourquoi dans la réponse de ThisSuitIsBlackNot)

Pourquoi c’est faux quand j’utilise le code suivant:
char **my_ssortingng; bytes_read = getline(my_ssortingng, &nbytes, stdin);

Ce qui suit fonctionnerait:

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

Je suis confondu avec * et &.

Le * a une double signification.
Lorsqu’il est utilisé dans une déclaration d’un pointeur, par exemple un pointeur sur un caractère, cela signifie que vous voulez un pointeur sur un caractère au lieu d’un caractère.
Lorsqu’il est utilisé ailleurs, il obtient la variable à laquelle un pointeur pointe.

Le & obtient l’adresse en mémoire d’une variable (quels pointeurs ont été créés pour contenir une valeur)

 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 signifie que vous me donnez l’adresse ( & ) de la variable à laquelle pointe ptr_to_letter ( * ) et correspond à l’écriture de ptr_to_letter
Vous pouvez penser à * comme l’opposé de & , et qu’ils s’annulent.