【C语言】指针进阶

指针初阶(一):指针和指针类型 指针的解引用
指针初阶(二):野指针 指针运算 指针和数组 二级指针 指针数组

简化不看版:
1.指针就是一个变量,用来存放地址,地址唯一标识一块内存空间
2.指针的大小是固定的4/8个字节(32/64位平台)
3.指针是有类型的,指针的类型决定了指针的整数步长,指针解引用操作时的权限
4.指针的运算

字符指针

int main() {
	char ch = 'w';
	char* pc = &ch;
	*pc = 'a';
	return 0;
}
int main() {
	char* pstr = "hello!";
	printf("%s\n", pstr);
	printf("%c\n", *pstr);
	return 0;
}

本质是把字符串str的首地址放到了ptsr中。

指针数组

指针数组是一个存放指针的数组

int* arr1[10]; //整型指针的数组
char* arr2[4]; //一级字符指针的数组
char** arr3[5]; //二级字符指针的数组

//指针数组
int main() {
	int a = 10, b = 20, c = 30;
	int* arr[3] = { &a,&b,&c };
	int i = 0;
	for (i = 0; i < 3; i++) {
		printf("%d ", *(arr[i]));
	}
	return 0;
}
int main() {
	int a[5] = { 1,2,3,4,5 };
	int b[] = { 2,3,4,5,6 };
	int c[] = { 3,4,5,6,7 };
	int* arr[3] = { a,b,c };
	int i = 0;
	for (i = 0; i < 3; i++) {
		int j = 0;
		for (j = 0; j < 5; j++) {
			//printf("%d ", *(arr[i] + j));
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

数组指针

数组指针是指针,是能够指向数组的指针

//数组指针
int main() {
	int arr[10] = { 1,2,3,4,5 };
	int(*parr)[10] = &arr;		//取出的是数组的地址
	//parr就是一个数组指针-存放数组的地址
	//arr是首元素arr[0]的地址
	return 0;
}
//例
int main() {
	double* d[5];				//指针数组
	double* (*pd)[5] = &d;		//pd就是一个数组指针
	return 0;
}

数组指针的使用

int main() {
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int(*pa)[10] = &arr;
	int i = 0;
	for (i = 0; i < 10; i++) {
		printf("%d ", *((*pa) + i));
	}
	return 0;
}
//常用于二维数组
void print1(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");
	}
}
void print2(int(*p)[5], int r, int c) {	//p是一个一维数组指针
	int i = 0, 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} };
	//print1(arr, 3, 5);
	print2(arr, 3, 5);					//arr表示首元素,即第一行的地址
	return 0;
}
int arr[5];				//整型数组
int  *parr1[10];		//存放整型指针的数组
int (*parr2)[10];		//数组指针,该指针能够指向一个数组,数组10个元素,每个元素的类型是int
int (*parr3[10])[5];	//存放数组指针的数组,该数组能存放10个数组指针,每个数组指针能指向"5个int元素"的数组

数组参数和指针参数

数组作为函数参数

将数组作为参数传入函数
形参可以写成两种形式:
1、数组形式
2、指针形式

void test1(int arr[]);		//数组形式
void test2(int *arr);		//指针形式

数组名

数组名是数组首元素的地址
但是有两个例外:
1、sizeof(数组名)——表示整个数组,计算整个数组的大小,单位字节
2、&数组名——表示整个数组,取出数组的地址

一维数组传参

//一维数组传参
void test(int arr[]) {}
void test(int arr[10]) {}
void test(int* arr) {}
void test2(int* arr[20]) {}
void test2(int** arr) {}
int main() {
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };		//整型指针数组
	test(arr);
	test(arr2);
	return 0;
}

二维数组传参

//二维数组传参
void test(int arr[3][5]) {}
void test(int arr[][]) {}		//error
void test(int arr[][5]) {}
int main() {
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };		//整型指针数组
	test(arr);
	test(arr2);
	return 0;
}

二维数组传参,函数形参的设计只能省略第一个[ ]的数字。因为对于一个二维数组,可以不知道有多少行,但是必须知道一行有多少列,这样才能找到下一行元素的地址。

void test(int* arr) {}			//第一行的地址,不能用int指针接收,error
void test(int* arr[5]) {}		//不能用数组接收,error
void test(int(*arr)[5]) {}		//指向5个int元素的指针,right
void test(int** arr) {}			//不能用二级指针接收,error

一级指针传参

//一级指针传参
void print(int* ptr, int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d ", *(ptr + i));
	}
}

int main() {
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//p是一级指针
	print(p, sz);
	return 0;
}

二级指针传参

