`
BlogDown
  • 浏览: 214255 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

C++编程点滴6:指针参数和内存分配

 
阅读更多

当以指针作为参数,在函数中分配内存的时候,其操作要格外小心。

====================

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream.h>
void fun(int *p)
{
cout<<"fun 1:"<<endl;
cout<<p<<endl;
cout<<*p<<endl;

p=(int *)malloc(sizeof(int));
*p=2;

cout<<"fun 2:"<<endl;
cout<<p<<endl;
cout<<*p<<endl;
}

void main()
{

int *p;
p=(int *)malloc(sizeof(int));
*p=1;

cout<<"Main 1:"<<endl;
cout<<p<<endl;
cout<<*p<<endl;

fun(p);

cout<<"Main 2:"<<endl;
cout<<p<<endl;
cout<<*p<<endl;

}

运行结果:

Main 1:
0x00032FA0
1
fun 1:
0x00032FA0
1
fun 2:
0x00032FD8
2
Main 2:
0x00032FA0
1

表明:主函数中指针p的位置没有改变。函数fun中的形参p指针,刚开始是与主函数p相同的位置,但是一旦创建内存空间后,其位置就不同了。所以主函数中其值还是1不是2

如果把参数改为指针引用,则可以实现对实际参数的修改。而且形参p和实参p地址也是一样的,也就是说此时形参p和实参p表示同一个意思。对形参的修改就是对实参的修改,包括内存分配和内存赋值。

如下:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream.h>
void fun(int *&p)
{
cout<<"fun 1:"<<endl;
cout<<p<<endl;
cout<<*p<<endl;

p=(int *)malloc(sizeof(int));
*p=2;

cout<<"fun 2:"<<endl;
cout<<p<<endl;
cout<<*p<<endl;
}

void main()
{

int *p;
p=(int *)malloc(sizeof(int));
*p=1;

cout<<"Main 1:"<<endl;
cout<<p<<endl;
cout<<*p<<endl;

fun(p);

cout<<"Main 2:"<<endl;
cout<<p<<endl;
cout<<*p<<endl;

}

运行结果如下:

Main 1:
0x00032FA0
1
fun 1:
0x00032FA0
1
fun 2:
0x00032FD8
2
Main 2:
0x00032FD8
2

参考网上文献如下:

================

void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}请问运行Test函数会有什么样的结果?

答:内存错误,GetMemory并没有给str分配内存空间,p是str的复制,p和str指向同一块内存区域

上面的程序p复制了str成为NULL指针,然后p分配到了内存空间,而str仍然为NULL,所以出错

修改为引用传递就可以了。或者改用指针的指针。

void GetMemory(char *&p)
{
p = (char *)malloc(100);
}

char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
请问运行Test函数会有什么样的结果?答:
str指向一块已经释放掉的内存,结果错误。指针p指向的“hello world”内存由于在栈中分配,在函数结束后释放

一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于

数据结构中的栈。
2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据

结构中的堆是两回事,分配方式倒是类似于链表。
3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态

变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统

释放。
4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。
5、程序代码区
这是一个例子
//main.cpp
int a=0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b;栈
char s[]="abc"; //栈
char *p2; //栈
char *p3="123456"; //123456/0在常量区,p3在栈上。
static int c=0; //全局(静态)初始化区
p1 = (char*)malloc(10);
p2 = (char*)malloc(20); //分配得来得10和20字节的区域就在堆区。
strcpy(p1,"123456"); //123456/0放在常量区,编译器可能会将它与p3所向"123456"优化成一个地方。

-----------------------

在使用指针作为函数参数传递的时候出现了问题,根本不知道从何得解:源代码如下:
createNode(BinNode *tree,char *p)
{
tree = (BinNode *) malloc(sizeof(BinNode));
tree->data = *p;
}
该代码段的意图是通过一个函数创建一个二叉树的节点,然而在,调用该函数后,试图访问该节点结构体的成员时候,却发生了内存访问错误,到底问题出在哪儿呢?

一直不明白指针作为函数参数传值的机制,翻开林锐的《高质量C/C++编程指南》,找到了答案。

[如果函数的参数是一个指针,不要指望用该指针去申请动态内存]

原来问题出在C编译器原理上:编译器总是要为函数的每个参数制作临时副本,指针参数tree的副本是 _tree,编译器使 _tree = tree。如果函数体内的程序修改了_tree的内容,就导致参数tree的内容作相应的修改。这就是指针可以用作输出参数的原因。
即上面的函数代码经过编译后成为:
createNode(BinNode *tree,char *p)
{
BinNode *_tree;
_tree = tree;
_tree = (BinNode *) malloc(sizeof(BinNode));
_tree->data = *p;
}
如果没有
_tree = (BinNode *) malloc(sizeof(BinNode));
这个语句,在函数体内修改了_tree的内容,将会导致参数tree的内容作相应的修改,因为它们指向相同的内存地址。而
_tree = (BinNode *) malloc(sizeof(BinNode));
这个句,系统重新分配内存给_tree指针,_tree指针指向了系统分配的新地址,函数体内修改的只是_tree的内容,对原tree所指的地址的内容没有任何影响。因此,函数的参数是一个指针时,不要在函数体内部改变指针所指的地址,那样毫无作用,需要修改的只能是指针所指向的内容。即应当把指针当作常量。

如果非要使用函数指针来申请内存空间,那么需要使用指向指针的指针
createNode(BinNode **tree,char *p)
{
*tree = (BinNode *) malloc(sizeof(BinNode));
}
上面的是林锐的说法,目前来说不知道怎么去理解,不过可以有另外的方案,通过函数返回值传递动态内存:
BinNode *createNode()
{
BinNode *tree;
tree = (BinNode *) malloc(sizeof(BinNode));
return tree;
}
这个倒还说得过去,因为函数返回的是一个地址的值,该地址就是申请的内存块首地址。但是,这个容易和另外的一个忠告相混绕
[不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡]

这里区分一下静态内存,栈内存和动态分配的内存(堆内存)的区别:
(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

因此,试图返回一个栈上分配的内存将会引发未知错误
char *GetString(void)
{
char p[] = "hello world";
return p;// 编译器将提出警告
}
p是在栈上分配的内存,函数结束后将会自动释放,p指向的内存区域内容不是"hello world",而是未知的内容。
如果是返回静态存储的内存呢:
char *GetString(void)
{
char *p = "hello world";
return p;
}
这里“hello world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString,它返回的始终是同一个“只读”的内存块。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics