IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言|指针进阶(2):常量指针和指针常量、数组参数和指针参数、函数指针 -> 正文阅读

[C++知识库]C语言|指针进阶(2):常量指针和指针常量、数组参数和指针参数、函数指针

目录

🍎1.常量指针、指针常量

🍌1.1常量指针

🍌 1.2指针常量

?🍌1.3const即修饰指针,又修饰变量

🍌代码?

?🍎2.数组参数、指针参数

🍌2.1一维数组传参

🍌2.2二维数组传参

?数组指针的内存原理

🍌2.3一级指针传参

?思考

🍌2.4二级指针传参

?思考:

🍎3.函数指针

🍌参考数组指针

🍌列1:

🍌列2:

🍌分析代码

?代码1:

?代码2:

?改写函数

🍎4.函数指针数组

🍌应用:

?案例1:

?案例2:

🍎5.指向函数指针数组的指针

🍎6.回调函数

🍌qsort函数

?使用qsort函数

?实现qsort函数


🍎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

  1. 上述代码是一次函数声明

  2. 声明的函数为:signal

  3. signal函数的第一个参数是int类型

  4. signal函数的第二个参数是一个函数指针类型,该函数指针指向的函数参数是int,返回类型是void

  5. 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;
?}   

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-08-19 18:45:50  更:2022-08-19 18:48:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 14:57:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码