一、数组指针
定义:指向数组的指针,即指向数组第一个元素地址的指针,数组名本质上是一个指向数组第一个元素的指针。
int (*ptr)[5];
指向数组的指针声明时需要明确指定其所指向数组的大小和类型。
数组指针的使用
int arr[5] = {1, 2, 3, 4, 5};
int (*ptr)[5] = &arr; // 这种方式为初始化
ptr = arr; // 这种方式为赋值
通过指向数组的指针访问数组元素时,有两种方式:
printf("%d\n", (*ptr)[0]); // 输出1
printf("%d\n", (*ptr)[1]); // 输出2
或者通过指针偏移访问
printf("%d\n", *(*ptr + 0)); // 输出1
printf("%d\n", *(*ptr + 1)); // 输出2
一维数组
a
等价于&a[0]
*(a + 0)
等价于a[0]
二维数组
a
等价于&a[0]
*(a + 0)
等价于a[0]
*(*(a + 0) + 1)
等价于a[0][1]
a[i][j]
等价于*(*(a + i) + j)
二、字符串与指针
-
字符串和指针的基本关系:
- 字符串常量和字符数组名都表示一个指向字符串首字符的指针。
- 字符数组(如
char str[]
)是可修改的,而字符串常量(如"Hello"
)是只读的。
-
字符串作为函数参数:
- 字符数组作为参数实际上传递的是指向数组第一个元素的指针。
- 字符指针可以直接传递,指向字符常量或动态分配的内存。
三、函数指针
函数指针是一个指向函数的指针,可以用来调用函数。定义函数指针时,你需要指定函数的返回类型和参数列表。
数据类型 (*指针变量名)(函数参数列表)
// 定义一个返回类型为int,参数为两个int类型的函数指针
int (*pfn)(int, int);
函数指针可以通过将其赋值为一个函数的名字来初始化。函数名代表函数的地址。
int add(int a, int b) {
return a + b;
}
int (*pfn)(int, int) = add; // 函数指针初始化
函数指针初始化后就可以通过指针来间接调用该函数
int result = pfn(5, 10); // 调用add函数
函数指针可以作为其他函数的参数,这在需要动态指定函数行为时特别有用。例如,回调函数的实现:
回调函数:回调函数本质上是一个普通的函数,只不过它是作为参数传递给另一个函数。回调函数的类型需要与接受它的函数的参数类型匹配。
使用函数指针可以降低程序的耦合性
四、指针数组
指针数组是一种数据结构,,整体是一个数组,其中的每个元素都是一个指针。
int *arr[10]; // 定义一个包含10个整型指针的数组
指针数组的使用
1. 访问指针数组元素
访问指针数组的元素时,首先要获取指针,然后通过指针操作数据:
int a = 1, b = 2, c = 3;
int *arr[3] = {&a, &b, &c};
// 访问并打印指针数组中的元素
for (int i = 0; i < 3; i++)
{
printf("%d\n", i, *arr[i]);
}
2. 使用指针数组管理字符串
指针数组常用于管理字符串数组,因为字符串是以字符指针形式存在的。
void printArray2D(int (*a)[4],int rows)
{
for(int i = 0 ; i < rows; ++i)
{
for(int j = 0 ; j < 4 ; ++j)
{
printf("%2d ",*(*(a + i) + j));
}
puts("");
}
}
指针数组作为main函数形参时,形式为
int main(int argc, char *argv[]);
其中:
argc
:参数个数,即命令行参数的数量,包括程序本身的名字。argv
:参数值数组,是一个指针数组,每个元素是一个指向字符数组(即字符串)的指针。
argv[0]
:通常是程序的名字或执行路径。argv[1]
到argv[argc-1]
:是实际的命令行参数。
五、指向指针的指针(二级指针)
1. 定义指向指针的指针
一个指向指针的指针是一个保存指针地址的变量。定义时需要两个星号(**
)。
int x = 10;
int *p = &x; // 指向int类型的指针
int **pp = &p; // 指向指针的指针
2. 修改函数中的指针
指针的指针允许我们在函数中修改指针的值,即使函数结束后,这种修改仍然有效。这在需要函数返回多个值或者需要修改传入指针的情况下非常有用。