#include <stdio.h>
#include "string.h"
#include "malloc.h"

void Swap(int a,int b)
{
    int temp;
    temp=a;
    a=b;
    b=temp;
}

int Get_Int(int a)
{
    +a;
    return i;
}

char* Get_Memory0()
{
    );
    strcpy_s(p,"hello world");
    return p;
}

char* Get_Memory1()
{
    char* p="hello world";
    return p;
}

char* Get_Memory2()
{
    char p[]="hello world";
    return p;
}

void main()
{
    ,y=;
    Swap(x,y);
    int z=x-y;
    printf("z=%d\n",z);

    z=Get_Int(z);
    printf("z=%d\n",z);

    char* c0=Get_Memory0();
    printf("c0=%s\n",c0);

    const char* c1=Get_Memory1();
    printf("c1=%s\n",c1);

    char* const c2=Get_Memory2();
    printf("c2=%s\n",c2);
}

面试题,上面的程序执行结果是什么?

基础知识

内存存储:

在C/C++中,通常可以把内存理解成4个分区:栈、堆、全局/静态存储区和常量存储区。

(1)栈:通常是用那些在编译期间就能确定其存储大小的变量的存储区,用于在函数作用域内创建、在离开作用域后自动销毁的变量的存储区。通常是局部变量、函数参数等的存储区。它的存储空间是连续的,两个紧挨着的定义的局部变量,它们的存储空间是紧挨着的。栈的大小是有限的,通常Visual C++编译器默认栈的大小是1M,所以不要定义int a[1000000]这样的超大数组。

(2)堆:通常是用于那些在编译期间不能确定存储大小的变量存储区,它的存储空间是不连续的,一般同malloc(或new)函数来分配内存块,并且需要要free(或delete)释放内存。如果程序员没有释放掉,那么就会出现常说的内存泄漏问题。需要注意的是,两个紧挨定义的指针变量,所指向的malloc出来的内存并不一定是紧挨着的。另外需要注意的一点是,堆的大小几乎是不受限制的,理论上每个程序最大可达4GB。

(3)全局/静态存储区:和“栈”一样,通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量和静态变量。

(4)常量存储区:和“全局/静态存储区”一样,通常是用于那些在编译期间就能确定存储大小的常量存储区,并且在程序运行期间,存储区内的常量也是全局可见的。这是一块比较特殊的存储区,它们里面放的是常量,不允许被修改。

程序解答:

(1)Swap函数:我们知道,所有函数都会在运行时从程序“栈”上得到分配给它的一块存储区(这里的“栈”就是前两面讲解内存存储时提到的“栈”,第个运行程序都拥有自己的运行时“栈”)。这块“栈”上的函数存储区随函数的开始面开始,随着函数的结束而结束。函数结束后,这块存储区就会自动释放,以供程序的其他用途使用。系统在函数运行时会为函数的每一个参数都提供存储区,参数在存储区中的存储长度由自身的类型决定。参数传递,就是系统用函数实参初始化函数参数存储区的过程。

函数的参数有传值(传指针)和传址(传引用)两种。所谓参会传值,就是实参的值复制到函数运行时分配给函数的参数存储区中。参数在传值时,函数不会访问当前调用的实参,函数处理的是实参在本地的拷贝,这些拷贝被存储在函数的“栈”中,所以这些拷贝值的改变不会影响实参的值。

答案:

z=1

(2)问题:返回值i是一个局部变量,函数的返回参数怎么能是局部变量呢?局部变量在离开作用域后就自动被销毁了,还怎么能返回值给调用都呢?

解答:函数的返回值有传值和传址两种。int Get_Int(int a)属于返回值是传值的函数,这就意味着函数int Get_Int(int a)会在函数返回处产生一个临时对象,用于存放局部变量i的值的一份拷贝(变量i的右值的拷贝),临时对象是没有名称的,这份没有名称的对象的值(右值)会存储在调用者的“栈”中。所以当i作为局部变量离开作用域后,虽然被销毁了,但它的拷贝仍然存在,并在函数返回时作为“右值”赋给“左值z”。

