C语言|C语言指针进阶(更加深入地了解指针)

目录
字符指针
指针数组 or 数组指针
指针数组
数组指针
&数组名与数组名的区别
数组传参、指针传参
一维数组传参
二维数组传参
一级指针传参
二级指针传参
函数指针
函数指针数组
回调函数
首先回顾一下指针的基础概念:

1.指针就是个变量,用来存放地址,地址唯一标识一块空间;
2.指针大小是固定的4/8字节(32位平台/64位平台);
3.指针是有类型的,指针的类型决定了指针+-整数的步长,指针解引用操作时的权限;
4.指针的运算
字符指针 在指针的类型中有一种指针类型为 char* ,即为字符指针。
对于字符指针的使用,一般有两种方式。
使用方式一:
#include int main() { char ch = 'w'; char* pc = &ch; printf("*pc = %c<===>pc = %p", *pc, pc); return 0; }

C语言|C语言指针进阶(更加深入地了解指针)
文章图片

从运行结果我们可以看出,char类型指针pc指向ch的地址,*pc即为ch的值。
使用方式二:
#include int main() { char* pstr = "Hello CSDN."; printf("%s\n", pstr); return 0; }

C语言|C语言指针进阶(更加深入地了解指针)
文章图片

我们可以直接用字符指针来存储和表示字符串,但这里的*pstr存储的是字符串“Hello CSDN”的首字符地址,即*pstr保存的是‘H’的地址。
举例说明:
#include int main() { char* str1 = "Hello CSDN."; char* str2 = "Hello CSDN."; char str3[] = "Hello CSDN."; char str4[] = "Hello CSDN."; if (str1 == str2) printf("str1 and str2 are same\n"); else printf("str1 and str2 arenot same\n"); if (str3 == str4) printf("str3 and str4 are same\n"); else printf("str3 and str4 arenot same\n"); return 0; }

str1、str2、str3、str4保存的都是同一个字符串,那么它们是否相等呢?
当以%s输出时,它们得出的结果都是相同的,但当我们运行上述代码时,会得到如下结果:
C语言|C语言指针进阶(更加深入地了解指针)
文章图片

这里的str1和str2指向的同一个字符串常量,C语言/C++会把常量字符串存储到一个单独的区域,当我们定义两个指针指向“Hello CSDN”时,str1和str2实际上是指向同一个内存地址,因此str1和str2是相同的;但是我们定义了str3和str4两个数组去保存“Hello CSDN”,两个数组会开辟不同的内存来保存常量字符串,因此str3和str4不相等。
指针数组 or 数组指针 指针数组
指针数组即存放指针的数组。
int*arr1[10]; //整形指针数组 char *arr2[10]; //一级字符指针的数组 char **arr3[10]; //二级字符指针的数组

数组指针
数组指针是指针,它指的是能指向数组的指针。
例如:
int *p1[10]; int (*p2)[10];

【C语言|C语言指针进阶(更加深入地了解指针)】p1和p2是什么?指针数组?数组指针?
p1:首先p1是一个数组,数组中有10个元素,每个元素都是一个整形指针,因此p1是指针数组。
p2:首先p2是一个指针,它指向的是一个数组,数组中有10个元素,每个元素都是整形,因此p2是数组指针。
判断方法:
[ ]的优先级高于*号,因此如果要定义数组指针,必须加上()来保证p先和*结合。
&数组名与数组名的区别
#include 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; }

以如上代码为例,我们知道arr代表的是数组的首元素的地址,&arr代表的数组的地址,arr与&arr是相等的,那它们的区别到底是什么,来看一下这段代码的运行结果:
C语言|C语言指针进阶(更加深入地了解指针)
文章图片