//二级指针传参
void test(int** p2) {
	**p2 = 20;
}
int main() {
	int a = 10;
	int* pa = &a;		//pa是一级指针
	int** ppa = &pa;	//ppa是二级指针
	test(ppa);
	//test(&pa);			//传一级指针的地址也没问题
	printf("%d\n", a);
	return 0;
}

传存放一级指针的数组的数组名也没问题,只要符合函数定义的参数类型

函数指针

存放函数地址的指针

&函数名 - 取到的就是函数的地址

//函数指针
int Add(int x, int y) {
	return x + y;
}

int main() {
	//函数指针——存放函数地址的指针
	//pf就是一个函数指针变量
	int(*pf)(int, int) = &Add;
	printf("%p\n", &Add);
	printf("%p\n", Add);
	return 0;
}
int main() {
	//pf就是一个函数指针变量
	int(*pf)(int, int) = &Add;
	int ret = (*pf)(3, 5);
	printf("%d\n", ret);
	return 0;
}
(*(void(*)())0)();
//()0——将0强制类型转换
//(void(*)())0——将0强制类型转换成一个函数的地址
// *(void(*)())0——对0地址解引用
// (*(void(*)())0)()——调用0地址处的函数,该函数无参,返回值是void

void (*signal(int, void(*)(int)))(int);
//signal是函数名,void(*)(int)是一个函数指针,参数为int,返回void,signal函数参数为一个整型和一个函数指针类型
//void(*)(int),说明signal函数的返回类型是函数指针,指向参数为int,返回void的函数

函数指针数组

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(*pfArr[2])(int, int) = { Add, Sub };
	//pfArr就是函数指针数组,存放同类型的函数指针
	return 0;
}

函数指针数组的应用

//计算器-计算整型变量的加减乘除
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;
}
void menu() {
	printf("************************************\n");
	printf("***1.Add 2.Sub 3.Mul 4.Div 0.exit***\n");
	printf("************************************\n");
}
int main() {
	int input = 0;
	do {
		menu();
		//pfArr就是函数指针数组
		int(*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
		int x = 0, y = 0, ret = 0;
		printf("请选择:>");
		scanf("%d", &input);
		if (input >= 1 && input <= 4) {
			printf("请输入两个数:>");
			scanf("%d %d", &x, &y);
			ret = (pfArr[input])(x, y);
			printf("ret=%d\n", ret);
		}
		else if (input == 0) {
			printf("退出程序\n");
			break;
		}
		else {
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}

指向函数指针数组的指针

int* (*p2)[5] = &arr;
//p2是指向整型指针数组的指针
int(*p)(int, int);		//函数指针
int(*p2[4])(int, int);	//函数指针的数组
p3 = &p2;				//取出函数指针数组的地址
//p3就是指向函数指针数组的指针

回调函数

回调函数是一个通过函数指针调用的函数。如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,就称为回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用,用于对该事件或条件进行响应。

//计算器
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;
}
void menu() {
	printf("************************************\n");
	printf("***1.Add 2.Sub 3.Mul 4.Div 0.exit***\n");
	printf("************************************\n");
}
int Calc(int(*pf)(int, int)) {
	int x = 0, y = 0;
	printf("请输入两个数:>");
	scanf("%d %d", &x, &y);
	return pf(x, y);
}
int main() {
	int input = 0;
	do {
		menu();
		int x = 0, y = 0, ret = 0;
		printf("请选择:>");
		scanf("%d", &input);
		switch (input) {
		case 1:
			ret = Calc(Add);
			printf("ret=%d\n", ret);
			break;
		case 2:
			ret = Calc(Sub);
			printf("ret=%d\n", ret);
			break;
		case 3:
			ret = Calc(Mul);
			printf("ret=%d\n", ret);
			break;
		case 4:
			ret = Calc(Div);
			printf("ret=%d\n", ret);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("请重新选择\n");
		}
	} while (input);
	return 0;
}

排序

冒泡排序

//冒泡排序
void bubble_sort(int arr[], int sz) {
	int i = 0;						//冒泡排序地趟数
	for (i = 0; i < sz - 1; i++) {	//一趟冒泡排序
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++) {
			if (arr[j] > arr[j + 1]) {
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
void print_arr(int arr[], int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main() {						//升序
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	print_arr(arr, sz);
	return 0;
}

但排序的类型不止整数还需要排序字符串等类型

快速排序

//快速排序
void qsort(void* base,				//base中存放待排序数据中第一个对象地地址
	size_t num,						//排序数据元素的个数
	size_t size,					//排序数据中一个元素的大小,单位是字节
	int(*cmp)(const void*, const void*)	//比较待排序数据中两个元素的函数
);
int cmp_int(const void* e1, const void* e2) {
	return (*(int*)e1 - *(int*)e2);
}
void print_arr(int arr[], int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main() {						//升序
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶雨莳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值