答案:

z=2

(3)此处考查对“堆”的理解:函数返回的是指向“堆”内存的指针。程序中malloc()是用于分配“堆”内存的库函数,而对于“堆”内存,只要程序中没有调用free()库函数去释放掉该“堆”内存,那么在程序运行期间,malloc()库函数分配的“堆”内存将一直存在。

char* p=(char*)malloc(sizeof(char)*20)表示分配一块“堆”内存并使得变量p指向这块“堆”内存。

strcpy(p,"hello world")表示往p指向的“堆”内存中复制字符串hello world。

return p,变量p的左值是一个局部变量指针,存储于函数栈上,右值 是“堆”的地址(“堆”的值是hello world)。

函数char* Get_Memory0()属于返回值是传指针的函数,这就意味着函数char* Get_Memory0()会在函数返回处产生一个对返回变量p的“左值”的拷贝,也就是在“左值”的拷贝中存储了指向“堆”的地址。作为局部变量的 p,在离开函数作用域的时候虽然被销毁了,但函数返回值珠“左值”的拷贝是存在的,该拷贝存储了指向“堆“的地址,而而该“堆”的值是hello world。

答案:

c1=hello world

(4)这里考查对函数返回值和指针的理解:变量分为左值和右值,在char* p="hello world"中,左值是局部变量指针p,存储于函数栈上,右值是字符串常量hello world,存储于常量存储区。

char* Get_Memory1()属于返回值是传指针的函数,这就意味着函数char* Get_Memory1()会在函数返回处产生一个对返回对象的“左值”的拷贝,也就是在“左值”拷贝中存储了指向字符串常量hello world的地址。作为局部变量的p,在离开函数作用域的时候虽然被销毁了,但函数 返回的“左值”拷贝仍然存储了指向常量存储区的字符串常量hello world的地址。

答案:

c1=hello world

(5)和问题(4)有些相似,考查的也是对函数返回值和指针的理解:返回值是传指针,会在函数返回时产生“左值”拷贝。可以看到,p[]不是指针,是一个数组变量。编译器根据右值“hello world”的长度(12个字符)在编译期间在函数的栈上为数组分配大小为12个字符的内存存储区,其值是“hello world”。

通常,变量的意义在于,它给一块内存存储区提供名字,方便程序对这块内存进行读写。变量包含两个值:左值和右值。左值是内存存储区的名字,右值是存放存储区中的值。从程序中可以看到,函数返回的“左值”拷贝 指向的是局部变量数组p[12]的首地址。当局部数组p[12]作用域后会被自动销毁。这时,函数返回的“左值”拷贝指向的是一个被销毁的局部变量地址。

答案:

warning C4172: returning address of local variable or temporary

c2=未知

