《深度探索C++对象模型》读书笔记——Data语意学【for_wind】

Amber-Garden:一种模块自治、状态可溯的轻量级系统架构范式 在分布式系统设计中,'模块化'与'可观测性'是保障长期可维护性的两大基石。传统微服务常因协议碎片化、状态黑盒化和环境漂移导致治理成本激增;而 Amber-Garden 范式通过模块即种子、状态即年轮等核心契约,将业务逻辑、数据契约与运行时行为统一约束在声明式结构中,实现低基础设施依赖下的确定性可控。它不替代现有技术栈,而是以 OpenAPI 驱动接口契约、以不可变事件流支撑溯源审计,天然适配 GitOps 流程与边缘部署场景——尤其适合 IoT 接入、多源异构集成及中小规模业务系统的渐进式现代化改造。 阅读详情

//整理之,分享之,欢迎指正。for_wind

1、类的实际大小。

      每一个class object必须有足够的大小以容纳它所有的nonstatic data members。除了其nonstatic data members的总和大小外,其值的大小受以下三个因素影响:
(1) 语言本身所造成的额外负担:即为了支持virtual而由内部产生的额外负担。当语言支持virtual base class 时,在derived class 中,这个额外的负担体现在某种形式的指针 bptr身上,它或者指向virtual base class subobject,或者指向一个相关表格(表内存放virtual base class subobject的地址,即其偏移量offset);此外还有因虚函数产生的 vptr。
(2) 编译器对特殊情况所提供的优化处理:某些编译器会对empty virtual base class提供特殊支持。
(3) alignment的限制:将数值填补到某数(如4bytes(32位))的整数倍,以使bus的“运输量”达到最高效率。

备注:
  • 一个空的class,其值的大小并不为零。它有一个隐晦的1byte,那是被编译器安插进去的一个char。这使得这个class的两个objects得以在内存中配置独一无二的地址。
  • 一个virtual base class subobject只会在derived class中存在一份实体,不管它在继承体系中出现了多少次。
举例:
下图给出class X, Y, Z, A之间的继承关系。 问class X, Y, Z, A的大小分别为多少。
    
下图给出X,Y,Z的对象布局(编译器不做优化处理)(1,8,8)

下图给出X,Y,Z的对象布局(编译器做了优化处理,不安插一个char)(1,4,4)

上例中的class A的大小又该如何计算,其内存布局又该如何?
(1)被Y、Z共享的 唯一的一个class X实体,大小为1byte;(对empty virtual base class特殊处理的编译器会将(1)中class X实体的1byte 拿掉)
(2)Base class Y的大小,减去“因virtual base class X而配置的大小”,结果是4bytes。同理Base calss Z。共8bytes。
(3)class A自己的大小为:0byte。
(4)class A的alignment数量。
      class A的大小,在有特殊优化的编译器上运行结果为8bytes;在无特殊优化的编译器上运行结果则为12bytes。
      注意:非空virtual base class两种编译器产生的对象布局一致。
下面给出VS编译器下的情况。
#include <iostream>

using namespace  std ;

class X{ };
class Y : public virtual X { };
class Z : public virtual X { };
class A : public Y, public Z { };

int main( void )
{
        cout <<"sizeof X=" << sizeof( X )<<endl ;
        cout <<"sizeof Y=" << sizeof( Y )<<endl ;
        cout <<"sizeof Z=" << sizeof( Z )<<endl ;
        cout <<"sizeof A=" << sizeof( A )<<endl ;
        return 0 ;
}
运行结果:

VS2010中 class X的内存布局:

VS2010中 class Y的内存布局:

VS2010中 class Z的内存布局:

VS2010中 classA 的内存布局:

分析:
      VS2010编译器中,对每个继承自虚基类的类实例,将增加一个隐藏的 “虚基类表指针”(vbptr)成员变量,从而达到间接计算虚基类位置的目的。该变量指向一个全类共享的偏移量表,表中项目记录了对于该类而言,“虚基类表指针”与虚基类之间的偏移量。
      class X的大小为1byte,而class Y,Z的大小为4bytes,含有一个vbptr,即该编译器把vbptr放在前面,对empty  virtual base class进行特殊处理,去除了1byte。
      而class A的大小的大小为8bytes,具体分析见上。

