C++ 内存模型

程序的内存分配

https://blog.csdn.net/u013007900/article/details/79338653

一个由C/C++编译的程序占用的内存分为以下几个部分:

  • 栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
  • 堆区(heap) — 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS(操作系统)回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
  • 全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
  • 文字常量区 —常量字符串就是放在这里的。程序结束后由系统释放。
  • 程序代码区—存放函数体的二进制代码。
int  a=0;   全局初始化区    

char *p1;   全局未初始化区    
int  main()    
{    
  int  b; //栈    
  char  s[]="abc"; //栈    
  char  *p2; //栈    
  char  *p3="123456"; //123456/0在常量区,p3在栈上。    

  static int c =0;//全局(静态)初始化区    
  p1 =  (char  *)malloc(10);  //分配得来的10和20字节的区域就在堆区
  p2  = (char  *)malloc(20);       
  strcpy(p3,"123456"); //123456/0放在常量区,编译器可能会将它与p3所指向的"123456"  优化成一个地方。    
}    

头文件

不要将函数定义或变量声明放到头文件中,如果同一程序的其他两个文件包含了该头文件,此时同一个程序将包含同一个函数的两个定义,即多重声明问题,这会导致错误。头文件常包含以下内容:

  • 函数原型(函数声明)
  • 宏定义;#define或const定义的符号常量。
  • 结构声明
  • 类声明
  • 模板声明
  • 内联函数

源代码文件用于具体实现,即头文件中所声明的函数或类的具体实现。

注意,只需将源代码文件加入到项目中,而不用加入头文件。这是因为#include指令管理头文件。另外,不要使用#include来包含源代码文件,这样做将导致多重声明。

https://blog.csdn.net/nei504293736/article/details/90200066

模板类的分离问题:

  • 声明和实现都写到.h文件中
  • 或者,声明写到.h中,实现写到.cpp中,同时在.cpp文件中声明具体的模板类(显式实例化)

https://www.jianshu.com/p/bd2e05aabf7a

https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file

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

有4种内存方案:

  • 自动存储持续性,在函数定义中声明的 变量(包括函数参数)的存储持续性是自动的。它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。
  • 静态存储持续性,在函数定义外定义的变量和使用关键字static定义的变量的存储持续性都为静态。它们在程序整个运行过程中都存在。
  • 线程存储持续性,对于多线程来说,如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长。
  • 动态存储持续性,用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止。这种内存的存储持续性为动态,有时被称为自由存储(free store)或堆(heap)。

作用域(scope)描述了名称在文件的多大范围内可见。可分为局部和全局,作用域为局部的变量只在定义它的代码块中可用,作用域为全局的变量在定义位置到文件结尾之间都可用。

链接性(linkage)描述了名称如何在不同单元间共享。链接性为外部的名称可在文件间共享,链接性为内部的名称只能由一个文件中的函数共享。

静态持续变量与static

程序不会使用栈或者其他来管理静态变量,而是编译器会分配固定的内存块来存储静态变量,这样这些变量在整个程序执行期间一直存在。另外如果没有显式地初始化静态变量,编译器将把它设置为默认值。

C++为静态存储持续性变量提供了3种链接性:

  • 外部链接性,可在其他文件中访问
  • 内部链接性,只能在当前文件中访问
  • 无链接性,只能在当前函数或代码块中访问
int global = 10;  //静态持续,外部链接
static int one_file = 50;  //静态持续,内部链接
void fun1(int n)
{
    static int count = 0;  //静态持续,无链接性
    int llama = 0;
}
/*
函数内的static限定表明该变量会一直驻留在内存中,而函数内的普通变量会随着函数结束而生命周期结束
函数外的static限定表明该变量是内部链接的,而函数外的普通变量是外部链接的
*/

所有静态持续变量在整个程序执行期间都存在。fun1()中声明的变量count的作用域是局部且没有链接性,但是该变量会一直存留在内存中;global和one_file的作用域都为整个文件,而one_file的链接性为内部只能在本文件中使用它,而global的链接性为外部,因此可在其他程序文件中使用它。

extern与static

链接性为外部的变量通常简称为外部变量,它们的存储持续性为静态,作用域为整个文件。外部变量是在函数外部定义的,其也可被称为全局变量。

单定义规则(One Definition Rule, ODR):变量只能由一次定义。C++提供了两种变量声明。一种是定义声明(defining declaration)或简称为定义(definition),它给变量分配存储空间;另一种声明是引用声明(referencing declaration)或简称为声明(declaration),它不给变量分配存储空间,因为它引用已有的变量。引用声明使用关键字extern,且不进行初始化。

引用声明使用关键字extern,且不进行初始化;否则,声明为定义,导致分配存储空间。如果要在多个文件中使用外部变量,只需在一个文件中包含该变量的定义(当定义规则),但在使用该变量的其他所有文件中,都必须使用关键字extern声明它。

//file1.cpp
int a;  //定义,默认为0
static int b = 4;  // 定义,因为初始化了
const int c = 5;
extern const int d = 6;
extern int e = 7;
int fun1(int m, int n) {
    //...
}

//file2.cpp
extern int a, d, e;  //声明,因为有extern且未初始化
int b, c;  // static, const的链接性均为内部
int fun1(int, int);  //函数声明的extern可省略

其他说明符和限定符

  • const

    声明为const的变量必须在声明的时候就初始化,并且一旦初始化后,其值就不可改变,且其声明一般是放在头文件中。const变量的链接性为内部。不过可以在定义时使用extern修饰来覆盖默认的内部链接性,将该变量的链接性为外部,

    extern const int states = 50; // definition with external linkage
    

    此时,必须在所有使用该常量的文件中使用extern关键字来声明它,这与常规外部变量不同,定义常规外部变量时,不必使用extern关键字,但在使用该变量的其他文件中必须使用extern。

  • mutable

    mutable修饰的变量表明其无论如何是可改变的。比如一个const对象,其成员变量不可变,此时若声明某个成员变量是mutable的,则它依旧可以通过该const对象来改变该成员变量的值。

函数和链接性

所有函数的存储持续性都为静态的,即在整个程序执行期间都存在。默认情况下,函数的链接性为外部,即可以在多个文件中共享,但是在另一个文件中可以不使用extern关键字来声明函数原型,而可以直接声明函数原型即可。如果使用static关键字修饰函数,则其链接性将变为内部,此时其只能在一个文件中使用而不能在其他文件中声明和使用,必须在函数原型和函数定义中都使用该关键字,此时如果外部有同名函数,该静态函数将覆盖外部同名函数,就同局部变量覆盖全局变量一样。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值