//NO.1:
程序首先申请一个char类型的指针str,并把str指向NULL(即str里存的是NULL的地址,*str为NULL中的值为0),调用函数的过程
中做了如下动作:1申请一个char
类型的指针p,2把str的内容copy到了p里(这是参数传递过程中系统所做的),3为p指针申请了100个空间,4返回Test函数.最后程序把字符
串hello
world拷贝到str指向的内存空间里.到这里错误出现了!str的空间始终为NULL而并没有实际的空间.深刻理解函数调用的第2步,将不难发现问题
所在!

void GetMemory(char *p)

{

p = (char*)malloc(100);

}

void Test(void)

{

char *str = NULL;

GetMemory(str);

strcpy(str, "hello world");

printf(str);

}

//请问运行Test函数后会是什么样的结果?

//NO.2:
程序首先申请一个char类型的指针str,并把str指向NULL.调用函数的过程中做了如下动作:1申请一数组p[]并将其赋值为hello
world(数组的空间大小为12),2返回数组名p付给str指针(即返回了数组的首地址).那么这样就可以打印出字符串"hello
world"了么?当然是不能的!因为在函数调用的时候漏掉了最后一步.也就是在第2步return数组名后,函数调用还要进行一步操作,也就是释放内存
空间.当一个函数被调用结束后它会释放掉它里面所有的变量所占用的空间.所以数组空间被释放掉了,也就是说str所指向的内容将不确定是什么东西.

char *GetMemory(void)

{

char p[] = "hello world";

return p;

}

void Test(void)

{

char *str = NULL;

str = GetMemory();

printf(str);

}

//NO.3:问题同NO.1,正确答案为可以打印出hello.但内存泄漏了!

void GetMemory(char **p, int num)

{

*p = (char*)malloc(num);

}

void Test(void)

{

char *str = NULL;

GetMemory(&str, 100);

strcpy(str, "hello");

printf(str);

}

//NO.4:
申请空间,拷贝字符串,释放空间.前三步操作都没有任何问题.到if语句里的判断条件开始出错了,因为一个指针被释放之后其内容并不是NULL,而是一个
不确定的值.所以if语句永远都不能被执行.这也是著名的"野"指针问题.所以我们在编写程序释放一个指针之后一定要人为的将指针付成NULL.这样就会
避免出现"野"指针的出现.有人说"野"指针很可怕,会带来意想不到的错误.

void Test(void)

{

char *str = (char*)malloc(100);

strcpy(str, "hello");

free(str);

if (str != NULL)

{

strcpy(str, "world");

printf(str);

}

}

void GetMemory1(char *p)

{

p = (char *)malloc(100);

}

void Test1(void)

{

char *str = NULL;

GetMemory1(str);

strcpy(str, "hello world");

printf(str);

}

//str一直是空,程序崩溃

char *GetMemory2(void)

{

char p[] = "hello world";

return p;

}

void Test2(void)

{

char *str = NULL;

str = GetMemory2();

printf(str);

}

char *GetMemory3(void)

{

return "hello world";

}

void Test3(void)

{

char *str = NULL;

str = GetMemory3();

printf(str);

}

//Test3 中打印hello world,因为返回常量区,而且并没有被修改过。Test2中不一定能打印出hello world,因为指向的是栈。

void GetMemory4(char **p, int num)

{

*p = (char *)malloc(num);

}

void Test4(void)

{

char *str = NULL;

GetMemory3(&str, 100);

strcpy(str, "hello");

printf(str);

}

//内存没释放

void Test5(void)

{

char *str = (char *) malloc(100);

strcpy(str, "hello");

free(str);

if(str != NULL)

{

strcpy(str, "world");

printf(str);

}

}

//str为野指针,打印的结果不得而知

void Test6()

{

char *str=(char *)malloc(100);

strcpy(str, "hello");

str+=6;

free(str);

if(str!=NULL)

{

strcpy(str, "world");

printf(str);

}

}

//VC断言失败,运行错误

另转一则:

char *GetMemory3(int num)

{

char *p = (char *)malloc
(sizeof(char) * num);

return p; //返回指针
p

}
 
void Test3(void)

{

char *str = NULL;

str = GetMemory3(100);  //这里指针
str也和指针
p的指向一样的内存块。

strcpy(str, "hello");

cout<< str << endl;

free(str);  //这里最终只是释放了str指向的内存块,对于str指针
依然还是指向那块内存,看了一些资料说应该加一句str=0;那指针
p呢?p也依然在指向那块内存啊。

}

