接下来我会为大家带来指针的超详细讲解。涵盖了指针的所有相关知识,内容比较多大家慢慢看。
我们先为大家详解一下指针这个概念
指针 指针==地址
1.指针变量就是个变量,用来存放地址,地址的唯一标识就是一块内存空间。
2.指针的大小是固定的4/8个字节(32位平台、64为平台)。
3.指针是有类型的,指针的类型决定了指针的+-正数的步长,指针解引用操作时的权限。
4.指针是可以进行运算的。
我们今天开始一点一点的学习与认识。
1.字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char*;
代码演示
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
现在我们有新的写法
int main()
{
char ch ='w';
char*pc=&ch;
char*p="abcdef";
还有另外两种写法"abcdef"可以替换为下面的写法
//[abcdef\0]
//char arr[]="abcdef";
return 0;
}
我们拿出 char *p= "abcdef"; 来分析。我们可以写出一个常量字符串"abcdef",把字符串的首元素‘a’的地址赋给p. 关键点拨:当我们想要打印字符串时我们可以直接使用printf函数.
printf("%s\n",p);
因为p中放的就是字符串的起始位置。
有了上述知识我们来看一道面试题
程序的运行结果究竟是什么呢?
可是这是为什么呢?接下来我为大家详细讲解一下。
就是str1和str2,各自开辟了一段空间存放"hello bit."
那么我们就很好理解了,两个不同的空间的首元素地址当然不想同啊。
然而在第二个前面加了Const是不可修改的,所以str3,与str4,指的是同一个地址
2.指针数组
指针数组是数组
例如
字符数组 — 存放字符的数组
整形数组 — 存放整形的数组
指针数组 — 存放指针的数组,存放在数组中的元素都是指正类型的。
我们简单来看一下作用(使用指针数组来模拟一个二维数组)
int main()
{
//定义三个一维数组
int arr1[]={ 1,2,3,4,5 };
int arr2[]={ 2,3,4,5,6 };
int arr3[]={ 3,4,5,6,7 };
//如何这三个一维数组变成二维数组的一行呢?
//由于上述三个数组都是int类型的所以arr也应为int型的
int* arr[]={ arr1, arr2, arr3 };
return 0;
}
我们画图不难得出一种指向关系
那么这样arr1作为一个整体存放入arr中那么其首元素1,就是在arr中下标为0。
则有
那么该如何实现数组的访问呢?
我们可以思考是否可以通过下标来访问,我们使用循环先访问数组,再访问数组的内容。
接下来为大家展示。
int i=0;
int j=0;
for(i=0;i<3;i++)
{
for(j=0;j<5;j++)
{
arr[i][j];
}
}
3.数组指针
在讲解之前先来温习一下数组名
数组名是数组首元素地址
存在两个列外
1.sizeof(数组名),这里的数组名表示的是整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节。
2.&数组名,这里的数组名表示整个数组,取出的是数组的地址。
指针数组---是数组,是存放指针的数组
数组指针--是指针
字符指针--指向字符的指针
整型指针--指向整型的指针
浮点型的指针--指向浮点型的指针
数组指针--指向数组的指针
接下来进入正题
int main()
{
//int*p
int arr[10]={0};
int(*p)[10]=&arr;
//p是用来存放数组地址的,p是数组指针
return 0;
}
我们来思考一个问题,数组指针到底有什么用呢?
1.用来访问数组
int main()
{
int arr[10]={ 1,2,3,4,5,6,7,8,9,10 };
int *p=arr;
int i=0;
for(i=0;i<10;i++)
{
printf("%d ", p[i]);
}
return ;
}
2.二维数组的使用
当我们想打印出一个二维数组时,我们自定义一个函数print来实现
形参也是二维数组的形式
void print(int arr[3][5],int r,int c)
{
int i=0;
for(i=0;i<3;i++)
{
int j=0;
for(j=0;j<5;j++)
{
printf("%d", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][6]={ {1,2,3,4,5} , {2,3,4,5,6} , {3,4,5,6,7} }
print(arr,3,5); 二维数组的传参
return 0;
}
以上是我们最简单二维数组的访问,那么我们该如何使用指针来操作呢?
关键点拨:二维数组的数组名是首元素的地址,首元素的地址是第一行的数组。
在二维数组中的首元素是一维数组如上图所示。
在第二个代码中我们可以观察到 int(*p)[5],我来为大家解释一下,其含义是什么意思?
我们可以理解为数组名传参传的是首元素地址(上面讲到二维数组的首元素是一维数组,{1,2,3,4,5,}),那么我们就可以使用一个指针(*p)来指向这个一维数组(这个一维数组有五个元素)。其中p[i][j]是什么意思? 其实很简单就是 *(p+i)的意思。我们先解引用拿到数组名然后进行访问。
对于上述大家可以会有疑问现在我为大家来解释清楚。
int arr【5】; arr是一个可以存放5个整形数据的数组
int *parr1【10】; parr1 是一个数组,数组有10个元素,每个元素的类型都是int*
int (*parr2)【10】; parr2是一个指针,该指针是指向数组的,指向的数组有10个元素,每个元素的类型是 int.
int (*parr3【10】)【5】;parr3是一个数组是存放数组指针的数组,存放的这个数组指针指向的数组有5个元素,每个元素是int类型。
对于*parr3我们来画图进行理解
存放的指针数组有是个元素,每个元素都指向一个数组。
4.数组参数,指针参数
在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
4.1 一维数组的传参
先告诉大家关键知识点:数组的传参的本质是,传递了数组首元素的地址。数组传参的形式也可以是指针形式!
在这里我会通过列子为大家展示
假设我们有个数组arr,现在我想把它传给test函数。该如何设计呢?
我设置的是判断,上面的第 2,4,6,8,10,行后面有个OK,大家来判断一下是否可以成功实现。
2.数组传参,形参的部分也是数组,所以2是符合的,因为传参的本质不是数组而是首元素地址。其大小可以省略就是int arr【】可以为空的。
4.由2中解释,【】中的大小可以写,也可以不写不影响其传参。4也是符合的
6.数组名首元素地址是int ,在6中接收的类型也是int类型。故6也符合
8 .实参是指针数组,形参也是指针数组。也符合
10.由于 int *arr2【20】={0};可知,arr2中每个元素都是int* 类型的由于传的是首元素地址那么传的就是 int* 所以把一级指针取出来放入 int **arr(二级指针中)是没有问题的。故10也符合
那么我们继续
4.2二维数组的传参
下述的 1, 2,3,4,5,6,7.指的是OK 1.代表的是第一个OK所在的语句。
1.由于都是3行5列的数组,传参没有任何问题。
2. arr[ ][ ]是行不通的,因为二维数组的行可以省略,但是列不能省略。
3.由2可知符合
4.int *arr是整形指针只能接收整形变量但test(arr)不是整形故不符合。
5.传的是二维数组,接收也应是二维数组,故5也行不通。
6.是正确的 上文中的就是使用这个方法
7.二级指针是错误的,二级指针是接收一级指针的地址的。
有了上述知识我们来学习,指针的传参。
4.3一级指针的传参
很简单我们在前面已经学习过了,就是使用指针来接收
重要的是我们要思考: 当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
比如 一个函数
void text (int *p)
{
}
那么有哪些可以传给它参数呢? 其实只要我们传的参数它可以 接收就行。
整形一级指针函数接收整形的地址
那么我们就可以写出很多例如:
int a=10;
int *ptr=&a;
int arr[5];
text(arr); // 传整形一维数组名
text(&a); // 传整形变量的地址
text(ptr): // 传整形指针
4.4二级指针的传参
二级指针传参使用二级指针来接收。
5.函数指针
我们先来类比一下
数组指针 -- 指向数组的指针 -- 存放的数组的地址 -- &数组名就是数组的地址。
函数指针 -- 指向函数的指针 -- 存放的是函数的地址 -- 怎么得到函数的地址呢? &函数名呢?
我们来验证一下
int Add(int x,int y)
{
return x+y ;
}
int main()
{
printf("%P\n", &Add); //我们试着得到Add的地址
return 0;
}
程序运行结果如下:
我们确实得到了地址可得结论:&函数名可以得到函数的地址。
此时又有一个疑问:数组名数组首元素的地址。那函数名呢?
我们执行程序可以得到
通过上述我们可以总结一下:&函数名就是函数的地址
函数名也是函数的地址
指针内容非常多,这只是其中一小部分,后续我会为大家发布(2)(3)(4)
请大家及时关注以免错过
谢谢你的阅读。