C++对象模型Data语意学分析、虚继承底层实现机制

自主编写程序实现strcmp函数功能 本文旨在通过自主编写程序实现函数strcmp的功能。代码如下: 阅读详情


1. Class 的大小


  • 一个空 class 的大小为 1 字节,因为编译器需要安插进去一个 char,使得这个 class 对象得以在内存中被配置独一无二的地址。虽然空 class 大小为 1 字节,但是假如某个类 A 继承该空 class,计算类 A 的大小时会优化父类空 class 的大小,如类 A 为空,sizeof(A) = 1,不空,则为类A真实数据大小。
  • 我们通常说某个 class 内部有 virtual function 尽管没有数据成员,它的大小仍为为 4 字节,因为它有一个 vptr,指向一个 vtbl ,所以按指针算大小就是 4 字节。实际上,就算没有 virtual function,如果某个类虚继承别的类,编译器仍生成有 vtbl ,因为它的 vtbl 还要用来保存 virtual base class object(虚基类子对象)的 offset 。
  • 对于 virtual 继承,所有的 virtual base class subobject 在派生类中只保留一份,所以计算派生类对象时,按一份计算即可。
  • 计算 class 的大小时,还要考虑内存对齐,标准就是一个 bus 的长度,32 位为 4 字节。不过要注意,派生类数据成员并不会填补基类花在内存对齐上的部分。

下面举出无数例子来验证结论 :)

case 1 :class A 为空时的普通继承、普通继承有成员、虚继承、有虚函数

class A { };
class B : public A { };
class C : public A { char ch; };
class X : virtual public A { };
class Y { virtual void fun(); };

std::cout<<sizeof(A)<<std::endl;    // 1
std::cout<<sizeof(B)<<std::endl;    // 1
std::cout<<sizeof(C)<<std::endl;    // 1
std::cout<<sizeof(X)<<std::endl;    // 4
std::cout<<sizeof(Y)<<std::endl;    // 4
输出结果分析:A 为空,除了它自己,别人都会优化掉它。虚继承和含有虚函数这两种情况都有 vptr,所指东西不同而已。 当然 vptr 在既有虚继承,又有虚函数的类中正负分别指向 virtual functions 和 virtual base class subobject 的 offset,只有一个 vptr。(Mircosoft 编译器有两个虚函数表 vtbl ,有两个 vptr;G++实际上只有一个 vtbl,一个vptr,并且 virtual funcions 和 virtual base class subobject 的 offset 按顺序存放)。

g++与虚继承

g++编译器生成的C++类实例,虚函数与虚基类地址偏移值共用一个虚表(vtable)。类实例的开始处即为指向所属类的虚指针(vptr)。实际上,一个类与它的若干祖先类(父类、祖父类、...)组成部分共用一个虚表,但各自使用的虚表部分依次相接、不相重叠。

g++编译下,一个类实例的虚指针指向该类虚表中的第一个虚函数的地址。如果该类没有虚函数(或者虚函数都写入了祖先类的虚表,覆盖了祖先类的对应虚函数),因而该类自身虚表中没有虚函数需要填入,但该类有虚继承的祖先类,则仍然必须要访问虚表中的虚基类地址偏移值。这种情况下,该类仍然需要有虚表,该类实例的虚指针指向类虚表中一个值为0的条目。

该类其它的虚函数的地址依次填在虚表中第一个虚函数条目之后(内存地址自低向高方向)。虚表中第一个虚函数条目之前(内存地址自高向低方向),依次填入了typeinfo(用于RTTI)、虚指针到整个对象开始处的偏移值、虚基类地址偏移值。因此,如果一个类虚继承了两个类,那么对于32位程序,虚继承的左父类地址偏移值位于vptr-0x0c,虚继承的右父类地址偏移值位于vptr-0x10.

一个类的祖先类有复杂的虚继承关系,则该类的各个虚基类偏移值在虚表中的存储顺序尊重自该类到祖先的深度优先遍历次序。

