zoukankan      html  css  js  c++  java
  • C++局部变量、全局变量、静态变量(堆、栈、静态存储区)

    1 static关键字

    1.1 隐藏

    eg:

    //a.c文件中

    char a = 'A';

    void msg()

    {

         printf("Hello ");

    }

    //main.c文件中

    extern char a;

    printf("%c",a);

    输出结果:A Hello

        所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。

        如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏。

    1.2 保持变量内容的持久(static变量中的记忆功能和全局生存期)

    eg:

    #include <stdio.h>

    int fun(){

        static int count = 10; //在第一次进入这个函数的时候,变量a被初始化为10!并接着自减1,以后每次进入该函数,a

        return count--;    //就不会被再次初始化了,仅进行自减1的操作;在static发明前,要达到同样的功能,则只能使用全局变量:   

    }

    int count = 1;

    int main(void)

    {

         printf("global local static ");

         for(; count <= 10; ++count)

         printf("%d %d ", count, fun());

         return 0;

    }

    输出结果:

    global  local static

    1 10

    2 9

    3 8

    4 7

    5 6

    6 5

    7 4

    8 3

    9 2

    10 1

    存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。

    ---基于以上两点可以得出一个结论:把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。

    1.3 默认初始化为0(static变量)

          其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加‘’;太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是‘’。

    1.4 C++中的类成员声明static(有些地方与以上作用重叠)

             静态成员函数不含this指针。

             静态成员是可以独立访问的,也就是说,无须创建任何对象实例就可以访问。

             类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致 了它仅能访问类的静态数据和静态成员函数。 

    2 局部变量、全局变量、静态变量

    局部变量:局部变量也只有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。

    全局变量:全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要用extern关键字再次声明这个全局变量。

    静态局部变量:静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。

    静态全局变量:静态全局变量也具有全局作用域,它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。

    3 堆

    亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。 但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。

    在C++中需要开辟内存单元的时候

    char* str = new char[100];

    使用完之后一定要记得释放内存单元

    delete[] str;

    str = 0;

    4 栈

    在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

    int a;

    5 静态存储区

    内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。

    全局变量

    static修饰的变量或函数等

    下面我们来看看这几种存储区的区别:

    示例1:静态存储区与栈区

    char* p = "Hello World1";

    char a[] = "Hello World2";

    p[2] = 'A';

    a[2] = 'A';

    char* p1 = "Hello World1";

    这个程序是有错误的,错误发生在p[2] = ‘A'这行代码处,为什么呢,是变量p和变量数组a都存在于栈区的(任何临时变量都是处于栈区的,包括在main()函数中定义的变量)。但是,数据“Hello World1”和数据“Hello World2”是存储于不同的区域的。因为数据“Hello World2”存在于数组中,所以,此数据存储于栈区,对它修改是没有任何问题的。因为指针变量p仅仅能够存储某个存储空间的地址,数据“Hello World1”为字符串常量,所以存储在静态存储区。虽然通过p[2]可以访问到静态存储区中的第三个数据单元,即字符‘l'所在的存储的单元。但是因为数据“Hello World1”为字符串常量,不可以改变,所以在程序运行时,会报告内存错误。并且,如果此时对p和p1输出的时候会发现p和p1里面保存的地址是完全相同的。

    示例2:栈区和堆区

    char* f1()

    {

    char* p = NULL;

    char a;

    p = return p;

    }

    char* f2()

    {

    char* p = NULL;

    p = (char*)new char[4];

    return p;

    }

    这两个函数都是将某个存储空间的地址返回,二者有何区别呢?f1()函数虽然返回的是一个存储空间,但是此空间为临时空间。也就是说,此空间只有短暂的生命周期,它的生命周期在函数f1()调用结束时,也就失去了它的生命价值,即:此空间被释放掉。所以,当调用f1()函数时,如果程序中有下面的语句:

    char* p;

    p = f1();

    *p = 'a';

    此时,编译并不会报告错误,但是在程序运行时,会发生异常错误。因为,你对不应该操作的内存(即,已经释放掉的存储空间)进行了操作。但是,相比之下,f2()函数不会有任何问题。因为,new这个命令是在堆中申请存储空间,一旦申请成功,除非你将其delete或者程序终结,这块内存将一直存在。也可以这样理解,堆内存是共享单元,能够被多个函数共同访问。如果你需要有多个数据返回却苦无办法,堆内存将是一个很好的选择。

    总之,对于堆区、栈区和静态存储区它们之间最大的不同在于,栈的生命周期很短暂。但是堆区和静态存储区的生命周期相当于与程序的生命同时存在(如果您不在程序运行中间将堆内存delete的话),我们将这种变量或数据成为全局变量或数据。但是,对于堆区的内存空间使用更加灵活,因为它允许你在不需要它的时候,随时将它释放掉,而静态存储区将一直存在于程序的整个生命周期中。

  • 相关阅读:
    Java知识系统回顾整理01基础05控制流程02 switch
    Java知识系统回顾整理01基础05控制流程01if
    Java知识系统回顾整理01基础04操作符07Scanner
    Java知识系统回顾整理01基础04操作符06三元运算符
    Java知识系统回顾整理01基础04操作符05赋值操作符
    Java知识系统回顾整理01基础04操作符04位操作符
    Java知识系统回顾整理01基础04操作符03逻辑运算符
    leetcode-----74. 搜索二维矩阵
    leetcode-----73. 矩阵置零
    leetcode-----72. 编辑距离
  • 原文地址:https://www.cnblogs.com/DannyShi/p/4584750.html
Copyright © 2011-2022 走看看