【C++】C++程序的四个区和智能指针的实现

这篇文章介绍下 C++ 程序的四个区,以及一个智能指针的简单实现。

起因

最近在公司审查代码的时候,coverity 对以下代码:

cpp 复制代码
T fun()
{
	Obj obj;
	//代码逻辑
}

报出了 obj 占用空间过大,有可能栈溢出的问题。

以前从来没有考虑过C++的代码存放位置的问题,这次顺便就学习了下。

C++四区

在执行一个C/C++语言程序时,此程序将拥有唯一的"内存四区"------栈区、堆区、全局区、代码区。不过这里的栈和堆并不是数据结构里的栈和堆。

栈区(stack)

存放各种临时变量,包括函数内部声明的临时变量,函数调用的参数以及函数本身调用产生的入栈出栈操作等。

堆区

存放程序员自己分配的内存,如果程序员自己分配了,没有释放,那程序结束以后可能会被操作系统释放?

静态区

存放静态变量,包括全局变量,static 修饰的变量。C语言时期,静态区的变量又分为初始化的和未初始化的,两者分别在不同的位置。C++取消了这个设计。

文本区

存放常量,以及代码本身。

我们用一个例子来展示这几个区:

cpp 复制代码
int global_a = 0;
char global_c;
int global_b;

我们声明三个全局变量,分别打印出他们的地址(防止入栈出栈操作干扰,这里用宏定义):

cpp 复制代码
#define PRINT_ADDR(val) cout << (int)&(val) << endl

打印出来的结果如下:

bash 复制代码
3264544
3264548
3264552

int 类型占用 4 个字节,char 类型占用 1 个字节,这里占用 4 个可能是由于内存对齐的问题。

接下来我们在 main 函数内部声明三个局部变量:

cpp 复制代码
    int temp_c;
    double *temp_d = new double(1.0);
    int temp_e = 3;

打印的结果为:

bash 复制代码
10942212
10942208
10942204

与上面变量的内存地址明显有很大差别,另外注意这里的 double 类型虽然占用 8 字节,但是我们声明的是一个指针,无关类型,是固定的 4 字节。同样,new 出来的内存在堆上,将堆上的地址返回存储在 temp_d中。

最后是两个静态变量和一个常量:

cpp 复制代码
    static int static_e = 2;
    static int static_f = 4;
    string temp_f = "f";

地址为:

cpp 复制代码
3207168
3207172
3215360

本次出问题的地方就在栈区。因此将临时变量改为从堆上 new/delete 内存即可。但是需要注意的是,new/delete 的操作会在运行期间动态分配内存,而入栈与出栈操作仅在编译期间就可以完成,这相当于把编译的内存压力转给了运行期,并不是一个合理的做法。

智能指针的简易实现

使用 new/delete 的组合容易出现忘记 delete 的问题,事实上,我们可以使用一个工具类"包裹"原始的类,在构造时调用 new,在析构时调用 delete,我给出一个简单的实现如下:

cpp 复制代码
template <typename Obj>
struct AutoObj
{
    AutoObj(Obj* obj)
    {
        actualObj = obj;
    }

    ~AutoObj()
    {
        if (actualObj != nullptr)
        {
            delete actualObj;
        }
    }

private:
    Obj *actualObj = nullptr;
};

可以编写一个类,测试一下效果:

cpp 复制代码
struct Student
{
    Student(int age):age(age)
    {
        cout << "Student construct" << endl;
    }

    ~Student()
    {
        cout << "Student delete" << endl;
    }
private:
    int age;
};
cpp 复制代码
AutoObj<Student> stu{new Student(10)};

看打印发现析构函数已经被正确地调用了:

bash 复制代码
Student construct
Student delete

这个简易的智能指针的实现实在过于简易了,无法处理一些异常情况,需要根据具体情况去实现其他比如复制构造函数之类的接口。

当然,你也可以直接用智能指针,见【C++】智能指针(一)

相关推荐
半桶水专家15 小时前
go语言中的结构体嵌入详解
开发语言·后端·golang
小Ti客栈15 小时前
安全之加密算法
安全
广州灵眸科技有限公司15 小时前
瑞芯微(EASY EAI)RV1126B CAN使用
linux·网络·单片机·嵌入式硬件
SmartRadio15 小时前
CH584M vs nRF52840 vs 主流BLE SoC全面对比
单片机·嵌入式硬件·mcu·物联网·开源·硬件工程
在屏幕前出油16 小时前
二、Python面向对象编程基础——理解self
开发语言·python
粉红色回忆16 小时前
用链表实现了简单版本的malloc/free函数
数据结构·c++
阿方索16 小时前
python文件与数据格式化
开发语言·python
写代码的小球17 小时前
C++计算器(学生版)
c++·算法
k***921618 小时前
【C++】继承和多态扩展学习
java·c++·学习
weixin_4407305018 小时前
java结构语句学习
java·开发语言·学习