回复:

答者:wutaozhao() 信誉:100 级别:user2 日期:2007-6-29 21:42:21 id:41451636

指针
之间传递的就是地址,所以str和p所指向的内存块是同一个,释放其中任何一个即可

答者:what_a_big() 信誉:100 级别:user1 日期:2007-6-29 21:42:21 id:41451637

指针
p呢?
=============================================================================
after returning from GetMemory()
p (局部
变量)没了,那块内存还在。
现在str 指向那块内存。

看了一些资料说应该加一句str=0
=================================
也可以不写str=NULL,只要你不再引用str。

答者:believefym(feng) 信誉:100 级别:user5 日期:2007-6-29 21:50:38 id:41451675

同一块内存,释放一次即可,只不过所有指向那块内存的指针
都失效了,就像楼主代码里的p和str

释放之后str就变成了野指针
,加str=NULL,可以避免之后错误使用该指针
,方便调试

答者:lightnut() 信誉:100 级别:star1 日期:2007-6-29 22:05:48 id:41451736

1.
函数进入Test3():
char* str = NULL;

======================
变量   变量的地址         变量的值(内容)
str    0x0013fe8c         0

2.
str = GetMemory3(..)=====>
char *p = (char *)malloc
(sizeof(char) * num);
将分配的内存起始地址(0x003a60b0)赋给栈变量p,

===============================================
变量     变量的地址       变量的值(内容)
p        0x0013fda8       0x003a60b0

3. 从GetMemory3(..)返回, p的值(0x003a60b0)
拷贝给str, 栈变量p生命结束, 释放其所占栈内存
(地址0x0013fda8)

=================================================
变量       变量的地址     变量的值(内容)
str        0x0013fe8c      0x003a60b0

4. free(str):  释放str指向的内存(0x003a60b0开始的内存空间)
5. 推出Test3()后,  栈变量str生命结束, 释放其所占栈内存
(地址0x0013fe8c)

答者:freshui(五月的风 -最近老犯困) 信誉:100 级别:user1 日期:2007-6-29 22:12:07 id:41451763

呵呵 p没了
已经死了 
:)

注意看变量的作用域

答者:buhaohaoxuexi() 信誉:100 级别:user1 日期:2007-6-29 22:37:00 id:41451888

为什么会死呢,不是说动态分配是分配在堆上的吗?他不应该死啊。

答者:yydrewdrew(满堂花醉三千客,一剑霜寒十四州) 信誉:100 级别:user1 日期:2007-6-29 23:55:01 id:41452325

char *GetMemory3(int num)

{

char *p = (char *)malloc
(sizeof(char) * num);

return p; //返回指针
p

}
 
void Test3(void)

{

char *str = NULL;

str = GetMemory3(100);  //调用完后p被析构,p为局部
作用域存在于函数GetMemory3的栈中,当GetMemory3被调用完后,栈空间被释放,因而被析构。

strcpy(str, "hello");

cout<< str << endl;

free(str);  //这里最好加一句str = NULL。

}

答者:yydrewdrew(满堂花醉三千客,一剑霜寒十四州) 信誉:100 级别:user1 日期:2007-6-29 23:56:59 id:41452334

注意p和它指向的对象的生命期是两回事
p是一个局部
指针
,而它指向的对象是堆空间具有全局生命期

答者:xlbdan(流浪剑客) 信誉:100 级别:user5 日期:2007-6-30 0:15:43 id:41452409

return p; //返回指针
p

这里返回之后,p已经消失.
但是p指向的那块内存依然存在,并且被返回到了调用端,用str接收了.
所以只要free(str)就行了,也就释放了p申请的那块内存
然后把 str赋为NULL,是防止再错误的去使用它

