深入理解指针4
字符指针变量
指针指向字符变量
char ch = 'w';
char* p = &ch;
指针指向字符数组
char arr[10] = "abcdef";
char* p = arr;
printf("%s\n", arr);
printf("%s\n", p);
结果是一样的
也可以写成:
char* p = "abcdef";//常量字符串
//将字符串首字符a的地址赋值给p
字符数组可以存放字符串,字符数组的内容可以修改
常量字符串和数组是非常相似的,也是在一个连续的空间中存放了多个字符,但是常量字符串的内容不能修改
*p='w';//错误的,常量字符串不能被修改
因此可以写为:
const char* p = "abcdef";
看一道题:
int main(){char strl[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";if (str1 == str2)printf("strl and str2 are same\n");//1elseprintf("strl and str2 are not same\n"); //2if (str3 == str4)printf("str3 and str4 are same\n"); //3elseprintf("str3 and str4 are not same\n");//4return 0;
}
问:打印结果
答案为:2 3
str3和str4完全相同,为了节省内存,str4并不会被创建
str1,str2为数组名,是数组首元素的地址,两个数组创建的是不同的空间,首元素地址不同,故str1!=str2
str3,str4比较的是两个指针变量中存放的地址,所以str3==str4
数组指针变量
存放的是数组的地址——指向数组的指针
&arr——数组的地址
int arr[5] = { 0 };
int(*p)[5] = &arr;
//p为数组指针变量
//int: p指向的数组的元素类型
char arr[8];
char(*pc)[8] = &arr;
char* arr[8];
char*(*pc)[8] = &arr;//
//pc数组指针变量
以
int arr[6] = { 1,2,3,4,5,6 };
int*(*p)[6] = &arr;
为例:
因为:
*p = *&arr = arr;
所以求数组长度:
printf("%zd\n", sizeof(arr));
或者:
printf("%zd\n", sizeof(*p));
输出数组:
for (i = 0;i < 6;i++) {printf("%d ", arr[i]);
}
或者:
for (i = 0;i < 6;i++) {printf("%d ", (*p)[i]);
}
但最简单的方法还是取出数组首元素的地址:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {int arr[6] = { 1,2,3,4,5,6 };int* p = arr;int i = 0;for (i = 0;i < 6;i++) {printf("%d ", p[i]);}return 0;
}
二维数组传参本质
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void Print(int arr[3][5], int r, int c) {//形参写成数组形式int i = 0, j = 0;for (i = 0;i < r;i++) {for (j = 0;j < c;j++) {printf("%d ", arr[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} };// 1 2 3 4 5// 2 3 4 5 6// 3 4 5 6 7//写一个函数打印arr数组Print(arr, 3, 5);//实参传数组名return 0;
}
二维数组每一行都是一个一维数组,所以二维数组可以理解为一维数组的数组,也就是说二维数组的每个元素其实是个一维数组
Print(arr, 3, 5);
arr是二维数组的数组名,数组名表示数组首元素的地址(就是二维数组第一行的地址)
根据前面学过的一维数组的传参本质,可知,二维数组的形参可以写为:
void Print(int (*arr)[5], int r, int c) {
p[i][j]=*(*(p+i)+j)*(p+i)——>第i+1行数组名
函数指针变量
printf("%p\n", &Add);//函数的地址
printf("%p\n", Add);//函数名也是函数的地址
int (*pf)(int x, int y ) = &Add;
int (*pf)(int x, int y ) = Add;
//pf就是函数指针变量
//形参的名字不会被使用,也可以省略
int (*pf)(int, int) = &Add;
int (*pf)(int, int) = Add;
//pf=Add
int r=(*pf)(a, b);
int r = Add(a, b);
int r=pf(a, b);
//一样
指针可以指向任何内存中的对象,变量、数组、函数
辨析两个语句:
1
(*(void(*)())0)();
可以分开来看:
void(*)()函数指针类型
(void(*)())0强制类型转换,0变成了地址,0的位置是一个函数
*(void(*)())0解引用
(*(void(*)())0)()函数调用
- 将0这个整数强制转换成函数指针类型
- 调用0地址处的函数
2
void(*signal(int, void(*)(int)))(int);
signal函数名
(int, void(*)(int))函数的两个参数
void(*)(int)指针类型
void(*……)(int);函数指针类型——>函数返回类型是一个函数指针类型,signal(int, void(*)(int))为函数名(参数,参数)
上面的代码是一个函数的声明
函数的名字是signal
signal函数的参数有两个,第一个是int类型,第二个是函数指针类型void(*)(int),这个函数指针指向的函数有一个int参数,返回类型是void
signal函数的返回值类型也是一个函数指针类型,这个函数指针指向的函数有一个int参数,返回类型是void
typedef关键字
typedef 是用来类型重命名的,可以将复杂的类型简单化
typedef unsigned int uint;//将unsigned int重命名为uint
指针类型重命名
typedef int* ptr_t;
//指针类型int*重命名为 ptr_t:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef int* ptr_t;
int main() {int* p1, p2;ptr_t p3, p4;return 0;
}
p2不是int*类型
应变为:
int* p1,* p2;
数组指针类型重命名
typedef int(*parr_t)[5]; //新的类型名必须在*的右边
//数组指针类型int(*)[5],需要重命名为parr_t
函数指针类型的重命名
typedef void(*pf_t)(int);//新的类型名必须在*的右边//将 void(*)(int)类型重命名为 pf_t
void(*signal(int, void(*)(int)))(int);
将这段代码简化:
typedef void(*pf_t)(int);
pf_t signal(int, pf_t);
函数指针数组
类比:指针数组char* arr1[5];//字符指针数组int* arr2[6];//整形指针数组
函数指针数组是数组,里面存放的都是函数的地址
int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) { return x - y;
}
int main() {int (*pf1)(int, int) = Add;int (*pf2)(int, int) = Sub;//参数和返回类型一样int (*parr[4])(int ,int) = {Add,Sub};
调用:
int r = parr[0](100, 200);//300
int r = parr[1](100, 200);//-100
转移表
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu() {printf("**********************\n");printf("*** 1.add 2.sub ***\n");printf("*** 3.mul 4.div ***\n");printf("*** 0.exit ***\n");printf("**********************\n");
}
int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) { return x - y;
}
int Mul(int x, int y) { return x * y;
}
int Div(int x, int y) { return x / y;
}
//写一个简易的计算器
//1.实现加减乘除
//2.不需要退出程序可以继续计算
int main() {int input = 0;do{menu();printf("请选择");scanf("%d",&input);int a = 0, b = 0, r = 0;switch (input) {case 1:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Add(a, b);printf("%d\n", r);break;case 2:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Sub(a, b);printf("%d\n", r);break;case 3:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Mul(a, b);printf("%d\n", r);break;case 4:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Div(a, b);printf("%d\n", r);break;case 0:printf("退出计算器");break;default:printf("选择错误,重新选择");break;}} while (input);return 0;
}
如果想增加计算器的功能,那么代码就会越来愈长,越来越冗余
这时就可以使用函数指针数组
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu() {printf("**********************\n");printf("*** 1.add 2.sub ***\n");printf("*** 3.mul 4.div ***\n");printf("*** 0.exit ***\n");printf("**********************\n");
}
int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) { return x - y;
}
int Mul(int x, int y) { return x * y;
}
int Div(int x, int y) { return x / y;
}
int main() {int input = 0;//转移表int (*pfArr[])(int, int) = { NULL,Add,Sub,Mul,Div };//整型双目运算//指针类型和指针指向参数的返回类型要一样do{menu();printf("请选择");scanf("%d",&input);int a = 0, b = 0, r = 0;if (input == 0) {printf("退出计算器");}else if (input >= 1 && input <= 4) {printf("请输入两个操作数");scanf("%d%d", &a, &b);int r=pfArr[input](a, b);printf("%d\n", r);}else printf("选择错误,重新选择");} while (input);return 0;
}
END……
午后读一本书。晚上在杏花树下
喝酒,聊天,直到月色和露水清凉。
在梦中,行至岩凤尾蕨茂盛的空空山谷,
鸟声清脆,一起在树下疲累而眠。
醒来时,我尚年少,你未老。
——庆山