2、Data member的绑定

      对member functions函数本身的分析将延迟直至class声明的右大括号出现才开始,因此在一个inline member function 内的一个data member绑定操作,在整个class 声明完成之后才发生。
      然而,这对member function中的argument list并 不为真。Arugment list中的名称会在第一次遇到时被适当地决议resolved。因此最好把“nested type 声明”放在class的起始处,以确保非直觉绑定的正确性。
举例:
#include <iostream>
using namespace  std;

typedef int length;

class Point3d
{
public:
     //typedef float length;//pos2
     void fun1(length val)
     {
          _val = val;
     }
     length fun1()
     {
          return _val;
     }
private:
     typedef float length;//pos1
     length _val;
};

int main(void)
{
     Point3d p;
     p.fun1(1.2);
     cout<<p.fun1()<<endl;
     return 0;
}
"nested type 声明”放在位置pos1:第一次遇到函数参数后,决议为global typedef,因此为int
运行结果:

"nested type 声明”放在位置pos2:注释掉pos1那句后
运行结果:

3、Data member的布局

(1)对于 nonstatic data member,把数据直接存放在每一个class object之中,继承而来的nonstatic data member也是如此,不过不强制其排列顺序。
(2)对于 static data member,则把被放在程序的global data segment中,永远只存在一份实体,和个别的class object无关。 template class稍微不同。
(3)access section(private、public、protected等区段)中members的排列 只需符合“较晚出现的members在class object中有较高的地址”。并非一定得连续。(凡处于同一个access section中的数据,必定保证以其声明的次序出现在内存布局当中,然而被放在多个access section的各个数据,排列次序就不一定了)但是当前的编译器都是把一个以上的access sections 连锁在一起,依照声明次序,形成一个连续区块。
(4)对于那些由编译器 内部产生的data members(如vptr)允许自由放在任何位置。当前所有编译器把vptr安插在每一个“内含virtual function的class”的object内(最后,或者最前)。

4、Data member的存取

(1)对于 static data member,
      每次程序取用static member,就会被内部转换为对该唯一的extern实体的直接参考操作。存取static members并不需要通过class object。对于继承而来的static member其存取路径也是同样直接。(因为static members只存在唯一的一份实体)
(2)对于 nonstatic data member,
      每一个nonstatic data member的偏移量offset,在编译时期即可获知。(派生自单一或多重继承串链也一样)。
而当虚继承时,虚继承将为“经由base class subobject 存取 class members”导入一层新的间接性。
总结:
      将对象分割为两部分,一个不变局部和一个共享局部。不变局部中的数据,不管后继如何衍化,总是拥有固定的偏移量,所以这一部分数据可以被直接存取,至于共享局部,所表现的就是虚拟继承的基类子对象,这一部分的数据,其位置会因为每次的派生操作而有变化,所以它们是间接存取。
举例:
Pointed origin, *pt = &origin;
origin.x = 0.0;
pt->x = 0.0;
从origin和pt存取有何差异?
      答:当Point3d是一个derived class,而在其继承结构中有一个virtual base class,并且被存取的member是一个从该virtual base class继承而来的member时,有差异。
      从pt存取,这时我们不知道pt指向哪一种class type,即无法知道编译时期这个member真正的offset位置,这个存取操作必须延迟至执行期,经由一个额外的间接引导,才能够解决。存取速度比较慢一些。从origin存取,origin的类型是明确的,members的offset位置在编译时期就固定了。

5、Data member与继承

(1)多态与多重继承

多态的弹性带来空间和存取时间上的额外负担:
  • 添加virtual table;
  • 添加vptr,提供执行期的链接。
  • 加强constructor,为vptr设定初值,让它指向class所对于的virtual table;
  • 加强destructor,消除指向class virtual table的vptr。
多重继承的问题:
在于derived class objects和其第二或其后继的base class objects之间的转换。
修正:加上或减去介于中间的base class subobjects大小。
(最左端或说第一个base class都指向相同的起始地址,无须修正)

举例1:

