我使用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;
}
这很好用。
我的怀疑是什么?
为什么使用char **lineptr
代替char *lineptr
作为函数getline
的参数?
使用以下代码时为什么会出错:
char **my_string;
bytes_read = getline(my_string, &nbytes, stdin);
我对*
和&
感到困惑。
以下是警告的一部分:
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)。
答案 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 ,,)来模糊这个重要的观点。
getline()允许你分配存储并传递getline()一个指针,指向malloc(),calloc()返回给你的指针,你指定指针。例如:
char *foo = calloc(size_t arbitrarily_large, 1);
可以传递它&amp; foo = NULL,在这种情况下,它会通过静默调用malloc(),calloc(),从视图中隐藏来为您进行盲目的存储分配。
char * foo,** p_foo =&amp; foo也可以。然后调用foo = calloc(size_t,size_t),然后调用getline(p_foo ,,);我认为getline(&amp; foo ,,)更好。
盲分配是非常糟糕的,并且是对有问题的内存泄漏的邀请,因为你的代码中没有任何地方你调用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
相同
您可以将*
视为&
的反面,并且它们相互抵消。