C语言中的指针变量
C 语言中,指针变量也是变量,只不过跟 char、int 型等基本类型变量不同,指针变量中保存的是其他变量的地址和类型。
通过这个地址,并根据类型来读取指定长度的字节,就可以把这个变量解析出来。
int a = 3;
int *p = &a; // & 是取地址符号,返回变量 a 的地址
printf("%d\n", *p);
C 语言中本身不支持字符串,但是通过 char *
类型的指针可以实现字符串效果:
char *s = "hello world";
printf("%s\n", s);
上面代码中,创建指针变量 s,指向字符类型的变量。然后把变量 s 的值当做字符串类型来解析,直到碰到字符串结束标志 \0
就结束读取。
字符串数组
char *arr[] = {"hello world", "test"};
printf("%s\n", arr[0]);
上面代码中,先创建数组 arr,数组中的每个元素都是 char *
类型,即指向字符的指针。然后把 arr[0]
数组第一个元素当做字符串解析。
char (*p)[10] = "hello";
printf("%s\n", *p);
二级指针
二级指针跟普通指针变量的区别在于:指针变量直接报错目标变量的地址,而二级指针变量保存的是另一个指针变量的地址,另一个指针变量才真正保存目标变量的地址。
二级指针的主要用途:
- 字符串数组
- main 函数的入参 argv
字符串数组
上面示例用数组的形式实现了字符串数组,在声明数组的同时初始化。其实还可以用二级指针实现字符串数组。
例如 char **p
,声明的变量 p 就是一个二级指针。p 指向的变量类型是 char *
,即字符指针,然后这个字符指针才真正指向一个字符变量。
下面代码是错误的示例,会报错:segmentation fault。
#include <stdio.h>
#include <malloc.h>
int main () {
char **p;
*p = "helo"; // 未分配内存空间就直接使用,报错
p[1] = "wold";
printf("%s\n", *p);
return 0;
}
出现段错误的原因是:编译器会自动为创建的 char **
类型的变量 p 分配内存空间。但是,*p
指向的 char *
类型的变量则并没有分配内存空间,就直接使用了。
只要让 p 变量指向一块经过初始化的内存,即可消除错误:
#include <stdio.h>
#include <malloc.h>
int main () {
// 分配内存空间,并将首地址返回给二级指针变量 p
// 然后用这块连续内存保存中间指针变量
char **p = malloc(sizeof(char *) * 2);
*p = "helo";
p[1] = "wold";
printf("%s\n", *p);
return 0;
}
指向数组的指针
一维数组
int a[10];
int *p = a;
二维数组
二维数组不能像一维数组那样简单的传递指针变量。
二维数组本质上仍是内存中的一块连续地址空间,只不过人为的将这块空间分隔为固定长度的子空间。
只要确保指针每次加一时,可以跳到下一块子空间,就可以用指针完美的表示二维数组。char (*p)[n]
就可以实现这个目的,其中 n 是子空间大小。
char (*p)[10]
这里的括号不能省略,否则 char *p[10]
,编译器会从右到左解析,变量 p 解析为数组,存放 10 个 char *
类型的变量。
#include <stdio.h>
int main () {
char arr[2][10] = {{"hello"}, {'w'}};
// 定义指针变量 p,指向有 10 个元素的 char 类型的数组
char (*p)[10] = arr;
printf("%s\n", *p);
printf("%s\n", *(p+1));
(*p)[0] = 'a';
printf("%s\n", *p);
(*(p + 1))[0] = 'a';
printf("%s\n", *(p+1));
return 0;
}