Loading

C++内存分布

内存分布

典型的C/C++内存表示有以下几部分构成

  • Text segment,存放代码段和只读常量的区域
  • Initialized data segment,通常叫做数据段,已经初始化的静态变量和全局变量存放的区域,如static int i = 5、全局变量int j = 10都存放在数据段
  • Uninitialized data segment(bss),未初始化的静态变量或者全局变量存放在bss(Block Started by Symbol)段,并且用0进行初始化。
  • Heap,动态内存分配时数据存放的位置
  • Stack,栈顶指针方向从自顶向下,与堆的方向相反,

参考:https://www.geeksforgeeks.org/memory-layout-of-c-program/

存储持续性、作用域、链接性

这里引入3个概念:

  • 存储持续性(storage duration)
  • 作用域(scope)
  • 链接性(linkage)

存储持续性

存储持续性就是数据保存在内存中的时间,C++11中有四种方案来存储数据。

  • 自动存储持续性,在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。这些变量在函数或代码段开始时创建,执行完函数或代码块时,它们的内存被释放。
  • 静态存储持续性,被static修饰的变量其内存在所有程序结束时才释放。
  • 线程存储持续性(C++11增加),如果变量是使用关键字thread_local 声明的,则其生命周期与所属的线程一样长。
  • 动态存储持续性,使用new运算符分配的内存一直存在,直到使用delete运算符将其释放或是程序结束。这种方式分配的内存,有时将其称为自由存储区或者是堆。

作用域、链接性

作用域即变量所作用的范围

链接性是指变量与其他单元如何实现共享

c++有5种变量存储方式:

在声明变量中有存储说明符(storage class specifier)或CV-限定符(cv-qualifier)。

存储说明符:

  • auto(C++11 表示自动类型判断)
  • register(C+11 指出变量是自动的)
  • static,变量不在任何函数中声明时链接性表示为内部链接性(即可以被文件内的所有单元访问),在某个函数中使用static声明时无链接性
  • extern,其他文件访问某个文件中的某个变量必须使用extern来引用声明(reference declaration)
  • thread_local(C++11新增)
  • mutable,用它来指出,即使结构(或类)变量为const,其某个成员也可以被修改。

CV-限定符:

  • const,它表明,内存被初始化后,程序便不能再对它进行修改

  • volatile(不常用),关键字 volatile 表明,即使程序代码没有对内存单元进行修改,其值也可能发生变化

#include <stdio.h>
int g;	//全局变量,对应第四种存储方式
static int s_g = 5;		//对应第五种存储方式

const int fingers = 10;		//与static int finger = 10,一样其链接性表现为内部

extern const int f;	//引用声明其他文件的变量f


void fun()
{
    int i = 5;	//局部变量,对应第一种存储方式
    register int m = 0;	//对应第二种存储方式
    static int j = 0;	//静态局部变量,对应第三种存储方式
    ....
}

int main() { ... }

动态内存分配

C++使用new关键字进行动态内存分配(dynamic memory allocation),这一部分内存单元称为堆(heap)或是自由存储区(free storage)。new关键字有两种使用方法,一是常规new运算符,二是定位(placement)new运算符。定位new运算符是在指定进行内存的分配,常规new运算符则是在堆分配内存,举个例子:

#include <iostream>
#include <new>  //使用new运算的特性,需要引进该文件
#include <cstring> //使用strcpy

struct chaff
{
    char dross[20];
    int slag;
};

const int size = 256;

char buff[size];    //缓冲区

int main()
{
    const int N = 2;
    //定位new运算符:指定内存单元位置
    chaff *cf1 = new (buff) chaff[2];    //在buff缓冲区分配单元给cf1指针
    //常规new运算符:在堆中分配内存单元
    chaff *cf2 = new chaff[2];  //常规方法在堆开辟单元
	
    char ds1[][20] = { "dross1", "dross2"};
    char ds2[][20] = { "ndross1", "ndross2"};

    std::cout << "缓冲区buff地址 " << (void*) buff << std::endl;
    std::cout << "new(buff)的地址 " << cf1 << std::endl;
    std::cout << "new的地址 " << cf2 << std::endl;

    std::cout << std::endl;

    for(int i = 0; i < N; i++)
    {
        strcpy(cf1[i].dross,ds1[i]); //字符串的复制
        cf1[i].slag = i;

        strcpy(cf2[i].dross,ds2[i]); //字符串的复制
        cf2[i].slag = i + 2;
    }

    for(int i = 0; i < N; i++)
    {
        std::cout << cf1[i].dross << ' ' 
            << cf1[i].slag << std::endl;
        std::cout << cf2[i].dross << ' ' 
            << cf2[i].slag << std::endl;
    }

    return 0;
}

输出结果:
image
全局变量buff数组在内存的bss段分配了空间,可以看到使用定位new运算符在buff所在内存单元进行内存的分配,而常规new运算符则是堆分配内存单元。

posted @ 2024-05-06 13:42  记录学习的Lyx  阅读(26)  评论(0编辑  收藏  举报