目录
1.📌一些常见指针的用法和意义
常见的指针有以下几种
指针的初阶用法就是认识并能熟练使用像char*,int*等一些用法,
比如说实现一个交换两个整数的函数,你要知道实参和形参的区别,也就是为什么要传指针。
这些就是一些初阶用法。
而指针的意义是什么呢?
其实指针类型的意义就是:决定了每一次访问内存的的大小
我们经常在定义指针的时候是不是都是按照它的类型进行定义的,比如是int类型,就定义一个int*,char类型就定义一个char*,其实你可以想象一下,地址全都是占4或8个字节的空间,哪它为什么要存在指针类型呢?
我把int*类型的指针放在char*行不行?你是否有这样的疑问?*
来我给你举个例子
//下面输出什么
#include<stdio.h>
int main()
{
int a = 0x00001234;
int b = 0x00001234;
int* pa = &a;
*pa = 1;
char* pb = (char*)&b;
*pb = 1;
printf("a=%x,b=%x\n", a, b);
return 0;
}
OK,你再回想一下,地址全都是占4或8个字节的空间,哪它为什么要存在指针类型呢?当你这样想之后,再回想一下上面的例子,就会更近一步想到,哦!原来是每次访问内存的大小!
那可能会有人问我把上面int类型的b的指针放在char*之后,能不能把它也改成1?
接下来给大家提升一点难度:字符char*指针的另一种用法!
上面的字符串是存在p里面吗??
首先我们知道char*类型是指针吧,那指针的大小是4或8个字节吧,所以上面的字符串是肯定放不下的,那可能有人学过或了解过数据截断的讲法,但是这里是不可能截断的,因为在后面已经完完整整的把字符串的内存打印出来了。
我给你举个和上面这个类似的例子
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,7,8,9,10 };
int* pa = arr;
return 0;
}
上面这个代码pa是存放整个数组的元素吗? 不是了吧,大家都知道是存放数组首元素地址。
所以我们上面的字符串是存在p里面吗??
显然不是,其实它也是存放首字符的地址,然后通过首字符的地址找到后面的字符!大家可以跟数组这一块类别一下就明白了!
而且呢!它指向的内容是无法被修改的,(不是因为我在char*p前加了const才不被修改的,是它本身就无法被修改,如果你修改了,程序会崩溃,加const就是为防止自己去修改,这样如果不小心修改了,编译器会报错提醒我们)
这样的字符也称“常量字符串”
下面有一个这样的题目
//下面输出什么?
#include <stdio.h>
int main()
{
char arr1[] = "hello,My friend!";
char arr2[] = "hello,My friend!";
const char* arr3 = "hello,My friend!";
const char* arr4 = "hello,My friend!";
if (arr1 == arr2)
printf("arr1 =arr2\n");
else
printf("arr1 != arr2 \n");
if (arr3 == arr4)
printf("arr3 = arr4 \n");
else
printf("arr3 != arr4 \n");
return 0;
}
为什么会是这样的结果呢,其实呢!数组arr1和arr2它们在开辟空间的时候是分别开不一样的空间,所以它们两的地址是不可能相同的,而arr3和arr4,就是我们上面讲的,它们是不可被修改的,也就是说它们在开辟空间的时候,没必要开两个不一样的空间了,反正它们的内容也不会被修改,那为什么还要浪费空间开新的呢,这时候编译器就很聪明了,它就把两个指针都指向同一块内存空间。因此arr3=arr4! |
2.📌指针数组
我们常用的有整型数组,字符数组等,整型数组就是存放整型的数组,而字符数组就是存放字符的数组,
所以指针数组,就是存放指针的数组!!
那它是如何在代码中定义的呢?
3.📌数组指针
数组指针 主语:是指针!
我们知道int*是指向int型的指针,char*是指向char型的指针
所以数组指针是指向数组的指针!
p1, p2分别是什么?
p1上面我们讲过是指针数组
而p2就是我们的数组指针了!
为什么呢?
那数组指针怎么用呢?它有什么作用呢?
我给大家写一个数组指针错误的使用方式!
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
int(*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
//但是我们一般很少这样写代码 一个数组指针的比较笨的使用方法
int i = 0;
for (i; i < 10; i++)
{
printf("%d ", *((*p) + i) );
}
printf("\n");
//上面的写法我们完全可以用一个整型指针来完成
int* parr = arr;
for (i = 0; i < 10; i++)
{
printf("%d ", *(parr + i));
}
return 0;
}
上面这个列子就是数组指针错误的使用方式,为什么说它错误呢!因为它是的用一个数组指针来实现数组打印,而这个功能完完全全可以用整型指针来实现,可它偏偏用了数组指针,这就将它复杂化,简直就是脱裤子放屁,多此一举!所以我们学数组指针可不是用来实现这样的功能的。
相对正确的使用方式是用于二维数组传参的时候使用的
举个例子
#include <stdio.h>
//以二维数组的形式接受地址
void print_arr1(int arr[3][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");
}
}
//以数组指针的形式接受地址
void print_arr2(int(*arr)[5], int row, int col)
{
int j = 0;
int i = 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,6,7,8,9,10 };
print_arr1(arr, 3, 5);
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
//可以数组指针来接收
print_arr2(arr, 3, 5);
return 0;
}
学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:
int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];
//思考一下上面的代码是表示数组呢,还是数组指针呢?还是其他什么呢?
int arr[5];//一个整型数组,数组有5个元素,每个元素的类型是整型
int *parr1[10];//一个指针数组,数组里有10个元素,每个元素的类型是一个整型指针(int*)
int (*parr2)[10];//数组指针,指向的对象是一个拥有10个元素的数组,每个元素的类型是整型(int)
int (*parr3[10])[5];//一个数组指针数组,数组有10个元素,每个元素的类型是数组指针(int(*)[5])
4.📌函数指针
“函数指针”我想大家首先得知道它是一个指针吧!
而且是一个指向函数的指针
可能有人会问—>什么鬼?函数也有地址吗?
有没有呢我给你测试一下就知道了,看下面一段代码
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
那问题来了,整型数,字符,数组等的地址我们都已经学会如何保存在相对应的指针中了。
那我们的函数的地址要想保存起来,怎么保存?
我有个选项想让大家选出正确的函数指针:
看下面代码
void test()
{
printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();
首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?
其实我想大家应该都能选出来,答案就是第一个pfun1,因为类似上面的数组指针嘛!*符号一般都要括号括起来的,因为它的优先级比较低
那怎么解释这个(void (*pfun1)())呢?
举个例子
//写出下面函数的函数指针
int add(int x, int y)
{
return x + y;
}
接下来给大家阅读两段有趣的代码:
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
知道上面的代码是什么意思吗?这可不是我胡编乱造出来的,上面的代码来自于《C陷阱和缺陷》这本书。
解释一下这两行代码的意思
有没有什么函数指针的使用场景呢?
有,比如c语言自带的标准库中有一个可以排序任何类型的排序算法qsort,它就涉及了函数指针的参数和使用!
而像qsort这样通过调用函数指针的函数就被称为回调函数
感兴趣的可以翻一下我之前的文章有模拟实现过qsort函数,和qsort函数的介绍!
5.📌函数指针数组
为了防止大家学懵了,给大家回忆一下什么是数组!
数组是一个存放相同类型数据的存储空间
int *arr[10];//数组的每个元素是int*
int arr1[5];//数组每个元素是int
char* arr[6];//数组每个元素是char*
那我们已经学习了指针数组,现在学这个函数指针数组我相信是不难理解的!
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组
那函数指针的数组如何定义呢?
还是给几个选项吧!
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
答案是:parr1
parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。*
教一下大家如何正确的写好一个函数指针数组
#include<stdio.h>
int add(int x, int y)
{
return x + y;
}
int sum(int x, int y)
{
return x - y;
}
int main()
{
//先写函数指针
int (*pf)(int, int) = &add;
//再把函数指针copy过来,在变量名后加个[]
int (*pf1[5])(int, int) = { &add,&sum };
printf("pf=%p\npf1[0]=%p\npf1[1]=%p \n", pf, pf1[0], pf1[1]);
return 0;
}