c/c++开发,无可避免的函数指针使用案例
一、函数指针简介
函数指针是指指向函数而非指向对象的指针。像其他指针一样,函数指针也指向某个特定的类型。函数类型由其返回类型以及形参表确定,而与函数名无关。例如:
char* (*pf1)(char * p1,char *p2);
这是一个函数指针,其真实词意如果转换一下,似乎更好理解,只是编译器不会这样排版而已:
char* (*)(char * p1,char *p2) pf1;
将 pf 声明为指向函数的指针,它所指向的函数带有两个 char* 类型的形参和 char*类型的返回值。注意*pf两侧的圆括号是必需的,否则就成了返回char**类型的普通函数声明了。参数类型是必须的,但参数名不是必须的,可以省略。
char* (*pf1)(char*,char*); //char* (*)(char*,char*) pf1;
通常我们在开发中,尤其是应用层级开发中,较少使用函数指针,也不建议使用函数指针。但是由于函数指针其特殊性,可以迸发出很多巧妙的代码组织方法,尤其是很多底层驱动开发中,不少地方都会用到函数指针。
函数指针类型相当地冗长。使用 typedef 为指针类型定义同义词,可将函数指针的使用大大简化:
typedef char* (*pfunc)(char*, char*);
声明函数指针后,需要给予初始化或赋值才能使用:
//先定义一个真实函数
char* fun1(char * p1,char *p2)
{return ((0== strcmp(p1,p2))?p1:p2);
};//char* (*pf1)(char*, char*) = &fun1;//直接初始化
char* (*pf1)(char*, char*);
pf1 = &fun1; //fun1等同于char* (*)(char*, char*)char p1[]="abc";
char p2[]="bcd";
printf("%s\n", (*pf1)(p1,p2));
函数名作为右值时,在引用函数名但又没有调用该函数时,函数名将被自动解释为指向函数的指针,因此采用&func或func给函数指针赋值都可以,以下四种中可以达成目的。
//func1除了用作函数调用的左操作数以外,对 fun1的任何使用都被解释为char* (*)(char*, char*)
char* (*pf1)(char*, char*) = fun1;//直接初始化
char* (*pf2)(char*, char*) = &fun1;//直接初始化
char* (*pf3)(char*, char*);//
char* (*pf4)(char*, char*);//
pf3 = func1;
pf4 = &func1;
typedef char* (*pfunc)(char*, char*);
pfunc pf5 = fun1;
pfunc pf6 = &fun1;
函数指针只能通过同类型的函数或函数指针或 0 值常量表达式进行初始化或赋值。
char* (*pf1)(char*, char*);
pf1 = &fun1;
//
pfunc pf3 = 0; //初始化为 0,表示该指针不指向任何函数
pf3 = pf1;char fun3(char * p1,char *p2)
{return ((0== strcmp(p1,p2))?p1:p2);
};char* fun4(char p1,char p2)
{return ((0== strcmp(p1,p2))?p1:p2);
};pf3 = fun3;//eroor,返回类型不一致
pf3 = &fun4;//eroor,参数类型不一致
指向函数的指针可用于调用它所指向的函数,支持显式或隐式方式。
char p1[]="abc";char p2[]="bcd";pfunc pf3 = fun1;printf("%s\n",pf3(p1,p2)); //隐式调用printf("%s\n",(*pf3)(p1,p2)); //显式调用
二、函数指针数组
函数指针数组,就是将函数指针存储在一个数组里,假设定义一个函数指针:
void (*FuncPtr)();
那么FuncPtr就是一个函数指针,既然 FuncPtr是一个指针,那就可以储存在一个数组里。
void (*FuncPtr[])();
或
void (*FuncPtr[2])();
定义一个函数指针数组。它是一个数组,数组名为 FuncPtr,数组内存储了 3 个或未知个数的指向函数的指针。这些指针指向一些返回值类型为void、无参数的函数。当然也可以通过typedef 修饰一下,就像定义变量数组一样:
typedef void (*FuncPtr)(); FuncPtr pfunc[3];
同样,函数指针数组也要给其赋值,指向真实函数地址才能使用
typedef void (*FuncPtr)(); void doSomething1()
{printf("doSomething1\n");
};void doSomething2()
{printf("doSomething2\n");
};void (*fp[2])();
fp[0] = &doSomething1;
fp[1] = doSomething2;
fp[0](); //函数调用FuncPtr pfunc[2] = {doSomething1,doSomething2};
pfunc[0](); //函数调用FuncPtr funcPtrArray[2];
funcPtrArray[0] = doSomething1; //可以直接用函数名
funcPtrArray[1] = &doSomething2; //可以用函数名加上取地址符
funcPtrArray[1](); //函数调用
如果函数返回值有差异,但又想通过函数指针数组归一化起来,在确保安全前提下,通过reinterpret_cast进行函数指针类型转换,注意c中是没有reinterpret_cast的。
typedef void (*FuncPtr)(); int doSomething3()
{printf("doSomething3\n");return 0;
};FuncPtr funcPtrArray[3];
funcPtrArray[0] = doSomething1; //可以直接用函数名
funcPtrArray[1] = &doSomething2; //可以用函数名加上取地址符
//注意doSomething3直接传递是类型不符合的,它对于funcPtrArray是一个错误类型
//c++的reinterpret_cast 可以让你迫使编译器以你的方法去处理,
//转换函数指针,C++不保证所有的函数指针都被用一样的方法表示,在一些情况下这样的转换会产生不正确的结果.
funcPtrArray[2] = reinterpret_cast<FuncPtr>(&doSomething3);//g++编译时生效
c语言编译时,可仿c++的reinterpret_cast创建一个类似的带参宏定义
#define reinterpret_cast(TYPE,EXPR) ((TYPE)(EXPR)) //仿c++的reinterpret_cast
//此处省略声明定义
funcPtrArray[2] = reinterpret_cast(FuncPtr, &doSomething3);//gcc或g++编译时都生效
funcPtrArray[2]();
三、指向重载函数的指针
C++语言允许使用函数指针指向重载的函数,假设在func.cpp中定义了函数func_test
//func.cpp
int func_test(int p1, int p2)
{return (p1<p2)?p1:p2;
};
在test.cpp中通过extern实现重载。
//test.cpp
extern int func_test(int, int);//指向重载函数的指针
int (*pft)(int, int) = &func_test;
printf("min_val:%d\n", (*pft)(6,7));
注意指针的类型必须与重载函数的精确匹配。如果没有精确匹配的函数,则对该指针的初始化或赋值都将导致编译错误:
//test.cpp
int (*pf2)(int) = &func_test; //error,invalid parametervoid (*pf3)(int, int);
pf3 = &func_test; // error, invalid return type
四、函数指针作为类成员
函数指针也是指针,既然是指针,就可以作为类成员变量来使用。
假设有如下类CallBack,其成员变量是一个函数指针CallBackPtr及void*的指针,初始化时传递一个函数指针和void变量指针实现初始化,通过doCallBack函数具体调用CallBackPtr实现函数回调:
//在传递函数指针时,建议进行这种异常规格的检查
typedef void (*CallBackPtr)(void *data_);
class CallBack
{public:CallBack(CallBackPtr fPtr, void *data_): func(fPtr), data(data_) {}void doCallBack() const throw();
private:CallBackPtr func; // function to call when// callback is madevoid *data; // data to pass to callback
}; void CallBack::doCallBack() const throw()
{func(data);
}
该类使用时,定义具体业务实现的真实函数,将这些函数指针传递给CallBack类,业务实际需要滞后到实现具体业务场景明确时,实现其逻辑,如下:
void callBackFcn1(void *data_)
{printf("callBackFcn1\n");
};void callBackFcn2(void *data_) throw()
{printf("callBackFcn2\n");
};
//回调,将具体实现放置构造传递进去的具体函数指针
void *callBackData;
CallBack c_instance(callBackFcn1,callBackData);
c_instance.doCallBack();
在传递函数指针时,建议进行这种异常规格的检查,大家可以想想如下函数指针定义时,指向的真实函数定义该如何设计。
typedef void (*CallBackPtr)(void *data_) throw();//如果不提供异常说明,该指针就可以指向能够抛出任意类型异常的具有匹配类型的函数
typedef void (*CallBackPtr)(void *data_) throw(runtime_error);//指定异常时,只能抛出 runtime_error 类型的异常
typedef void (*CallBackPtr)(void *data_) const throw();//类型定义更严格,源指针的异常说明必须至少与目标指针的一样严格
五、函数指针作为形参
函数指针也是指针,既然是指针,就可以作为参数传递给函数,函数就可以在其内部使用该函数指针,从而实现对函数指针指向的函数进行调用,实现函数回调。想STL标准了内的排序函数 模板就是通过传递一个数值比较的函数指针来实现的。
//函数指针作为形参
class Aclass
{
public:Aclass(void* data_) : vdata(data_){};void doSomething1(){printf("Aclass doSomething1\n");};void doSomething2(){printf("Aclass doSomething2\n");};void* getData(){return vdata;}
private:void* vdata;
};void myfunc(Aclass* ptr,void* data,bool (*compare)(void const* pd1, void const* pd2))
{if(compare(ptr->getData(),data))ptr->doSomething1();elseptr->doSomething2();
};bool compare_int(void const* pd1, void const* pd2)
{return (*(int*)pd1=*(int*)pd2)?false:true;
}
如上述代码,myfunc函数参数很复杂,将类对象,数值及函数指针作为参数传递进去。函数指针通过重定义一下:
typedef bool (*Func_compare)(void const* pd1, void const* pd2);void myfunc(Aclass* ptr,void* data,Func_compare compare);
函数调用如下:
int aval = 10;
int bval = 8;
Aclass a_obj(&aval);
myfunc(&a_obj,&bval,compare_int); //
六、 转换表
有以下一种功能要求,这是一个实现操作符计算的分支设计,如果有上百个操作符需要实现呢,这个switch语句就会很长:
switch(oper)
{case ADD:ret = add(val1,val2);break;case SUB:ret = sub(val1,val2);break;case MUL:ret = mul(val1,val2);break;case DIV:ret = div(val1,val2);break;......default:ret = 0.0;break;
}
通过建立一个函数指针数组作为转换表,就可以实现类似switch的代码功能:
enum Method{ADD=0,SUB,MUL,DIV
};typedef float (*operFunc)(float fa, float fb);operFunc operf[] = {add,sub,mul,div};
其调用如下,确保这些操作符函数定义在初始化列表之前,初始化列表的函数名顺序取决与程序用来表示每个操作符的整型代码。
//操作符具体实现函数
float add(float fa, float fb)
{return fa+fb;
};
float sub(float fa, float fb)
{return fa-fb;
};
float mul(float fa, float fb)
{return fa*fb;
};const float abs_min_val = 0.0000001;
float div(float fa, float fb)
{if((fb<abs_min_val)&&(fb>(-abs_min_val))){return fa/abs_min_val;}return fa/fb;
};
//函数指针数组调用,实现类switch功能
float afval = 7.8, bfval = 8.5;
float ret_fval = operf[Method::ADD](afval,bfval);
printf("%0.4f\n",ret_fval);
七、类函数指针成员与注册函数
函数指针是指针,就可以作为类的成员变量,通常用于将具体业务实现剥离在类外定义实现,在业务执行是注册到类中,从而针对具体业务来调用需要的业务模块(业务函数)。
//函数指针成员,注册函数
class BClass
{
public:BClass(){my_compare = NULL;};typedef bool (*compare)(void const* pd1, void const* pd2);void my_compare_do(void const* cm1, void const* cm2) //具体实现通过调用外部函数{if(NULL==my_compare)printf("please register func first!\n");if(my_compare(cm1,cm2)) //外部函数调用printf("do it A\n");elseprintf("do it B\n");};void register_func(compare cp1) //注册函数{my_compare = cp1;};compare my_compare; //函数指针变量
};
在具体业务逻辑过程中,先给类实例注入调用函数,在调用具体函数实现业务逻辑。
//注册函数,实现外部函数调用BClass bObject;bObject.register_func(compare_int);bObject.my_compare_do(&aval,&bval);
八、类函数指针数组
同样地,函数指针还可以通过函数指针数组作为类成员列表,这些成员的实现细节当然是可以在类内部,外部或外部模块来具体实现。例如,在很多程序会这样做,在程序中的每个类只要声明函数指针数组,然后在外部来具体实现业务,甚至是交给二次开发者依据具体业务场景来针对性设计功能函数。
//类函数指针数组test(成员列表)
class OperateKey
{
public:OperateKey(){};enum Directions { FORWARD, BACK, UP, DOWN };OperateKey& do_it(Directions direct_){ //记需要有效性判断(this->vtbl[direct_])();return *this;};typedef void (*Action)();static Action vtbl[];
};void forward()
{printf("forward\n");
};
void back()
{printf("back\n");
};
void up()
{printf("up\n");
};
void down()
{printf("down\n");
};OperateKey::Action OperateKey::vtbl[] = {forward, back, up, down};
类的函数指针数组成员列表就像平常类实例使用数组变量一样使用即可,只是数组变量是作为变量使用,而函数指针数组存储的是函数名(函数地址),作为一个个函数使用。
//类函数指针数组成员列表OperateKey myKeys;myKeys.do_it(OperateKey::FORWARD);myKeys.do_it(OperateKey::DOWN);
九、函数指针测试案例
创建test.cpp和func.cpp文件,主要是代码是在test.cpp中实现。
func.cpp,用来测试函数指针重载
//
int func_test(int p1, int p2)
{return (p1<p2)?p1:p2;
};
test.cpp
#include <string.h>
#include <stdio.h>//函数指针test
char* fun1(char * p1,char *p2)
{return ((0== strcmp(p1,p2))?p1:p2);
};char* fun2(char * p1,char *p2)
{return ((0== strcmp(p1,p2))?p2:p1);
};typedef char* (*pfunc)(char*, char*);//函数指针数组test
typedef void (*FuncPtr)(); #define reinterpret_cast(TYPE,EXPR) ((TYPE)(EXPR))void doSomething1()
{printf("doSomething1\n");
};void doSomething2()
{printf("doSomething2\n");
};int doSomething3()
{printf("doSomething3\n");return 0;
};
//
extern int func_test(int, int);
//回调
//在传递函数指针时,建议进行这种异常规格的检查
typedef void (*CallBackPtr)(void *data_);
//typedef void (*CallBackPtr)(void *data_) throw();?//如果不提供异常说明,该指针就可以指向能够抛出任意类型异常的具有匹配类型的函数
//typedef void (*CallBackPtr)(void *data_) throw(runtime_error);?//指定异常时,只能抛出 runtime_error 类型的异常
//typedef void (*CallBackPtr)(void *data_) const throw();?//类型定义更严格,源指针的异常说明必须至少与目标指针的一样严格
class CallBack
{public:CallBack(CallBackPtr fPtr, void *data_): func(fPtr), data(data_) {}void doCallBack() const throw();
private:CallBackPtr func; // function to call when// callback is madevoid *data; // data to pass to callback
}; void CallBack::doCallBack() const throw()
{func(data);
}void callBackFcn1(void *data_)
{printf("callBackFcn1\n");
};void callBackFcn2(void *data_) throw()
{printf("callBackFcn2\n");
};//函数指针作为形参
class Aclass
{
public:Aclass(void* data_) : vdata(data_){};void doSomething1(){printf("Aclass doSomething1\n");};void doSomething2(){printf("Aclass doSomething2\n");};void* getData(){return vdata;}
private:void* vdata;
};void myfunc(Aclass* ptr,void* data,bool (*compare)(void const* pd1, void const* pd2))
{if(compare(ptr->getData(),data))ptr->doSomething1();elseptr->doSomething2();
};bool compare_int(void const* pd1, void const* pd2)
{return (*(int*)pd1=*(int*)pd2)?false:true;
}
//
float add(float fa, float fb)
{return fa+fb;
};
float sub(float fa, float fb)
{return fa-fb;
};
float mul(float fa, float fb)
{return fa*fb;
};const float abs_min_val = 0.0000001;
float div(float fa, float fb)
{if((fb<abs_min_val)&&(fb>(-abs_min_val))){return fa/abs_min_val;}return fa/fb;
};typedef float (*operFunc)(float fa, float fb);enum Method{ADD=0,SUB,MUL,DIV
};
//函数指针成员,注册函数
class BClass
{
public:BClass(){my_compare = NULL;};typedef bool (*compare)(void const* pd1, void const* pd2);void my_compare_do(void const* cm1, void const* cm2){if(NULL==my_compare)printf("please register func first!\n");if(my_compare(cm1,cm2))printf("do it A\n");elseprintf("do it B\n");};void register_func(compare cp1){my_compare = cp1;};compare my_compare;
};//类函数指针数组test(成员列表)
class OperateKey
{
public:OperateKey(){};enum Directions { FORWARD, BACK, UP, DOWN };OperateKey& do_it(Directions direct_){(this->vtbl[direct_])();return *this;};typedef void (*Action)();static Action vtbl[];
};void forward()
{printf("forward\n");
};
void back()
{printf("back\n");
};
void up()
{printf("up\n");
};
void down()
{printf("down\n");
};OperateKey::Action OperateKey::vtbl[] = {forward, back, up, down};int main(int argc, char* argv[])
{//char* (*pf1)(char * p1,char *p2);//定义一个函数指针变量,等同于下一句char* (*pf1)(char*, char*);char p1[]="abc";char p2[]="bcd";//char* (*pf1)(char*, char*) = &fun1;//直接初始化pf1 = &fun1; //fun等同于char* (*)(char*, char*)printf("%s\n", (*pf1)(p1,p2));pfunc pf2=fun2;printf("%s\n",(*pf2)(p1,p2));//pfunc pf3 = 0; //初始化为 0,表示该指针不指向任何函数pf3 = pf1;printf("%s\n",pf3(p1,p2)); //隐式调用pf3 = pf2;printf("%s\n",(*pf3)(p1,p2)); //显式调用//FuncPtr pfuncs[2] = {doSomething1,doSomething2};FuncPtr funcPtrArray[3];funcPtrArray[0] = doSomething1; //可以直接用函数名funcPtrArray[1] = &doSomething2; //可以用函数名加上取地址符//注意doSomething3直接传递是类型不符合的,它对于funcPtrArray是一个错误类型//c++的reinterpret_cast 可以让你迫使编译器以你的方法去处理,//转换函数指针,C++不保证所有的函数指针都被用一样的方法表示,在一些情况下这样的转换会产生不正确的结果.//funcPtrArray[2] = reinterpret_cast<FuncPtr>(&doSomething3);//g++编译时生效funcPtrArray[2] = reinterpret_cast(FuncPtr, &doSomething3);//gcc或g++编译时都生效for(int i=0; i<3; i++){funcPtrArray[i]();}//指向重载函数的指针int (*pft)(int, int) = &func_test;printf("min_val:%d\n", (*pft)(6,7));//回调void *callBackData;CallBack c_instance(callBackFcn1,callBackData);c_instance.doCallBack();//int aval = 10;int bval = 8;Aclass a_obj(&aval);myfunc(&a_obj,&bval,compare_int); ////operFunc operf[] = {add,sub,mul,div};float afval = 7.8, bfval = 8.5;float ret_fval = operf[Method::ADD](afval,bfval);printf("%0.4f\n",ret_fval);//注册函数BClass bObject;bObject.register_func(compare_int);bObject.my_compare_do(&aval,&bval);//类函数指针数组成员列表OperateKey myKeys;myKeys.do_it(OperateKey::FORWARD);myKeys.do_it(OperateKey::DOWN);return 0;
}
编译
g++ test.cpp func.cpp -o test.exe
或者构建Makefile文件,调用make指令
CX = g++BIN := .
TARGET := test.exe
FLAGS := Include := .
source := test.cpp func.cpp
$(TARGET) :$(CX) $(FLAGS) $(source) -I$(Include) -o $(BIN)/$(TARGET)clean:rm $(BIN)/$(TARGET)
测试程序运行输出如下:
相关文章:
c/c++开发,无可避免的函数指针使用案例
一、函数指针简介 函数指针是指指向函数而非指向对象的指针。像其他指针一样,函数指针也指向某个特定的类型。函数类型由其返回类型以及形参表确定,而与函数名无关。例如: char* (*pf1)(char * p1,char *p2); 这是一个函数指针,其…...
QT(12)-QThreadPool
1 简介 QThreadPool是Qt框架中的一个类,提供了一组工作线程池。该线程池自动管理一组工作线程,在线程可用时分配任务。使用线程池的主要优点是,它可以减少创建和销毁线程的开销,因为可以重复使用线程。 线程池设计用于场景中&am…...
【Java|golang】1138. 字母板上的路径
我们从一块字母板上的位置 (0, 0) 出发,该坐标对应的字符为 board[0][0]。 在本题里,字母板为board [“abcde”, “fghij”, “klmno”, “pqrst”, “uvwxy”, “z”],如下所示。 我们可以按下面的指令规则行动: 如果方格存…...
Flink 1.14从简单到源码第三讲
文章目录 1.flink多流操作Api1.1split 分流操作1.2.侧输出流1.3.connect 连接操作1.4.union 操作1.5 coGroup 协同分组1.6 join1.7 broadcast 广播2.process3.并行度和Api3.1 任务提交简单流程3.2 task与算子链4. Flink 时间相关(窗口计算)4.1时间语义(窗口计算)4.2 新版api指定…...
淘宝API接口系列,获取购买到的商品订单列表,卖出的商品订单列表,订单详情,订单物流,买家信息,收货地址列表,买家token
custom自定义API操作buyer_order_list获取购买到的商品订单列表buyer_order_detail获取购买到的商品订单详情buyer_order_express获取购买到的商品订单物流buyer_address_list收货地址列表buyer_address_add添加收货地址buyer_info买家信息buyer_token买家tokenseller_order_li…...
ucos-ii 的任务调度原理和实现
ucosii 任务调度和原理1、ucos-ii 任务创建与任务调度 1.1、任务的创建 当你调用 OSTaskCreate( ) 进行任务的创建的时候,会初始化任务的堆栈、保存cpu的寄存器、创建任务的控制块(OS_TCB)等的操作; if (OSTCBPrioTbl[prio] (OS_…...
Solon2 开发之容器,七、切面与函数环绕拦截
想要环绕拦截一个 Bean 的函数。需要三个前置条件: 通过注解做为“切点”,进行拦截(不能无缘无故给拦了吧?费性能)Bean 的 method 是被代理的在 Bean 被扫描之前,完成环绕拦截的注册 1、定义切点和注册环…...
代码随想录第十天(28)
文章目录28. 找出字符串中第一个匹配项的下标看答案KMPnext数组(前缀表)最长公共前后缀如何计算前缀表前缀表与next数组时间复杂度分析28. 找出字符串中第一个匹配项的下标 莫得思路……好久没做题,都已经忘得差不多了 看答案 其实就是自己…...
循环队列来了解一下!!
笔者在之前的一篇文章,详细的介绍了:队列之单向链表与双向链表的模拟实现:https://blog.csdn.net/weixin_64308540/article/details/128742090?spm1001.2014.3001.5502 感兴趣的各位老铁,可以参考一下啦!下面进入循环…...
Idea打包springboot项目war包,测试通过
pom.xml文件 <!--包名以及版本号,这个是打包时候使用,版本可写可不写,建议写有利于维护系统--> <artifactId>tsgdemo</artifactId> <version>0.0.1-SNAPSHOT</version> <!--打包形式--> <packaging&…...
python+django高校师生健康信息管理系统pycharm
管理员功能模块 4.1登录页面 管理员登录,通过填写注册时输入的用户名、密码、角色进行登录,如图所示。 4.2系统首页 管理员登录进入师生健康信息管理系统可以查看个人中心、学生管理、教师管理、数据收集管理、问卷分类管理、疫情问卷管理、问卷调查管理…...
CUDA中的流序内存分配
文章目录CUDA中的流序内存分配1. Introduction2. Query for Support3. API Fundamentals (cudaMallocAsync and cudaFreeAsync)4. Memory Pools and the cudaMemPool_t注意:设备的内存池当前将是该设备的本地。因此,在不指定内存池的情况下进行分配将始终…...
开源、低成本的 Xilinx FPGA 下载器(高速30MHz)
目前主流的Xilinx下载器主要有两种:一种是Xilinx官方出品的Xilinx Platfom Cable USB,还有一个就是Xilinx的合作伙伴Digilent开发的JTAG-HS3 Programming Cable。 JTAG-HS系列最大支持30MHz下载速度,基于FTDI的FT2232方案。 JTAG-HS系列对比…...
Maven专题总结
1. 什么是Maven Maven 是一个项目管理工具,它包含了一个项目对象模型 (POM: Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和…...
谷粒商城--SPU和SKU
目录 1.SPU和SKU概念 2.表的关系理解 3.导入前端代码 4.完善后端接口 5.属性分组详情 6.规格参数详情 7. 销售属性详情 8.分组与属性关联 9.发布商品 10.仓库服务 1.SPU和SKU概念 SPU:standard product unit(标准化产品单元):是商品信息聚合的…...
二叉树OJ题(上)
✅每日一练:100. 相同的树 - 力扣(LeetCode) 题目的意思是俩棵树的结构不仅要相同,而且每个节点的值还要相同,如果满足上面2个条件,则成立! 解题思路: 从三个方面去考虑࿱…...
第一章 PDF语法
第一章 PDF语法PDF ObjectsNull ObjectsBoolean ObjectsNumeric ObjectsName ObjectsString ObjectsArray ObjectsDictionary ObjectsName treesNumber treesStream ObjectsDirect versus Indirect ObjectsFile StructureWhite-SpaceThe Four Sections of a PDFHeaderTrailerBo…...
IntelliJ IDEA 创建JavaFX项目运行
IntelliJ IDEA 创建JavaFX项目运行JavaFX官网文档:https://openjfx.io/openjfx-docs/ JavaFX 2008年12月05日诞生,是一个开源的下一代客户端应用程序平台,适用于基于 Java 构建的桌面、移动和嵌入式系统。这是许多个人和公司的协作努力&#…...
IC封装常见形式
参考:https://blog.csdn.net/dhs888888/article/details/127673300?utm_mediumdistribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-127673300-blog-115610343.pc_relevant_multi_platform_whitelistv4&spm1001.2101.3001.4242…...
Linux通配符、转义符讲解
目录 通配符 通过通配符定义匹配条件 转义符 将所有的逻辑操作符都转换成字符 通配符 通过通配符定义匹配条件 * 任意字符都可以通配(也可以匹配空值) ? 匹配单个字符 [a-z] 匹配单个的小写英文字母 [A-Z] 匹配单个的大写英文…...
[OpenMMLab]提交pr时所需的git操作
git开发流程 准备工作 作为一个开发者,fork一个仓库之后应该先做什么? 1、下载仓库,创建上游代码库,查看当前的分支情况 git clone https://github.com/<your_name>/<repo_name>.git git remote add upstream git…...
pandas——groupby操作
Pandas——groupby操作 文章目录Pandas——groupby操作一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤一、实验目的 熟练掌握pandas中的groupby操作 二、实验原理 groupby(byNone, axis0, levelNone, as_indexTrue, sortTrue, group_keysTrue, squeezeFalse&…...
webpack.config.js哪里找?react项目关闭eslint监测
目录 webpack.config.js哪里找? react项目关闭eslint监测 webpack.config.js哪里找? 在React项目中,当我们需要修改一些配置时,发现找不到webpack.config.js,是我们创建的项目有问题吗,还需新创建项目的项…...
OpenCV 图像梯度算子
本文是OpenCV图像视觉入门之路的第12篇文章,本文详细的介绍了图像梯度算子的各种操作,例如:Sobel算子Scharr算子laplacian算子等操作。 OpenCV 图像梯度算子目录 1 Sobel算子 2 Scharr算子 3 laplacian算子 1 Sobel算子 Sobel算子是一种图…...
Linux c编程之Wireshark
Wireshark是一个网络报文分析软件,是网络应用问题分析必不可少的工具软件。网络管理员可以使用wireshark排查网络问题。程序开发人员可以用来分析应用协议、定位分析应用问题。无论是网络应用程序开发人员、测试人员、部署人员、技术支持人员,掌握wireshark的使用对于分析网络…...
极客时间_FlinkSQL 实战
一、批处理以及流处理技术发展 1.Lambda架构三层划分Batch Layer、Speed Layer和Serving Layer。 ①、Batch Layer:主要用于实现对历史数据计算结果的保存,每天计算的结果都保存成为一个Batch View,然后通过对Batch View的计算,实现历史数据的计算。 ②、Speed Layer正是用…...
Pytorch 混合精度训练 (Automatically Mixed Precision, AMP)
Contents混合精度训练 (Mixed Precision Training)单精度浮点数 (FP32) 和半精度浮点数 (FP16)为什么要用 FP16为什么只用 FP16 会有问题解决方案损失缩放 (Loss Scaling)FP32 权重备份黑名单Tensor CoreNVIDIA apex 库代码解读opt-level (o1, o2, o3, o4)apex 的 o1 实现apex …...
使用太极taichi写一个只有一个三角形的有限元
公式来源 https://blog.csdn.net/weixin_43940314/article/details/128935230 GAME103 https://games-cn.org/games103-slides/ 初始化我们的三角形 全局的坐标范围为0-1 我们的三角形如图所示 ti.kernel def init():X[0] [0.5, 0.5]X[1] [0.5, 0.6]X[2] [0.6, 0.5]x[0…...
进程,线程
进程是操作系统分配资源的基本单位,线程是CPU调度的基本单位。 PCB:进程控制块,操作系统描述程序的运行状态,通过结构体task,struct{…},统称为PCB(process control block)。是进程管理和控制的…...
第03章_基本的SELECT语句
第03章_基本的SELECT语句 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 1. SQL概述 1.1 SQL背景知识 1946 年,世界上第一台电脑诞生,如今,借由这台电脑发展…...
网站建设没有图片/嘉兴百度seo
一、list转字符串命令:.join(list)其中,引号中是字符之间的分割符,如“,”,“;”,“\t”等等如:list [1, 2, 3, 4, 5].join(list) 结果即为:12345,.join(list) 结果即为:1,2,3,4,5二…...
wordpress免费商城主题/潍坊自动seo
ASP.NET 支持两组性能计数器:系统和应用程序。前者在 ASP.NET 性能计数器对象中的 PerfMon 中公开;后者在 ASP.NET Applications 性能对象中公开。ASP.NET 性能对象中的 State Server Sessions 计数器(仅适用于在其中运行状态服务器的服务器计…...
有多少专门做兼职的网站/营销网络是啥意思
写在前面的话: AMD安装MAC是一件很蛋疼的事情, 我这里主要是面向需要学习苹果平台的开发的同学,不想浪费太多时间去折腾的同学可以参考我的做法。 我的建议是安装mac os x 10.6.3,对应的xcode版本是3.2.2 如果想升级到更高版本的话࿰…...
事业单位网站建设工作方案/艾滋病多久能检查出来
邮箱登录方式有两种,一种是官方提供的统一登录网址,另外一种就是foxmail、outlook这样的客户端了。 在网页端登录邮箱可通过群发单显、抄送多人来群发邮件,用TOM VIP有5个套餐选择,最高可发500封。如果在邮箱客户端登录邮箱&…...
图书馆登录系统网站建设代码/接单平台app
1. Mantle Mantle 让我们能简化 Cocoa 和 Cocoa Touch 应用的 model 层。简单点说,程序中经常要进行网络请求,请求到得一般是 json 字符串,我们一般会建一个 Model 类来存放这些数据。这就要求我们编写一系列的序列化代码,来把 js…...
公司建设网站制作/本周新闻热点
#3安裝過程進行到80%多(所有文件復制完畢,並開始鏈接后), 報錯1報錯ins_precomp.mkINFO: /usr/bin/ld: /lib//libnls12.a(lxhlang.o): undefined reference to symbol ‘__tls_get_addrGLIBC_2.3′這是因為oracle安裝文件自帶的 庫文件太老了需要刪除 {ORACLE_HOME}/lib/stubs …...