getMemory的经典例子的更多相关文章

  1. python之路第五篇之递归(进阶篇:续:经典例子剖析)

    递归 在函数内部,可以调用其他函数; 如果一个函数在内部调用自身本身,这个函数就是递归函数. 例如,我们来计算阶乘: n! = 1 x 2 x 3 x ... x n, 用函数f1(n)表示,可以看出 ...

  2. jQuery监听事件经典例子

    关键字:jQuery监听事件经典例子  js代码:  ============================================================  $(function( ...

  3. linux Posix 信号量 三 (经典例子)

    本文将阐述一下信号量的作用及经典例子,当中包括“<越狱>寄信”,“家庭吃水果”,“五子棋”,“接力赛跑”,“读者写者”,“四方恋爱”等 首先,讲 semWait操作(P操作)和semSig ...

  4. sql优化经典例子

    场景 我用的数据库是mysql5.6,下面简单的介绍下场景 课程表 create table Course( c_id int PRIMARY KEY, name varchar(10) ) 数据10 ...

  5. 转:一个经典例子让你彻彻底底理解java回调机制

    一个经典例子让你彻彻底底理解java回调机制 转帖请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273 ...

  6. 一个经典例子让你彻彻底底理解java回调机制

    转帖请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273),请尊重他人的辛勤劳动成果,谢谢 所谓回调: ...

  7. 回调--一个经典例子让你彻彻底底理解java回调机制

    本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273),请尊重他人的辛勤劳动成果,谢谢 以前不理解什么叫回调 ...

  8. 【C语言学习笔记】空间换时间,查表法的经典例子!知识就是这么学到的~

    我们怎么衡量一个函数/代码块/算法的优劣呢?这需要从多个角度看待.本篇笔记我们先不考虑代码可读性.规范性.可移植性那些角度. 在我们嵌入式中,我们需要根据实际资源的情况来设计我们的代码.比如当我们能用 ...

  9. Conquer and Divide经典例子之汉诺塔问题

    递归是许多经典算法的backbone, 是一种常用的高效的编程策略.简单的几行代码就能把一团遭的问题迎刃而解.这篇博客主要通过解决汉诺塔问题来理解递归的精髓. 汉诺塔问题简介: 在印度,有这么一个古老 ...

随机推荐

  1. SQL CE 和 SQLite数据库对比测试

    于项目需要,在客户端需要做数据存储功能,考虑到部署方便同时满足功能需要的情况下选择了SQLCE 和SQLite两种数据库进行客户端数据存储.当然还有很多其他的方式做本地数据存储,比如本地文件存储.微软 ...

  2. 【原创】大数据基础之Impala(3)部分调优

    1)将coordinator和executor角色分离 By default, each host in the cluster that runs the impalad daemon can ac ...

  3. python基础--absl.flags

    之前在tensorflow的mnist例程中看到了使用 absl.flags的方法来载入和解析参数的,出于学习的目的,就自己试验了一下, 代码如下: # *_*coding:utf-8 *_* # a ...

  4. GitLab CI/CD 进行持续集成

    简介 从 GitLab 8.0 开始,GitLab CI 就已经集成在 GitLab 中,我们只要在项目中添加一个 .gitlab-ci.yml 文件,然后添加一个 Runner,即可进行持续集成. ...

  5. JQuery ajax 前后端传值介绍

    https://jingyan.baidu.com/album/ca41422f0bf08e1eae99ed04.html?picindex=5 现在我们话不多说,开始仔细讲解一下我们ajax内部传递 ...

  6. Java_异常以及处理

    目录 JAVA异常 异常的处理机制 自定义异常 写了一天的bug,来try...catch...finally了解一下.异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. ...

  7. 宏定义define和const的区别

    define和const都可以用来定义常量,define的格式为:#define 标识符 字符串,const在定义常量前面,const类型定以后不能被修改,区别主要有如下几点: 1.编译器处理方式不同 ...

  8. Java常见的10个异常

    1.NullPointerException: 空指针异常,当操作一个 null 对象的方法或属性时会抛出这个异常.是一个很头疼的异常,因为它是运行时异常,不需要手动捕获,但运行时碰到这个异常会中断程 ...

  9. SpringBoot 整合 Redis缓存

    在我们的日常项目开发过程中缓存是无处不在的,因为它可以极大的提高系统的访问速度,关于缓存的框架也种类繁多,今天主要介绍的是使用现在非常流行的NoSQL数据库(Redis)来实现我们的缓存需求. Spr ...

  10. Linux磁盘管理及LVM讲解

    硬盘接口 硬盘接口分为IDE.SATA.SCSI和SAS四种, IDE接口硬盘多用于家用产品中,也部分应用于服务器.不支持热添加,比较老. SCSI接口的硬盘则主要应用于服务器市场.linux. 而S ...