为什么getline的第一个参数指向指针“char **”而不是“char *”?

我使用getline函数从STDIN读取一行。

getline的原型是:

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

我将此作为测试程序使用,该程序来自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_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; } 

这很好用。

我的怀疑是?

  1. 为什么使用char **lineptr而不是char *lineptr作为函数getline的参数?

  2. 使用以下代码时出错的原因:

     char **my_string; bytes_read = getline(my_string, &nbytes, stdin); 
  3. 我对*&感到困惑。

以下是警告的一部分:

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

我使用GCC版本4.4.5(Ubuntu / Linaro 4.4.4-14ubuntu5)。

为什么使用char **lineptr而不是char *lineptr作为函数getline的参数?

想象一下getline的原型看起来像这样:

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

你这样称呼它:

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

在调用getline之前, buffer为null:

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

当调用getlineline获取buffer的副本,因为函数参数是通过C中的值传递的。在getline ,我们不再能够访问buffer

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

getline使用malloc和点line分配一些内存到块的开头:

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

getline返回后,我们无法再访问line

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

我们回到了我们开始的地方。 我们不能将buffer重新指向getline新分配的内存,因为我们只有buffer的副本。


getline的原型实际上是:

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

你这样称呼它:

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

&foo返回一个指向&foo的指针,所以我们有:

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

调用getlinelineptr会获取&buffer的副本,因为C是按值调用的。 lineptr指向&buffer相同的位置:

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

getline使用malloc分配一些内存,并在块的开头指向lineptr (即lineptr指向的东西)的指针:

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

getline返回之后,我们不再能访问lineptr ,但我们仍然可以通过buffer访问新分配的内存:

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

因为如果传入指向空指针的指针, getline()将为您分配内存。

从手册页 :

getline()从流中读取整行,将包含文本的缓冲区的地址存储到* lineptr中。 缓冲区以空值终止,并包含换行符(如果找到)。

如果* lineptr为NULL,则getline()将分配用于存储该行的缓冲区,该缓冲区应由用户程序释放。 (在这种情况下,忽略* n中的值。)

您需要传入一个char** (即一个指向char的指针),以便该函数能够更新它指向的char*的值。

你可以使用:

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

不要忘记,如果你这样做,你负责free()getline()分配的内存。

对于你的第一个问题,答案是正确的。 将来查看联机帮助页,它包含您需要的信息。

您的第二行不起作用,因为指针未初始化。 如果你想这样做,你需要写:

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

本质上,当您创建变量时,*表示指针,当您引用变量时,它意味着取消引用指针(获取指针指向的内容)。 &表示“指向此的指针”。

在我的新演出中接管了一些遗留代码后,我想我应该谨慎对待调用calloc并返回一个指针指针。 它应该工作,但它模糊了getline()的运作方式。 &运算符清楚地表明您正在传递从malloc(),calloc()返回的指针的地址。 虽然在技术上完全相同,但是将foo声明为char ** foo而不是char * foo,然后调用getline(foo ,,)而不是getline(&foo ,,)来模糊这个重要的观点。

  1. getline()允许你分配存储并传递getline()指向malloc(),calloc()返回给你的指针,指针指向你的指针。 例如:

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

  2. 可以传递它&foo = NULL,在这种情况下,它将通过静默调用malloc(),calloc(),从视图中隐藏来为您进行盲目的存储分配。

  3. char * foo,** p_foo =&foo也可以。 然后调用foo = calloc(size_t,size_t),然后调用getline(p_foo ,,); 我认为getline(&foo ,,)更好。

盲目分配是非常糟糕的,并且是对有问题的内存泄漏的邀请,因为你的代码中没有任何地方你调用malloc(),calloc(),所以你或后来负责维护你的代码的人不会知道免费()指向该存储的指针,因为您调用的某个函数在不知道的情况下分配内存(除了读取函数描述并理解它正在进行盲分配)。

因为getline()将realloc()调用malloc(),calloc()所提供的内存,如果它太小,最好通过调用calloc()来分配对所需存储的最佳猜测,并使其成为清除指针char * foo正在做什么。 我不相信getline()会对存储做任何事情,只要你有calloc()d就足够了。

请记住,如果getline()必须调用realloc()来分配更多存储空间,则指针的值可能会更改,因为新存储可能来自堆上的其他位置。 IE:如果你传递&foo,并且foo的地址是12345,并且getline()realloc()是你的存储,并且在新的位置,foo的新地址可能是45678。

这不是反对你自己调用calloc()的争论,因为如果你设置foo = NULL,你可以保证 getline()必须调用realloc()。

总而言之,调用calloc()时会有一些关于大小的好猜测,这将使读取代码的任何人都明白内存正在被分配,而且必须是free()d,无论getline()做什么或不做什么不要再做了。

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

为什么使用char ** lineptr而不是char * lineptr作为函数getline的参数?

char **lineptr是因为getline()要求指向指向字符串存储位置的地址。
如果getline()期望指针本身(这不起作用,请参阅ThisSuitIsBlackNot的答案中的原因),你会使用char *lineptr

使用以下代码时出错的原因:
char **my_string; bytes_read = getline(my_string, &nbytes, stdin);

以下方法可行:

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

我对*和&感到困惑。

*具有双重含义。
当在指针的声明中使用时,例如指向char的指针,这意味着您需要指向char而不是char的指针。
当在别处使用时,它获取指针所指向的变量。

&获取变量的内存中的地址(创建指针作为值保存的指针)

 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表示给我ptr_to_letter指向( * )的变量的地址( & ),并且与ptr_to_letter相同
您可以将*视为&的相反,并且它们相互抵消。