字符指针变量

常规使用方法

1
2
3
char ch = 'A';
char* pch = &ch;
*pch = 'w';

还有另一种使用方法

1
2
char* pstr = "hello world!";
printf("%s\n", pstr);

字符型指针变量存放的是字符串首元素的地址

来看看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
char str1[] = "hello world.";
char str2[] = "hello world.";
const char* str3 = "hello world.";
const char* str4 = "hello world.";
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");

这里 str1 和 str2 是在内存中开辟了两块不同的内存空间。所以不等。

而 str3 和 str4 指向的是同一个常量字符串,C语言中没必要开辟两块内存空间用

于存放同一个常量字符串。

数组指针变量

指向一个数组的指针变量称为数组指针变量,存放的是一个数组的地址

1
type (*p)[];// 语法格式

在定义时需要用()将 * 和 p 结合起来,表明 p 是一个指针变量。

2.1 数组指针变量的初始化

使用 & 取地址符

二维数组传参的本质


可以看到,二维数组的数组名是首元素即该数组第一行的地址

下面仅需一个二维数组传参的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void test(int arr[3][5], int r, int c)
{
for (int i = 0;i < r;i++)
{
for (int j = 0;j < c;j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}

int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };
test(arr, 3, 5);

可以看到二维数组本质上也是传递了地址,传递的是第一行这个一维数组的地址

这就意味着二维数组传参也可以写成指针形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void test(int (*p)[5], int r, int c)
{
for (int i = 0;i < r;i++)
{
for (int j = 0;j < c;j++)
{
printf("%d ", *(*(p + i) + j));
// p 存放的是 arr 首行的地址 p + i 是 arr 第 i 行的地址
// *(p + i) 可以拿到第 i 行的数组
// *(p + i) + j 可以拿到第 i 行第 j 个元素的地址
}
printf("\n");
}
}


int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };
int (*p)[5] = &arr;
test(p, 3, 5);


而对于一个数组名来说

1
*(arr + i) 与 arr[i] 等价

所以可以这样写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void test(int (*p)[5], int r, int c)
{
for (int i = 0;i < r;i++)
{
for (int j = 0;j < c;j++)
{
printf("%d ", p[i][j]);
}
printf("\n");
}
}
int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };
int (*p)[5] = &arr;
test(p, 3, 5);

函数指针变量

函数指针变量的创建

函数是否也有地址

1
2
3
4
5
6
7
8
9
10
void test2()
{
// 假如这里写了什么功能
}

int main()
{
printf("%p\n", &test2);
return 0;
}

可以发现函数名本身也是地址

1
2
printf("%p\n", &test2);
printf("%p\n", test2);

既然函数名是地址,那也有对应的指针变量可以进行接收

语法格式如下

1
2
3
4
5
6
7
8
type (*p)(type,type)


type 指函数的返回值类型

*p 是指针变量名

(type,type) 对应函数的形参个数与类型

示例:

1
2
3
4
5
6
7
8
9
10
11
12
int Add(int a, int b)
{
return a + b;
}

int main()
{
int (*p)(int, int) = Add;
printf("%p\n", p);
printf("%p\n", Add);
return 0;
}

函数指针变量的使用

可以通过函数指针变量调用函数

1
2
3
4
int (*p)(int, int) = Add;
// 两种写法都可以
printf("%d\n", (*p)(2, 3));
printf("%d\n", p(4, 5));

函数指针数组

用于存放指针变量的数组称为指针数组

用于存放函数指针变量的数组称为函数指针数组

语法格式如下:

1
2
3
4
5
6
7
8
type     (*p[]) (type,type)

type 指数组中函数的返回值类型

*p[] 是指针数组名

(type,type) 是数组中函数的形参类型与个数

函数指针数组的简单使用

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int Div(int x, int y)
{
return x / y;
}

int Mul(int x, int y)
{
return x * y;
}

int Sub(int x, int y)
{
return x - y;
}

int Add(int a, int b)
{
return a + b;
}

int main()
{
int (*p[4])(int, int) = { Add,Sub,Mul,Div };
for (int i = 0;i < 4;i++)
{
printf("%d ", p[i](2, 3));
}
return 0;
}

简易计算器的实现

由上面的代码可以实现一个简易计算器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
int Div(int x, int y)
{
return x / y;
}

int Mul(int x, int y)
{
return x * y;
}

int Sub(int x, int y)
{
return x - y;
}

int Add(int a, int b)
{
return a + b;
}

int main()
{
int a = 0;
int b = 0;
int input = 0; // 判断用户选择
int (*p[5])(int, int) = { 0,Add,Sub,Mul,Div }; // 数组指针
do
{
menu();
scanf("%d", &input);
if (input >= 1 && input <= 4)
{
printf("输入两个整数:>");
scanf("%d,%d", &a, &b);
printf("%d\n", p[input](a, b));
}
else if (input == 0)
{
printf("exit!\n");
}
else
{
printf("输入不合法,重新输入!\n");
}
} while (input);
return 0;
}