C/C++内存存储问题的更多相关文章

  1. Redis内存存储结构分析

    1 Redis 内存存储结构 本文是基于 Redis-v2.2.4 版本进行分析. 1.1 Redis 内存存储总体结构 Redis 是支持多key-value数据库(表)的,并用 RedisDb 来 ...

  2. C/C++内存存储

    #include <stdio.h> #include "string.h" #include "malloc.h" void Swap(int a ...

  3. memcached全面剖析–2. 理解memcached的内存存储

    Slab Allocation机制:整理内存以便重复使用 最近的memcached默认情况下采用了名为Slab Allocator的机制分配.管理内存. 在该机制出现以前,内存的分配是通过对所有记录简 ...

  4. 1.C#基础学习笔记3---C#字符串(转义符和内存存储无关)

    技术qq交流群:JavaDream:251572072  教程下载,在线交流:创梦IT社区:www.credream.com ------------------------------------- ...

  5. 【解惑】剖析float型的内存存储和精度丢失问题

    问题提出:12.0f-11.9f=0.10000038,"减不尽"为什么? 现在我们就详细剖析一下浮点型运算为什么会造成精度丢失? 1.小数的二进制表示问题 首先我们要搞清楚下面两 ...

  6. Go Web:数据存储(1)——内存存储

    数据可以存储在内存中.文件中.按二进制序列化存储的文件中.数据库中等. 1.内存存储 2.CSV文件存储 3.gob序列化存储 内存存储 将数据存储到内存中.此处所指的内存是指应用程序自身的内存空间( ...

  7. memcached全面剖析--2.理解memcached的内存存储

    下面是<memcached全面剖析>的第二部分. 发表日:2008/7/9 作者:前坂徹(Toru Maesaka) 原文链接:http://gihyo.jp/dev/feature/01 ...

  8. spark 源码分析之十六 -- Spark内存存储剖析

    上篇spark 源码分析之十五 -- Spark内存管理剖析 讲解了Spark的内存管理机制,主要是MemoryManager的内容.跟Spark的内存管理机制最密切相关的就是内存存储,本篇文章主要介 ...

  9. 32 位bitmap 内存存储 顺序 bgra 前3位 与23位一致。 都是 bgr 呵呵 与rgb 相反

    32 位bitmap     内存存储 顺序   bgra       前3位 与23位一致.   都是 bgr  呵呵 与rgb 相反

随机推荐

  1. debug openStack

    ERROR neutron.agent.l3.agent [-] An interface driver must be specified No valid host was found. Ther ...

  2. day57:00:26:34

    今天开始用博客记录倒计时,也只是为了看看今天做了什么.这也是我第一用博客园记录考研生活了 倒计时57天,我在想每天花时间在这记录生活会不会浪费复习的时间,其实不会的了,不去看微博,少刷新闻....仔细 ...

  3. ASIHttpRequest:创建队列、下载请求、断点续传、解压缩

    ps:本文转载自网络:http://ryan.easymorse.com/?p=12 感谢作者 工程完整代码下载地址:RequestTestDownload1 可完成: 下载指定链接的zip压缩文件 ...

  4. install-file -Dfile=J:\project01\workspace\service\lib\javapns-jdk16-163.jar -DgroupId=org.json -Dar

    今天在开发项目的时候发现了一个问题,所以通过博客来记录起来! 为了以后在问题的解决方面能得到借鉴! 问题的现象是这种: 这样会报错的.pom.xml文件他在编译.检查他的文件语法的时候是须要參考库中的 ...

  5. LoadRunner监控windows资源报错Monitor name :Windows Resources. Cannot connect to machine

    目标机:被监控的机器,windows server 2008 R2. 测试机:执行control的机器,windows7 操作:在测试机上执行Control,添加windows的监控 问题现象:Mon ...

  6. Android企业级程序完全退出的解决方案

    一.问题描述 在平常开发的过程中可以发现,很多开发者对于程序的退出都没有去认真的解决.一般要么是一个简单的finish(只是退出当前的activity),要么是其他的方法,比如: 1.第一种方法:首先 ...

  7. python进阶之路4.1---生成器与迭代器

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  8. SELECT--UNION,UNION ALL,MINUS, INTERSECT,EXISTS

    SELECT--UNION,UNION ALL,MINUS, INTERSECT返回两个查询结果的集合操作,两个查询结果集必须字段相同.UNION和UNION ALL并集操作,UNION并集后去掉重复 ...

  9. HTML与CSS入门——第十一章  在网页中使用图像

    知识点: 1.在网页上放置图像的方法 2.用文本描述图像的方法 3.指定图像高度和宽度的方法 4.对齐图像的方法 5.将图像转换为俩接的方法 6.使用背景图像的方法 7.使用图像映射的方法 11.1 ...

  10. <!DOCTYPE html>的问题

    <!DOCTYPE> 声明必须位于 HTML5 文档中的第一行,也就是位于 <html> 标签之前.该标签告知浏览器文档所使用的 HTML 规范. doctype 声明不属于 ...