1.字符指针变量
我们知道字符指针是存放字符的指针,那么字符指针是否能存放字符串呢?我们来看一下
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
char* p = "asdfghjkl";
printf("%c\n", *p);
return 0;
}
会输出字符串吗?
可以看到,并不能输出字符串,只输出了字符a
所以得出结论:不是把整一个字符串存入指针变量p中,而是将首元素字符a的地址存储到p中。
既然字符指针在存放字符串的时候存放的是首元素字符的地址,那用指针变量是不是就无法打印出整个字符串呢?我们来看看下面代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
char* p = "asdfghjkl";
printf("%s\n", p);
//复习:%s的作用:输出字符串中的字符直至字符串中的空字符(字符串以'\0‘结尾,这个'\0'即空字符)
return 0;
}
运行结果如下
可以看到,是可以输出整个字符串的
为什么可以仅凭一个首元素字符的地址,就能打印整个字符串呢?
那是因为:字符指针指向字符串的本质是因为字符串是连续存放的,实际上字符指针还是指向首元素"a"的地址,但是因为字符串在内存中是连续存放的,所以当字符指针指向a的时候也能访问字符串中a后面的字符
接下来,再看看下面的代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
char* p = "asdfghjkl";
*p = 'f';
printf("%s\n", p);
return 0;
}
这段代码输出了什么?
答案是什么都没输出。
因为“asdfghjkl"是常量字符串,既然是常量,那么就无法被修改,无法被修改但是你指针还要修改他,那么是不是就会出问题,为了防止这个问题出现,我们通常可以在前面加个const,这样就能限制*p无法被修改了
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
const char* p = "asdfghjkl";
*p = 'f';
printf("%s\n", p);
return 0;
}
如下图所示(*p无法被修改):
2.数组指针
什么是数组指针?数组指针就是存放数组的指针,是一种指针
2.1数组指针变量及其初始化
一、什么是数组指针变量?
数组指针变量就是能够存放数组的指针,能够指向数组的指针变量
int (*p)[10];
这个是什么意思呢?
对int (*p)[10]
一个一个拆解
* p
:p是数组指针变量名,前面带个*,代表p是一个指针
[10]
:代表p指向数组的元素个数为10,即p指向数组的元素个数
int
:代表p指向的数组元素类型是整型,即p指向的数组的元素类型
注意:为什么不能写成int *p[10]
,而要写成int (*p)[10]
呢?
因为:[]的优先级比*高,那么p会先和[]结合,此时p[]就是一个数组,此时p就是数组名,数组的元素类型是int * 类型,他是一个存放指针的数组,叫做数组指针。
而int (*p)[10]
,此时()优先级最高,那么(*p)
代表p是一个指针,(*p)[10]
证明p指向的数组的元素个数为10,int (*p)[10]
代表p指向的的是一个大小为10的整型数组
二、数组指针变量的初始化
数组指针变量应该如何初始化呢?前面我们说过,&arr,取出的是整个数组的地址,所以我们只需要&arr,然后赋给数组指针变量即可
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int(*p)[10] = &arr;
return 0;
}
通过调试来看:
可以看到,p存放的是整个arr数组的地址
2.2二维数组传参的本质
理解了数组指针,就能学习二维数组传参的本质了
首先我们先来复习一下二维数组
2.2.1二维数组
先看图片
二维数组的每一行都是一个一维数组,我们可以把一维数组看作是二维数组的元素,所以我们也可以认为,二维数组是一个数组,是存放一维数组的数组。
2.2.2二维数组的地址
我们知道,数组名表示首元素的地址,那么arr是不是就是二维数组的首元素地址呢?
答案是正确的,arr表示首元素地址,我们刚刚讲过,二维数组是存放一维数组的数组,所以arr表示首元素地址,就是第一行一维数组的地址,存放一维数组的地址,我们用指针来存放,既然是数组,我们就用数组指针来存放,一维数组的类型是int [5],那么数组指针类型就是int (*) [5]。这样我们就得到了二维数组传参的本质:二维数组传参本质上传递是地址,这个地址是一维数组的地址,也就是首元素的地址,我们可以将二维数组写成指针的形式,便于我们理解
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void test(int(*arr)[5], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf("%d", *(*(arr + i) + j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7} };
int row = 3;
int col = 5;
test(arr, row, col);
return 0;
}
3.函数指针
3.1函数指针变量
我们先来类比一下
整型指针是指向整型的指针,是存放整型地址的指针
数组指针是指向数组的指针,是存放数组地址的指针
那么,
函数指针就是指向函数的指针,是存放函数地址的指针
3.1.1函数指针的创建
既然我们知道函数指针是存放函数的地址的,那么存放函数的地址,就要用到函数指针变量,那么函数指针变量应该如何创建呢?
函数指针的创建:
int (*pf) (int x, int y);
int (*pf) (int, int);
- (*pf):代表函数指针变量
- int:函数指针所指向的函数的返回类型
- (int x, int y):函数指针pf所指向的函数的参数类型以及参数个数的交代
- 里面的x和y是可以省略的,只需要知道参数类型以及参数个数即可
函数指针的类型:把变量去掉就是函数指针的类型
int (*)(int , int)
3.1.2函数指针的初始化
我们知道了函数指针是怎么创建的,那么接下来我们来看看函数指针是如何初始化的
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int x = 3;
int y = 4;
int (*pf)(int, int) = Add;
int ret = pf(x, y);
int ret2 = (*pf)(x, y);
printf("%d\n", ret);
printf("%d\n", ret2);
return 0;
}
我们先看这个
int (*pf)(int, int) = Add;
这个就是函数指针的初始化,我们可以直接把Add传给pf,因为Add函数名就代表函数的地址,
我们还可以这样写
int (*pf)(int, int) = &Add;
这样也可以取出函数的地址
然后我们来看一下这段代码的运行结果
运行结果如下:
我们可以看到,ret和ret2返回的结果都是7,那么就代表这两种用法都是可以通过指针来调用到函数的,(*pf)(x,y)
会更清晰,我们一看就知道(*pf)是一个指针变量,解引用一下就可以访问函数的地址,实际上编译器只要知道函数的地址就能调用
3.1.3 typedef关键字
用处:用来类型重命名,可以将复杂的类型简单化
我们可以把函数指针的类型给简单化
如果我们要把上述的int (*)(int, int)
类型重命名为pf_t,可以这样写
typedef int (*pf_t)(int, int);
//将int (*)(int, int)重命名为pf_t
然后把上述代码改编一下
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
typedef int (*pf_t)(int, int);
int Add(int x, int y)
{
return x + y;
}
int main()
{
int x = 3;
int y = 4;
pf_t pf = Add;
int ret = pf(x, y);
printf("%d\n", ret);
return 0;
}
3.2函数指针数组
3.2.1函数指针数组的定义
函数指针数组:就是存放函数指针的数组
int (*parr[4])(int ,int);
- parr先跟[]结合,那么parr[3]就是数组,这个数组的元素类型就是函数指针类型:int(*)(int ,int)
3.2.2函数指针数组初始化
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int main()
{
int x = 6;
int y = 4;
int(*parr[4])(int, int) = { Add, Sub };
return 0;
}
放在数组里每个元素类型是一样的,所以这个函数指针数组里面的元素类型必须都是int(*)(int , int)