在c语言开发过程中,指针是一个绕不过的大山,横亘在每位程序员的心中。在大量的代码中,一级指针的使用频率相当高,并且一级指针确实能解决大部分问题,而在某些时候却又不得不使用二级指针。比如:
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
很多人可能就困惑于此。为什么getline函数的第一个参数是二级指针,用一级指针不行吗?此时你应该需要区查询该函数的官方解释,如下:
getline() reads an entire line from stream, storing the address of the buffer containing the text into *lineptr. The buffer is null-terminated and includes the newline character, if one was found.
getline作用是读一整行,把读到的内容存到*lineptr指向的内存,直到遇到’ ’结束。那么在此可能大家会和常见的fgets进行对比。
char *fgets(char *s, int size, FILE *stream);
fgets函数的作用是读取size-1个字符到s指向的内存。那么fgets和getline最大的区别在于:fgets使用一级指针,getline使用二级指针;fgets一个在读之前已经确定了最大读取长度,而getline读一行的长度未知。所以如果我在调用此函数之前就分配好一段内存用于存储读取的内容,那么我应该分配多长内存呢?结论是分配多长都不合适,而应该使用动态内存分配方式(结合malloc和realloc,具体此处不做展开)。
那为什么我使用动态内存分配方式就需要二级指针呢?接下来我们来讲过程简化并讨论。
首先我们来看如下代码:
void getMemory( char *ptr )
{
ptr = malloc( 100 );
}
int main()
{
char *str = NULL;
getMemory( str ); // 此处期望调用函数分配内存
strcpy( str, “hello” );
printf( str );
free( str );
}
此代码在编译运行后,必定出现错误segment fault。原因是str在传参过程是典型的值传递。而分配的内存首地址存在ptr,和str没有任何关系。函数调用结束str仍然为NULL,指针ptr内存回收,分配的100字节堆内存出现内存泄漏。内存图如下:
如果我将上述代码略作修改,将str的传参过程改为地址传递。代码如下
void getMemory( char **ptr )
{
*ptr = malloc( 100 );
}
int main()
{
char *str = NULL;
getMemory( &str ); // 此处期望调用函数分配内存
strcpy( str, “hello” );
printf( str );
free( str );
}
那么,此时分配的内存地址存于*ptr,也就是str。所有函数结束后,str指针即指向分配的100字节内存。代码正常运行。内存图如下:
如此,应该能很清楚认识到一级指针和二级指针的区别,亦可理解为什么有时候需要使用二级指针才能解决问题。
那么,此刻你是否理解了getline的第一个参数为什么需要使用二级指针了?
你还见过哪些地方使用了二级指针的情况?