大家好!又要讲解指针的内容了,这次我会对指针的内容进行一次拔高。如果基础不好的同学,希望能看一下我的指针(基础篇),这样会更好的理解。内容很多我会分为几块为大家解析。好了,话不多说,让我们开始吧!
文章目录
1. 字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char* 。
一般使用:
int main()
{
char ch = 'w';
char *pc = &ch;
return 0;
}
内存布局图:
还有一种使用方式如下:
char* pstr = "abcdef";
这里是把一个字符串放到pstr指针变量里了吗?不是,是字符串首元素地址。
这里补充一点知识:我们知道这里abcdef是常量字符串,不能修改的。所有我们写这样代码的时候,最好加入const,确保代码的安全性。
const char* pstr = "abcdef";
2. 指针数组
在指针(基础篇)我们也学了指针数组,指针数组是一个存放指针的数组。
这里我们稍微复习一下,下面指针数组是什么意思?
int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
我们举个例子吧:
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[] = { arr1,arr2,arr3 };
return 0;
}
我们来看一下这个代码的内存布局:
在数组arr里面存放的是arr1,arr2,arr3,各各首元素的地址。
我们将这些数组的内容打印一下:
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[] = { arr1,arr2,arr3 };
int i = 0;
//控制arr数组的下标
for (i = 0; i < 3; i++)
{
int j = 0;
//控制每一个小数组里的下标
for (j = 0; j < 5; j++)
{
printf("%d ", *(*(arr + i) + j));//arr[i][j]
}
printf("%\n");
}
return 0;
}
arr+i的意思是当i为0,1,2时,控制arr数组的下标,找到arr1,arr2,arr3各各首元素的地址,然后解引用找到arr1,arr2,arr3各各空间。然后+j的意思就是找到各各小数组里的元素的地址,解引用打印就行了。它也可以看作为一个二维数组。
3. 数组指针
3.1 数组指针的定义
数组指针是指针?还是数组?答案是:指针。
我们已经熟悉:
整形指针:int * pint; 能够指向整形数据的指针。
浮点型指针:float * pf; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。
下面代码哪个是数组指针?
int *p1[10];
int (*p2)[10];
p1, p2分别是什么?
这里要注意:[]的优先级要高于*号的。
第一个p1先和[]结合,说明是一个数组,有10个元素,每个元素是int *
所以,是指针数组。
第二个,p2和*先结合,说明是指针类型,指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
3.2 &数组名VS数组名
对于下面的数组:
int arr[10];
arr 和 &arr 分别是啥?
我们知道arr是数组名,数组名表示数组首元素的地址。
那&arr数组名到底是什么?
我们看一段代码:
运行结果如下:
根据上面的结果我们发现,其实&arr和arr,&arr[0],虽然值是一样的,但是意义是不一样的。
&arr[0]+1和arr+1跳过4个字节,而&arr+1跳过的是28(16进制)转换为10进制为40,所以跳过了40个字节。
实际上: &arr 表示的是数组的地址,而不是数组首元素的地址。
本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40。
3.3 数组指针的使用
那么一个数组指针该怎么使用呢:
我们知道数组名传参传的是数组首元素的地址。那么二维数组数组名传参也是数组首元素的地址。但二维数组的首元素是第一行。类似一个一维数组。
所以,我们可以用一个数组指针来接收。
我们用指针来打印一下这个二维数组:
这里,p是第一行的地址,p+i是第i行的地址。
*(p+i)相当于拿到了二维数组的第i行,也相当于第i行的数组名,数组名表示首元素的地址,其实也是第i行第一个元素的地址。
4. 数组参数、指针参数
在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
4.1 一维数组传参
我们看一下下面的代码:
上面几种的写法都是可以的。一维数组传参时,写成数组的形式是可以的,后面[]里的大小可写可不写,因为本质上传的是首元素的地址,所以,我们也可以用指针来接收。
4.2 二维数组传参
我们看一下下面的代码:
我们先看前三个用数组接收的,可以看到第二个是不行的。
因为二维数组传参,函数形参的设计只能省略第一个[]的数字。因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。这样才方便运算。
我们再来看用指针接收:
我们知道数组名是首元素地址,二维数组的首元素是第一行,所以传递的是第一行的地址,所以我们应该用一个数组指针来接收。
所以第4个和第5个都是指针数组,所以是不行的。
第6个是一个数组指针,指向一个数组有5个元素,每个元素是int型。所以是可以的。
第7个是不行的。
4.3 一级指针传参
一级指针传参比较简单,我们举个例子:
void print(int *p, int sz) {
int i = 0;
for(i=0; i<sz; i++)
{
printf("%d\n", *(p+i));
}
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9};
int *p = arr;
int sz = sizeof(arr)/sizeof(arr[0]);
//一级指针p,传给函数
print(p, sz);
return 0;
}
p里面放的是arr的地址,是首元素的地址,所以我们也用一个一级指针接收。
4.4 二级指针传参
二级指针传参也不难,代码如下:
void test(int** ptr) {
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
test(pp);
test(&p);
return 0;
}
总结:
第一部分的讲解先到这里,这部分有难度,希望大家能慢慢消化。我们一定要清楚指针数组和数组指针的含义,区分它们。之后的内容我会尽我所能给大家讲清楚。如果大家认为我有哪些不足之处或者知识上的错误都可以告诉我,我会在之后的文章中不断改正,也请大家多多包涵。如果大家觉得这篇文章有用的话,也希望大家可以给我关注点赞,你们的支持就是对我最大的鼓励,我们下一篇文章再见。