看 高质量C++编程指南 碰到的指针的问题

thisisll 2005-11-24 03:31:29
7.4指针参数是如何传递内存的?
如果函数的参数是一个指针,不要指望用该指针去申请动态内存。示例7-4-1中,Test函数的语句GetMemory(str, 200)并没有使str获得期望的内存,str依旧是NULL,为什么?

void GetMemory(char *p, int num)
{ p = (char *)malloc(sizeof(char) * num);}

void Test(void){ char *str = NULL; GetMemory(str, 100); // str 仍然为 NULL strcpy(str, "hello"); // 运行错误}
示例7-4-1 试图用指针参数申请动态内存

毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,因为没有用free释放内存。

-------------------------
他解释说有个副本,为什么要造个副本?
下下一个例子7-4-3
他为什么一return P的值就成副本了
还有为什么指针的指针就没有副本的问题?
总之是我没看明白,希望大家给解释下
-------------------------

如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,见示例7-4-2。

void GetMemory2(char **p, int num){ *p = (char *)malloc(sizeof(char) * num);}
void Test2(void){ char *str = NULL; GetMemory2(&str, 100); // 注意参数是 &str,而不是str strcpy(str, "hello"); cout<< str << endl; free(str); }
示例7-4-2用指向指针的指针申请动态内存

由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态内存。这种方法更加简单,见示例7-4-3。
char *GetMemory3(int num)
{ char *p = (char *)malloc(sizeof(char) * num);
return p;}
void Test3(void){ char *str = NULL; str = GetMemory3(100); strcpy(str, "hello"); cout<< str << endl; free(str); }
...全文
216 17 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
luvybird 2005-11-30
  • 打赏
  • 举报
回复
up
寻开心 2005-11-24
  • 打赏
  • 举报
回复
"进入函数体的是指针指向的地址(假设是0x0001),而不是指针那4个字节自身的地址(假设是0xA000) 所以他会在0x0001那里写上malloc申请出来的地址
"
基本正确

1 变量的数值 和 变量的地址 是两个概念, 这点可以理解吧
2 指针变量的数值 和 指针变量的地址 当然也就不同了
3 指针的数值是0x0001,也就是指针指向的地址
指针的地址是另外的别的(就是你所谓的4字节的地址0xa000)
4 传递到函数体里面的是指针的数值,也就是 0x0001; 而不是指针的地址0xa000
5 因此在函数体里面只知道0x0001这个地方,可以对其修改
不知道0xa000,所以指针变量自己不会变化
thisisll 2005-11-24
  • 打赏
  • 举报
回复
我声明一个指针
一出来不是NULL
是个垃圾值
我只是假设一下
dragonzxh 2005-11-24
  • 打赏
  • 举报
回复
就是你传指针的指针就是传指针的地址,传进去的指针P的地址和副本指针_P的地址不一样,但是P和副本_P本身的指向的地址是一样的。。。。
bing_huo 2005-11-24
  • 打赏
  • 举报
回复
假设:
外部指针p值的地址是0xA000 因为还没有分配内存 所以指向无效地址空间 假设为0x0001

通过传值方式 传递到函数内部 系统构造一个临时对象 tp (系统构造的是无名字的临时对象,为了方便叙述 假设名字为tp)

tp拥有和p同样的指向 0x0001 和不同的值地址空间假设为0xA0002

内部分配内存后 tp 指向已分配的内存 假设为 0x00FF 然后函数返回 注意 这中间p 还是p 没有任何改变。。。所以自然申请不到内存

函数返回时tp被销毁 tp指向的内存被泄露(因为除了tp外 再也没有其他方法访问和释放0x00FF起始的这块内存)
moany 2005-11-24
  • 打赏
  • 举报
回复
你对指针还不怎么理解,指针在初始化之前,是指向NULL的,没有假设0x0001之类的
lzp229 2005-11-24
  • 打赏
  • 举报
回复
用传进去指针的指针来改变指针,跟传指针进去来改变指针所指内容一个道理。
thisisll 2005-11-24
  • 打赏
  • 举报
回复
void GetMemory2(char **p, int num)
{*p = (char *)malloc(sizeof(char) * num);}

void Test2(void){
char *str = NULL;
GetMemory2(&str, 100);<<---所以这里用的是引用进入函数体的就是这个指针的本身
cout<< str << endl;free(str);
}
thisisll 2005-11-24
  • 打赏
  • 举报
回复
看了这么多后
我的理解是这样的:
进入函数体的是指针指向的地址(假设是0x0001),而不是指针那4个字节自身的地址(假设是0xA000)
所以他会在0x0001那里写上malloc申请出来的地址

我想应该是这样的吧
bing_huo 2005-11-24
  • 打赏
  • 举报
回复
传值的方式会构造临时对象 也就是所谓的“副本” 操作都是针对这个临时对象的

所以才会有给函数传递参数 尤其是“大对象” 要使用const引用来避免 构造临时对象的一系列开销
moany 2005-11-24
  • 打赏
  • 举报
回复
char *GetMemory3(int num)
{
char *p = (char *)malloc(sizeof(char) * num);//分配一段内存
return p; //把分配到的内存的地址传出来
}

str = GetMemory3(100) //str 得到一个地址,这个地址指向了一段大小为sizeof(char) * num的内存
bing_huo 2005-11-24
  • 打赏
  • 举报
回复
函数用传值的方式传递参数 在函数内对参数的修改不会影响外部的参数本身
用指针和引用传递参数 在函数内部对参数的修改直接影响函数外的参数
thisisll 2005-11-24
  • 打赏
  • 举报
回复
那他说的副本就是用来放值的?
似乎明白了些
可又自己说不清楚
lzp229 2005-11-24
  • 打赏
  • 举报
回复
是不是指针都一样的。
后面那个例子return P回来,用str 接受,这样str 就指向那块动态申请的区域了。
如果不return ,这样申请内存跟主函数里的str没有关系。
寻开心 2005-11-24
  • 打赏
  • 举报
回复
整数变量也好,指针变量也好,是一样的

作为指针的时候,虽然指针本身的数值不会改变,但是指针指向的地址的内容是可以改变的
因为指针作为数值参数传递进入函数体后,函数找不到指针变量的位置,但是可以找到指针指向的地址(这个地址作为数值已经传递进来了),所以可以对地址内的数据进行修改;
这个修改本身实际上和函数体外的指针变量也没有关系;
dragonzxh 2005-11-24
  • 打赏
  • 举报
回复
为什么指向指针的指针就可以了.因为将双重指针**p的内容传递到了函数中,就是把&p传递给函数.函数内部改变指针的内容,这样p就申请了内存 .
寻开心 2005-11-24
  • 打赏
  • 举报
回复
就是c/c++当中的行参还是值参的问题, 和是否是指针没有什么关系


fun(int i) {
i = 100; // 这里对i无论如何修改,也只是在函数体内有效
}

int k = 10;
fun(k);
函数运行返回后, k依然是10
因为在调用fun的时候,并不是把k这个变量传递进去了,而是把k变量对应的数值传递进去了
所以在fun内部对那个数值如何修改已经和k没有关系了

上面说的指针的例子也是一样,只是类型不同而已
目 录 前 言6 第1 章 文件结构 1.1 版权和版本的声明. 1.2 头文件的结构. 1.3 定义文件的结构. 1.4 头文件的作用. 1.5 目录结构. 第2 章 程序的版式 2.1 空行. 2.2 代码行. 2.3 代码行内的空格. 2.4 对齐. 2.5 长行拆分. 2.6 修饰符的位置. 2.7 注释. 2.8 类的版式. 第3 章 命名规则 3.1 共性规则. 3.2 简单的WINDOWS 应用程序命名规则. 3.3 简单的UNIX 应用程序命名规则 第4 章 表达式和基本语句 4.1 运算符的优先级. 4.2 复合表达式. 4.3 IF 语句 4.4 循环语句的效率. 4.5 FOR 语句的循环控制变量. 4.6 SWITCH 语句. 4.7 GOTO 语句. 第5 章 常量 5.1 为什么需要常量. 5.2 CONST 与 #DEFINE 的比较. 5.3 常量定义规则. 5.4 类中的常量. 第6 章 函数设计 高质量C++/C 编程指南,v 1.0 2001 Page 4 of 101 6.1 参数的规则. 6.2 返回值的规则. 6.3 函数内部实现的规则. 6.4 其它建议. 6.5 使用断言. 6.6 引用与指针的比较. 第7 章 内存管理 7.1 内存分配方式 7.2 常见的内存错误及其对策 7.3 指针与数组的对比 7.4 指针参数是如何传递内存的? 7.5 FREE 和DELETE 把指针怎么啦? 7.6 动态内存会被自动释放吗?. 7.7 杜绝“野指针”. 7.8 有了MALLOC/FREE 为什么还要NEW/DELETE ?. 7.9 内存耗尽怎么办?. 7.10 MALLOC/FREE 的使用要点 7.11 NEW/DELETE 的使用要点. 7.12 一些心得体会 第8 章 C++函数的高级特性 8.1 函数重载的概念. 8.2 成员函数的重载、覆盖与隐藏. 8.3 参数的缺省值. 8.4 运算符重载. 8.5 函数内联. 8.6 一些心得体会. 第9 章 类的构造函数、析构函数与赋值函数 9.1 构造函数与析构函数的起源. 9.2 构造函数的初始化表. 9.3 构造和析构的次序. 9.4 示例:类STRING 的构造函数与析构函数 9.5 不要轻视拷贝构造函数与赋值函数. 9.6 示例:类STRING 的拷贝构造函数与赋值函数 9.7 偷懒的办法处理拷贝构造函数与赋值函数. 9.8 如何在派生类中实现类的基本函数. 9.9 一些心得体会. 第10 章 类的继承与组合. 高质量C++/C 编程指南,v 1.0 2001 Page 5 of 101 10.1 继承 10.2 组合 第11 章 其它编程经验. 11.1 使用CONST 提高函数的健壮性 11.2 提高程序的效率 11.3 一些有益的建议 参考文献 附录A :C++/C 代码审查表. 附录B :C++/C 试题. 附录C :C++/C 试题的答案与评分标准.

65,198

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