赞
踩
目录
字符指针:指向字符的指针
【使用场景一】
- #include <stdio.h>
-
- int main()
- {
- char c = 'w';
- char* pc = &c;
- *pc = 'x';
- printf("%c\n", *pc);
-
- return 0;
- }
【使用场景二】
- #include <stdio.h>
-
- int main()
- {
- char* p = "abcdef";//这里把首字符a的地址赋给了变量p
- printf("%s\n", p);
-
- return 0;
- }
注意
这里printf中%s的格式逻辑是:从给定的地址处开始,逐个向后输出字符,直到遇见结束标记\0为止。解引用的操作符在printf函数内部造成。
如果由用户解引用,那printf函数将只能拿到单个字符,反而无法实现功能。
易错点1:
场景二中如果修改*p的值,代码就会报错。报错类型如下:
原因:
"abcdef"是个常量字符串。
常量字符串的意思是:这个字符串本身是不能被更改的。
而这个*p没有被限制,它其实是可以去改变后面的字符串的,所以char* p="abcdef"; 报警告是正常的。
防止方法:在char*p 前加const
易错点2:
不能解引用p,解引用打印的是一个字符,一个字符不能用%s打印。
- char* p = "abcdef";
- printf("%s", *p);//error
以下是将字符串放在数组里面:
char arr[] = "abcdef";
而数组的内容是可变的。
- #include <stdio.h>
-
- int main()
- {
- char arr1[] = "hello";
- char arr2[] = "hello";
-
- const char* p1 = "hello";
- const char* p2 = "hello";
-
- if (arr1 == arr2)
- printf("arr1=arr2\n");
- else
- printf("arr1!=arr2\n");
-
- if (p1 == p2)
- printf("p1=p2\n");
- else
- printf("p1!=p2\n");
-
- return 0;
- }

运行结果如下:
原因如下:
arr1!=arr2
- 我们知道:arr1和arr2是数组首元素的地址,这两个数组是两块独立的内存空间,它们只是存储的内容相同,都是hello字符串。
p1=p2
- 这里的p1和p2指向的是同一个常量字符串。c/c++会把常量字符串存储到一个单独的一个内存区域,当几个指针指向同一个字符串的时候,它们实际会指向同一块内存(代码段中)。
- arr1,arr2,p1,p2都是放在栈区,指向的hello(常量字符串)是放在代码段。
整型指针:存放整型变量的地址,能够指向整型数据的指针
浮点型指针:存放浮点型变量的地址,能够指向浮点型数据的指针
那数组指针:存放数组的地址,指向数组的指针
看下面两行代码,p1,p2分别是什么?
- int* p1[10];
- int(*p2)[10];
p1是数组名,该数组里存放了10个元素,每个元素是int*类型
因为p2先和*结合,说明p是一个指针变量,然后指向一个大小为10个整型的数组,所以p是一个指针,指向一个数组,所以叫数组指针。
注意:
- [ ]的优先级要高于*号,所以必须加上()来保证p先和*结合。
- int(*p2)[10];里面的*不是解引用的意思,这颗星就代表是指针,只有前面不加类型的时候才是解引用。
去掉指针变量名就是指针(变量)的类型。
看如下代码:
它们跳过的字节不同就是因为他们的类型不同导致。
注意:
- 一个指针是否是野指针取决于你是否用它。这个指针虽然指向这里,但是没有产生坏的结果。只要不使用它就没关系。
- 虽然它指向的空间不属于“我”,但是它并不危险。
数组指针存放的是数组的地址。
所以初始化的时候要给整个数组的地址。代码如下:
注意:
[ ]里的元素个数不能省略,不然编译器会自动认为是数组元素个数为0
首先我们来理解一下二维数组及其数组名:
在c语言中,只有一维数组(N维数组的元素是数组),数组名作为指针时永远指向第一个元素。
如:
这时候的a[0]又是指向它自己元素的第一个元素,又有 *a[0]=a[0][0]
举个例子: int board[3][4];
board:一维数组的地址。
二维数组的数组名,数组名就是首元素地址。我们知道,可以把一维数组看作二维数组的元素。所以,board就是一维数组的地址。
&board:取出的是整个二维数组的地址。
board[0]:第一行第一个元素的地址。
解引用,相当于拿到第一行数组的数组名,也就是首元素地址,即第一行第一个元素的地址。
board[0]=*board=&board[0][0]
&board[0]:第一行的地址。
board=&board[0]
清楚了上面的概念之后,我们来看下面一段代码:
之前二维数组传参时,形参部分用的是数组接收。
- #include <stdio.h>
-
- void test(int a[3][5], int r, int c)
- {
- int i = 0;
- int j = 0;
- for (i = 0; i < r; i++)
- {
- for (j = 0; j < c; j++)
- {
- printf("%d ", a[i][j]);
- }
- printf("\n");
- }
- }
-
- int main()
- {
- int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };
- test(arr, 3, 5);
- return 0;
- }

发现:实参arr是数组名,通过刚才的分析,直到数组名是首元素地址,首元素地址就是第一行的地址,也就是一维数组的地址,那么它的类型就是数组指针类型。
那么实参就可以写成数组指针的形式,代码如下:
- #include <stdio.h>
-
- void test(int(*p)[5], int r, int c)//数组指针
- {
- int i = 0;
- int j = 0;
- for (i = 0; i < r; i++)
- {
- for (j = 0; j < c; j++)
- {
- printf("%d ", *(*(p + i) + j));//指针解引用
- }
- printf("\n");
- }
- }
-
- int main()
- {
- int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };
- test(arr, 3, 5);
- return 0;
- }

