- 博客主页:向不悔
- 本篇专栏:[C]
- 您的支持,是我的创作动力。
文章目录
- 0、总结
- 1、字符指针变量
- 2、数组指针变量
- 2.1 数组指针变量是什么?
- 2.2 数组指针变量怎么初始化?
- 3、二维数组传参的本质
- 4、函数指针变量
- 4.1 函数指针变量的创建
- 4.2 函数指针变量的使用
- 4.3 两端有趣的代码
- 4.3.1 typedef关键字
- 5、函数指针数组
- 6、转移表
0、总结
1、字符指针变量
思考字符数组和常量字符串的区别?经思考,发现,字符数组里的数组是可变的,常量字符串是不可变的。
#include <stdio.h>
int main()
{char arr[] = "abcdef";char* p1 = arr;*p1 = 'w';const char* p2 = "abcdef";// *p2 = 'w';printf("%s\n", p1);printf("%s\n", p2);return 0;
}
运行:
wbcdef
abcdef
如图加深理解:
我们知道,字符指针变量一般使用如下:
#include <stdio.h>
int main()
{char ch = 'w';char* pc = &ch;*pc = 'z';printf("%c\n", *pc);return 0;
}
那么,为什么这个也可以呢?
#include <stdio.h>
int main()
{const char* pstr = "hello world.";printf("%s\n", pstr);return 0;
}
经思考,代码const char* pstr = "hello world.";
本质上是把字符串首字符的地址放到了pstr
中。如图:
《剑指Offer》中收录了一道和字符串相关的题,如下:
#include <stdio.h>
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");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}
运行:
str1 and str2 are not same
str3 and str4 are same
在C/C++中,常量字符串会被存储到一个共享的内存区域。当多个指针指向相同的常量字符串时,它们实际上指向同一块内存。而如果用相同的常量字符串初始化不同的数组,则会为每个数组开辟独立的内存块。因此,str1
和str2
指向不同的内存,而str3
和str4
指向同一块内存。
2、数组指针变量
2.1 数组指针变量是什么?
数组指针变量是指针变量,存放的是数组的地址,指向数组的指针变量。
思考以下,哪个是数组指针变量?
int *p1[10];
int (*p2)[10];
结论:
p1
是指针数组,具体的说,一个包含10个指针的数组,每个指针指向一个int
。p2
是数组指针,具体地说,指向一个包含10个int的数组。- 主要区别在于括号的使用,括号的位置决定了声明的优先级。
p1
是数组优先,p2
是指针优先。 - 值得注意:
[]
的优先级要高于*
号的,所以声明数组指针,就必须加上()
来保证p2
先和*
结合。
2.2 数组指针变量怎么初始化?
数组指针变量是用来存放数组地址的,因此用&数组名
。如下:
#include <stdio.h>
int main()
{char arr[10] = { 0 };char* p1 = arr;printf("%p\n", p1);printf("%p\n", p1+1);// 初始化数组指针变量char (*p2)[10] = &arr;printf("%p\n", p2);printf("%p\n", p2+1);return 0;
}
运行(32位平台):
00B5F7A8
00B5F7A9
00B5F7A8
00B5F7B2
经调试,可知,&arr
和p2
的类型是完全一致的。
数组指针类型解析:
int (*p) [10] = &arr;| | || | || | p指向数组的元素个数| p是数组指针变量名 p指向的数组的元素类型
3、二维数组传参的本质
二维数组的数组名表示的就是第一行的地址,是一维数组的地址。所以第一行的地址的类型是数组指针类型。二维数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址。形参可以写成指针形式的,如下:
#include <stdio.h>
void print(int (*ptr)[5], int r, int c)
{for (int i = 0; i < r; i++){for (int j = 0; j < c; j++){printf("%d ", *(*(ptr + 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} };print(arr, 3, 5);return 0;
}
运行:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
总结:
二维数组传参,形参部分可以写成数组,也可以写成指针形式。
4、函数指针变量
4.1 函数指针变量的创建
函数指针变量用来存放函数地址的,未来通过地址能够调用函数。如下:
#include <stdio.h>
void test()
{printf("hello\n");
}
int main()
{printf("test: %p\n", test);printf("&test: %p\n", &test);return 0;
}
运行:
test: 008513CA
&test: 008513CA
可知,函数是有地址的,有两个方式:
- 函数名就是函数的地址
&函数名
获得函数的地址。
如果要把函数的地址存放起来,就需要创建函数指针变量,函数指针变量的写法跟数组指针写法非常类似,如下:
#include <stdio.h>
void test()
{printf("hello\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;int Add(int x, int y)
{return x + y;
}
int (*pf3)(int, int) = &Add;
int (*pf4)(int, int) = Add; // x和y写上或者省略都是可以的。
函数指针类型解析:
int (*pf3) (int x, int y) | | ------------| | || | pf3指向函数的参数类型和个数的交代| 函数指针变量名pf3指向函数的返回类型int (*) (int x, int y) // pf3函数指针变量的类型
4.2 函数指针变量的使用
#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));printf("%d\n", pf(3, 5));return 0;
}
运行:
5
8
4.3 两端有趣的代码
代码1:
(*(void (*)())0)();
代码2:
void (*signal(int, void(*)(int)))(int);
这两段代码均出自:《C陷阱和缺陷》这本书。
4.3.1 typedef关键字
typedef
是用来类型重命名的,可以将复杂的类型进行简单化。
例如,可以对unsigned int
进行简化,如下:
typedef unsigned int uint;
如果对指针类型呢?如下:
// 将int * 重命名为 ptr_t
typedef int* ptr_t;
但是对于数组指针和函数指针稍微有点区别:
比如说,数组指针类型int(*)[5]
,需要重命名为parr_t
,那可以这样写:
typedef int(*parr_t)[5];
函数指针类型的重命名也是一样的,比如将void(*)(int)
类型重命名为pf_t
,就可以这样写:
typedef void(*pf_t)(int);
因此,如果对void (*signal(int, void(*)(int)))(int);
进行简化,可以这样写:
typedef void(*pf_t)(int);
pf_t signal(int, pf_t);
5、函数指针数组
把函数的地址存放到一个数组中,那么这个数组就叫函数指针数组,以下哪个是函数指针数组?
int (*parr1[3])();
int *parr2[3]();
int (*)() parr3[3];
答案是:parr1
parr1
先和[]
结合,说明parr1
是数组,那么数组内容是什么呢?
是int (*)()
类型的函数指针。
6、转移表
函数指针数组的用途一般为转移表。
举个例子,计算机通过函数指针数组的实现:
#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;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;
}
完。