C语言灵魂核心——指针深度修炼

news2025/1/8 18:11:14
🐒个人主页:平凡的小苏
📚学习格言:别人可以拷贝我的模式,但不能拷贝我不断往前的激情

目录

1. 字符指针

2. 指针数组

3. 数组指针

3.1 数组指针的定义

3.2 &数组名VS数组名

3.3 数组指针的使用

4. 数组参数、指针参数

4.1 一维数组传参

4.2 二维数组传参

4.3 一级指针传参

4.4 二级指针传参

5. 函数指针

6. 函数指针数组

7. 回调函数


指针的主题,我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念:

1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。

2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。

3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。

4. 指针的运算。

这个章节,我们继续探讨指针的高级主题。

1. 字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char* ;

一般使用:

int main()
{
    char ch = 'w';
    char *pc = &ch;
    *pc = 'w';
    return 0;
}
这里指针pc放的ch的地址,而ch放了一个字符'w'。

还有一种使用方式如下:

int main()
{
    const char* pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?
    printf("%s\n", pstr);
    return 0;
}
注:一个指针变量在32位机器下是4个字节,所以一个指针变量是存不下一个字符串的。
代码 const char* pstr = "hello bit."特别容易让同学以为是把字符串 hello bit 放到字符指针 pstr 里了,但是/本质是把字符串 hello bit. 首字符的地址放到了pstr中。
该指针变量存入的是该字符串首字符的地址。并且可以通过该地址打印出后面的字符,并且该指针变量加入const使代码更为严谨。

那就有可这样的面试题:

#include <stdio.h>
int main()
{
    char str1[] = "hello bit.";
    char str2[] = "hello bit.";
    const char *str3 = "hello bit.";
    const char *str4 = "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和str2是否相等比较的不是该数组的内容,而是比较该数组名首元素的地址,而两个数组名的首元素的地址必然不相等。
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。

2. 指针数组

在《指针》章节我们也学了指针数组,指针数组是一个存放指针的数组。

这里我们再复习一下,下面指针数组是什么意思?

整型数组 - 存放整型的数组

字符数组 - 存放字符的数组

指针数组 - 存放指针(地址)的数组

int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
    const char* arr[4] = { "abcdef","qwer", "hello bit", "hehe" };
char **arr3[5];//二级字符指针的数组

例子:指针数组

#include<stdio.h>
int main()
{
    int arr1[5] = { 1,2,3,4,5 };
    int arr2[5] = { 2,3,4,5,6 };
    int arr3[5] = { 3,4,5,6,7 };
    int arr4[5] = { 0,0,0,0,0 };
    //指针数组
    int* arr[4] = {arr1, arr2, arr3, arr4};
    int i = 0;
    for (i = 0; i < 4; i++)
    {
        int j = 0;
        for (j = 0; j < 5; j++)
        {
            printf("%d ", *(arr[i] + j));//arr[i][j]
        }
        printf("\n");
    }

    return 0;
}

这里是用一维数组来模拟二维数组,但是该一维数组不一定是连续的,因为严格意义上的二维数组的地址是连续的,所以该模拟的二维数组不是严格意义上的二维数组

3. 数组指针

3.1 数组指针的定义

数组指针是指针?还是数组?

答案是:指针。

我们已经熟悉:

整形指针: int * pint; 能够指向整形数据的指针。

浮点型指针: float * pf; 能够指向浮点型数据的指针。

那数组指针应该是:能够指向数组的指针。

下面代码哪个是数组指针?

int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?
p1是指针数组
p2是数组指针
解释:
int (*p)[10];
解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫 数组指针
这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

3.2 &数组名VS数组名

对于下面的数组:

int arr[10];

arr 和 &arr 分别是啥?

我们知道arr是数组名,数组名表示数组首元素的地址。

那&arr数组名到底是啥?

我们看一段代码:

#include <stdio.h>
int main()
{
    int arr[10] = {0};
    printf("%p\n", arr);
    printf("%p\n", &arr);
    return 0;
}

可见数组名和&数组名打印的地址是一样的。

难道两个是一样的吗?

我们再看一段代码:

#include <stdio.h>
int main()
{
 int arr[10] = { 0 };
 printf("arr = %p\n", arr);
 printf("&arr= %p\n", &arr);
 printf("arr+1 = %p\n", arr+1);
 printf("&arr+1= %p\n", &arr+1);
 return 0;
}

根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。
实际上: &arr 表示的是 数组的地址,而不是数组首元素的地址。(细细体会一下)
本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40.

3.3 数组指针的使用

那数组指针是怎么使用的呢?

既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。

看代码:

#include <stdio.h>
void print_arr1(int(*arr)[5], int row, int col)
{
    int i = 0;
    for (i = 0; i < row; i++)
    {
        int j = 0;
        for (j = 0; j < col; j++)
        {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}
int main()
{
    int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };

    //数组名arr,表示首元素的地址
    //但是二维数组的首元素是二维数组的第一行
    //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
    //可以数组指针来接收
    print_arr1(arr, 3, 5);
    return 0;
}
注意:这里不能用二级指针来当做形参来接收它的数组的地址,类型不匹配。

学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:

int arr[5];//整型数组,数组是5个元素
int *parr1[10];//指针数组,每个元素是int*类型
int (*parr2)[10];//数组指针,该指针指向一个数组,数组是10个元素,每个元素是int类型
int (*parr3[10])[5];//数组指针数组。parr3是一个数组,数组的元素有10个,数组每个元素的类型是int (*)[5]的数组指针类型

4. 数组参数、指针参数

在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

4.1 一维数组传参

#include <stdio.h>
void test(int arr[])//ok?可以
{}
void test(int arr[10])//ok?可以
{}
void test(int *arr)//ok?可以
{}
void test2(int *arr[20])//ok?可以
{}
void test2(int **arr)//ok?可以
{}
int main()
{
 int arr[10] = {0};
int *arr2[20] = {0};
 test(arr);
 test2(arr2);
}

4.2 二维数组传参

void test(int arr[3][5])//ok?可以
{}
void test(int arr[][])//ok?不可以,列的元素个数不可以省略
{}
void test(int arr[][5])//ok?可以
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok?不可以,数组的地址不可以用一级指针接收,要用数组指针接收
{}
void test(int* arr[5])//ok?不可以
{}
void test(int (*arr)[5])//ok?可以,这个是数组指针
{}
void test(int **arr)//ok?不可以,数组的地址要数组指针接收,不可以用二级指针接收,类型不匹配
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
}

4.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;
}

思考:

当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

比如:

void test1(int *p)
{}
//test1函数能接收什么参数?
int a =10;//可以接收变量a的地址
int arr[10];//可以传数组名

4.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;
}

思考:

当函数的参数为二级指针的时候,可以接收什么参数?
void test(char **p)
{
 
}
int main()
{
 char c = 'b';
 char*pc = &c;
 char**ppc = &pc;
 char* arr[10];
 test(&pc);//ok
 test(ppc);//ok
 test(arr);//ok
 return 0;
}
这里的test传参都是可以的

5. 函数指针

首先看一段代码:

#include <stdio.h>
void test()
{
 printf("hehe\n");
}
int main()
{
 printf("%p\n", test);
 printf("%p\n", &test);
 return 0;
}void test()
{
 printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

输出的结果:

输出的是两个地址,这两个地址是 test 函数的地址。

那我们的函数的地址要想保存起来,怎么保存?

下面我们看代码:

void test()
{
 printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();
首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?
答案是:
pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参
数,返回值类型为void。

代码例子:

#include<stdio.h>
int Add(int x, int y)
{
    return x + y;
}

int main()
{
    //int (*pf)(int, int) = &Add;
    int (* pf)(int, int) = Add;//这里Add的函数名就是函数的地址所以不用取地址符号
    //int ret = Add(2, 3);
    int ret = pf(2, 3);//利用函数指针调用函数,pf指向的就是Add的地址,所以就不需要解引用也是调用该函数

    printf("%d\n", ret);

    //int (*pf)(int, int) = Add;
    //&函数名和函数名都是函数的地址
    
    //pf 是一个存放函数地址的指针变量 -  函数指针
    return 0;
}

这样一样可以实现加法函数的调用,并且返回正确的结果

6. 函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,

比如:

int *arr[10];
//数组的每个元素是int*

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

定义:int (*parr1[10])();

parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?
是 int (*)() 类型的函数指针。

函数指针数组的用途:转移表

例子:(计算器)

#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;
}

使用函数指针数组的实现:

#include<stdio.h>
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;
}
int main()
{
    int input = 1;
    int ret = 0;
    int x, y = 0;
    int (*p[5])(int x, int y) = { NULL,Add,Sub,Mul,Div };
    do
    {
        printf("*************************\n");
        printf("****1.Add   2.Sub********\n");
        printf("****3.Mul   4.Div********\n");
        printf("*************************\n");
        printf("请选择:\n");
        scanf("%d",&input);
        if (input >= 1 && input <= 4)
        {
            printf("输入操作数:");
            scanf("%d %d", &x, &y);
            ret = p[input](x, y);
            printf("%d\n", ret);
        }
        else if(input==0)
        {
            printf("退出计算器\n");
            break;
        }
        else
        {
            printf("输入错误,请重新输入\n");
        }
    } while (input);
    return 0;
}

7. 回调函数

回调函数就是一个通过 函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个
函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数
的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进
行响应。
注:qsort库函数需要四个参数,那么每个参数表示什么意思呢?
void qsort(void* base, //待排序的数组的起始地址
size_t num, //元素个数
size_t width, //一个元素的大小
int (*cmp)(const void* e1, const void* e2)//两个元素的比较函数
);

我们知道,冒泡排序如果实现的话,只能进行一种数据类型的排序,那么我们想排序多种数据类型应该怎样做呢?这时我们就可以利用C语言的库函数qsort来进行排序了

首先演示一下qsort函数的使用:

#include<stdio.h>
//实现一个比较整型的函数
int cmp_int(const void* e1, const void* e2)
{
    return *(int*)e1 - *(int*)e2;
}

//使用qsort对数组进行排序,升序
void test1()
{

    int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    //bubble_sort(arr, sz);
    //库函数中一个排序函数:qsort
    qsort(arr, sz, sizeof(arr[0]), cmp_int);
    //0 1 2 3 4 5 6 7 8 9
    //打印
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
}
int main()
{
    test1();
    return 0;
}
注:这里是利用qsort库函数来实现int类型数据的排序

我们该如何来模拟实现qsort呢?

代码演示:

模拟实现qsort来进行冒泡排序

