指针的进阶

指针的主题,我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念:
1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。

 字符指针

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

一般使用

int main()
{char ch = 'w';char *pc = &ch;*pc = 'w';return 0;
}

还有一种使用方式如下:

int main()
{const char* pstr = "hello jzy.";//这里是把一个字符串放到pstr指针变量里了吗?不是的,是将字符串首元素地址传过去了,最好前边要加const 修饰常量字符串printf("%s\n", pstr);char arr[] = "abcdef";//[a b c d e f \0],这种初始化方式等价于char arr[]={'a','b','c','d','e','f','\0'};,因为字符串末尾自动跟一个\0return 0;
}

这个字符串是存在代码段的(内存的只读常量区)

26613168c6754d19b38a99e96258444c.png     “hello jzy."3001b7169e774c5c839fdad2305c6906.png

                                                         

                                                             

面试题:

#include <stdio.h>
int main()
{
char str1[] = "hello jzy.";
char str2[] = "hello jzy.";
const char *str3 = "hello jzy.";
const char *str4 = "hello jzy.";
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;
}
输出:
22b6360d11fd4477b0a8d02ebe2c52e0.png
这里str3和str4指向的是一个同一个常量字符串。(因为常量字符串不做修改,可以多个指针指向字符串)C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。

指针数组(就是存放指针的数组)

在《指针》章节我们也学了指针数组,指针数组是一个存放指针的数组。
这里我们再复习一下,下面指针数组是什么意思?
int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
我们看个例子
使用指针数组模拟实现二维数组
int main()
{int arr1[] = { 1,2,3,4,5 };//arr1 - int*int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };//指针数组int* arr[3] = { arr1, arr2, arr3 };int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", arr[i][j]);}printf("\n");}return 0;
}

数组名是数组首元素的地址,arr[1]拿到的是arr1数组名,再使用下标就能访问数组的元素,很简单

补充一下数组名的理解    数组名是数组首元素的地址
有2个例外:
1. sizeof(数组名),这里的数组名不是数组首元素的地址,数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
2. &数组名,这里的数组名表示整个数组, &数组名取出的是整个数组的地址
除此之外,所有的地方的数组名都是数组首元素的地址
825480c471ce4bce9488c3b36c459e85.png
可以看到sizeof数组名并不是4或者8,而是整个数组的大小40,这是一个例外
27bf26d87ce64f16b3866e7c707da8ce.png
可以看到arr+1跳过4个字节1个整形,说明arr正常是首元素地址
&arr+1,取出来整个数组的地址,所以+1会跳过整个数组40字节

数组指针(指向一个数组的指针)

数组指针的定义
数组指针是指针?还是数组?
答案是:指针。
我们已经熟悉:
整形指针: int * pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。
下面代码哪个是数组指针?
int *p1[10];//是存放指针的数组,指针数组
int (*p2)[10];//是指向数组的指针,数组指针
//p1, p2分别是什么?
解释
int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
数组指针的使用(遍历可以用到),p是一个数组指针变量,解引用p拿到数组名然后可以用方括号进行访问元素
4b6dae2a2f1349a6a6fe454eb2e3b908.png
void Print(int arr[3][5], int r, int 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("\n");}
}
int main()
{int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };Print(arr, 3, 5);return 0;
}

//数组指针怎么使用呢?一般在二维数组上才方便
//
//1 2 3 4 5
//2 3 4 5 6
//3 4 5 6 7
//

//二维数组传参,形参是二维数组的形式

也可以换一种形式

//二维数组传参,形参是指针的形式

void Print(int (*p)[5], int r, int c)
{
    int i = 0;
    for (i = 0; i < r; i++)
    {
        int j = 0;
        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 };

    //arr 是数组名,数组名表示数组首元素的地址

    Print(arr, 3, 5);
        
    return 0;
}


void test1(int arr[5], int sz)
{}
void test2(int* p, int sz)
{}int main()
{int arr[5] = { 0 };test1(arr, 5);test2(arr, 5);return 0;
}

//一维数组传参,形参的部分可以是数组,也可以是指针

void test3(char arr[3][5], int r, int c)
{}void test4(char (*p)[5], int r, int c)
{}
int main()
{char arr[3][5] = {0};test3(arr, 3, 5);test4(arr, 3, 5);return 0;
}

//二维数组传参,形参的部分可以是数组,也可以是指针

以上就是对数组指针的介绍

数组参数、指针参数

在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
1 一维数组传参
#include <stdio.h>
void test(int arr[])//ok?ok数组名传参可以是指针可以是数组
{}
void test(int arr[10])//ok?//ok注意方块里的数字填不填都可以,因为本质这部分不会创建数组,本质是指针
{}
void test(int *arr)//ok?//包ok的
{}
void test2(int *arr[20])//ok?包ok的是一个指针数组
{}
void test2(int **arr)//ok?包的,二级指针,一级指针数组首元素的地址包是二级指针类型
{}
int main()
{
int arr[10] = {0};
int *arr2[20] = {0};
test(arr);
test2(arr2);
}
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);
}

函数指针(指向函数的指针)

92817f90e18d45fab5482be3f274da77.png

看段代码

#include <stdio.h>
void test()
{printf("hehe\n");
}
int main()
{printf("%p\n", test);printf("%p\n", &test);return 0;
}

8c061b8883fb40129a652964e6e9d300.png

输出的是两个地址,这两个地址是 test 函数的地址。
那我们的函数的地址要想保存起来,怎么保存?
下面我们看代码:
void test()
{printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();//对
void *pfun2();//err
首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?
答案是:
pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。
我们来阅读两段有趣的代码
//代码1
(*(void (*)())0)();//本质上是调用0地址处的函数,先将0强制类型转换为void (*)()  类型的函数指针,后调用0地址处的函数-》函数名()只是没有传参
//代码2
void (*signal(int , void(*)(int)))(int);
:推荐《C陷阱和缺陷》
这本书中提及这两个代码。
代码2太复杂,如何简化:
typedef void(*pfun_t)(int);//把类型重命名
pfun_t signal(int, pfun_t);
函数指针数组
数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,
比如:
int *arr[10];
//数组的每个元素是int*
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
答案是:parr1
parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?
是 int (*)() 类型的函数指针。
用函数指针数组的方式实现一个转移表
//函数指针数组的方式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 main()
{int input = 0;int x = 0;int y = 0;int ret = 0;//函数指针数组的使用 - 转移表int (* pfArr[5])(int, int) = {NULL, Add, Sub, Mul, Div};//                            0     1    2    3    4do{menu();printf("请选择:>");scanf_s("%d", &input);if (input >= 1 && input <= 4){printf("请输入两个操作数:");scanf_s("%d %d", &x, &y);ret = pfArr[input](x, y);printf("ret = %d\n", ret);}else if(input == 0){printf("退出计算器\n");}else{printf("选择错误,重新选择\n");}} while (input);return 0;
}

830137abcc0049cebf0a0acb9d5adb44.png就可以玩了,原理代码就很清晰了

指向函数指针数组的指针(了解一下就行)
void test(const char* str)
{printf("%s\n", str);
}int main()
{void (*pf)(const char*) = test;//pf是函数指针变量void (*pfArr[10])(const char*);//pfArr是存放函数指针的数组void (* (*p) [10])(const char*) = &pfArr;//p指向函数指针数组的指针return 0;
}

回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
5ffe9c780b2841d78bd8b7ef3b06d69b.png
ffb5b7a4d0a543be82ac9632c5c8944d.png
2fa3193f1e184d289a21005b896ba063.png
6f48ee6af5dc41de8798c1bc1f595fbc.png
直接使用qsort
#include <stdlib.h>int cmp_int(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}void print(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}//测试qsort排序整型数据
void test1()
{int arr[10] = { 3,1,5,2,4,7,9,6,8,0 };int sz = sizeof(arr) / sizeof(arr[0]);//默认是升序的qsort(arr, sz, sizeof(arr[0]),cmp_int);//print(arr, sz);
}int main()
{test1();return 0;
}

这里前三个参数都很简单,第四个参数要自己写,通过自己比较的数据相邻数据的大小判断升序降序,左边数据大于右边数据,返回大于0的数字,默认是升序(这就是回调函数,我们把函数指针传递给参数,等我们真正需要用的时候会调用这个cmp_int函数)!!!注意,我们自己写的cmp_int函数一定要和库里的保持一致,参数是const void*,在cmp_int函数体内,我们想比较什么类型的数据,就强转为什么类型很方便)

ea2397f89e2e4b2facb5e6216e45c6d0.png

测试结构体年龄排序


#include <stdlib.h>
struct Stu
{char name[20];int age;
};int cmp_stu_by_age(const void* p1, const void* p2)
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}void test2()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}int main()
{test2();return 0;
}

387d44efb4b849ec8607ee3044d6ba3d.png

可以看到没排序之前是乱序

a38a7603c90649b0a9465e3d761d07e9.png

排序后是升序

测试结构体名字排序

#include <stdlib.h>
struct Stu
{char name[20];int age;
};int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}void test3()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}int main()
{test3();return 0;
}

没排序之前是乱序

c437dd59ab1945c291b77008bff2ea1d.png

排序之后变成升序了

注意我们为什么要用void*作为参数接受呢

int main()
{int a = 10;float f = 3.14f;//int* pa = &a;//char* pc = &a;//err会提示类型不兼容void* pv = &a;pv = &f;*pv;//errpv++;//errreturn 0;
}


void* 的指针 - 无具体类型的指针
void* 类型的指针可以接收任意类型的地址(可以看到pv可以接收int也可以接收float没有报错,不能进行解引用和++操作,因为void*类型不确定)

我们采用回调函数模拟实现qsort函数(用冒泡排序实现,但是底层是快排)
8c716bb5935c4635906d84d0ae13f1ba.png

排序整形严格按照库里qsort函数的参数(注意:交换函数要实现为char* 因为这样可以一次拿到一个字节,进行交换,理解为两个整数的交换就行)

