2022-04-02-
摘要
指针的进阶
- 字符指针
- 数组指针
- 指针数组
- 数组传参和指针传参
- 函数指针
- 函数指针数组
- 指向函数指针数组的指针
- 回调函数
- 指针和数组面试题的解析
总结
目录
指针的概念
- 指针是个变量,用来存储地址。
- 指针的大小只与是64位平台还是32位平台有关,与指针类型无关。
- 指针类型决定了指针的解引用权限和读取方式。
- 指针±正数与指针所指向类型数据的长度有关。
字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char*
;
一般使用:
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
还有一种使用方式如下:
int main()
{
const char* pstr = "hello sx.";//这里是把一个字符串放到pstr指针变量里了吗?
printf("%s\n", pstr);
return 0;
}
const char* pstr = "hello sx.";
这段代码的意思实际是将"hello sx"的首地址赋值给pstr.,这样pstr就拥有了访问这个字符串的能力。
但很多人会陷入一个误区:认为"hello sx"字符串被整个放到了pstr中。
像这样" "中放入字符直接写出来的字符串的我们叫做常量字符串,它存储在静态存储区,字符串的内容不能更改,并且只有在整个程序结束后,常量字符串所使用的内存空间才会被系统回收。
所以使用直接收常量字符串的指针时,通常使用const修饰。
实际pstr与常量字符串的关系
看一道例题:
#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char *str3 = "hello bit.";
const char *str4 = "hello bit.";
if(str1 ==str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 ==str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
你可能感到奇怪,字符串明明时相同的,怎么str3、str4就不同了?
实际上:
char arr[] = "hello sx";
//先创建一块空间,然后将字符串依次放入数组中。
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当
几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化
不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
注意:同一个程序中出现的相同常量字符串都是同一个,类似与python将常量字符串存储在内存池中。
指针数组
指针数组即元素类型为指针的数组。
我们可以使用指针数组模拟一个二维数组。
int arr1[3] = {
1,2,3};
int arr2[3] = {
2,3,4};
int arr3[3] = {
3,4,5};
int* arr[3] = {
arr1, arr2, arr3};
如果想访问arr[i]中的第下标为j的元素,可以使用 *(*(arr + i) + j).
同样可以使用数组下标的方式 arr[i] [j];
但这只是模拟二维数组,并不是真正的二维数组。
二维数组的空间是连续的,可是在这里一定是连续的吗?
二维数组的每一行的大小是相同的,这里一定相同吗?
答案是否定的,这里的arr1 、 arr2 、 arr3的地址空间是随机的,长度也并没有固定。
数组指针
数组指针:意为数组的指针。
那么当然这是一个指针,它是指向数组的。
内置类型的指针:
例如 int* p, double* p
,来剖析它的结构。
p被 * 修饰代表p是指针,那么指向什么类型?==》int。
下面代码哪个是数组指针?
int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?
先来看看最普通的类型。
int arr1[10];
//arr1与[10]先结合,代表arr1是个数组,那么剩下的就是数组元素的类型。
int *arr;
//arr与*结合,代表arr是个指针,那么剩下的就是指针指向的类型。
p1先与[10]结合,表示p是个数组。
将p1[10]去掉剩下的就是数组的元素类型。
显然,p1这是一个指针数组,数组的每个元素是指向int
的指针。
p2先与*结合,代表p2是个指针。
再与[10]结合,代表指向的是十个元素的数组,那么剩下的就是数组的元素类型了。
p2是一个指针,指向有十个元素,每个元素是int
的数组。
int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个
指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
对数组名取地址
int arr[10];
arr
和&arr
有什么区别呢?
arr是数组名,首元素地址。
那&arr
是个什么呢?
看代码:
int main()
{
int arr[10] = {
0};
printf("%p\n", arr);
printf("%p\n", &arr);
return 0;
}
两者的值是一样的,难道就一样吗?
继续看:
int main()
{
int arr[10] = {
0 };
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr+1= %p\n", &arr+1);
return 0;
}
都执行 + 1操作后就不同了,那么说明它们指向的类型长度是有差异的。
可以看到 &arr
和 &arr + 1
相差了40个字节,是arr
这个数组的长度。
说明&arr 指向的应该是一整个数组,因此一次跳过一个数组的长度,那么也就合理了。
数组指针的使用
那数组指针是怎么使用的呢?
既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。
上代码:
#include <stdio.h>
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9,0};
int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
return 0;
}
这样的用法多少显得我们有点不太聪明的样子,这根本无法体现数组指针的价值。
如下使用可以完美契合二维数组:
#include <stdio.h>
void print_arr1(int arr