为什么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; }
这很好用。
我的怀疑是?
-
为什么使用
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)。
为什么使用
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 +------+
当调用getline
, line
获取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 +-------+ +---+--+
调用getline
, lineptr
会获取&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 ,,)来模糊这个重要的观点。
-
getline()允许你分配存储并传递getline()指向malloc(),calloc()返回给你的指针,指针指向你的指针。 例如:
char *foo = calloc(size_t arbitrarily_large, 1);
-
可以传递它&foo = NULL,在这种情况下,它将通过静默调用malloc(),calloc(),从视图中隐藏来为您进行盲目的存储分配。
-
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
相同
您可以将*
视为&
的相反,并且它们相互抵消。