void Swap(char* buf1, char* buf2, int size)//交换arr[j],arr[j+1]这两个元素
{int i = 0;char tmp = 0;for (i = 0; i < size; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{int  i = 0;//趟数for (i = 0; i < num - 1; i++){int j = 0;//一趟内部比较的对数for (j = 0; j < num - 1 - i; j++){//假设需要升序cmp返回>0,交换if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)//两个元素比较,需要将arr[j],arr[j+1]的地址要传给cmp{//交换Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}}int cmp_int(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}//测试bubble_sort 排序整型数据
void test1()
{int arr[10] = { 3,1,5,2,4,7,9,6,8,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
}
int main()
{test1();return 0;
}

没排序之前

124ab6beebd349de9947e11dfa7f0069.png

排序之后

0dd12ad3f4d44afd98fcc301dfbaee1d.png

测试结构体age

void Swap(char* buf1, char* buf2, int size)//交换arr[j],arr[j+1]这两个元素
{int i = 0;char tmp = 0;for (i = 0; i < size; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{int  i = 0;//趟数for (i = 0; i < num - 1; i++){int j = 0;//一趟内部比较的对数for (j = 0; j < num - 1 - i; j++){//假设需要升序cmp返回>0,交换if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)//两个元素比较,需要将arr[j],arr[j+1]的地址要传给cmp{//交换Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}}struct Stu
{char name[20];int age;
};int cmp_stu_by_age(const void* p1, const void* p2)
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}//测试bubble_sort 排序结构体数据
void test2()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}int main()
{test2();//test3();return 0;
}

没排序之前

7c772c7dcbfe44739fc319199d3389d6.png

排序之后可以看到以年龄排序成功

873d162792e84800a915664a5b4a03f0.png

测试结构体name

void Swap(char* buf1, char* buf2, int size)//交换arr[j],arr[j+1]这两个元素
{int i = 0;char tmp = 0;for (i = 0; i < size; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{int  i = 0;//趟数for (i = 0; i < num - 1; i++){int j = 0;//一趟内部比较的对数for (j = 0; j < num - 1 - i; j++){//假设需要升序cmp返回>0,交换if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)//两个元素比较,需要将arr[j],arr[j+1]的地址要传给cmp{//交换Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}}struct Stu
{char name[20];int age;
};int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}void test3()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]);printf("%d\n", sizeof(struct Stu));bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}int main()
{test3();return 0;
}

排序之前

9fb7faef6b0648a4b2caac8336fba67d.png

排序之后

499a31b6609f4ed1a2e5ad2cb21024d2.png

以上就是我对回调函数的认识和讲解,感谢支持!!!以后会创作更多有用文章

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

B站评论系统的多级存储架构

1. 背景 评论是 B站生态的重要组成部分&#xff0c;涵盖了 UP 主与用户的互动、平台内容的推荐与优化、社区文化建设以及用户情感满足。B站的评论区不仅是用户互动的核心场所&#xff0c;也是平台运营和用户粘性的关键因素之一&#xff0c;尤其是在与弹幕结合的情况下&#xf…

若依分页插件失效问题

若依对数据二次处理导致查询total只有十条的问题处理办法_若依分页查询total-CSDN博客

css盒子水平垂直居中

目录 1采用flex弹性布局&#xff1a; 2子绝父相margin&#xff1a;负值&#xff1a; 3.子绝父相margin:auto&#xff1a; 4子绝父相transform&#xff1a; 5通过伪元素 6table布局 7grid弹性布局 文字 水平垂直居中链接&#xff1a;文字水平垂直居中-CSDN博客 以下为盒子…

Golang Gin系列-3:Gin Framework的项目结构

在Gin教程的第3篇&#xff0c;我们将讨论如何设置你的项目。这不仅仅是把文件扔得到处都是&#xff0c;而是要对所有东西的位置做出明智的选择。相信我&#xff0c;这些东西很重要。如果你做得对&#xff0c;你的项目会更容易处理。当你以后不再为了找东西或添加新功能而绞尽脑…

03JavaWeb——Ajax-Vue-Element(项目实战)

1 Ajax 1.1 Ajax介绍 1.1.1 Ajax概述 我们前端页面中的数据&#xff0c;如下图所示的表格中的学生信息&#xff0c;应该来自于后台&#xff0c;那么我们的后台和前端是互不影响的2个程序&#xff0c;那么我们前端应该如何从后台获取数据呢&#xff1f;因为是2个程序&#xf…

【无法下载github文件】虚拟机下ubuntu无法拉取github文件

修改hosts来进行解决。 步骤一&#xff1a;打开hosts文件 sudo vim /etc/hosts步骤二&#xff1a;查询 github.com的ip地址 https://sites.ipaddress.com/github.com/#ipinfo将github.com的ip地址添加到hosts文件末尾&#xff0c;如下所示。 140.82.114.3 github.com步骤三…

【Idea启动项目报错NegativeArraySizeException】

项目场景&#xff1a; Idea启动项目报错&#xff08;打包不报错&#xff09;&#xff0c;项目在服务器部署运行没有问题&#xff0c;尝试了重启idea、重启电脑、maven clean/install 都不行 maven-resources-production:sample: java.lang.NegativeArraySizeException: -5833…

【 MySQL 学习2】常用命令

文章目录 一、基础命令1.1、登录1.2 、退出1.3、查看数据库中有哪些数据库1.4 、选择使用的数据库1.5、创建数据库1.6 查看哪个数据库下有哪些表 二、SQL语句的分类2.1 DQL 数据查询语言2.2 DML 数据操作语言2.3 DDL 数据定义语言2.4 TCL 事物控制语言2.5 DCL 数据控制语言 三、…

JVM直击重点

JVM直击重点 JVM内存模型 JVM中类加载器分类与核心功能 Java里有如下几种类加载器 引导类加载器&#xff1a;负责加载支撑JVM运行的位于JRE的lib目录下的核心类库&#xff0c;比如rt.jar、charsets.jar等 扩展类加载器&#xff1a;负责加载支撑JVM运行的位于JRE的lib目录下的e…

图数据库 | 18、高可用分布式设计(中)

上文我们聊了在设计高性能、高可用图数据库的时候&#xff0c;从单实例、单节点出发&#xff0c;一般有3种架构演进选项&#xff1a;主备高可用&#xff0c;今天我们具体讲讲分布式共识&#xff0c;以及大规模水平分布式。 主备高可用、分布式共识、大规模水平分布式&#xff…

【常见BUG】Spring Boot 和 Springfox(Swagger)版本兼容问题

???欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老…

关于vite+vue3+ts项目中env.d.ts 文件详解

env.d.ts 文件是 Vite 项目中用于定义全局类型声明的 TypeScript 文件。它帮助开发者向 TypeScript提供全局的类型提示&#xff0c;特别是在使用一些特定于 Vite 的功能时&#xff08;如 import.meta.env&#xff09;。以下是详细讲解及代码示例 文章目录 **1. env.d.ts 文件的…

数字化时代,传统代理模式的变革之路

在数字化飞速发展的今天&#xff0c;线上线下融合&#xff08;O2O&#xff09;成了商业领域的大趋势。这股潮流&#xff0c;正猛烈冲击着传统代理模式&#xff0c;给它带来了新的改变。 咱们先看看线上线下融合现在啥情况。线上渠道那是越来越多&#xff0c;企业纷纷在电商平台…

接口测试自动化实战(超详细的)

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 前言 自从看到阿里云性能测试 PTS 接口测试开启免费公测&#xff0c;就想着跟大家分享交流一下如何实现高效的接口测试为出发点&#xff0c;本文包含了我在接口测…

意图颠覆电影行业的视频生成模型:Runway的Gen系列

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍Runway开发的视频生成模型Gen系列&#xff0c;包括Gen-1、Gen-2和Gen3 Alpha等&#xff0c;这些模型每次发布都震惊AI圈&#xff0c;荣获多个视频生成的…

ant design vue的级联选择器cascader的悬浮层样式怎么修改

平时想要修改组件内定样式会使用穿透deep和&#xff01;important调优先级&#xff0c;但是在这里都不行&#xff0c;样式都不能改变 后来尝试出来是因为加了scoped&#xff0c;样式不起作用&#xff0c;但是不能直接去掉scoped&#xff0c;别的样式会受到影响&#xff0c;单独…

linux手动安装mysql5.7

一、下载mysql5.7 1、可以去官方网站下载mysql-5.7.24-linux-glibc2.12-x86_64.tar压缩包&#xff1a; https://downloads.mysql.com/archives/community/ 2、在线下载&#xff0c;使用wget命令&#xff0c;直接从官网下载到linux服务器上 wget https://downloads.mysql.co…

使用 ChatGPT 生成和改进你的论文

文章目录 零、前言一、操作引导二、 生成段落或文章片段三、重写段落四、扩展内容五、生成大纲内容六、提高清晰度和精准度七、解决特定的写作挑战八、感受 零、前言 我是虚竹哥&#xff0c;目标是带十万人玩转ChatGPT。 ChatGPT 是一个非常有用的工具&#xff0c;可以帮助你…

TinyEngine v2.1版本发布:全新的区块方案和画布通信方案,打造更强力的可拓展低代码引擎

前言 2025年蛇年已经到来&#xff0c;TinyEngine v2.1.0 版本也已经蛇气腾腾的发布了出来&#xff0c;新年新气象&#xff0c;为了让大家更详细了解到 v2.1.0 的内容更新&#xff0c;我们特此列举了该版本中的一些重要特性更新。 v2.1.0变更特性概览 1、使用了新的纯前端区块…

【机器学习实战入门】使用OpenCV和Keras的驾驶员疲劳检测系统

嗜睡驾驶者警报系统 防止司机疲劳驾驶警报系统 中级 Python 项目 - 司机疲劳检测系统 疲劳检测是一种安全技术&#xff0c;能够预防因司机在驾驶过程中入睡而造成的事故。 本中级 Python 项目的目标是建立一个疲劳检测系统&#xff0c;该系统将检测到一个人的眼睛闭合了一段时…