Microsoft Visual C++与虚继承

Microsoft Visual C++与g++不同,把类的虚函数与虚基类地址偏移值分别放入了两个虚表中,前者称为虚函数表vftbl,后者称虚基类表vbtbl。因此一个类实例可能有两个虚指针分别指向类的虚函数表与虚基类表,这两个虚指针分别称为虚函数表指针vftbl与虚基类表指针vbtbl。当然,类实例也可以只有一个虚指针,或者没有虚指针。虚指针总是放在类实例的数据成员之前,且虚函数表指针总是在虚基类表指针之前。因而,对于某个类实例来说,如果它有虚基类指针,那么虚基类指针可能在类实例的0字节偏移处,也可能在类实例的4字节偏移处(对于32位程序来说),这给类成员函数指针的实现带来了很大麻烦。

一个类的虚基类指针指向的虚基类表的首个条目,该条目的值是虚基类指针到整个类实例内存首地址的偏移值。即obj.vbtbl - &obj。虚基类第2、第3、... 个条目依次为该类的最左虚继承父类、次左虚继承父类、...的内存地址相对于虚基类表指针自身地址(即 &vbtbl)的偏移值。

如果一个类同时有虚继承的父类与祖父类,则虚祖父类放在虚父类前面。


case 2:class A 含有一个 char 时,还是上述种类:

class A { char ch; };
class B : public A { };

class C : public A { char ch; };
class C_ : public A { char ch; int val; };   // C_

class X : virtual public A { };

std::cout<<sizeof(A)<<std::endl;    // 1
std::cout<<sizeof(B)<<std::endl;    // 1
std::cout<<sizeof(C)<<std::endl;    // 2
std::cout<<sizeof(C_)<<std::endl;   // 8   // 1 + 3(对齐数值)+ sizeof(int) = 8 
std::cout<<sizeof(X)<<std::endl;    // 8   // 1 + 3(对齐数值)+ sizeof(vptr) = 8
输出结果分析:注意内存对齐就行了。

case 3:特殊一点的
class A { char ch; };
class B : virtual public A { char ch; };

std::cout<<sizeof(A)<<std::endl;    // 1
std::cout<<sizeof(B)<<std::endl;    // 8
输出结果分析:8 这个结果或许有点惊讶,并没有直接 char + char = 2,然后再对齐至 4 字节。因为实际上这是错误的。
派生类不会填补基类花在内存对齐上的无用空间!
看一下《深度探索C++对象模型》原话,其中 Concrete1 为基类,Concreae2 为派生类 :
也就是说发生 Concrete1 subobject 的复制操作时,就会破坏 Concrete2 members。即基类子对象复制到派生类,会覆盖掉派生类后来增加的成员。


case 4:(重点)关于虚基类有2个例子,这是第一个:

class A { };
class B : virtual public A { };
class C : virtual public A { };
class X : public B, public C { };

std::cout<<sizeof(A)<<std::endl;    // 1
std::cout<<sizeof(B)<<std::endl;    // 4
std::cout<<sizeof(C)<<std::endl;    // 4
std::cout<<sizeof(X)<<std::endl;    // 8
输出结果分析:A 为空,大小为 1。B 和 C 内部都有指向有关 virtual base class subobject 的 vptr,所以大小都为 4。但是虽然它们的 vptr 指向的内容最终都汇聚于 A,但是就像虚表一样,每个类都有自己的虚表,是不共享的。所以,尽管虚基类在派生类 X 中只保留一根,这两个 vptr 确是都要算上的。由于虚基类为空,被优化掉,派生类自己也为空,所以 sizeof(X) 的大小用伪码表示就是 sizeof(B.vptr) + sizeof(C.vptr) !

case 5:同样关于虚基类

class A { char ch; };
class B : virtual public A { };
class C : virtual public A { };
class X : public B, public C { };

