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

时间:2011-04-21 12:54:11

标签: c function gcc

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

getline的原型是:

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

我将此作为来自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;
}

这很好用。

我的怀疑是什么?

  1. 为什么使用char **lineptr代替char *lineptr作为函数getline的参数?

  2. 使用以下代码时为什么会出错:

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

  4. 以下是警告的一部分:

    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)。

5 个答案:

答案 0 :(得分:26)

  

为什么使用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为空:

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

调用getline时,line会获得buffer的副本,因为函数参数是按照C中的值传递的。在getline内,我们无法再访问{ {1}}:

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 +-------+ +---+--+ 时,getline会获得lineptr的副本,因为C是按值调用的。 &buffer指向与lineptr相同的位置:

&buffer

+-------+ +------+ |&buffer+------->+buffer+-------> NULL +-------+ +---+--+ ^ +-------+ | |lineptr+------------+ +-------+ 使用getline分配一些内存,并在块的开头指向malloc(即lineptr指向的东西)的指针:

lineptr

+-------+ +------+ +---+---+---+---+---+ |&buffer+------->+buffer+------->+ | | | | | +-------+ +---+--+ +---+---+---+---+---+ ^ +-------+ | |lineptr+------------+ +-------+ 返回后,我们无法再访问getline,但我们仍然可以通过lineptr访问新分配的内存:

buffer

答案 1 :(得分:12)

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

来自man page

  

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()分配的内存。

答案 2 :(得分:6)

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

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

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

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

答案 3 :(得分:2)

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

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

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

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

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

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

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

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

    这不是对你自己调用calloc()的争论,因为如果你设置foo = NULL,你有保证,getline()将不得不调用realloc的()。

    总之,调用calloc()并对其大小进行了一些很好的猜测,这对于读取代码的任何人来说都很明显,内存正在被分配,而且必须是免费的()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);
    

答案 4 :(得分:1)

  

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

使用

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

  

使用以下代码时为什么会出错:
  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);
  

我对*和&amp;。

感到困惑

*具有双重含义 当用于指针的声明时,例如指向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相同
您可以将*视为&的反面,并且它们相互抵消。