zoukankan      html  css  js  c++  java
  • C++中内存分配、函数调用和返回值问题

    转载博客:http://blog.csdn.net/q_l_s/article/details/52176159(源地址找不到,就贴了这位大神的博客地址,他也是转载的,不过要是学习的话,他的博客很不错)

    在谈述函数调用和返回值问题之前,先来看看C++中内存分配的问题。

    C++编译器将计算机内存分为代码区和数据区,很显然,代码区就是存放程序代码,而数据区则是存放程序编译和执行过程出现的变量和常量。数据区又分为静态数据区、动态数据区,动态数据区包括堆区和栈区。

    以下是各个区的作用:

    (1)代码区:存放程序代码;

    (2)数据区

       a.静态数据区: 在编译器进行编译的时候就为该变量分配的内存,存放在这个区的数据在程序全部执行结束后系统自动释放,生命周期贯穿于整个程序执行过程。

       b.动态数据区:包括堆区和栈区

        堆区:这部分存储空间完全由程序员自己负责管理,它的分配和释放都由程序员自己负责。这个区是唯一一个可以由程序员自己决定变量生存期的区间。可以用malloc,new申请对内存,并通过free和delete释放空间。如果程序员自己在堆区申请了空间,又忘记将这片内存释放掉,就会造成内存泄露的问题,导致后面一直无法访问这片存储区域。

        栈区:存放函数的形式参数和局部变量,由编译器分配和自动释放,函数执行完后,局部变量和形参占用的空间会自动被释放。效率比较高,但是分配的容量很有限。

     

    注意:1)全局变量以及静态变量存放在静态数据区;

        2)注意常量的存放区域,通常情况下,常量存放在程序区(程序区是只读的,因此任何修改常量的行为都是非法的),而不是数据区。有的系统,也将部分常量分配到静态数据区,比如字符串常量(有的系统也将其分配在程序区)。但是要记住一点,常量所在的内存空间都是受系统保护的,不能修改。对常量空间的修改将造成访问内存出错,一般系统都会提示。常量的生命周期一直到程序执行结束为止。

       在弄懂内存分配的问题过后,来看看函数调用的过程:

    执行某个函数时,如果有参数,则在栈上为形式参数分配空间(如果是引用类型的参数则类外),继续进入到函数体内部,如果遇到变量,则按情况为变量在不同的存储区域分配空间(如果是static类型的变量,则是在进行编译的过程中已经就分配了空间),函数内的语句执行完后,如果函数没有返回值,则直接返回调用该函数的地方(即执行远点),如果存在返回值,则先将返回值进行拷贝传回,再返回执行远点,函数全部执行完毕后,进行退栈操作,将刚才函数内部在栈上申请的内存空间释放掉。

     

    下面通过几个例子来谈谈内存分配和函数返回值的问题:

    内存分配的问题:

    int a=1;           a在栈区

    char s[]="123";    s在栈区,“123”在栈区,其值可以被修改

    char *s="123";     s在栈区,“123”在常量区,其值不能被修改

    int *p=new int;    p在栈区,申请的空间在堆区(p指向的区域)

    int *p=(int *)malloc(sizeof(int)); p在栈区,p指向的空间在堆区

    static int b=0;    b在静态区

     

    1.test1 

    复制代码
    #include<iostream>
    using namespace std;

    void test(int *p)
    {
    int b=2;
    p=&b;
    cout<<p<<endl;
    }

    int main(void)
    {
    int a=10;
    int *p=&a;
    cout<<p<<endl;
    test(p);
    cout<<p<<endl;
    return 0;
    }
    复制代码


    第一行输出和第三行输出的结果相同,而第一行、第三行与第二行输出的结果不同。从这里可以看出,当指针作为参数进行传递时传递的也只是一个值,只不过该值只一个地址,因此对于形参的改变并不影响实参。

    2.test2

    复制代码
    #include<iostream>
    using namespace std;

    char* test(void)
    {
    char str[]="hello world!";
    return str;
    }

    int main(void)
    {
    char *p;
    p=test();
    cout<<p<<endl;
    return 0;
    }
    复制代码

     

    输出结果可能是hello world!,也可能是乱麻。

    出现这种情况的原因在于:在test函数内部声明的str数组以及它的值"hello world”是在栈上保存的,当用return将str的值返回时,将str的值拷贝一份传回,当test函数执行结束后,会自动释放栈上的空间,即存放hello world的单元可能被重新写入数据,因此虽然main函数中的指针p是指向存放hello world的单元,但是无法保证test函数执行完后该存储单元里面存放的还是hello world,所以打印出的结果有时候是hello world,有时候是乱麻。

    3.test3  

    复制代码
    #include<iostream>
    using namespace std;

    int test(void)
    {
    int a=1;
    return a;
    }

    int main(void)
    {
    int b;
    b=test();
    cout<<b<<endl;
    return 0;
    }
    复制代码

    输出结果为 1

    有人会问为什么这里传回来的值可以正确打印出来,不是栈会被刷新内容么?是的,确实,在test函数执行完后,存放a值的单元是可能会被重写,但是在函数执行return时,会创建一个int型的零时变量,将a的值复制拷贝给该零时变量,因此返回后能够得到正确的值,即使存放a值的单元被重写数据,但是不会受到影响。

    4.test4

    复制代码
    #include<iostream>
    using namespace std;

    char* test(void)
    {
    char *p="hello world!";
    return p;
    }

    int main(void)
    {
    char *str;
    str=test();
    cout<<str<<endl;
    return 0;
    }
    复制代码

    执行结果是 hello world!

    同样返回的是指针,为什么这里会正确地打印出hello world1?这是因为char *p="hello world!",指针p是存放在栈上的,但是"hello world!”是一个常量字符串,因此存放在常量区,而常量区的变量的生存期与整个程序执行的生命期是一样的,因此在test函数执行完后,str指向存放“hello world!”的单元,并且该单元里的内容在程序没有执行完是不会被修改的,因此可以正确输出结果。

    5.test5

    复制代码
    #include<iostream>
    using namespace std;

    char* test(void)
    {
    char *p=(char *)malloc(sizeof(char)*100);
    strcpy(p,"hello world");
    return p;
    }

    int main(void)
    {
    char *str;
    str=test();
    cout<<str<<endl;
    return 0;
    }
    复制代码

    运行结果 hello world

    这种情况下同样可以输出正确的结果,是因为是用malloc在堆上申请的空间,这部分空间是由程序员自己管理的,如果程序员没有手动释放堆区的空间,那么存储单元里的内容是不会被重写的,因此可以正确输出结果。

    6.test6

    复制代码
    #include<iostream>
    using namespace std;

    void test(void)
    {
    char *p=(char *)malloc(sizeof(char)*100);
    strcpy(p,"hello world");
    free(p);
    if(p==NULL)
    {
    cout<<"NULL"<<endl;
    }
    }

    int main(void)
    {
    test();
    return 0;
    }
    复制代码

    没有输出

    在这里注意了,free()释放的是指针指向的内存!注意!释放的是内存,不是指针!这点非常非常重 要!指针是一个变量,只有程序结束时才被销毁。释放了内存空间后,原来指向这块空间的指针还是存在!只不过现在指针指向的内容的垃圾,是未定义的,所以说是垃圾。因此,释放内存后应把把指针指向NULL,防止指针在后面不小心又被使用,造成无法估计的后果。

  • 相关阅读:
    CSS浮动(float、clear)通俗讲解
    JAVA 类的加载
    数据库操作 delete和truncate的区别
    正则表达式 匹配相同数字
    Oracle EBS OM 取消订单
    Oracle EBS OM 取消订单行
    Oracle EBS OM 已存在的OM订单增加物料
    Oracle EBS OM 创建订单
    Oracle EBS INV 创建物料搬运单头
    Oracle EBS INV 创建物料搬运单
  • 原文地址:https://www.cnblogs.com/HPAHPA/p/8301625.html
Copyright © 2011-2022 走看看