std::cout<<sizeof(A)<<std::endl;
std::cout<<sizeof(B)<<std::endl;
std::cout<<sizeof(C)<<std::endl;
std::cout<<sizeof(X)<<std::endl;
这次就能更好体现虚基类只有一份的说法了。B 和 C 大小都是各自 vptr 大小 + 针对 char 的内存对齐 = 8。但是针对类 X,它的大小是仅有一份的A的大小,以及 B 和 C 的vptr的大小,所以是 4 + 4 + 4 = 12。

case 6:注意这种全为空

class A { };
class B : public A {}; 
class C : public A {}; 

class D : public B, public C {};

std::cout<<sizeof(D)<<std::endl;    // 2
这个大小为2哦,编译器至少要确定是从两路继承来的,所以至少需要两个字节。


2. Data member 的绑定

extern float x;  
  
class Point3d  
{  
    public:  
        point3d();  
        //问题:被传回和被设定的x是哪一个x呢?  
        float X() const  
        {  
            return x;  
        }  
          
    private:  
        float x, y, z;//Point3d::X()将返回内部的x。  
};  
在早期(2.0之前)C++的编译器上,将会指向global x object, 因为编译器还没有看到数据成员,就开始编译函数。这导致C++的两种防御性程序设计风格:
  1. 把所有的数据成员放在 class 声明的开头,而不是下面,以保证正确的绑定。这就是所谓的以数据为中心。
  2. 把所有的内联函数,不管大小都放在 class 的声明之外。
但是对于成员函数的参数列表同上面情况相反:
typedef int length;  
   
 class Point3d  
 {  
     public:  
         //length 将被决议为global  
         //_val将被决议为Point3d::_val  
         void mumbel(length val)  
         {      
             _val = val;  
         }  
           
         length mumble()  
         {  
             return _val;  
         }  
  
     private:  
         //导致 _val = val; return _val;不合法      
         typedef float length;  
         length _val;      
 };  
对于参数列表,除非typedef嵌套类型声明放在 class 的起始处,否则会认为不合法。


3. Data Member 的存取

Point3d origin *pt = &origion;
origin.x = 0.0;
pt->x = 0.0;
这两种方式差异?
对于 static 数据成员:一般情况,完全相同。原因是:每一个 static 成员只有一个实体,存放在成语的 data segment 之中, 成员并不在 class 对象之中。
对于 non-static 数据成员:一般情况,完全相同。原因是:
  • px->x = 0.0,事实上是经由一个”implicit class onject“(由 this 指针表达式)完成,即 this->x = pt.x = 0.0
  • origin.x = 0.0,事实上欲对一个 nonstatic data member 进行存取操作,编译器需要把 class object 的起始地址加上 data member 的偏移量。那么此表达式实际上等于;&origin + (&Point3d::_y - 1)
注意其中的 -1 操作。指向 data member 指针,其 offset 值总是被加上 1,这样可以使编译系统区分出”一个指向数据成员的指针,用以指出第一个成员” 和 “一个指向数据成员的指针,没有指出任何数据成员” 这两种情况。这个 offset 下面会说到。

上面说了一般情况完全相同,但是对于虚拟继承,则:


4. 继承与 Data Member


继承:
一个派生类所表现出来的东西,是其自己成员加上其基类成员的综合。

继承易犯的 错误:
  1. 重复设计一些相同的操作函数
  2. 把一个 class 分解为两层或者更多层,有可能会为了“表现 class 体系之抽象画” 而膨胀所需空间。因为内存对齐。
多态的缺点, 空间和存取时间的负担:
  1. 导入一个 vtbl 和 一个 vptr 的负担
  2. constructor 和 destructor 要负责初始化和抹消 vtbl 和 vptr 的负担。
把 vptr 放置在 class object 的哪里最好?
  1. 放置在尾部保留 C struct 的对象布局,C++初期,这种方式被许多人采用。
  2. 放在最前端。对于 “在多重继承之下,通过指向 class members 的指针调用 virtual function" 会带来一些帮助。当然代价就是丧失 C 语言的兼容性。

