我觉得目前大多数人对于指针的理解可能还是处于懵懵懂懂的状态,当然,我也不例外,今天在做项目的过程中,发现其实这个点还是有点不是很清楚,所以我索性写一篇关于这个指针的总结。
很多人在学 c 的过程中,看到指针会很懵,尤其是这种代码:
char *tokens[10];
传进函数后却写成:
int func(char **tokens)
明明数组里的每个元素已经是 char * 了,为什么函数参数还要再加一个 *?
这个问题其实不难,关键是要分清楚: “数组里的元素是什么” 和 “传给函数的是什么” 不是一回事。
一、先看 char * 和 char ** 分别表示什么
char *:表示“字符指针”,通常拿来表示一个字符串。
例如:
char *s = "hello";
这里 s 指向字符串 "hello",所以:
-
s 是一个指针
-
*s 是一个字符
也就是说:
char * -> 一个字符串
char **:表示“指向 char * 的指针”。
也就是说,它指向的不是字符,而是一个“字符串指针”。
char *s = "hello";
char **p = &s;
这里:
-
s 是 char *
-
p 是 char **
-
p 存的是 s 的地址
所以:
char ** -> 指向字符串指针
你也可以暂时把它通俗地理解成:“一组字符串的入口”
二、为什么 char *tokens[10] 传参后会变成 char **
先看定义:
char *tokens[10];
这表示:
-
tokens 是一个数组
-
数组里每个元素的类型是 char *
也就是说:
tokens[0] // 类型是 char *
tokens[1] // 类型是 char *
tokens[2] // 类型是 char *
但是,当数组作为函数参数传递时,数组名会退化成指针。
比如:
void func(char **tokens)
{
printf("%s\\n", tokens[0]);
}
调用:
func(tokens);
这里传进去的不是整个完整数组实体,而是:数组第一个元素的地址(之所以这样设计,是因为数组元素在内存里是连续存放的,拿到第一个元素的地址后,就能顺着访问后面的元素。)
tokens == &tokens[0] // 在函数传参这种场景里理解成这样就行
而数组第一个元素的类型是什么?是:char *
那么“第一个元素的地址”类型自然就是:char **
所以函数参数写成:char **tokens
三、为什么“元素已经是 char *”了,还要再加一个 *
因为函数里接收的不是“元素本身”,而是“元素的地址”。
这是很多人最容易绕进去的地方。
看这两个东西:
1. 单个元素:tokens[0]
它的类型是:char *,因为它本身就是数组里的一个元素。
2. 整个数组名:tokens
它不是某个元素,而是数组的起始位置。 数组传参时,它会变成“指向第一个元素的指针”。
而第一个元素类型是 char *,所以:tokens -> char **
所以不要把这两个混了:
-
tokens[0] 是 char *
-
tokens 传参后是 char **
四、结合代码看
例子 1:定义字符串数组
#include <stdio.h>
int main() {
char *tokens[3];
tokens[0] = "SET";
tokens[1] = "name";
tokens[2] = "alice";
printf("%s\\n", tokens[0]);
printf("%s\\n", tokens[1]);
printf("%s\\n", tokens[2]);
return 0;
}
这里:
-
tokens 是数组
-
tokens[0]、tokens[1]、tokens[2] 都是 char *
例子 2:把这个数组传给函数
#include <stdio.h>
void print_tokens(char **tokens) {
printf("%s\\n", tokens[0]);
printf("%s\\n", tokens[1]);
printf("%s\\n", tokens[2]);
}
int main() {
char *tokens[3];
tokens[0] = "SET";
tokens[1] = "name";
tokens[2] = "alice";
print_tokens(tokens);
return 0;
}
这里函数参数为什么是 char **tokens?
因为传进去的是数组名 tokens,数组名传参后退化成指针。 而数组元素类型是 char *,所以参数类型就是:char **
五、总结
char ** 并没有那么神秘。它本质上表示:指向 char * 的指针
如果你定义的是:char *tokens[10];
那么数组里的每个元素都是 char *。 而当这个数组传给函数时,传过去的是“第一个元素的地址”,也就是“指向 char * 的指针”,数组名退化成指向第一个元素的指针,所以传参时可以把它看成第一个元素的地址。所以函数参数要写成:char **tokens
你只要记住下面这两句话,基本就不会乱:
-
char *:一个字符串
-
char **:指向字符串指针,常用来接收字符串数组参数
0voice · GitHub