#include<stdio.h>
//模拟实现bubble_qsort函数
void Swap(char* buf1, char* buf2,int width)
{
    for (int i = 0; i < width; i++)
    {
        char temp = *buf1;
        *buf1 = *buf2;
        *buf2 = temp;
        buf1++;
        buf2++;
    }
}
int cmp(const void *e1,const void *e2)
{
    return (*(int*)e1) - (*(int*)e2);
}
void bubble_qsort(void*base,size_t num,size_t width,int (*cmp)(const void *e1,const void *e2))//第四个参数为函数指针,为了实现回调函数
{
    for (int i = 0; i < num - 1; i++)
    {
        for (int j = 0; j < num - 1 - i; j++)
        {
            if (cmp((char*)base+j*width, (char*)base + (j+1) * width)>0)//回调函数的调用,判断前面的数大于后面的数就进行交换
            {
                Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
            }
        }
    }
}
int main()
{
    int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    bubble_qsort(arr, sz, sizeof(arr[0]), cmp);
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

好了!小编的指针进阶分享到这里就结束了,如果有什么不足的地方请大佬多多指点一二!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/146514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【读论文】TCPMFNet

【读论文】TCPMFNet简单介绍网络结构编码器图像融合网络Vision Transformer特征融合网络网格连接解码器损失函数总结参考论文&#xff1a;https://www.sciencedirect.com/science/article/pii/S1350449522003863 如有侵权请联系博主 简单介绍 今天要介绍的是TCPMFNet&#xf…

六大排序算法

1. 插入排序步骤&#xff1a;1.从第一个元素开始&#xff0c;该元素可以认为已经被排序2.取下一个元素tem&#xff0c;从已排序的元素序列从后往前扫描3.如果该元素大于tem&#xff0c;则将该元素移到下一位4.重复步骤3&#xff0c;直到找到已排序元素中小于等于tem的元素5.tem…

如何使用 Pandas 清洗二手房数据并存储文件

目录 一、实战场景 二、知识点 python 基础语法 python 文件读写 pandas 数据清洗 三、菜鸟实战 清洗前的文件 读取源文件 对二手房数据进行清洗 清洗完成后保存到文件 运行结果 运行截图 结果文件 一、实战场景 如何使用 Pandas 清洗的二手房数据并存储文件 二…

初识结构体(详细版)

目录 一、结构体的声明 1、结构的基础知识 2、结构的声明 3、结构成员的类型 4、结构体变量的定义和初始化 二、结构体成员的访问 1、点操作符访问 2、->操作符访问 3、解引用访问 三、结构体嵌套 四、结构体传参 1、传值调用 2、传址调用 一、结构体的声明 1、结构的基…

Vue2前端路由(vue-router的使用)

一、vue2axiosExpressMySQL实现前后端交互1、后台&#xff1a;&#xff08;1&#xff09;确定MySQL的表格&#xff1a;明确数据库 &#xff08;mvc&#xff09; —- 数据表(ssm_book)&#xff08;2&#xff09;创建Express项目&#xff1a;mysql2、cors、Sequelize(ORM)、nodem…

imx6ull Linux sdk下载验证

本文章是基于整点原子的imx6ull alpha开发板一.Linux SDK源码以及image1.环境准备其他的工具我们就不做介绍了&#xff0c;比如ubuntu ftp,ssh等等&#xff0c;我们主要来介绍下编译链1.1 交叉编译链背景&#xff1a;因为在原子的教程中有强调最新的Linaro gcc编译完uboot后无法…

备受认可!中睿天下荣登“2022创业邦100未来独角兽”年度榜单

近日&#xff0c;由创业邦、复旦大学管理学院主办的2022创业邦100未来独角兽峰会暨创业邦年会在上海举办。在峰会现场&#xff0c;2022创业邦100未来独角兽榜单正式揭晓&#xff0c;中睿天下凭借出众的综合实力荣登榜单。作为一家以“实战对抗”为特点的能力价值型网络安全厂商…

Java版设计模式/设计模式的作用是什么/类之间有哪些关系?又怎么表示

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 1. 设计模式概述 1.1 设计模式创始“4人组” ErichGamma–艾瑞克伽马Richard Helm—理查德赫尔码Ralph Johnson----拉尔夫约翰逊John Vlissides—约翰威力斯蒂斯…

【Linux】Linux编译器-gcc/g++的使用和程序执行的基础底层原理

Linux编译器1.gcc/g 的使用2. 程序的基本翻译过程3.预处理3.1验证预处理的功能&#xff08;gcc -E&#xff09;4.编译&#xff08;变成汇编语言&#xff09;4.1验证编译过程&#xff08;gcc -S&#xff09;5.汇编&#xff08;生成机器可识别代码&#xff09;5.1验证汇编过程&am…

K_A11_002 基于STM32等单片机驱动DS18B20串口与OLED0.96双显示

K_A11_002 基于STM32等单片机驱动DS18B20 串口与OLED0.96双显示一、资源说明二、基本参数1.参数2.引脚说明三、驱动说明时序对应程序:四、部分代码说明1、接线说明1.1、STC89C52RCDHT11模块1.2、STM32F103C8T6DHT11模块五、基础知识学习与相关资料下载六、视频效果展示与程序资…

内核解读之内存管理(4)内存管理三级架构之page

我们前面介绍了linux内存管理的三级架构&#xff0c;node->zone->page。本节就来介绍page。 页是内核管理内存的基本单位&#xff0c;体系结构不同&#xff0c;支持的页大小也不尽相同&#xff0c;还有些体系结构甚至支持几种不同的页大小。大多数32位体系结构支持4KB的页…

【Python】爬取弹幕并保存到Excel中

hello&#xff0c;我是李华同学&#xff0c;最近开始学习爬虫&#xff0c;下面是我实现的一个得到弹幕的代码&#x1f60f;找一个URL&#x1f449;想要得到一个网站的内容&#xff0c;首先要找到你想要内容的具体位置&#xff0c;首先你先找到一个有弹幕的地方&#xff0c;找到…

linux系统重装yum工具与python环境

文章目录前言一、强制删除原有环境1.删除python2.删除yum二、安装新环境前置准备三、下载依赖命令1.内核版本7.6.1810下载python依赖包下载yum包2.内核版本7.4.1708下载python依赖包下载yum包3.内核版本7.8.2003下载python依赖包下载yum依赖包4.下载结果四、安装前言 安装之前…

swagger入门

目录 1.前后端分离的特点 2.在没有swagger之前 3.swagger的作用 4.swagger的优点 5.集成swagger 5.1 新建springboot项目 5.2 集成swagger 5.3 开发一个controller用于测试 5.4 启动服务&#xff0c;验证集成效果 6.swagger常用注解 7.swagger使用综合案例 8.会议OA之sw…

第三章 Flink DataStream API

Flink 系列教程传送门 第一章 Flink 简介 第二章 Flink 环境部署 第三章 Flink DataStream API 第四章 Flink 窗口和水位线 第五章 Flink Table API&SQL 第六章 新闻热搜实时分析系统 一、DataStream API是什么&#xff1f; Flink 中的 DataStream 程序是对数据流&a…

Android 深入系统完全讲解(2)

1 系统启动过程、嵌入式系统启动过程 这是我之前画的启动过程的图&#xff0c;这个主要就是给大家讲明白&#xff0c;启动过程整个的流程。 第一个阶段&#xff0c;bootloader 系统在上电的时候&#xff0c;系统会从固定的地方加载一段代码进入内部 ram 进行运行。这段代码 通…

【数学思维】Quasi-convex and quesi-concave

【数学思维】Quasi-convex and quesi-concaveConvex function 定义如下 f(λx(1−λ)y)≤λf(x)(1−λ)f(y)f(\lambda x(1-\lambda)y)\le \lambda f(x)(1-\lambda)f(y) f(λx(1−λ)y)≤λf(x)(1−λ)f(y)Quasi-convex function 定义如下 f(λx(1−λ)y)≤max⁡{f(x),f(y)}f(\l…

【阶段三】Python机器学习01篇:机器学习概念、机器学习类别、机器学习应用场景与机器学习基本技术:特征、标签、模型

本篇的思维导图: 机器学习概念 机器学习是AI人工智能的分支技术,而深度学习是机器学习的重要分支。 人工智能、机器学习、深度学习三者的关系 机器学习就是从数据中发现规律,机器学习的关键内涵之一在于利用计算机的运算能力从大量的数据中发现一个“函数”或…

Linux——VMware Tools的介绍及安装方法

一、VMware Tools的作用 1.最大的好处是可以直接把windows界面的文件拖进linux虚拟机内。 2.鼠标可以直接从虚拟机移动到windows等等好处。 二、VMware Tools的安装步骤 1.首先把linux虚拟机关机或退出&#xff0c;然后点击“编辑虚拟机设置”。 2.点击CD/DVD&#xff0c;…

电子、半导体废水深度除氟、除重金属的技术详解

电子半导体行业废水来源及水质特点电子废水主要是印刷线路板中每个环节产生的废水&#xff0c;如线路板上的赋铜线路、电子元器件、二极管、三极管、电容等&#xff0c;日常生活中常见的就是电脑元器件&#xff0c;如内存条、CPU、主板等。这些东西在生产成型的过程中主要通过电…