滴水三期:day45.1-动态绑定(多态)

滴水逆向——C++_动态绑定/多态 1、绑定概念 1.什么是绑定? 绑定就是将函数调用与地址关联起来. 观察下面代码反汇编 class Base { public: int x; public: Base() { x = 100; } void Function_1() { printf("Base:Funct... 阅读详情

一、继承与虚函数表

1.单继承无重写

  • day44.2-虚函数表作业中详细说过,这里只说结论

    image-20230425213545341
    class Base{	
    public:	
        virtual void Function_1(){	
            printf("Base:Function_1...\n");	
        }	
        virtual void Function_2(){	
            printf("Base:Function_2...\n");	
        }	
        virtual void Function_3(){	
            printf("Base:Function_3...\n");	
        }	
    };	
    class Sub:public Base{	
    public:	
        virtual void Function_4(){	
            printf("Sub:Function_4...\n");	
        }	
        virtual void Function_5(){	
            printf("Sub:Function_5...\n");	
        }	
        virtual void Function_6(){	
            printf("Sub:Function_6...\n");	
        }	
    };
    

2.单继承有重写

  • 如图所示:

    image-20230425213847525
    class Base{
    public:
        virtual void Function_1(){
            printf("Base:Function_1...\n");
        }
        virtual void Function_2(){
            printf("Base:Function_2...\n");
        }
        virtual void Function_3(){
            printf("Base:Function_3...\n");
        }
    };
    class Sub:public Base{
    public:
        virtual void Function_1(){
            printf("Sub:Function_1...\n");
        }
        virtual void Function_2(){
            printf("Sub:Function_2...\n");
        }
        virtual void Function_6(){
            printf("Sub:Function_6...\n");
        }
    };
    

3.多继承无重写

  • 如图所示:如果Sub有两个父类Base1和Base2,那么就有两个虚函数表:第一个虚表中有Base1和Sub中的虚函数地址,第二个虚表中有Base2中的虚函数地址。所以Sub对象的大小就会多出来8字节

    image-20230425215312197
    class Base1{
    public:
        virtual void Fn_1(){
            printf("Base1:Fn_1...\n");
        }
        virtual void Fn_2(){
            printf("Base1:Fn_2...\n");
        }
    };
    class Base2{
    public:
        virtual void Fn_3(){
            printf("Base2:Fn_3...\n");
        }
        virtual void Fn_4(){
            printf("Base2:Fn_4...\n");
        }
    };
    class Sub: public Base1,public Base2{
    public:
        virtual void Fn_5(){
            printf("Sub:Fn_5...\n");
        }
        virtual void Fn_6(){
            printf("Sub:Fn_6...\n");
        }
    };
    

4.多继承有重写

  • 如图所示:同样有两个虚函数表:第一个虚表中有Base1中的没有函数覆盖的虚函数地址和Sub中重写Base1中虚函数地址以及Sub自己的虚函数地址,第二个虚表中有Base2中没有函数覆盖的虚函数地址和Sub中重写Base2中虚函数地址。所以Sub对象大小就会多出来8字节

    image-20230425220353571
    class Base1{
    public:
        virtual void Fn_1(){
            printf("Base1:Fn_1...\n");
        }
        virtual void Fn_2(){
            printf("Base1:Fn_2...\n");
        }
    };
    class Base2{
    public:
        virtual void Fn_3(){
            printf("Base2:Fn_3...\n");
        }
        virtual void Fn_4(){
            printf("Base2:Fn_4...\n");
        }
    };
    class Sub:public Base1,public Base2{
    public:
        virtual void Fn_1(){
            printf("Sub:Fn_1...\n");
        }
        virtual void Fn_3(){
            printf("Sub:Fn_3...\n");
        }
    	virtual void Fn_5(){
            printf("Sub:Fn_5...\n");
        }
    };
    

5.多重继承无重写

  • 如图所示:Sub继承Base2,Base2又继承Base1,那么Sub对象就只有一张虚函数表:依次存储爷爷、父亲、自己的虚函数地址。所以Sub对象只会多出来4字节

    image-20230425220823948
    class Base1{
    public:
        virtual void Fn_1(){
            printf("Base1:Fn_1...\n");
        }
        virtual void Fn_2(){
            printf("Base1:Fn_2...\n");
        }
    };
    class Base2:public Base1{
    public:
        virtual void Fn_3(){
            printf("Base2:Fn_3...\n");
        }
        virtual void Fn_4(){
            printf("Base2:Fn_4...\n");
        }
    };
    class Sub:public Base2{
    public:
        virtual void Fn_5(){
            printf("Sub:Fn_5...\n");
        }
        virtual void Fn_6(){
            printf("Sub:Fn_6...\n");
        }
    };
    

6.多重继承有重写

  • 如图所示:和前面都差不多,只要重写了,就把子类重写的虚函数地址改到父类对应虚函数地址的位置即可。同样Sub对象只多了4字节

    image-20230425221905628
    class Base1{
    public:
        virtual void Fn_1(){
            printf("Base1:Fn_1...\n");
        }
        virtual void Fn_2(){
            printf("Base1:Fn_2...\n");
        }
    };
    class Base2:public Base1{
    public:
        virtual void Fn_1(){
            printf("Base2:Fn_1...\n");
        }
        virtual void Fn_3(){
            printf("Base2:Fn_3...\n");
        }
    };
    class Sub:public Base2{
    public:
        virtual void Fn_3(){
            printf("Sub:Fn_3...\n");
        }
    	virtual void Fn_5(){
            printf("Sub:Fn_5...\n");
        }
    };
    

二、动态绑定

  • 绑定就是将函数调用与地址关联起来,即在函数调用的地方将要调用的真正函数地址确定下来的过程

1.举例一

#include "stdafx.h"	
class Base{
public:
    int x;
    Base(){
        x = 100;
    }
    void Function_1(){  //Func1没有加virtual
        printf("Base:Function_1...\n");
    }
    virtual void Function_2(){  //Func2加Virtual
        printf("Base:Function_2...\n");
    }
};
class Sub:public Base{
public:
    int x;
    Sub(){
        x = 200;
    }
    void Function_1(){
        printf("Sub:Function_1...\n");
    }
    virtual void Function_2(){
        printf("Sub:Function_2...\n");
    }
};
void Test(Base* pb){
    int n = pb->x;
	printf("%d\n",n);  //100
	pb->Function_1();  //Base:Function_1...
	pb->Function_2();  //Base:Function_2...
}
int main(int argc, char* argv[]){
    Base base;  //创建父类对象
    Test(&base);
	return 0;	
}
  • 通过反汇编发现:只要该程序一编译完,调用普通成员变量的地方的成员变量地址就写死了

    65647
  • 只要该程序一编译完,调用普通成员方法的地方的函数地址也写死了,即编译时就把函数地址确定下来了,该调哪个类的对象的哪个函数:(这里就是把Base中的Function_1函数地址确定下来了,硬编码写死了)

    165927
  • 但是对于类中的虚函数,编译时,在调用的地方是没办法把要真正要调用的函数地址确定下来的,而是采用虚函数表的方式:(这里call的是base对象的虚函数表中的第一个值!但是虚函数表中的第一个值是什么,只有运行时才知道)

    170323
  • 所以先理解一下什么是动态绑定:假如调用的是虚函数表中的第一个值,但是通过一、继承与虚函数表可以得知,虚函数表中的值是可以被重写的!比如多重继承有重写中的例子:正常来说,虚函数表的第一个值应该为Base1:Fn_1函数地址值,但是现在由于Base2继承了Base1,且Base1中也有同名Fn_1虚函数,所以会把Base2:Fn_1函数地址值写到虚函数表的第一个位置上替换了原来的值。故这些虚函数的调用,有时候真正要调用哪个函数在编译时是不确定的!编译器确定的是调用虚函数表中第一个位置的函数,但是具体是什么函数,就要在运行时才能确定

    image-20230428170749722

2.举例二

#include "stdafx.h"	
class Base{
public:
    int x;
    Base(){
        x = 100;
    }
    void Function_1(){  //Func1没有加virtual
        printf("Base:Function_1...\n");
    }
    virtual void Function_2(){  //Func2加Virtual
        printf("Base:Function_2...\n");
    }
};
class Sub:public Base{
public:
    int x;
    Sub(){
        x = 200;
    }
    void Function_1(){
        printf("Sub:Function_1...\n");
    }
    virtual void Function_2(){
        printf("Sub:Function_2...\n");
    }
};
void Test(Base* pb){  //父类的指针指向子类的对象
	int n = pb->x;
	printf("%d\n",n);  //100
	pb->Function_1();  //Base:Function_1...
	pb->Function_2();  //Sub:Function_2...
}
int main(int argc, char* argv[]){
    Sub sub;  //创建子类对象
    Test(&sub);  //父类的指针指向子类的对象
	return 0;	
}
  • 对于普通成员函数,地址还是写死的:(所以父类指针->普通成员函数Function_1,就调用父类中的Function_1普通成员函数,跟父类指针指向的对象是谁无关)

    image-20230428173514429
  • 对于虚函数成员,地址依然是动态绑定的:由于此时调用的是sub对象的虚函数表中的第一个值:而根据举例一中的说明可知,编译器只能确定下来调用的是sub虚函数表中的第一个值,但是由于子类sub重写了其父类base中的虚函数Function_2,所以sub虚函数表中的第一个值就不是Base:Function_2了,而改成了Sub:Function_2的地址值!

    174215

3.总结

  • 普通成员函数:编译完成后,在调用的地方,地址就写死了。称为==前期绑定或编译期绑定==
  • 只有virtual的函数是动态绑定,又称晚绑定,或运行时绑定
  • 动态绑定又称为==多态==:即虽然Test方法的参数是Base*指针pb,但是Base*指针指向的是Base类对象,还是Base的子类对象sub是不确定的,就可能导致pb->Function_2()这个虚函数体现出不同的行为
  • 本质原因还是在于:Base*指针指向base对象,即调用虚函数时使用的是base对象的虚函数表;但Base*指针指向其子类对象sub,调用虚函数时使用的就是sub对象的虚函数表。(就要考虑继承、重写带给虚函数表的变化)
  • 所以动态绑定是通过虚函数表实现的
  • 如果没有多态,一个父类的指针永远只能访问自己类中的方法,访问不了其子类中重写的方法!

三、作业

1.体会多态

  • 定义一个父类Base:有两个成员X,Y;有一个函数Print(非virtul)能够打印X,Y的值

  • 定义3个子类:Sub1,有一个成员A;Sub2,有一个成员B。每个子类有一个函数Print(非virtul),打印所有成员----Sub1:打印X Y A;Sub2:打印X Y B

  • 定义一个数组,存储Base Sub1 Sub2对象;再使用一个循环语句调用所有的Print函数

    #include "stdafx.h"	
    class Base{			
    public:	
    	int X;
    	int Y;
    public:
    	Base(){
    		X = 1;
    		Y = 2;
    	}
        void print(){
            printf("Base:%x %x\n",X,Y);
        }
    };		
    class Sub1:public Base{	
    public:
    	int A;
    public:	
    	Sub1(){
    		X = 3;
    		Y = 4;
    		A = 5;
    	}
        void print(){
            printf("Sub1:%x %x %x\n",X,Y,A);
        }			
    };
    class Sub2:public Base{	
    public:
    	int B;
    public:	
    	Sub2(){
    		X = 6;
    		Y = 7;
    		B = 8;
    	}
        void print(){
            printf("Sub2:%x %x %x\n",X,Y,B);
        }			
    };
    void Test(){
    	Base b;
    	Sub1 s1;
    	Sub2 s2;
    	//定义一个Base*指针类型的数组!所以arr[0]就是父类指针指向自己的对象;arr[1]和arr[2]就是父类的指针指向其子类的对象
    	Base* arr[] = {&b,&s1,&s2};
    	for(int i = 0;i < 3;i++){  
    		arr[i]->print();  //Base:1 2    Base:3 4    Base:6 7
    	}
    }
    int main(int argc, char* argv[]){		
    	Test();
    	return 0;	
    }
    

    因为都是用**Base*指针的方式调用的普通成员函数**:所以无论Base*指针指向的是Base对象、还是其子类的对象,都调用的是Base类中的print函数!但是由于创建子类对象s1时先调用父类构造器、再调用自己的构造器,导致X,Y的值变成了3,4。所以调用Base类中的print函数打印X,Y,A的值时结果为3,4,5。s2以此类推

  • 将上面所有的Print函数改成virtul,继续观察效果:

    #include "stdafx.h"	
    class Base{			
    public:	
    	int X;
    	int Y;
    public:
    	Base(){
    		X = 1;
    		Y = 2;
    	}
        virtual void print(){
            printf("Base:%x %x\n",X,Y);
        }
    };		
    class Sub1:public Base{	
    public:
    	int A;
    public:	
    	Sub1(){
    		X = 3;
    		Y = 4;
    		A = 5;
    	}
        virtual void print(){
            printf("Sub1:%x %x %x\n",X,Y,A);
        }			
    };
    class Sub2:public Base{	
    public:
    	int B;
    public:	
    	Sub2(){
    		X = 6;
    		Y = 7;
    		B = 8;
    	}
        virtual void print(){
            printf("Sub2:%x %x %x\n",X,Y,B);
        }			
    };
    void Test(){
    	Base b;
    	Sub1 s1;
    	Sub2 s2;
    	//定义一个Base*指针类型的数组!所以arr[0]就是父类指针指向自己的对象;arr[1]和arr[2]就是父类的指针指向其子类的对象
    	Base* arr[] = {&b,&s1,&s2};
    	for(int i = 0;i < 3;i++){
    		arr[i]->print();
    	}
    }
    int main(int argc, char* argv[]){		
    	Test();
    	return 0;	
    }
    

    arr[0]->print()就是用Base对象调用虚函数print,所以使用的就是Base对象的虚函数表,结果为Base:1 2

    arr[1]->print()就是用父类指针Base*指向子类对象s1,所以调用虚函数print时,使用的就是Sub1对象的虚函数表,结果为Sub1:3 4 5

    arr[2]->print()也是用父类指针指向子类的对象s2,所以调用虚函数print时,使用的是Sub2对象的虚函数表,结果为Sub2: 6 7 8

    这就是多态:同一个类型的指针调用一个函数,却表现出不同的行为

2.为什么析构函数建议写成virtul

  • 如果有一个父类Base,它有一个子类Sub,现在做如下操作

    Base b;
    Sub s;
    Base* pb = &b;  //Base类指针指向自己的对象
    Base* ps = &s;  //父类指针指向子类的对象
    
  • 如果Base类中和Sub类中的析构函数都不是Virtual,当上述中s对象销毁时,由于是用Base*指针指向的s对象,所以会调用Base类中的析构函数!这里本应该销毁什么对象,就调用这个对象的类中的析构函数,清理该对象自己的资源

  • 如果Base类中和Sub类中的析构函数是Virtual,当上述中s对象销毁时,即使是用Base*指针指向的s对象,但是还是会调用Sub类中的虚析构函数

滴水三期:day01.1-初识逆向 一、机制与0,1>二、PE文件结构>三、硬编码>四、逆向的过程>五、初级阶段需要完成的项目 阅读详情

相关推荐

滴水逆向三期笔记和作业-c语言总结1

裸函数,调用约定,if语句,if语句逆向

weixin_44449397的博客 2479

2021-04-06 函数的参数是如何入栈出栈的

函数的参数是如何入栈出栈的 制作逆向靶 建议用debug版而不是release版,因为release版会自作聪明把很多东西省略掉 在调试器中 寄存器EIP 表示当前程序走到哪一步了 ctrl+G 光标定位到某一地址 在单步调试中 右侧哪个寄存器变红了 就是哪个寄存器发生了变化 call 的第一个作用就是 将EIP的值改为函数所在的地址 由硬编码知识 可以计算出返回地址的值,举例 E8 84 FF FF FF (call stack.00401005) 这行硬编码是5个字节 所以 call完的返

mfmfmmf1的专栏 230

滴水三期完整版(96课时)

第1讲:2015-01-12(进制01) 第2讲:2015-01-13(进制02) 第3讲:2015-01-14(数据宽度-逻辑运算03) 第4讲:2015-01-15(通用寄存器-内存读写04) 第5讲:2015-01-16(内存寻址-堆栈05) 第6讲:2015-01-19(EFLAGS寄存器06) 第7讲:2015-01-20(JCC) 第8讲:2015-01-21(堆栈图) 第8讲:2015-01-21(宝马问题) 第9讲:2015-01-22(堆栈图2) 第10讲:2015-01-23(C语言01_后半段) 第10讲:2015-01-23(C语言完整版) 第11讲:2015-01-26(C语言02_数据类型) 第12讲:2015-01-27(C语言03_数据类型_IF语句) 第13讲:2015-01-28(C语言04_IF语句逆向分析上) 第14讲:2015-01-28(C语言04_IF语句逆向分析下) 第15讲:2015-01-29(C语言04_正向基础) 第16讲:2015-01-30(C语言05_循环语句) 第17讲:2015-02-02(C语言06_参数_返回值_局部变量_数组反汇编) 第18讲:2015-02-02(2015-01-30课后练习) 第19讲:2015-02-03(C语言07_多维数组) 第20讲:2015-02-03(2015-02-02课后练习) 第21讲:2015-02-04(C语言08_结构体) 第22讲:2015-02-05(C语言09_字节对齐_结构体数组) 第23讲:2015-02-06(C语言10_Switch语句反汇编) 第24讲:2015-02-26(C语言11_指针1) 第25讲:2015-02-27(C语言11_指针2) 第26讲:2015-02-28(C语言11_指针3) 第27讲:2015-02-28(C语言11_指针4) 第28讲:2015-03-02(C语言11_指针5) 第29讲:2015-03-03(C语言11_指针6) 第30讲:2015-03-04(C语言11_指针7) 第31讲:2015-03-06(C语言11_指针8) 第32讲:2015-03-09(位运算) 第33讲:2015-03-10(内存分配_文件读写) 第34讲:2015-03-11(PE头解析_手动) 第35讲:2015-03-12(PE头字段说明) 第36讲:2015-03-13(PE节表) 第37讲:2015-03-16(FileBuffer转ImageBuffer) 第38讲:2015-03-17(代码节空白区添加代码) 第39讲:2015-03-18(任意节空白区添加代码) 第40讲:2015-03-19(新增节添加代码) 第41讲:2015-03-20(扩大节-合并节-数据目录) 第42讲:2015-03-23(静态连接库-动态链接库) 第43讲:2015-03-24(导出表) 第44讲:2015-03-25(重定位表) 第45讲:2015-03-26(移动导出表-重定位表) 第46讲:2015-03-27(IAT表) 第47讲:2015-03-27(导入表) 第48讲:2015-03-30(绑定导入表) 第49讲:2015-03-31(导入表注入) 第50讲:2015-04-01(C++ this指针 类 上) 第51讲:2015-04-01(C++ this指针 类 下) 第52讲:2015-04-02(C++ 构造-析构函数 继承) 第53讲:2015-04-03(C++ 权限控制) 第54讲:2015-04-07(C++ 虚函数表) 第55讲:2015-04-08(C++ 动态绑定-多态-上) 第56讲:2015-04-08(C++ 动态绑定-多态-下) 第57讲:2015-04-09(C++ 模版) 第58讲:2015-04-10(C++ 引用-友元-运算符重载) 第59讲:2015-04-13(C++ new-delete-Vector) 第60讲:2015-04-14(C++Vector实现) 第61讲:2015-04-15(C++链表) 第62讲:2015-04-16(C++链表实现) 第63讲:2015-04-16(C++二叉树) 第64讲:2015-04-17(C++二叉树实现) 第65讲:2015-04-20(Win32 宽字符) 第66讲:2015-04-21(Win32 事件-消息-消息处理函数) 第67讲:2015-04-22(Win32 ESP寻址-定位回调函数-条件断点) 第68讲:2015-04-23(Win32 子窗口-消息处理函数定位) 第69讲:2015-04-24(Win32 资源文件-消息断点) 第70讲:2015-04-27(Win32 提取图标-修改标题) 第71讲:2015-04-28(Win32 通用控件-VM_NOTIFY) 第72讲:2015-04-29(Win32 PE查看器-项目要求) 项目一:PE查看器 开发周期(5天) 需求文档 第73讲:2015-05-07(Win32 创建线程) 第74讲:2015-05-08(Win32 线程控制_CONTEXT) 第75讲:2015-05-11(Win32 临界区) 第76讲:2015-05-12(Win32 互斥体) 第77讲:2015-05-13(Win32 事件) 第78讲:2015-05-14(Win32 信号量) 第79讲:2015-05-15(Win32 线程同步与线程互斥) 第80讲:2015-05-18(Win32 进程创建_句柄表) 第81讲:2015-05-20(Win32 以挂起形式创建进程) 第82讲:2015-05-21(Win32 加密壳_项目说明) 项目二:加密壳 开发周期(5天) 需求文档 第83讲:2015-05-28(Win32 枚举窗口_鼠标键盘事件) 第84讲:2015-05-29(Win32 CE练习) 第85讲:2015-06-01(Win32 OD练习) 第86讲:2015-06-03(Win32 ShellCode_远程线程注入) 第87讲:2015-06-04(Win32 加载EXE_模块隐藏) 第88讲:2015-06-09(Win32 IAT_HOOK) 第89讲:2015-06-10(Win32 InlineHook) 第90讲:2015-06-11(Win32 进程通信) 第91讲:2015-06-11(Win32 进程监控_项目说明) 项目三:进程监控 开发周期(5天) 需求文档 第92讲:2015-06-15(硬编码_01) 第93讲:2015-06-16(硬编码_02) 第94讲:2015-06-17(硬编码_03) 第95讲:2015-06-18(硬编码_04) 第96讲:2015-06-19(硬编码_05)

滴水逆向三期笔记和作业-PE总结1

PE头手动解析,节表,FileBuffer-ImageBuffer

weixin_44449397的博客 3304

滴水逆向第三期课件(全)

滴水逆向课程课件,完整版! 滴水逆向课程课件,完整版!

滴水逆向3期初级和中级课件(海东老师讲)

滴水逆向3期初级和中级课件(海东老师讲) 全套包括 EXE 练手文件 配套电子书......

滴水逆向三期笔记和作业-前言-汇编总结1

进制1,进制2,数据宽度_逻辑运算,通用寄存器_内存读写,内存地址_堆栈

weixin_44449397的博客 2646

滴水逆向三期课件(全)

滴水逆向三期所有课件,需要的速度下载,全部课件,是xls格式的,也就是视频里的excel课件,用的7z最小体积压缩,方便下载!

滴水逆向三期笔记和作业-PE总结3

导出表,重定位表,IAT表和导入表,绑定导入表

weixin_44449397的博客 2005

滴水逆向三期课件(全)

滴水逆向初级班三期课件,全套包括 EXE 练手文件 配套电子书 推荐下载绝对不亏!!

滴水逆向三期课件.zip

滴水逆向三期全部课件(海东老师讲),全套包括 EXE 练手文件 配套电子书,源码 滴水逆向三期全部课件(海东老师讲),全套包括 EXE 练手文件 配套电子书,源码

虚函数的动态绑定机制

C++虚函数的动态绑定机制

图来搞事情 264

【数据分析与挖掘实战】金融风控之贷款违约预测详解1(有代码和数据集)

金融风控一.题目理解1.1.题目概况1.2数据概况1.3预测指标二.数据来源三.查看数据四.分类指标计算示例4.1混淆矩阵4.2准确度4.3precision(精确度),recall(召回率),f1-score4.4P-R曲线4.5ROC曲线4.6AUC曲线4.7KS值五.数据分析5.1基本信息5.2查看数据集中特征缺失值,唯一值等5.3查看特征的数值类型有哪些,对象类型有哪些5.3.1数值连续型变量分析5.3.2非数值类别型变量分析5.3.3总结:5.4变量分布可视化5.4.1单一变量分布可视化 更新中~

m0_49263811的博客 1万+

滴水逆向三期实践15:根据重定位表修正地址

占坑

Rivival_S 的博客 3255

FPGA新手必看:Quartus生成jic文件全流程详解(附AS/JTAG模式选择指南)

本文详细解析了在Quartus中生成JIC文件以固化FPGA程序到Flash的全流程。核心在于理解JIC文件作为连接JTAG调试与AS固化的桥梁作用,并重点阐述了Convert Programming Files工具的关键配置步骤、AS与JTAG模式的选择逻辑,以及烧录过程中的常见问题排查指南,帮助开发者实现程序断电不丢失。

weixin_29271053的博客 275

优惠券管理

(请设计一个优惠券兑换码生成方案,可以支持20亿以上的唯一兑换码,兑换码长度不超过10,只能包含字母数字,并且要保证生成和校验算法的高效)最后把签名值的后14位、新鲜值(4位)、自增序列(32位)拼接,得到一个50位二进制数,然后与一个较大的质数做异或运算加以混淆,再基于Base32或Base64转码,即可的对兑换码。现在,就剩下防止爆刷了。自增id从1增加到Integer的最大值,可以达到40亿以上个数字,而占用的字节仅仅4个字节,也就是32个bit位,距离50个bit位的限制还有很大的剩余,符合要求!

qq_36312661的博客 2204

滴水三期逆向基础系列(一)-读取文件到内存再读取回文件

跟着滴水三期学了很长时间了,本着,每一点都要吃透的精神,跟“读文件到内存(拉伸),再读回文件(压缩回来)”杠了一天。先看看按着老师的架构写的代码吧(老师的代码有很多问题(可能是我太菜了吧),踩了很多坑,最后自己推翻重写,全改过来了。) 先看看全部函数的声明 #include <windows.h> #include <stdio.h> #define FilePa...

henuyl的博客 1648

Python基础语法与最佳实践【python基础篇】

本文全面介绍了Python基础语法与最佳实践,涵盖变量与数据类型、控制流、函数、模块与包等核心概念。通过实际代码示例展示了条件语句、循环结构、函数定义及模块导入的基本用法。在最佳实践部分,重点讲解了PEP 8代码风格规范、异常处理技巧、列表推导式、上下文管理器、文档字符串编写以及enumerate和zip的高效使用。这些内容既适合Python初学者掌握基础语法,也能帮助有经验的开发者编写更规范、高效、可维护的代码。文章强调遵循Python官方推荐实践,通过合理运用语言特性提升代码质量。

博客 1975

滴水三期:day06.1-堆栈图

一、画堆栈图(debug版)>二、作业(画出下列三个函数的堆栈图)

Edimade的博客 2349

RandLA-Net Tensorrt 推理 c++

RandLA-Net 实现 Tensorrt 部署.

J。 1109

Python爬虫实战:全面采集国家自然科学基金项目数据的全流程指南

国家自然科学基金委员会(NSFC)支持基础研究、应用基础研究,是我国科研投入的核心项目之一。公开的项目数据包括项目名称、负责人、资助金额、年度、单位等,具备极高科研和数据分析价值。requests:基础HTTP请求库,简单直观:异步HTTP请求库,适合大规模并发:HTML解析,提取数据:处理JavaScript渲染网页pandas:数据分析与清洗:数据库存储:重试机制实现。

2201_76125261的博客 753

滴水三期-Win32---事件_消息_消息处理函数

Windows事件就是一个"动作",而消息则是用来描述这些"动作"的。

tscg_的博客 502

【四足机器人项目实战】四足机器人运动学、动力学模型

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、四足机器人实际模型的物理难点二、四足机器人运动学模型1.方法一:DH法建立运动学模型(1)运动学建模构型方法(2)运动学模型描述方法1、基于DH法四足机器人单腿运动学模型公式推导(0)DH法科普1)原理2)实现过程3)评价(1)四足机器人的建模规则、坐标系定义及实物参数确定1)标准Denavit-Hartenberg法建模规则2)单腿运动学模型坐标系定义示意图及简化模型3)

qq_35635374的博客 1万+

滴水三期:day35.1-静态、动态链接库

一、代码复用的实现>二、静态链接库>三、动态链接库>四、使用.def导出

Edimade的博客 868

华为OD机试双机位B卷 - 比赛的冠亚季军 (C++ & Python & JAVA & JS & GO)

华为OD机试双机位B卷 比赛的冠亚季军,提供Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解。 华为OD机试 比赛的冠亚季军

qq_45776114的博客 703

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

滴水三期:day10.2-条件语句

一、IF语句>二、IF反汇编指令>三、IF...ELSE反汇编指令>四、IF..ELSE IF..ELSE反汇编指令>五、数组的初识>六、作业

Edimade的博客 706

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

上一篇: 滴水三期:day44.2-虚函数表
EdimadeZhou
博客等级 码龄5年 241粉丝 128原创
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值