class Point2d
{
public:
     virtual void fun1(){ };
protected:
     float _x, _y;
};

class Point3d : public Point2d
{
public:
     virtual void fun1(){ };
protected:
     float _z;
};


class Vertex
{
public:
     virtual void fun2(){ };
protected:
     Vertex *next;
};

class Vertex3d : public Point3d, public Vertex
{
public:
     virtual void fun1(){ };
     virtual void fun2(){ };
     void fun3(){ };
protected:
     float mumble;
};
下图是VS2010中 class Vertex3d的内存布局(多重继承):

对比上下两图:
      除了vfptr的位置(在前或在后)不同,其他内容是一致的。对于单一继承和多重继承,VS2010编译器采用直接复制模型,将base class subobject的data members被直接放在derived class object中。
此外也注意到 -16。
下图是可能的数据分布(多重继承):


(2)虚继承


举例2:


class Point2d
{
public:
     virtual void fun1(){ };
protected:
     float _x, _y;
};

class Point3d : public virtual Point2d
{
public:
     virtual void fun1(){ };
protected:
     float _z;
};


class Vertex : public virtual Point2d
{
public:
     virtual void fun2(){ };
protected:
     Vertex *next;
};

class Vertex3d : public Point3d, public Vertex
{
public:
     virtual void fun1(){ };
     virtual void fun2(){ };
     void fun3(){ };
protected:
     float mumble;
};
下图是VS2010中 class Point2d的内存布局(虚继承):

  • 观察class Point2d的内存布局,
      存在虚函数,增加vfptr,将其放入表中
Point2d::$vftable@:
        | &Point2d_meta  //第一次声明
        |  0                    //偏移量
0      | &Point2d::fun1     //函数名
下图是VS2010中 class Point3d的内存布局(虚继承):

  • 观察class Point2d和Point3d的内存布局,
      可知,VS2010编译器中对于虚继承,是在 直接复制模型的基础上,选择了virtual base class table模型:即增加了一个vbptr。
      观察到Point3d中 vbptr指向的virtual base class table如下:
Point3d::$vbtable@:
 0      | 0
 1      | 8 (Point3dd(Point3d+0)Point2d)
      偏移量 0是相对整个derived class而言,而8是该vbptr相对于virtual base Point2d的位置。
      此外注意到,直接复制(继承)的 vfptr应该进行修正。
      Point3d::$vftable@:偏移量为 -8,即指向base Point2d的vfptr位置。
下图是VS2010中 class Vertex的内存布局(虚继承):

  • 观察class Point2d和Vertex的内存布局,
      可知,VS2010编译器中 对于虚继承, 直接复制后,增加了一个vbptr。
      观察到Vertex中 vbptr指向的virtual base class table如下:
Vertex::$vbtable@:
0      | -4
1      | 8 (Vertexd(Vertex+4)Point2d)
      偏移量 -4是相对整个derived class而言,而8是该vbptr相对于virtual base Point2d的位置。

      对于新增的虚函数:添加新的 vfptr(注:放在vbptr之前)
Vertex::$vftable@Vertex@:
        | &Vertex_meta //第一次声明
        |  0
0      | &Vertex::fun2
      此外注意到,直接复制(继承) vfptr应该进行修正。
      Vertex::$ vftable@Point2d@:偏移量为 -12,即指向base Point2d的vfptr位置。
下图是VS2010中 class Vertex3d的内存布局(虚继承):

  • 观察class Point3d、Vertex和Vertex3d的内存布局,
      对于虚继承,是在 直接复制模型的基础上,virtural base class Point2d只 复制一份。
      注意到,
Vertex3d::$vftable@Vertex@:
        | &Vertex3d_meta
        |  0                    //Vertex3d::$vftable@Vertex@:偏移量为0,即指向base Vertex的vfptr位置。
 0      | &Vertex3d::fun2
Vertex3d::$vftable@:
        | -24                    //Vertex3d::$vftable@::偏移量为-24,即指向base Point2d的vfptr位置。
0      | &Vertex3d::fun1
      直接复制(继承)的 vfptr应该进行修正。
      注意到,