问题:为什么*(p+i) 跳过的是一行数组?
回答:
数组的类型决定了它+1跳过几个字节。
p的类型是:int(*)[5];
p是指向一个整型数组的,数组5个元素 int [5]
p+1 :跳过一个5个int元素的数组
函数指针:存放函数的地址,指向函数的指针
函数是否有地址呢?我们来测试一下:
由测试结果可知:函数存在地址,取&函数名和函数名拿到的都是函数的地址。
我们通过函数指针来存储函数的地址。
- int (*pf) (int x, int y) = &Add;
- int (*pf) (int , int ) = Add;//xy可以省略,只写类型
- int (*pf) (int x, int y)
- | | ------------
- | | |
- | | pf指向函数的参数类型和个数的交代
- | 函数指针变量名
- pf指向函数的返回类型
- int (*) (int x, int y) //pf函数指针变量的类型
通过函数指针调用指针指向的函数。
代码如下:
- #include <stdio.h>
- int Add(int x, int y)
- {
- return x + y;
- }
- int main()
- {
- int(*pf)(int, int) = Add;
-
- printf("%d\n", (*pf)(2, 3));//输出结果为5
-
- printf("%d\n", pf(2, 3));//输出结果为5
-
- return 0;
- }
把函数的地址存到pf里,通过解引用pf找到函数,找到这个函数要调用这个函数,调用函数需要传参,所以();(传参),传2,3。这样的话它会把2和3相加,得到5。
问题一:为什么*pf必须带上括号()呢?
回答:因为假如不带上括号,调用返回5,就会对5进行解引用。
问题二:为什么能写成pf(2, 3)这种形式?
回答:在C语言里,pf前的*其实是个摆设,可以不写,也可以写多个。
这是个技术细节问题,不涉及到语法原则,从不同的思考角度出发,观点会略有不同,但不影响C语言实践,初学者也不必过多纠结。
应用:通过函数指针的方式进行调用
- #include <stdio.h>
-
- int Add(x, y)
- {
- return x + y;
-
- }
- void cale(int(*pf)(int,int))
- {
- int a = 3;
- int b = 5;
- int ret = pf(a, b);
- printf("%d\n", ret);
- }
- int main()
- {
- cale(Add);
- return 0;
- }

这里的cale没有直接调用Add函数,而是通过函数指针的方式进行调用。
函数指针数组:把一个函数的地址存放到一个数组中。
是个函数指针类型的数组。
去掉 函数名+[ ] 就是该数组的类型。
int (*parr[3])( );
解释:parr先和[ ]结合,说明parr是数组,数组的内容是int(*)()类型的函数指针。
当对函数指针数组进行初始化的时候,后面初始化的可以省略掉数组的大小,它会根据后面初始化的内容来确定数组的大小。
例如:
- int(*p[5])(int x, int y) = { 0, add, sub, mul, div };
- int(*p[])(int x, int y) = { 0, add, sub, mul, div };
使用了函数指针数组,避免大篇幅地修改内容;也可实现跳转的功能。
所以函数指针数组也叫:转移表。
计算机的一般实现
- #define _CRT_SECURE_NO_WARNINGS 1
-
- #include <stdio.h>
-
- 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;
-
- do
- {
- printf("————----------------------\n");
- printf("1.add 2.sub \n");
- printf("3.mul 4.div \n");
- printf("0.exit \n");
- printf("————----------------------\n");
- printf("请选择:");
- scanf("%d", &input);
-
- switch (input)
- {
- case 1:
- printf("输入操作数:");
- scanf("%d %d", &x, &y);
- ret = add(x, y);
- printf("ret = %d\n", ret);
- break;
-
- case 2:
- printf("输入操作数:");
- scanf("%d %d", &x, &y);
- ret = sub(x, y);
- printf("ret = %d\n", ret);
- break;
-
- case 3:
- printf("输入操作数:");
- scanf("%d %d", &x, &y);
- ret = mul(x, y);
- printf("ret = %d\n", ret);
- break;
-
- case 4:
- printf("输入操作数:");
- scanf("%d %d", &x, &y);
- ret = div(x, y);
- printf("ret = %d\n", ret);
- break;
-
- case 0:
- printf("退出程序\n");
- break;
-
- default:
- printf("选择错误\n");
- break;
- }
- } while (input);
-
- return 0;
-
- }

使用函数指针数组的实现
- #include <stdio.h>
-
- 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 }; //转移表
- do
- {
- printf("————----------------------\n");
- printf("1.add 2.sub \n");
- printf("3.mul 4.div \n");
- printf("0.exit \n");
- printf("————----------------------\n");
- printf("请选择:");
- scanf("%d", &input);
-
- if ((input <= 4 && input >= 1))
- {
- printf("输入操作数:");
- scanf("%d %d", &x, &y);
- ret = (*p[input])(x, y);
- printf("ret = %d\n", ret);
- }
-
- else if (input == 0)
- {
- printf("退出计算器\n");
- }
-
- else
- {
- printf("输入有误\n");
- }
-
- } while (input);
-
- return 0;
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。