多重继承:
多重继承的复杂度在于派生类和其上一个基类乃至于上上一个基类之间的非自然关系。

如图一个继承体系:
下列操作并不需要编译器去调停或修改地址,它很自然地可以发生,而且提供了最佳执行效率。
Point3d p3d;
Point2d *p = &p3d;           
多重继承的问题主要发生于derived class object 和其第二或后续的base class object之间的转换:
Vertex3d  v3d;
Vertex    *pv;
Point2d   *p2d;
Point3d   *p3d;

pv = &v3d;
// 需要这样的内部转换:
pv = (Vertex*)(((char*)&v3d) + sizeof(Point3d));

// 下列转换只需要简单地拷贝地址就行了
p2d = &v3d;    
p3d = &v3d;
C++ Standard 并未要求Vertex3d中的base classes Point3d和Vetex有特定的排列次序。原始的cfont编译器是根据声明次序来排序它们。目前各编译器仍然是以此方式完成多重base classes的布局(但如果加上虚拟继承,就不一样了)

虚拟继承
多重继承的一个语意上的副作用就是,它必须支持某种形式的“shared subobject继承”。

对于多重启程,我们只需要一份基类 subobject 就可以了,所以引入虚拟继承。

虚拟继承的布局策略是先安排好派生类的不变部分(某些不是虚拟继承来的父类也是不变的,要按照顺序安排),然后再建立共享部分(即 virtual base class subobject)。

cfront 的布局是引入把 virtual base class subobject (即共享部分)的偏移量加入 vtbl,vtvl 索引为负值时,可以访问到 offset。
继承链如图:


布局模型如下:

当然 Mirsoft 和 G++ 的布局在前面已经说过了,大同小异。

5.指向 Data Members 的指针

 class Point
        {
            public:
                virtual ~Point();
                static Point origion;
                float x,y,z;
        };
如果vptr放在对象的尾端,则三个坐标值在对象布局中的offset分别是0,4,8。如果vptr放在对象的起头,则三个坐标值在对象布局中的offset分别是4,8,12.然而你若去取data members的地址, 传回的值总是多1, 就是1,5,9,或5,9,13。

打印 offset 可以通过面这句代码:
 std::cout<<&point::x<<std::endl;

编译器为了区分 “没有指向任何数据成员的指针“(其实就是指向了该对象地址,但指向不是第一个数据成员) 和 一个指向 “第一个数据成员的指针”,给所有指针偏移量都加上了 1。因此,无论编译器或者使用者都必须记住,在真正使用该值以指出一个 member 之前,请先减掉 1。

如何区分一个 “没有指向任何数据成员的指针” 和 一个指向 ”第一个数据成员“ 的指针:
float point::*p1 = 0;
float point::*p2 = &point::x;