Vertex3d::$vbtable@Point3d@:
0      | 0
1      | 12 (Vertex3dd(Point3d+0)Point2d)
Vertex3d::$vbtable@Vertex@:
0      | -4        // Vertex3d::$vbtable@Vertex,偏移量-4是相对整个derived class而言,而20是该vbptr相对于virtual base Point2d的位置。
1      | 20 (Vertex3dd(Vertex+4)Point2d)

6.指向Data Members的指针

如何区分一个“没有指向任何data member”的指针和一个指向“第一个data member”的指针?

如
float Point3d::*p1 = NULL;
float Point3d::*p2 = &pointed::x;

每一个真正的member的offset值都被加上1。(为此,在真正使用该值去指出一个member之前,需先减掉1。)

如何通过origin.z计算origin的起始地址?

取一个nonstatic data member的地址,将得到它在class中的offset;
取一个绑定于真正class object身上的data member的地址,将会得到该member在内存中的真正地址。
因此 , &origin.z所得结果 - z的偏移量(相对应origin的起始地址) + 1 = origin的起始地址

参考资料及推荐资料:


深入剖析eMule 【for_wind】 分布式系统课程的读书笔记,关于eMule的整理。不算透彻,欢迎指正,提供相关的资料链接。//for_wind 前言: 针对P2P分布式系统这个大课题,本人将研究重点锁定到eMule这个经典的点对点文件分享系统上,并通过查阅图书馆书籍、搜索图书馆数据库资料、查阅有关期刊和查找网页上相关的资料四种方式,寻找到相关的信息资料,从2012年11月到2012年12月根据所收集的资料完成如下文献综述。本文 阅读详情

相关推荐

后缀数组模版 及 可重叠和不可重叠最长重复子串【for_wind】

后缀数组 基本概念

for_wind的专栏 1290

C++ 多态基础demo

C++ 多态基础demo

月山知了的博客 265

基础矩阵、本质矩阵、单应矩阵

基础矩阵是一个 3×3 的秩为2的矩阵,用于描述两个不同视图(或称为图像)之间对应点的几何约束。更具体地说,如果你有两幅图像,分别从两个不同的相机或者相机位置拍摄,那么基础矩阵可以用于找到一个图像中的点在另一个图像中的对应点。本质矩阵也是一个 3×3 的秩为2的矩阵,与基础矩阵非常类似。当两个视图(图像)之间的场景是一个平面或者两个视图之间仅有旋转和平移时,可以使用单应矩阵来关联这两个视图。这三个矩阵都是在计算机视觉中广泛应用的基础工具,但它们适用于不同类型的视图和场景。给定两个图像中的一对对应点。

Gloamirror的博客 1328

第一届天池 PolarDB 数据库性能大赛

这次天池 PolarDB 数据库性能大赛竞争相当激烈,眼睛一闭一睁成绩就会被血洗,最后榜单成绩是第三名,答辩翻车了,最终取得了大赛季军。云计算领域接触的是最前沿的技术,阿里云的 PolarDB 作为云原生数据库里程碑式的革新产品,也为这次比赛提供了最先进的硬件环境。 整个比赛获益良多,体会比较深的两点: 为了充分使用新硬件, 榨干硬件的红利来达到极致的性能,一定要 Benchmark Every...

weixin_34411563的博客 751

ABAQUS二次开发中创建SET

在ABAQUS中创建加载、边界条件时,需要使用set,创建set时需要使用对象集合对象中的Set函数,这个函数需要提供加入对象集合的列表,但这个对象列表好像并不能自行创建,只能通过ABAQUS各类对象集合中的findAt、getSequenceFromMask、getByBoundingSphere、getByBoundingCylinder或getByBoundingSphere函数得到,但这几个函数在直接使用PYTHON编程时都不太好用。这个函数返回一个字典,如下所示。

hohoyu的专栏 2482

平台跳跃游戏中的自动寻路,与地图导航扩展

