【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++】智能指针(一)

相关推荐
程序员-King.3 分钟前
2、桥接模式
c++·桥接模式
chnming19877 分钟前
STL关联式容器之map
开发语言·c++
进击的六角龙9 分钟前
深入浅出:使用Python调用API实现智能天气预报
开发语言·python
檀越剑指大厂9 分钟前
【Python系列】浅析 Python 中的字典更新与应用场景
开发语言·python
湫ccc16 分钟前
Python简介以及解释器安装(保姆级教学)
开发语言·python
程序伍六七20 分钟前
day16
开发语言·c++
wkj00125 分钟前
php操作redis
开发语言·redis·php
极客代码30 分钟前
【Python TensorFlow】进阶指南(续篇三)
开发语言·人工智能·python·深度学习·tensorflow
__基本操作__34 分钟前
历遍单片机下的IIC设备[ESP--0]
单片机·嵌入式硬件
土豆湿36 分钟前
拥抱极简主义前端开发:NoCss.js 引领无 CSS 编程潮流
开发语言·javascript·css