J'utilise la 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 cela comme un programme de test obtenu depuis 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;
}
Cela fonctionne bien.
Mes doutes sont?
Pourquoi utiliser char **lineptr
à la place de char *lineptr
en tant que paramètre de la fonction getline
?
Pourquoi c'est faux quand j'utilise le code suivant:
char **my_string;
bytes_read = getline(my_string, &nbytes, stdin);
Je suis confus 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 * __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 *’
J'utilise GCC version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5).
Pourquoi utiliser
char **lineptr
au lieu dechar *lineptr
en tant que paramètre de la fonctiongetline
?
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
, buffer
est null:
+------+
|buffer+-------> NULL
+------+
Lorsque getline
est appelé, line
obtient une copie de buffer
car les arguments de la fonction sont passés par valeur dans C. Dans getline
, nous n'avons plus accès à buffer
:
+------+
|buffer+-------> NULL
+------+ ^
|
+------+ |
| line +----------+
+------+
getline
alloue de la mémoire avec malloc
et des points line
au début du bloc:
+------+
|buffer+-------> NULL
+------+
+------+ +---+---+---+---+---+
| line +------->+ | | | | |
+------+ +---+---+---+---+---+
Après le retour de getline
, nous n’avons plus accès à line
:
+------+
|buffer+-------> NULL
+------+
Et nous sommes de retour là où nous avons commencé. Nous ne pouvons pas re-pointer buffer
vers la mémoire récemment allouée à l'intérieur de getline
car nous ne disposons que d'une copie de buffer
.
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 que la chose que lineptr
pointe sur) au début du bloc:
+-------+ +------+ +---+---+---+---+---+
|&buffer+------->+buffer+------->+ | | | | |
+-------+ +---+--+ +---+---+---+---+---+
^
+-------+ |
|lineptr+------------+
+-------+
Après le retour de getline
, nous n’avons plus accès à lineptr
, mais nous pouvons toujours accéder à la mémoire nouvellement allouée via 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 à partir de flux, stockant l'adresse du fichier tampon contenant le texte en * lineptr. La mémoire tampon est terminée par zéro et inclut le fichier caractère de nouvelle ligne, si un a été trouvé.
Si * lineptr est NULL, alors getline () allouera un tampon pour stocker le fichier ligne, qui devrait être libérée par le programme utilisateur. (Dans ce cas, la valeur Dans * n est ignorée.)
Vous devez passer un char**
(c'est-à-dire un pointeur vers un pointeur vers un caractère) afin que la fonction puisse mettre à jour la valeur du char*
à laquelle elle pointe.
Vous auriez pu utiliser:
char *my_string = NULL; // getline will alloc
puts("Please enter a line of text");
bytes_read = getline(&my_string, &nbytes, stdin);
N'oubliez pas que si vous faites cela, vous êtes responsable de free()
- dans 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_string = malloc(sizeof(char**))
Essentiellement, lorsque vous êtes créant une variable, * signifie un pointeur, lorsque vous êtes référençant une variable, cela signifie de déréférencer le pointeur (obtenir ce que le pointeur pointe sur). & signifie "Le pointeur qui pointe vers ceci".
Pourquoi utiliser char ** lineptr à la place de char * lineptr en tant que paramètre de la fonction getline?
char **lineptr
est utilisé car getline()
demande l'adresse du pointeur qui pointe vers l'endroit où la chaîne sera stockée.
Vous utiliseriez char *lineptr
si getline()
prévoyait 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_string; bytes_read = getline(my_string, &nbytes, stdin);
Ce qui suit fonctionnerait:
char *my_string;
char **pointer_to_my_string = &my_string;
bytes_read = getline(my_string, &nbytes, stdin);
Je suis confondu avec * et &.
Le *
a une double signification.
Lorsqu'il est utilisé dans une déclaration d'un pointeur, par ex. un pointeur sur char, cela signifie que vous voulez un pointeur sur char 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 me donner l'adresse (&
) de la variable à laquelle ptr_to_letter
pointe (*
), et correspond à l'écritureptr_to_letter
Vous pouvez penser que *
est le contraire de &
et qu’ils s’annulent.