文章目录
- 前言
- 1.字符指针
- 2.指针数组
- 3.数组指针
- 3.1数组指针的针进定义
- 3.2数组名vs&数组名
- 3.3数组指针的使用
- 4.数组参数、指针参数
- 4.1一维数组传参
- 4.2二维数组传参
- 4.3一级指针传参
- 4.4二级指针传参
- 5.函数指针
- 6.函数指针数组
- 7.指向函数数组的阶上指针
前言
指针是c语言的精华,我们之前已经学习了c语言的针进初阶指针,对初阶指针有了一个大概的阶上了解:
1.指针就是一个变量,是针进用来存放地址的,一个地址唯一标识一块内存空间,阶上可以通过这个地址来访问这个对应内存空间的针进数据
2.指针的大小是固定的4/8个字节(32位平台下是4个字节,64位平台下是阶上8个字节。同时我们平时通常看到的针进是x86,其实就是阶上32位平台,只是针进以前的叫法,被沿用至今。阶上x64就是针进64位平台,可千万不能以为x86就是阶上86位平台)
3.指针的类型决定了指针+1/-1时指针往前跳几个字节,即指针的针进步长,以及指针解引用时的权限
4.指针的运算
接下来就是我对指针进阶的学习。这里对指针的研究的广度和深度将会更进一步。
1.字符指针
字符指针的一般使用方法是
#includeint main(){ char ch = 'a'; char* pc = &ch; return 0;}
而还有一种使用方式是:
int main(){ const char* pch = "hello world"; printf("%s", ch); return 0;}
这时可能第一次见到的同学会比较诧异,为什么可以把字符串赋给一个字符指针呢?
其实这样使用在语法上是支持的,它的意思其实是把一个常量字符串的首字符’h’的地址存放到指针变量pch里面。 它的打印结果是就是:
这里的是一个常量字符串,那么一般在声明这样的一个字符串指针时,我们要在指针变量前面加上一个const修饰,表示指向的是一个常量。
从这个知识点可以往后衍伸。
C会把常量字符存储到一个单独的内存空间,当几个指针。指向同一个字符串的时候,他们实际上会指向同一块内存空间。但是相同的字符串去初始化不同的数组时,就会开辟出不同的内存空间。这句话理解可能会比较抽象,我们通过一段代码来解释一下:
#include 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; }
这里的输出结果是
这其中涉及的内存关系就是我上面描述的那一段文字。这样回头再去看那一段文字就很好理解了。
2.指针数组
指针数组我们之前初阶的时候就已经介绍过了,这里再重新提一嘴。 指针数组其实就是一个数组。一个什么数组呢?一个存放指针的数组。
它的声明方式:
int* arr1[10]; //整形指针的数组char *arr2[4]; //一级字符指针的数组char **arr3[5];//二级字符指针的数组
3.数组指针
3.1数组指针的定义
数组指针是数组还是指针?我们从名字就可以看出来。
草莓味饼干是草莓还是饼干?答案是饼干。
那么数组指针就是指针。一个什么指针?一个指向数组的指针
声明方式:
我们先看一段代码:
int *p1[10];int (*p2)[10];//p1, p2分别是什么?
p1是指针数组。p2是数组指针。 为什么呢?
解释:*的优先级比[]更高,我们看p1,p1先和[]结合,那么它是一个数组。再与’*‘结合,说明它是一个存放指针变量的数组。
p2先和*结合,说明p2是一个指针,再与[]结合,说明它是一个指向数组的指针变量。
3.2数组名vs&数组名
我们来看一段代码:
#include int main(){ int arr[10] = { 0 }; printf("%p\n", arr); printf("%p\n", &arr); return 0;}
运行结果如下:
打印结果一样,那么我们是不是就可以认为数组名等于&数组名了呢?
其实不是的。
我们再看一段代码:
#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的地址是一样的,但是它们+1的结果截然不同,arr+1跳过了1个整型,而&arr+1跳过了10个整型,即一个数组的长度。
实际上。arr是首元素的地址,而&arr是整个数组的地址。那么这个地址的类型是什么?怎么接收呢?本例中,&arr的类型是:int(*)[10],是一种数组指针类型。
数组的地址+1,跳过了整个数组的大小,所以&arr+1相对于&arr的差值是40.
3.3数组指针的使用
当我们想传入一个数组到函数里去的时候,这时候数组指针就起效果了。
看以下代码:
#include
void print_arr1(int arr[3][5], int row, int col) {
printf("%d ", arr[i][j]);
}
学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:
void print_arr2(int (*arr)[5], int row, int col) { int i = 0; for(i=0; i|
我们可以看到,当我们想传入一个二维数组的时候,我们知道数组名是首元素地址,而二维数组的首元素却是一个数组,这样,我们函数在接收数组指针时就需要数组指针类型来接收。这就是数组指针的作用。
4.数组参数、指针参数
我们经常遇到这样一个问题,当我们想把数组或者指针传给函数时,函数的参数我们应该如何设计呢?
4.1一维数组传参
当我们要传入一维数组时,我们的函数的参数应该怎么设置呢?这里我已经进行了一个汇总
#include void test(int arr[])//ok{}void test(int arr[10])//ok{}void test(int* arr)//ok{}void test2(int* arr[20])//ok{}void test2(int** arr)//ok{}int main(){ int arr[10] = { 0 }; int* arr2[20] = { 0 }; test(arr); test2(arr2);}
4.2二维数组传参
void test(int arr[3][5])//ok{}//void test(int arr[][])//no//{}void test(int arr[][5])//ok{}//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。//这样才方便运算。void test(int* arr)//no{}void test(int* arr[5])//no{}void test(int(*arr)[5])//ok{}void test(int** arr)//no{}int main(){ int arr[3][5] = { 0 }; test(arr);}
同样的,我也用代码对它进行了归纳总结。一定要搞清楚它当中的逻辑。为什么可以,为什么不可以,从它传入数据的类型的本质出发。
4.3一级指针传参
例子:
#include void print(int *p, int sz) { int i = 0; for(i=0; i
4.4二级指针传参
#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; }
5.函数指针
老惯例,我们先来看代码:
#include void test(){ printf("hehe\n");}int main(){ printf("%p\n", test); printf("%p\n", &test); return 0; }
这段代码的打印结果是一致的,函数不需要区分这个。因为区分了也没有什么意义。重点我们放在后面
函数指针的变量初始化:
void (*pfun1)();
pfun1先与*结合,表示pfun1是一个指针,一个什么指针呢?一个指向返回值为void,没有参数的函数。
6.函数指针数组
函数指针数组是什么?是数组,什么数组?存放函数指针的数组,而函数指针前面已经介绍了。
函数指针数组的定义:
int (*parr1[10])();
int parr210;
int ()() parr3[10];
这个其实是不需要去记忆的,只需要按照以下思路:
首先找到你的变量名:以parr1为例,看它左右,左边有一个*,右边有一个括号,我们知道括号的优先级比*高,所以我们可以先知道它是一个数组。是一个什么数组,它左边有一个*号,那么我们知道它是一个指针数组。同时后面跟着括号,以及前面有一个int,我们知道它是一个存放没有参数的返回类型是int的函数的指针的数组。
它的作用:转移表。
仍然通过一段代码来理解:
#include int add(int a, int b) { return a + b; }int sub(int a, int b) { return a - b;}int mul(int a, int b) { return a*b; }int div(int a, int b){ return a / b; }int main(){ int x, y; int input = 1; int ret = 0; int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表 while (input) { printf( " 作者:焦点