指针进阶
指针初阶(一):指针和指针类型 指针的解引用
指针初阶(二):野指针 指针运算 指针和数组 二级指针 指针数组
简化不看版:
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;
}