平台游戏:主要的游戏方式是在2D平面上使用各种方式在悬浮平台上进行移动和穿过各种障碍。 现如的平台游戏中的平台特性与种类是很多的,像马里奥(FC)的单向支撑不可穿透的,或魂斗罗(FC)单向支撑可穿透的… \ 超级玛丽 魂斗罗 空洞骑士 蔚蓝 本博文 支撑 上 上 上 左、上、右 上 穿透 无 上、下 无 上(部分) 上下 所以本文主要以向上支撑并上下穿透的平台特性来进行实现 需求分析 在关卡设计后,地形的数据需要自动识别 地形中的平台可上下穿透移动,并可向上站立 平台的移动路..

MOV的博客 3062

C++ Data Member语意

C++ Data Member语意

wu_wentao的博客 686

DSSAT模型实战:从基础配置到气候情景模拟

本文详细介绍了DSSAT模型在农业模拟中的应用,从基础配置到气候情景模拟的全流程实战指南。通过土壤-植物-大气连续体的动态关系建模,DSSAT能精准预测作物产量,误差率低至8%。文章涵盖气象数据处理、土壤参数配置、管理措施调控等核心技巧,并提供了Python脚本和可视化方法,助力农业科研与决策。

weixin_29264201的博客 1013

ida生成vftable