它们的区别就在于地址+1时的操作:
arr是数组首元素的地址,当arr+1时,arr指向的是数组的第二个元素,以如上代码为例arr+1和arr的差值是4;
而&arr是数组的地址,当&arr+1时,即移动的是整个数组的大小,&arr+1和arr的差值就是40。
数组传参、指针传参 我们在编程过程中经常遇到需要将数组或者指针传给函数,那么该如何进行传参呢?
一维数组传参
#include void test(int arr[10]); void test(int arr[]); void test(int *arr); void test1(int *arr1[10]); void test1(int **arr1); void test1(int *arr1[]); int main() { int arr[10] = { 0 }; int *arr1[10] = { 0 }; test(arr); test1(arr1); return 0; }

如上代码所示,在进行一维数组传参时,我们可以使用以上方式。
二维数组传参
#include void test(int arr[5][10]); void test(int arr[][10]); void test(int (*arr)[10]); void test(int** arr); int main() { int arr[5][10] = { 0 }; test(arr); return 0; }

在二维数组传参时,注意可以省略有多少行,但一定不要省略列,在传参时只能省略第一个[]中的数字。例如:void test(int arr[][10]);
一级指针传参
#include void Print(int* p, int size) { int i = 0; for (i = 0; i < size; i++) { printf("%d ", *(p + i)); } } int main() { int arr[10] = { 0,1,2,3,4,5,6,7,8,9 }; int* p = arr; int size = sizeof(arr) / sizeof(arr[0]); Print(p, size); //一级指针p,传递给函数Print return 0; }

一级指针做形参:首先要清楚形参和实参是两个不同的变量。指针传递的是一个变量或者一个值的地址,它本身还是采用值传递的方式。即你不能使它指向另一块地址,但可以改变它指向的空间里存的值。
二级指针传参
#include 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; }


二级指针做形参:二级指针也是传值,但它指向的地址是一个一维指针,所以可以改变二维地址指向的地址空间里的内容也就是要申请空间的一维指针,不能改变二维地址本身的值,即不能让它指向一个新的一维指针。所以二维指针传递的是一个一维指针。
函数指针 什么是函数指针?指针函数?
函数指针:即指向函数的指针。
指针函数:即返回指针的函数。
如果要保存函数的地址,应该怎么来保存呢?
要存储函数的地址,那必须有一个指针,用指针来指向该函数,保存该函数的地址,这就是函数指针。
函数指针的使用举例:
#include void test() { printf("hehe\n"); } void (*pf)() = &test; int main() { test(); (*pf)(); pf(); return 0; }

C语言|C语言指针进阶(更加深入地了解指针)
文章图片

我们可以利用上述举例方式来使用函数指针,对于上述三条语句进行分析:
1.test():该语句简单使用函数名来调用函数,但它的执行过程实际上是先把函数名test转换为一个函数指针,该指针指定函数在内存中的位置。然后函数调用操作符调用函数,执行开始于这个地址的代码;
2.(*pf)():该语句对pf进行间接访问操作,它把函数指针转换成一个函数名。但这个转换并不是真正需要的,因为编译器在执行函数调用操作符之前又会把它转换回去;
3.pf():第三条语句和前两条效果一样。间接访问操作并非必须,因为编译器需要的是一个函数指针。
函数指针数组 数组是一个存放相同类型数据的存储空间,我们已经了解了指针数组,例如:
int *arr[10]; //数组的每个元素都是int *类型

把函数的地址存放在一个数组中,那这个数组就是函数指针数组,如何定义函数指针数组?例如:
int (*parr[10])();

如上述举例,首先parr和[]结合,说明parr是一个数组,该数组有十个元素,每个元素都是一个指针,指针用来指向函数,所指的函数无参数,但是有一个int类型的返回值。
函数指针的使用:转移表
回调函数
回调函数就是一个通过函数指针调用的函数,如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
回调函数的使用:模拟实现qsort(利用回调函数和冒泡的方式)。

关于指针基础的知识大家可以浏览我的上一篇文章:
一篇文章带你了解C语言重点——“指针”(C指针详解)_Li_yizYa的博客-CSDN博客








    推荐阅读