字符指针
数组指针
指针数组
数组传参和指针传参
函数指针
函数指针数组
指向函数指针数组的指针
回调函数
回顾初阶指针
指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
指针的大小是固定的4/8个字节(32位平台/64位平台)。
指针是有类型的,指针的类型决定了指针的+-整数的步长,指针解引用操作时候的权限。
指针的运算。
下面讨论指针的高阶主题。
字符指针
在指针的类型中我们知道有一种指针的类型为字符指针为char*
指针指向字符:
int main()
{
char ch = 'w';
char *pc = &ch;
*ddpc = 'w';
return 0;
}
指针指向字符串:
int main()
{
char* pstr = "hello bit.";//实质是把“hello bit.”中字符h的地址放入pstr中
printf("%s\n",pstr);
return 0;
}
例子:
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
char* str1 = "hello bit."; // const char* str1 = "hello bit.";
char* str2 = "hello bit."; // const char* str2 = "hello bit.";
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");
return 0;
}
输出:
str1 and str2 are not same
str3 and str4 are same
思考:Why 👀
指针数组
是存放指针的数组,本质上是数组。
思考下面指针数组是什么意思?
int* arr1[10]; //整形指针的数组
char* arr2[4]; //一级字符型指针的数组
char** arr3[5]; //二级字符型指针的数组
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* arr[4] = { &a, &b, &c};
for(int i = 0; i < 3; i++ )
printf("%d ", *(arr[i]));
return 0;
}
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int b[5] = { 2, 3, 4, 5, 6 };
int c[5] = { 3, 4, 5, 6, 7 };
int* arr[3] = { a, b, c };
for(int i = 0; i < 3; i++)
{
for(nt j = 0; j < 5; j++)
{
printf("%d ", *(arr[i] + j) ); //等价printf("%d ", arr[i][j]);
//模拟二维数组。实质并不是二维数组(内存连续存放)。
}
printf("\n");
}
return 0;
}
对比以上两种使用方式。
数组指针
是指针?还是数组?
答案是:指针😶。
整形指针:int* pint; 能够指向整形数据的指针。
浮点型指针:float* pf;能够指向浮点数据的指针。
So,数组指针应该是指向数组的指针。
int main()
{
int a = 10;
int *pa = &a;
char ch = 'W';
char* pc = &ch;
double* d[5];
double* (*pd) = &d; //pd就是一个数组指针
int arr[10] = { 1,2,3,4,5 };
//int* parr = &arr; 错误。将数组的地址存放在整形指针中
int (*parr)[10] = &arr; //正确的使用方法,parr就是一个数组指针,存放的是数组的地址
return 0;
}
注:[ ]的优先级要高于*,所以必须加上()保证p先和*结合。
&数组名VS数组名
对于下面的数组:
int arr[10];
arr和&arr分别是什么?
arr是数组名,表示数组首元素的地址。
那&arr是什么呢?
int main()
{
int arr[10] = {0};
int* p1 = arr;
int (*p2)[10] = &arr;
printf(” %p “, p1);
printf(" %p ", p2);
//打印结果相同,但类型不同
//arr:整形指针
//&arr:数组指针
printf(” %p “, p1 + 1 );
printf(" %p ", p2 + 1 );
//从步长角度考虑运行结果
return 0;
}
注:数组名是数组首元素的地址,但有两个例外:
sizeof( 数组名 ),数组名表示整个数组,计算的是整个数组的大小。
&数组名,数组名表示整个数组,取出的是整个数组的地址。
数组指针的使用
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
int (*pa)[10] = &arr;
for(int i = 0; i < 10; i++)
printf(" %d ", *(*pa) + i );
return 0;
}
void print1( 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“);
}
}
void print2( 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 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);
return 0;
}
🙋区别:
int arr[5] 整形数组
int *parr1[10] 整形指针的数组
int (*parr2)[10] 数组指针,能够指向一个数组,数组10个元素,每个元素类型为int
int (*parr3[10])[5] 存放数组指针的数组,能够存放10个数组指针,每个数组指针能够指向一个数组,数组5个元素,每个元素是int类型。
数组传参和指针传参
一维数组传参
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int *arr])
{}
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);
test2(arr2)
}
思考:哪些可以传参,哪些不能传参?(评论区讨论)
二维数组传参
void test(int arr[3][5])
{}
void test(int arr[][])
{}
void test(int arr[][5])
{}
//二维数组可以不知道多少行,但必须知道多少列
void test(int *arr)
{}
void test(int* arr[5])
{}
void test(int (*arr)[5])
{}
void test(int **arr)
{}
int main()
{
int arr[3][5] = {0};
test(arr)
return 0;
}
思考:哪些可以传参,哪些不能传参?(评论区讨论)
一级指针传参
void print( int* ptr, int sz)
{
for(int 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]);
print( p, sz);// p是一级指针
return 0;
}
思考:当一个函数的参数部分为一级指针的时候,函数能够接收什么参数?(评论区讨论)
二级指针传参
void test(int** p2)
{
**p2 = 20;
}
int main()
{
int a = 10;
int* pa = &a; //pa是一级指针
int** ppa = &pa; //ppa是二级指针
int* arr[10] = {0};
//二级指针传参
test(ppa);
test(&pa); //传一级指针变量的地址
test(arr); //传存放一级指针的数组
printf( "%d", a );
return 0;
}
思考:当函数的参数为二级指针的时候,可以接受什么参数?(评论区讨论)
函数指针
指向函数的指针
存放函数地址的指针
int Add( int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int* pa = &a;
char ch = 'w';
char* pc = &ch;
int arr[10] ={0};
int (*parr)[10] = &arr; //parr是指向数组的指针,存放的是数组的地址
printf(" %p ", &Add);
printf(" %p ", Add); //输出结果相同
int(*pf)(int ,int) = &Add; //pf就是一个函数指针变量
return 0;
}
注:数组名 != &数组名
函数名 == &函数名
void test(char* str)
{
}
int main()
{
void (*pt)(char *) = &test;
return 0;
}
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf)(int ,int) = Add; //add == pf
int ret = (*pf)(3,5);
//int ret = pf(3,5);
//int ret = Add(3,5);
//三种方式等价
printf(" %d \n",ret);
return 0;
}
函数指针数组
数组是一个存放相同类型数据的存储空间,上面介绍了指针数组。那么要把函数的地址存到一个数组中,这个数组就叫函数指针数组,函数指针数组如何定义呢?
存放函数指针的数组。
int (*parr1[10])();
int *parr2[10]();
int (*)()parr3[10];
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 };//函数指针数组
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("*******************************");
printf("**********0.exit***************");
printf("****1.Add*******2.Sub**********");
printf("****3.Mul*******4.div**********");
printf("*******************************");
}
int main()
{
int input = 0;
do{
menu();
int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
int x = 0;
int y = 0;
int ret = 0;
printf("请选择:\n");
scanf(" %d ", &input);
printf("请输入两个操作数:");
scanf(”%d %d“,&x , &y);
ret = (pfArr[input])(x,y);
printf(" ret = %d ", ret);
}while(input);
return 0;
}
指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针。
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 ****\n");
printf("**** 3. mul 4. div ****\n");
printf("**** 0. exit ****\n");
printf("**************************\n");
}
int Calc(int (*pf)(int, int))
{
int x = 0;
int y = 0;
printf("请输入2个操作数>:");
scanf("%d %d", &x, &y);
return pf(x, y);
}
int main()
{
int input = 0;
//计算器-计算整型变量的加、减、乘、除
//a&b a^b a|b a>>b a<<b a>b
do {
menu();
int 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");
break;
}
} while (input);
return 0;
}
冒泡排序算法:
void bubble_sort(int arr[], int sz)
{
//冒泡排序趟数
for(int i = 0; i < sz - 1; i++)
{
//一趟冒泡排序
for(int j = 0; j < sz - 1 - i; j++)
{
if(arr[j] > arr[j + 1])
{
//交换
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
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);
return 0;
}
思考:上面程序只能排序int类型的数据(只能排一种类型的数据),怎样实现排多种类型的数据呢?(tip:参考上面计算器程序,用回调函数怎样实现?)
qsort函数
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
void test1()
{
//整形数据的排序
int arr[] = { 1,3,5,7,9,2,4,6,8,0};
int sz = sizeof(arr) / sizeof(arr[0]);
//排序
qsort(arr, sz, sizeof(arr[0]), cmp_int);
//打印
print_arr(arr, sz);
}
struct Stu
{
char name[20];
int age;
};
int sort_by_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int sort_by_name(const void*e1, const void*e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void test2()
{
//使用qsort函数排序结构体数据
struct Stu s[3] = { {"zhangsan", 30},{"lisi", 34},{"wangwu", 20} };
int sz = sizeof(s) / sizeof(s[0]);
//按照年龄来排序
//qsort(s, sz, sizeof(s[0]), sort_by_age);
//按照名字来排序
qsort(s, sz, sizeof(s[0]), sort_by_name);
}
void Swap(char*buf1, char*buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
//模仿qsort实现一个冒泡排序的通用算法
void bubble_sort(void* base,int sz,int width,int (*cmp)(const void*e1, const void*e2) )
{
int i = 0;
//趟数
for (i = 0; i < sz - 1; i++)
{
//一趟的排序
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
//两个元素比较
//arr[j] arr[j+1]
if (cmp( (char*)base+j*width, (char*)base+(j+1)*width )>0)
{
//交换
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
void test3()
{
//整形数据的排序
int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
//排序
bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
//打印
print_arr(arr, sz);
}
void test4()
{
//使用qsort函数排序结构体数据
struct Stu s[3] = { {"zhangsan", 30},{"lisi", 34},{"wangwu", 20} };
int sz = sizeof(s) / sizeof(s[0]);
//按照年龄来排序
//bubble_sort(s, sz, sizeof(s[0]), sort_by_age);
//按照名字来排序
bubble_sort(s, sz, sizeof(s[0]), sort_by_name);
}
int main()
{
//test1();
//test2();
//test3();
test4();
return 0;
}
完结,继续为成为更好的搬砖人而努力奋斗!!!😤