ea=here() while ea!=idaapi.BADADDR: if "dd offset" in idc.GetDisasm(ea): print "unsigned int (__stdcall **"+ idc.GetDisasm(ea).replace('dd offset ','').replace('(',')(IUnknownVtbl *this,'...

如鹿渴慕泉水的专栏 879

从IPv6到Tomcat:彻底解决127.0.0.1拒绝连接的完整指南

本文提供了从IPv6到Tomcat的全面指南,帮助开发者彻底解决127.0.0.1拒绝连接的问题。涵盖网络协议栈、服务配置、端口管理等多个层面的排查方法,特别针对Tomcat服务提供了专项解决方案,确保开发者能够快速定位并修复连接问题。

weixin_29207439的博客 471

Sigrity IBIS建模实战:如何用T2B工具快速转换SPICE模型(附避坑指南)

本文详细介绍了如何使用Sigrity T2B工具将SPICE模型高效转换为IBIS模型,涵盖从环境配置、模型预处理到转换流程和验证策略的全过程。通过实战案例和避坑指南,帮助硬件工程师掌握高速电路设计中的IBIS建模技术,提升信号完整性分析效率。

weixin_29243279的博客 395

Inside the C++ Object Model 笔记2 非直觉绑定

P91  #include using namespace std; typedef int len;class CA{public: void fun(len alen) { m_len = alen; } typedef float len; len m_len;};int main(int argc, cha

miyunhong的专栏 643

【ffmpeg+cuda实现硬编解码】

最近在搞一个项目,我需要向AI提供视频帧数,本来一开始摄像头是H.264的编码,我直接复制流推到AI层去推理,然后需要将事件回溯成30秒的视频截取。结果摄像头换成了HEVC编码,截取的视频不能在网页直接播放(需要JS实现调用硬解码)。一开始的方案是ffmpeg直接将HEVC转成H.264结果因为事务巨多CPU占用量过大,导致影响到了推理,因为本身选用的模型+杂七杂八的业务逻辑也要占用大量的CPU资源,所以经过研究把ffmpeg和nvcodec结合起来来平衡设备性能,算是解决了这个问题吧。这两个是前提条件,我

jiexijihe945的博客 7231

深度探索C++对象模型——Data Member的布局(2)书上的错误

书上给了一个代码实验,比较同一个类中不同access section(访问权限区域)内的两个成员变量的地址,由此来判断哪个section在地址空间中先出现。但书上的这个实验出现了很多错误。以下为书上内容,错误的地方用红色标出。Data Member的布局C++ Standard 也允许编译器将多个access sections之中的data members 自由排列,不必在乎它们出现在class声明

cape的博客 499

双目相机标定理论与编程实现

双目相机标定是计算机视觉中的重要任务,通过确定双目相机的内参和外参,可以实现立体视觉的深度感知和三维重建。通过使用OpenCV库提供的函数,我们可以方便地进行双目相机的标定工作。双目相机标定是计算机视觉中的重要任务,用于确定双目相机的内参和外参,以实现立体视觉的深度感知和三维重建。通过上述代码,我们可以获得左右相机的内参矩阵、畸变系数、旋转矩阵和平移矩阵等参数,从而完成双目相机的标定过程。使用双目相机采集一系列左右相机的图像,确保标定板在不同位置和角度下都能被双目相机拍摄到。函数来检测标定板的角点。

ByteKnight的博客 324

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

3.2 Data Member的布局(Data Member Layout)

已知下面一组data member: class Point3d { public: //... private: float x; static List&lt;Point3d*&gt;* freeList; float y; static const int chuckSize = 250; float z; }; Nonstatic data members在class o...

hallå的博客 243

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

深入探索C++对象模型笔记之六 —— Data语意学

<br />对类或类的实例进行sizeof取大小<br /> <br />大小主要有三个因素影响:<br />1.语言本身所造成的额外负担:语言支持虚函数和虚拟基类时,导致一些额外负担。<br />2.编译器对于特殊情况所提供的优化处理:某些编译器会对empty virtual base class提供特殊支持。<br />3.边界对齐<br /> <br />Data Member的绑定<br /> <br />全局变量作用域与类的作用域。<br /> <br />早期C++的两种防御性程序设计风格:<b

wangsifu2009的专栏 678

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

c++_vbptr和vfptr

文章目录虚基表虚函数表 虚基表 #include<iostream> class Base { public: char m_base1; char m_base2; Base(int a) : m_base1(a),m_base2(a){} }; class Child : virtual public Base { public: char m_child; Chil...

Starr 1294

《深度探索C++对象模型》读书笔记——关于对象【for_wind】

//整理之,分享之,欢迎指正。for_wind 1、C与C++的区别:       概括来说,C程序中程序性地使用全局数据[注1]。而C++采用ADT(abstract data tpye)或class hierarchy的数据封装。       加上封装后,C++在布局以及存取时间上的主要额外负担是由virtual引起的(见本文3、加上继承部分)。       包括: (1)vir

for_wind的专栏 1897

《算法导论》读书笔记——动态规划与贪心算法【for_wind】

//整理笔记之算法部分。。。for_wind 1、动态规划        动态规划适用于子问题不是独立的情况,也就是各子问题包含公共的子子问题。动态规划算法对每个子子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案。       如:装配线调度、矩阵链成法、最长公共子序列、最优二叉查找树。 动态规划算法的设计分如下4个步骤: 1

for_wind的专栏 1169

《深度探索C++对象模型》读书笔记——Function 语意学【for_wind】

Member的各种调用方式

for_wind的专栏 914

排序算法的归纳与比较,还有相关视频哦【for_wind】

好久没有看算法相关的东西,把以前的笔记整理一下,当作复习。加油~~//for_wind 名称 插入排序 合并排序 堆排序 快速排序 计数排序 奇数排序 桶排序 思路 通过比较在已排序的数组A[1,...,j-1]中插入新数据A[j]形成新的已排序数组A[1,...,j] (分解)分成2个子序列

for_wind的专栏 911

Effective C++ 读书笔记【for_wind】

1、尽量以const,enum,inline替换#define,或说尽量以编译器取代预处理器     #define可能并不进入符号表(symbol table)     const:注意:A、常量指针;B、class专属常量,为了确保常量至多只有一份实体,必须让它成为static成员。 2、尽量以代替 3、尽量以new和delete取代malloc和free     差别在构造函数、析

for_wind的专栏 870

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 238

选型笔记:Type‑C 扩展坞方案评估|DP1.4 转 HDMI2.0 桥接芯片 GSV2201S@ACP

本文记录 GSV2201S 这款 DP1.4a 转 HDMI2.0 桥接芯片评估,梳理视频、HDCP、PD 集成能力,总结 Type‑C 转接 Dongle、扩展坞方案的适配边界,以及差分走线、PD 时序、HDCP 握手等高频调试问题,为 C 口视频转接方案选型提供参考。

ACP广源盛的博客 220
上一篇: 《深度探索C++对象模型》读书笔记——关于对象【for_wind】
下一篇: 后缀数组模版 及 可重叠和不可重叠最长重复子串【for_wind】
for_wind
博客等级 码龄14年 14粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值