if(p1 == p2){
    std::cout<<"same"<<std::endl;
相等即可,只不过不可能相等,除非减掉 1。

取一个非静态数据成员的地址,将会得到它在类中的偏移量(offset),取一个绑定与真正类对象身上的数据成员地址,将会得到该数据成员在内存中的真正地址。
二者区别,即 &point::x 和 point p; &p.x; 的区别而已。


            虚继承问题
          






              







大小端模式解析(联合体union) 以前一直知道大小端,很久不做题又把大端和小端记混了,这次来总结一下,以后再也不会混了。 一:什么是大小端 大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。 小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中 阅读详情

相关推荐

C++引用计数思想--利用引用计数器自定义String类

什么是引用计数?  最直观的垃圾收集策略是引用计数。引用计数很简单,但是需要编译器的重要配合,并且增加了赋值函数 (mutator) 的开销(这个术语是针对用户程序的,是从垃圾收集器的角度来看的)。每一个对象都有一个关联的引用计数 —— 对该对象的活跃引用的数量。如果对象的引用计数是零,那么它就是垃圾(用户程序不可到达它),并可以回收。每次修改指针引用时(比如通过赋值语句),或者当引

FreeeLinux's blog 2361

Class类文件结构

Class文件是一组以8位字节为基础单位的二进制流。1. 魔数与Class文件的版本 Class文件的头4个字节称为魔数( Magic Number) , 它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。紧接着魔数的4个字节存储的是Class文件的版本号: 第5和第6个字节是次版本号( Minor Version) , 第7和第8个字节是主版本号( Major Version) 。2. 常量池 常量池可以理解为Class文件之中的资源仓库。常量池中主要存放两大类常量: 字面量( Lite

307

一致性哈希算法C++模板实现

关于一致性哈希概念上的理解可以参考:http://blog.youkuaiyun.com/cywosp/article/details/23397179        参考代码:http://www.cnblogs.com/coser/archive/2011/11/27/2265134.html 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因

FreeeLinux's blog 1807

《深度探索C++对象模型》读书笔记第三章:Data语意学

这是第三章,第三章当时我的博客: C++对象模型Data语意学分析、虚继承底层实现机制 写的比较详细,所以这次可能总结会少一些。 关于 class 的大小分析上面提到的博客有详细分析。对于 origin.x=0.0 和 pt->x=0.0 两种方式存取数据有什么重大差异?答案是:当调用类是一个派生类,而且在其继承体系中有一个虚基类,并且被存取的成员(如本例的x)是一个从该虚基类继

FreeeLinux's blog 748

产业园区如何高效对接企业技术需求与高校科研成果?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

akshare 布林通道策略

import datetime import pandas as pd import backtrader as bt import matplotlib.pyplot as plt from datetime import datetime import matplotlib import akshare as ak %matplotlib inline class Boll_strategy(bt.Strategy): #自定义参数,每次买入1800手 params=(('size'.

qq_26742269的博客 5338

C++面试核心考点深度解析:从内存管理到现代特性实战指南

在软件开发领域,内存管理是构建高效、稳定程序的基础,它涉及堆、栈等不同内存区域的生命周期与资源分配。其核心原理在于通过RAII(资源获取即初始化)等机制,确保资源使用的安全性与确定性。这一技术价值在于能从根本上防止内存泄漏和悬垂指针,提升程序的健壮性。在实际应用场景中,无论是系统底层开发还是高性能服务端编程,对内存的精细控制都至关重要。本文聚焦于C++面试中的高频考点,深入剖析智能指针(如std::unique_ptr、std::shared_ptr)如何作为现代C++内存管理的基石,以及多线程并发环境下如

weixin_34185512的博客 804

EU 2021/646 eCall工程拆解:MSD、测试模式与型式核准

紧急呼叫系统(eCall)是车载通信中面向事故救援的安全功能,其原理是在碰撞或人工触发后,通过蜂窝网络向公共安全应答点发送最小数据集(MSD),并建立语音链路。技术价值在于缩短救援响应时间、提升事故后生存率,应用场景覆盖乘用车、轻型商用车及车载T-Box等前装通信模块。面向欧盟市场时,EU 2021/646把eCall从安装要求推进到型式核准测试,关注触发链路、备用电源、GNSS定位、MSD字段、音频质量与回拨能力。工程落地需要围绕测试模式、字段校验、配置管理和送检样件建立可交付合规方案。

weixin_31815843的博客 209

C++服务端春招面试问题总汇

记录了本人求职时期遇到的一些面试问题

m0_59347684的博客 1534

别再自己造轮子了!用MONAI Model Zoo里的预训练模型,5分钟搞定医学影像AI项目

本文介绍了如何利用MONAI Model Zoo中的预训练模型快速搭建医学影像AI项目,大幅节省开发时间和计算资源。通过实战案例展示了UNet、DenseNet121等核心模型的应用技巧,帮助临床医生和研究者5分钟内启动高质量医学影像分析,特别适合小样本场景下的病灶分割和分类任务。

weixin_33704234的博客 239

Synopsys工具filter命令:从数据筛选到高效IC设计的实战指南

在数字集成电路设计流程中,数据筛选与查询是工程师处理海量网表、时序报告和设计约束的核心需求。其原理在于通过声明式查询语言,直接对设计对象(如单元、网络、路径)的属性进行逻辑判断与模式匹配,从而精准定位目标。这种技术价值在于将工程师从繁琐的过程式脚本循环中解放出来,大幅提升代码可读性与运行效率,直接调用工具内置的高性能查询引擎。在实际应用场景中,它广泛用于物理与时序联合分析、层次化模块操作、动态约束生成以及自动化报告生成。本文聚焦于Synopsys工具链中filter命令的高阶用法,深入解析其表达式语法、正则

weixin_28007513的博客 577

C++模板之SFINAE技术

今天在阅读muduo库的源码时,里面有这样几行代码: template struct has_no_destroy { template static char test(typeof(&C::no_destroy)); // or decltype in C++11 template static int32_t test(...); const static bool val

FreeeLinux's blog 4849

基于LSTM语义分析的企业风险预测系统

在数字化时代,企业面临着前所未有的挑战和机遇,而风险管理作为确保企业稳定发展的关键一环,其重要性日益凸显。为了及时、准确地监测和分析企业相关的风险,并预测可能带来的风险,本文介绍了一种基于长短期记忆(LSTM)语义分析的企业风险预测系统。该系统利用深度学习技术,通过将收集到的企业数据进行预处理和分析,结合LSTM模型对文本进行深度语义分析,从而实现对潜在风险的预测和评估。系统设计方面,该系统包括数据预处理、LSTM模型构建与训练、风险预测以及可视化展示等模块。

qiufangtaowvnyyidtf的博客 1392

shared_ptr源码分析

shared_ptr与scoped_ptr一样包装了new操作符在堆上分配的动态对象,但它实现的是引用技术型的智能指针,可以自由地被拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0时)它才删除包装的动态分配的对象。shared_ptr也可以安全地放到标准容器中,弥补了auto_ptr因为转移语义而不能把指针作为STL容器的缺陷。  源码分析首先看shared_ptr类:template

FreeeLinux's blog 4720

基于知识图谱的职位推荐系统的设计与实现

在职位推荐系统中,实体可以包括职位、公司、技能等,而关系可以包括职位与公司的关联、职位与技能的关联等。基于知识图谱的职位推荐系统可以提供更加智能和精确的职位推荐服务。通过收集和预处理数据构建知识图谱,并设计推荐算法和用户界面,可以实现一个完整的职位推荐系统。通过利用知识图谱技术,我们可以构建一个更加智能和精确的职位推荐系统。本文将介绍如何设计和实现基于知识图谱的职位推荐系统,并提供相应的源代码。通过定义实体和关系的节点和边,可以将数据存储在图数据库中,并建立它们之间的关系。

RiqaEditor的博客 1668

使用 IMDb API 获取电影评分和评论的 Python 爬虫

电影是人们娱乐生活中的重要组成部分,随着电影产业的快速发展,观众对电影的评价也越来越多样化。IMDb(互联网电影数据库)作为全球最大的电影评分和评论平台之一,收录了成千上万部电影、电视节目及其相关数据。它提供了一个强大的 API 接口,允许开发者访问电影的评分、评论、演员信息等数据。本文将带你一步一步实现一个 Python 爬虫,使用 IMDb API 获取电影的评分和评论。我们将通过这个爬虫来分析一些热门电影的观众反馈,并可视化分析结果。

2201_76125261的博客 1055

反汇编专用工具——objdump

命令 参数: -d: 将代码段反汇编 -S: 将代码段反汇编的同时,将反汇编代码和源代码穿插显示,编译时需要给出-g,即需要调试信息 -C: 解决Name mangling导致函数名无法看懂的问题 -l: (小写L)插入源代码的文件名称和行号 -j section: 仅反汇编指定的section。可以有多个-j参数来选择多个section -M: 语法模式,例如att(AT&amp;t),int...

FreeeLinux's blog 4553

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

shared_ptr的理解和注意事项

昨天把shared_ptr的源码分析了一遍,也看了一些有关shared_ptr分析的文章,今天自己来总结一下。  以独立语句将newed对象置入智能指针对于下面这样一个使用智能指针的函数调用,可能会造成内存泄漏:process(std::shared_ptr<int>(new int(10)), priority());编译器没有规定传入参数的调用顺序,可能先执行new表达式,然后执行priori

FreeeLinux's blog 4130

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

使用scanf初始化指针

无意间想到用scanf初始化指针,觉得挺有意思的,代码如下: #include int main() { int num = 5; printf("%p ", &num); int *p = NULL; scanf("%p", &p); printf("%p\n", p); printf("%d\n", *p);

FreeeLinux's blog 4038

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

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

字符串逆序输出与main函数的递归调用

昨天有人问问了我这样一道题,如何不利用第三方变量,将字符串逆序输出。我给出了几种方案,有非递归的,也有递归的。 一: 首先来非递归的,这个就比较简单了,直接利用strlen函数求得字符串长度,然后逆序输出即可。 代码如下: #include #include void reverse_str(const char *str) { auto len = strlen(str); f

FreeeLinux's blog 2772

C++模板之trait与模板技巧

一:trait 1.Fixed Traits 主要是构造适应各种类型的函数Fixed主要指,一旦定义了这个分离的 trait,则无法在算法中进行改写。value trait 是用于针对型别进行 value 方面的操作,如 zero(),它是 fixed trait 和下面的另外一种参数化萃取的一部分。 template class accumulation_traits; t

FreeeLinux's blog 2626

关于sizeof在调用函数中获取被调函数数组长度的思考

我们知道, sizeof 是一个操作符,其作用是返回一个操作数所占的内存字节数。那么在函数调用中,如何求一个被调函数数组的长度呢? 现在我们来看下面的示例代码: #include void f(int a[]) { for(int i=0; i<sizeof(a)/sizeof(int); ++i){ a[i] = i; }

FreeeLinux's blog 2451

weak_ptr源码分析

之前分析了shared_ptr的框架,这次来说一下shared_ptr的兄弟weak_ptr,它们常常是一起出现的。weak_ptr顾名思义就是弱指针,它不会增加对象的引用计数,欸有重载operator*和operator->,使用时,应当通过weak_ptr的lock函数构造一个shared_ptr。作用:防止循环引用(以后博客分析)。应用:在enable_shared_from_this类中,它

FreeeLinux's blog 2192

valist、vsnprintf、snprintf、localtime分析

今天写一个日志类,不过目前还有bug。condition的destroy不知道怎么的死锁了,之前测试很好的,并入其他模块就不能工作了,我的想办法调试一下,毕竟多线程程序不好调试。 先来分析一下今天用的这几个函数,其实以前看libevent,muduo这些函数都出现过,不过学了又忘了,这次总结一下。 1.valist C语言标准库中头文件stdarg.h声明了一组能够变长参数的宏。主要

FreeeLinux's blog 1768

前置和后置++运算符的重载

首先是类的声明头文件: 在类的生命中,分别用了四个函数实现重载的不同方式,已标注,程序可和类定义中的相应函数配合实现相应的重载功能。 /******************************Integer.h***********************************/ #ifndef _INTEGER_H_ #define _INTEGER_H_ class Integer

FreeeLinux's blog 1619
上一篇: C++拷贝构造函数、无名临时对象以及NRV优化分析
下一篇: 十二月状态总结
FreeeLinux
博客等级 码龄11年 421粉丝 356原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值