目录
🍎1.常量指针、指针常量
🍌1.1常量指针
语法:const 类型* 变量名;
- const修饰指针
- 指针指向可以修改,指针指向的指针不可以修改
🍌 1.2指针常量
语法: 类型* const 变量名;
- const修饰变量
- 指针指向的值可以改变,指针指向不可以改变
🍌1.3const即修饰指针,又修饰变量
语法: const 类型* const 变量名;
- 指针的指向和指针指向的值都不可以改
🍌代码
int main()
{
int a = 10;
int b = 20;
//1.const修饰的是指针,指针指向可以修改,指针指向的值不可以修改
const int* p1 = &a;
p1 = &b;
//*p1=100;//报错,此表达式左值不可修改
printf("p1=%d\n", *p1);
//2.const修饰的是变量,为指针常量,指针的指向不可以改,指针指向的值可以修改
int* const p2 = &a;
*p2 = 100;
//p2=&b;//报错,指针的指向不可以修改
printf("p2=%d\n", *p2);
printf("a=%d\n", a);
//3.const即修饰指针又修饰常量,指针的指向和指针指向的值都不可以修改
const int* const p3 = &a;
printf("p3=%d\n", *p3);
return 0;
}
🍎2.数组参数、指针参数
🍌2.1一维数组传参
void test(int arr[])//形参是数组
{}
void test(int* arr)//形参是指针
{}
void test(int arr[10])//形参是数组
{}
void text1(int** arr2)//形参是指针
{}
//此处arr2接收的是,数组arr2首元素的地址(以首元素为例)
//第一次解引用,得到数组首元素的值(也是地址)
//第二次解引用,得到首元素值(地址)对应的值
void text1(int* arr[20])//形参是数组
{}
int main()
{
int arr[10] = { 0 };
int* arr2[20] = { 0 };
test(arr);
text1(arr2);
return 0;
}
🍌2.2二维数组传参
void test(int arr1[3][5])
{}
void test(int arr1[][])//错误用法
{}
void test(int arr1[][5])
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int* arr1)//用法错误,
{}
void test(int* arr1[5])
{}
//错误用法,指针数组
//数组传过去需要用指针接收
void test(int (*arr1)[5])
{}
//数组指针是指向数组的指针,接收数组的地址(&arr[0])
//使用arr1接收二维数组的地址(第一行,一维数组的地址)
//为解引用前,表示一行的地址(类型为int* [5]),解引用后表示类型为int [5]的数组的地址
void test(int** arr1)//错误用法
{}
//二级指针是指向整数的指针的指针
//arr1接收的是二维数组的地址
//二维数组的地址与二维数组第一行的地址,二维数组第一个元素的地址相同,终究只是一个地址
//这个地址所对应的空间存储的是首元素的值,解引用这个地址,得到准确数值而不是另一个地址
//无法再次解引用,故不能使用二级指针
int main()
{
int arr[3][5] = { 0 };
test(arr);
return 0;
}
✍数组指针的内存原理
🍌2.3一级指针传参
#include <stdio.h>
void print(int *p, int sz)
{
int i = 0;
for(i=0; i<sz; i++)
{
printf("%d\n", *(p+i));
}
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9};
int *p = arr;
int sz = sizeof(arr)/sizeof(arr[0]);
//一级指针p,传给函数
print(p, sz);
return 0;
}
✍思考
当一个函数的参数部分为一级指针的时候,函数能接收什么参数 ?
//1
int a=0;
test(&a);
//2
int* ptr=&a;
test(ptr);
//3
int arr[10];
test(arr);
🍌2.4二级指针传参
#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
test(pp);
test(&p);
return 0;
}
✍思考:
当函数的参数为二级指针的时候,可以接收什么参数?
int a =0;
int* p=&a;
int** pp = &p;
//1
test(pp);
//2
test(&p);
//3.指针数组
int* arr[10];
test(arr);
🍎3.函数指针
-
指向函数的指针
void Add(int x,int y)
{
return x + y;
}
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", &arr);
printf("%p\n", Add);
printf("%p\n", &Add);
//Add和&Add都是函数的地址
return 0;
}
🍌参考数组指针
int arr[10] = {0};
int (*p)[10] = &arr;
void add(int a,int b);
void (*p)(int,int) = add;
//void (*p)(int,int) = &add;
🍌列1:
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*p)(int, int) = Add;
int a = p(1, 2);
//int a = (*p)(1,2);
//也是可以的
//&Add和Add是相同的地址
printf("%d", a);
return 0;
}
🍌列2:
void text(char (*p)[5])
{
for (int i = 0; i < 5; i++)
{
printf("%c ", *(*p + i));
}
printf("\n");
}
int main()
{
char arr[5] = { '1','2','3','4','7'};
char(*p1)[5] = &arr;
void (*p)(char(*)[5]) = text;
p(p1);
return 0;
}
🍌分析代码
✍代码1:
(*(void (*)() )0)();
✍代码2:
void ( *signal( int,void(*)(int) ) )(int);//声明函数
表示函数signal( int,void(*)(int) )的指向int返回值为void
-
上述代码是一次函数声明
-
声明的函数为:signal
-
signal函数的第一个参数是int类型
-
signal函数的第二个参数是一个函数指针类型,该函数指针指向的函数参数是int,返回类型是void
-
signal函数的返回类型也是一个函数指针类型,该函数指针指向的函数参数是int,返回类型是void
✍改写函数
typedef void (*flat1)();
typedef void (*flat2)(int);
(*(flat1)0)();
flat2 signal(int,flat2);
🍎4.函数指针数组
//函数指针
int (*p)(int,int);
//函数指针数组
int (*pfArr[])(int,int);
//对比
int* p;//整形指针
int* arr[];//整形指针数组
🍌应用:
✍案例1:
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
✍案例2:
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
while (input)
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
if ((input <= 4 && input >= 1))
{
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = (*p[input])(x, y);
}
else
printf("输入有误\n");
printf("ret = %d\n", ret);
}
return 0;
}
🍎5.指向函数指针数组的指针
//函数指针
int (*pf)(int,int);
//函数指针数组
int (*pfArr[4])(int,int);
//指向函数指针数组的指针
int (*(*ptr)[4])(int,int) = &pfArr;
🍎6.回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
🍌qsort函数
✍使用qsort函数
typedef struct s
{
char name[20];
int age;
}s;
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p2 - *(int*)p1);
}
int double_cmp(const void* p1, const void* p2)
{
return (*(double*)p2 - *(double*)p1);
}
int char_cmp(const void* p1, const void* p2)
{
return (*(char*)p2 - *(char*)p1);
}
int struct_age_cmp(const void* p1, const void* p2)
{
return (*(s*)p1).age - (*(s*)p2).age;
}
void int_print(void* arr, int num)
{
for (int i = 0; i < num; i++)
{
printf("%d ", *((int*)arr+i));
}
printf("\n");
}
void double_print(void* arr, int num)
{
for (int i = 0; i < num; i++)
{
printf("%.2f ", *((double*)arr + i));
}
printf("\n");
}
void char_print(void* arr, int num)
{
for (int i = 0; i < num; i++)
{
printf("%c ", *((char*)arr + i));
}
printf("\n");
}
void struct_print(void* arr, int num)
{
for (int i = 0; i < num; i++)
{
printf("%s:%d ", ((s*)arr+i)->name,(*((s*)arr+i)).age);
}
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,78,9,10 };
double arr2[] = { 1.2,2.3,2.5,8.9,4.5,6.4,7.5 };
char arr3[] = "abcdef";
s arr4[] = { {"zhangsan",10},{"lisi",20},{"wangwu",18} };
qsort(arr1, sizeof(arr1) / sizeof(arr1[0]), sizeof(arr1[0]), int_cmp);//int类型排序
qsort(arr2, sizeof(arr2) / sizeof(arr2[0]), sizeof(arr2[0]), double_cmp);//double类型排序
qsort(arr3, sizeof(arr3) / sizeof(arr3[0]), sizeof(arr3[0]), char_cmp);//char类型排序
qsort(arr4, sizeof(arr4) / sizeof(arr4[0]), sizeof(arr4[0]), struct_age_cmp);//struct类型排序
int_print(arr1, sizeof(arr1) / sizeof(arr1[0]));
double_print(arr2, sizeof(arr2) / sizeof(arr2[0]));
char_print(arr3, sizeof(arr3) / sizeof(arr3[0]));
struct_print(arr4, sizeof(arr4) / sizeof(arr4[0]));
return 0;
}
✍实现qsort函数
typedef struct s
{
char name[20];
int age;
}s;
int cmp(void* p1,void* p2)
{
return (*(s*)p2).age - (*(s*)p1).age;
}
void swap(void* p1, void* p2,int size)
{
//因为传递的类型不明确,无法简单的通过它一个元素的空间的大小确定类型
//强制类型转换为char*类型,并传递它一个元素空间的大小数值过来
//通过循环一个字节,一个字节的传递数值,最终完全传递
for (int i = 0; i < size; i++)
{
char temp = *((char*)p1 + i);
*((char*)p1 + i) = *((char*)p2 + i);
*((char*)p2 + i) = temp;
}
}
void my_psort(void* arr4, int num, int size, int (*cmp)(void*,void*))
{
for (int i = 0; i < num; i++)
{
for (int j = 0; j < num-1-i; j++)
{
if (cmp((char*)arr4+j*size, (char*)arr4 + (j+1) * size) > 0)
{
swap((char*)arr4 + j * size, (char*)arr4 + (j + 1) * size,size);
}
}
}
}
void struct_print(void* arr, int num)
{
for (int i = 0; i < num; i++)
{
printf("%s:%d ", ((s*)arr+i)->name,(*((s*)arr+i)).age);
}
printf("\n");
}
int main()
{
s arr4[] = { {"zhangsan",10},{"lisi",20},{"wangwu",18} };
my_psort(arr4, sizeof(arr4) / sizeof(arr4[0]), sizeof(arr4[0]), cmp);
struct_print(arr4, sizeof(arr4) / sizeof(arr4[0]));
return 0;
}