前言
在步入正文前,我们先回顾一下指针的概念:
1.指针是个变量,用于存放地址,地址唯一标识一块内存空间。
2.指针大小为两种,固定的4/8个字节(32位平台/64位平台)
3.指针是有类型的,指针的类型决定了指针的±整数的步长,解引用操作的时候的权限
4.指针的运算
回顾结束,进入更深层次的指针
一、字符指针
先来介绍一个简单,字符指针-----char*,
常规使用:
int main() { char str='w'; char* p=&str; //在p存放的是str的地址 *p='w'; //利用地址直接修改str的值; return 0; }
另一种使用方法:
int main() { const char* p="abcdef"; printf("%s",p); //打印出abcdef; return 0;
虽然可以打印出abcdef,但是不代表字符指针存放的是字符串abcdef;指针只能存放地址,而这里的本质是将字符串“abcdef”的首字符的地址存放在了p中。(注:const和指针的联系)
二、指针数组
int arr[10]; 存放整形的数组----整形数组
char arr[10]; 存放字符的数组-----字符数组
int* arr[10]; 存放指针的数组-----指针数组
指针数组存放的是指针;如:int* p[3]={&a,&b,&c};存在三个指针分别指向a,b,c的地址。
三、数组指针
1.数组指针的定义
数组指针是一个指向数组的指针,容易与指针数组弄混,区分小技巧(看最后2个字,如果是数组,则是数组,如果是指针则是指针)
int* p[10]; //指针数组 int(*p1)[10]; //数组指针
注:为什么要括号呢?因为[]的优先级高于* ,所以会先和[]结合,变成数组,存放指针的数组;而如果有了括号,则是指针指向了一个大小为10个整形的数组
2.数组名和&数组名的区别
众所周知:数组名表示数组的首元素地址,那么&数组是什么呢?和数组名有什么关系?我们现在举行一个例子。
注:sizeof(数组名)表示整个数组
int main() { int arr[10]={0}; printf("%p",arr); //打印地址 printf("%p",&arr); //打印地址 return 0; }
废话不多说,先看运行结果:
事实证明:数组名和&数组名的地址是一样的,而且&是取地址符号,那么两个真的没区别嘛?不不不,让我们再看一段代码:
int main() { int arr[10]={0}; printf("arr=%p\n\n",arr); //打印地址 printf("&arr=%p\n\n",&arr); //打印地址 printf("arr+1=%p\n\n",arr+1); //打印地址 printf("&arr+1=%p\n",&arr+1); return 0; }
我们可以发现,虽然arr和&arr的地址相同,但是他们加1之后地址却不相同,所以他们的意义应该不一样。
分析:int代表4个字节,arr+1和arr相差4,但是&arr和&arr+1却相差40(数组有10个整形元素,每一个都是4个字节)。
解释:&arr代表的是数组的地址,而不是首元素的地址,所以当&arr+1则是跳过整个数组的大小,地址相差了40。
注:其实&arr的类型是:int(*)[10],是一种数组指针类型数组的地址。例如:int(*p)[3]={&arr,&arr1,&arr2}; 其中的arr1、arr、arr2都是数组。
3.数组指针的使用
数组指针是指针指向了数组,那么存放的是数组的地址。
代码使用:
int main() { int arr[5]={1,2,3,4,5}; int(*p)[10]=&arr; return 0; }
但是我们一般很少使用这种模式,一般数组指针都用于函数传参,如下:
void print_arr2(int(*arr)[5], int row, int col) { int i = 0; for (i = 0; i < row; i++) { int j = 0; 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; }
数组指针一般用于二维数组的传参,而数组指针的数组大小取决于二维数组的列。
四、数组、指针参数
说到底,我们会发现指针和数组环环相扣,尤其涉及到函数传参时,但是面对不同的传参,函数应该如何接收呢?现在我们来详细讲解讲解。
1.一维数组传参
#include <stdio.h> void test(int arr[]) {} //可以 void test(int arr[10]) {} //一和二是一样的道理,数组传参,数组接收,只不过没有规定数组大小 void test(int* arr) {} //可以,传得地址,指针接收 void test2(int* arr[20]) {} //可以,数组传参,数组接收 void test2(int** arr)/ {} //传过来的是一个数组,元素类型是int* //二级指针就是用来存放一级指针的地址,当然也是可以的 int main() { int arr[10] = { 0 }; int* arr2[20] = { 0 }; test(arr); test2(arr2); return 0; }
数组传参中最简单的就是数组传参,数组接收。一维数组传参时,接收也可以是一级指针,但不能是二级指针,因为二级指针存放的是一级指针的地址。
2.二维数组传参
void test(int arr[3][5]) {} // 可以,数组传参,数组接受 void test(int arr[][]) {} //不可以,二维数组传参行可以省略,但列不能省略 void test(int arr[][5]) {} //可以 void test(int* arr) {} //二维数组传参传递的是第一行的元素,当然不能指针接收 void test(int* arr[5]) {} //同理类型也是不匹配的 void test(int(*arr)[5]) {} //可以的,数组指针就是用来存放一个数组的地址,就是第一行的地址 void test(int** arr) {} //不可以,传的是整个数组的地址,但是接收的是一级指针的地址,类型不匹配 int main() { int arr[3][5] = { 0 }; test(arr); return 0; }
二维数组传参时,数组接收时,行可以省略,列不能。
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; }
传参小结
当函数参数是一级指针,可接收:同类型的地址、同类型的指针、同类型的数组名
void test1(int* p) {} //test1函数能接收什么参数? //int的类型的地址 //int类型的指针 //int 类型的数组名 void test2(char* p) {} //test2函数能接收什么参数?
当函数参数是二级指针,可接收:一级指针的地址、同类型的数组名、同类型的二级指针
void test(char** p) { } //一级指针的地址 //char*类型的数组名 //二级指针 int main() { char c = 'b'; char* pc = &c; char** ppc = &pc; char* arr[10]; test(&pc); test(ppc); return 0; }
结束语
由于指针太深奥了,因此分为2部分写,下部是函数和指针的联系,不需要翻来覆去的,方便理解,若有不懂可在评论区留言,也可以私信我。
-----------小白tq