【C语言指针进阶】一篇搞懂数组指针与指针数组、二级指针、指针函数与函数指针的区别
一、数组指针
数组指针:本质是指针,指向数组整体的指针。
语法:
int (*变量名)[5]; // 定义了一个指针变量,指针变量自己占 8 字节空间,可以指向一个具有 5 个元素的整型数组。 // 定义了一个指向 5 个整形元素的数组的指针。 a:首元素地址 ---> &a[0] ---> int* &a:数组的指针 ---> int (*)[5]; &a+1:数组指针+1 ---> 代表偏移整个数组(sizeof(a)字节)注意:
int a[5] = {1,2,3,4,5}; a:首元素地址 ---> int * 除了 2 种场景: 1.sizeof(a/数组名); a 不能当作 int * 类型,当作数组整体,代表整个数组。 2.&a; 数组指针,该指针类型为 int (*)[5]; 指向一个数组整体。二、指针和二维数组
二维数组的数组名是首元素地址,首元素是一维指针,所以二维数组数组名本质是一维数组指针。
int a[5] = {1,2,3,4,5}; a ---> 首元素地址 ---> &a[0] ---> int * ---> 指针 a[n] ---> *(a+n) int a[2][3] = {1,2,3,4,5,6}; a ---> 首元素地址 ---> &a[0] ---> int (*)[3] ---> 数组指针 a[m][n] ---> *(a[m]+n) ---> ((a+m)+n) ---> *(a+m)[n]二维数组的数组名作为函数参数传递:
int a[2][3] = {1,2,3,4,5,6}; void print_2D_array(int a[][列数], int row) {} void print_2D_array(int (*pa)[列数], int row); {} print_2D_array(a, 2); char str[2][100] = {"hello world", "how are you"}; void print_str_array(char str[][100], int row) {} void print_str_array(char (*pstr)[100], int row) {} print_str_array(str, 2);三、指针数组
指针数组:本质是数组,该数组中保存的是指针。
语法:
int a[5]; int *a[5]; // int *:数组中保存的数据类型,该数组中保存的是 int * 指针。 // [5]:该数组中可以保存 5 个指针(int *)。 // 定义了一个可以保存 5 个整形指针的数组,整体占 40 个字节。 char *p[5];(1)指针数组的定义
指针类型 数组名[元素个数];(2)指针数组的初始化
char *p = NULL; char *p[5] = {NULL}; // 将 5 个字符型指针初始化成 NULL char *p = "hello"; char *p[5] = {"hello", "world", "how", "are", "you"}; // p[0] ---> char * ---> p[0] 指针保存字符串常量 "hello" 的首地址 // p[1] ---> char * ---> p[1] 指针保存字符串常量 "world" 的首地址 // p[2] ---> char * ---> p[2] 指针保存字符串常量 "how" 的首地址 // p[3] ---> char * ---> p[3] 指针保存字符串常量 "are" 的首地址 // p[4] ---> char * ---> p[4] 指针保存字符串常量 "you" 的首地址 char *p1 = "hello"; char *p2 = "world"; char *p3 = "how"; char *p[3] = {p1, p2, p3};(3)指针数组和二维字符型数组的区别
char str[3][100] = {"hello", "world", "how"}; sizeof(str) = 300 字节 char *pstr3[3] = {"hello", "world", "how"}; sizeof(pstr) = 24 字节 ---> sizeof(char *) = 8 字节 区别: 二维字符型数组中保存的是多个字符串本身;指针数组中保存的是多个字符串的首地址。 要保存多个字符串用二维字符型数组;要操作多个字符串用指针数组。四、二级指针
二级指针:本质还是指针,是一个指针变量的地址。
int **指针名称; // 保存整形指针的指针 char **指针名称; // 保存字符型指针的指针C 语言中主要有两个二级指针的应用场景:
(1)想要在被调函数中修改主调函数中的指针变量,实参需要传递该指针变量的地址;形参用二级指针接收;
(2)指针数组的数组名,是一个二级指针,实参传递指针数组的数组名时,形参用二级指针接收。
char *str[3] = {"hello", "world", "how"}; // str[0] ---> char * ---> 保存了字符串常量 "hello" 的地址 // str ---> &str[0] ---> char ** ---> 二级指针值传递:无法在被调中修改主调中的变量,只能使用该变量。
指针传参:可以在被调中修改主调中定义的变量,也可以使用该变量。
值传递:恰好传递的值是一个指针变量,无法在被调用修改主调中定义的指针变量,可以使用该指针变量。
指针传参:可以在被调函数中修改主调函数中定义的指针变量,也可以使用该变量。
五、指针函数
指针函数:本质是函数,具有指针类型返回值的函数。
指针函数能返回什么指针?
(1)局部变量的地址不能被返回(变量,数组);
char str[32] = {"hello world"}; char *str = "hello world";(2)具有静态生命周期的变量的地址可以被返回;
1)static 修饰的静态变量的地址;
2)全局变量的地址。
(3)可以返回字符串常量的地址;
(4)可以返回通过参数传递给该函数的地址;
(5)可以返回堆区申请的且没有被释放的空间地址。
六、函数指针
函数指针:本质是指针,指向函数的指针。
函数:函数的函数名,为函数的入口地址。
(1)定义函数指针
函数声明: int fun(int , int ); int *pfun(int , int ); 函数指针: int (*pfun)(int , int ); // 定义了一个函数指针变量,该指针在内存中占 8 字节, // 该函数指针可以指向一个具有 int 返回值、两个 int 类型参数的函数。 char *(*pfun)(char *); 总结: 返回值类型 (*指针名称)(形参表);(2)函数指针初始化
int (*pfun)(int, int) = NULL;(3)函数指针赋值
int add(int num1, int num2) {} int (*pfun)(int, int) = NULL; pfun = add;(4)函数指针调用函数
函数名(实参表); 函数指针的名称(实参表);(5)函数指针的应用
回调函数
<p>将函数作为某个函数的参数传递,传递的函数称为回调函数;</p> <p>主要为了降低代码的耦合度(关联度),减少重复代码。</p> </li>
