指针作为形参进行传递注意事项

来源:互联网 发布:网络 英文单词 编辑:程序博客网 时间:2024/06/13 23:44

一个例子

参考:http://blog.csdn.net/sszgg2006/article/details/9037675

#include<iostream>using namespace std;int m_value = 1;void func(int *p){    p = &m_value;}int main(int argc, char *argv[]){    int n = 2;    int *pn = &n;    cout << *pn << endl;    func(pn);    cout << *pn << endl;    return 0;}

运行结果:
2
2

修改func函数如下

void func(int *p){    /*p = &m_value;*/    *p = 3;}

运行结果:
2
3

修改func函数如下:
void func(int *p)
{
/p = &m_value;/
//*p = 3;
int a = 3;
*p =a;
}
依然是2,3

修改func函数如下:

void func(int *p){    /*p = &m_value;*/    //*p = 3;    int a = 3;    p =&a;}

结果是2,2

解释:
编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p = p
如果函数体内的程序修改了_p所指向的内容,就导致参数p的内容作相应的修改。把_p所指的内存地址改变了,那么 _p指向的内存块,和p指向的内存块就不一样了。所以对p毫无影响。只要程序里不改变 _p的指向, _p和p就是一样的,操作 _p内容,就是操作p的内容。

下面常用的交换程序:

#include <stdio.h>int *swap(int *px, int *py){    int temp;    temp = *px;    *px = *py;    *py = temp;    return px;}int main(void){    int i = 10, j = 20;   int *p = swap(&i, &j);    printf("now i=%d j=%d *p=%d\n", i, j, *p);    return 0;}

结果:

now i=20 j=10 *p=20

进一步思考

函数里面分配内存

void myMalloc(char *s) //我想在函数中分配内存,再返回  {    s = (char *)malloc(100);}void main(){    char *p = NULL;    myMalloc(p); //这里的p实际还是NULL,p的值没有改变,为什么?      if (p)    {        cout << "p不为空" << endl;        free(p);    }    else        cout << "p为空" << endl;}

结果

p为空

看另一个例子:

void myMalloc(char **s) //我想在函数中分配内存,再返回  {    *s = (char *)malloc(100);}void main(){    char *p = NULL;    myMalloc(&p); //传递的是p的地址,这里的p可以得到正确的值了      if (p)    {        cout << "p不为空" << endl;        memcpy(p, "Hello",6);//如果拷贝5个的话,最后一个'\0'没有拷贝过去,输出p会出现Hello,烫烫烫...        strcpy(p, "hello");        cout << p << endl;        free(p);    }    else        cout << "p为空" << endl;}

结果:

p不为空hello

解释:
1.被分配内存的是形参s,p没有分配内存
2.被分配内存的是形参s指向的指针p,所以分配了内存

第三个小例子:

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");      // 运行错误  }  

道理一样。

所以要想借助函数进行分配内存,需要传递的是“指针的指针”,就是第一个例子。

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

另外还可以:用函数返回值来传递动态内存

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);       } 

输出结果:hello
注意这里函数返回值传递的动态内存,必须是在“堆上”申请的内存!!!

不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡

char *GetString(void)  {       char p[] = "hello world";       return p;      // 编译器将提出警告  }  void Test4(void)  {  char *str = NULL;  str = GetString();      // str 的内容是垃圾  cout<< str << endl;  }  

但是下面的又会让人疑惑:

char *GetString2(void)  {       char *p = "hello world";       return p;  }  void Test5(void)  {       char *str = NULL;       str = GetString2();       cout<< str << endl;  }  

这个能运行,输出:hello world
函数Test5运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。

    char *pp = "wocao!";    pp[0] = '1';//这样赋值是错误的。

所以上述测试时:
多加一句是会报错的

void main(void){    char *str = NULL;    str = GetString2();    cout << str << endl;    str[0] = '1';//报错} 

<高质量C C++编程指南>这本书上说: 指针p 指向常量字符串(位于常量存储区),常量字符串的内容是不可以被修改的,企图修改常量字符串的内容而导致运行错误。所以这个问题出现的原因是char*p=”abcdefghi”,赋值的是字符串常量,存储在常量存储区,而常量存储区的内容是无法修改的。
如果使用数组来代替的话,数据就存储在堆栈空间,堆栈空间的内容是可以修改的,就不会出现运行时错误。

另外再加一个小总结:

程序1void myMalloc(char *s) //我想在函数中分配内存,再返回{  s=(char *) malloc(100); // s是值参, 函数返回后就回复传递前的数值,无法带回分配的结果}这个和调用 void func (int i) {i=1;}; 一样,退出函数体,i指复原的程序2void myMalloc(char **s){  *s=(char *) malloc(100); // 这个是可以的}等价于void int func(int * pI) {*pI=1;} pI指针不变,指针指向的数据内容是变化的值参本身不变,但是值参指向的内存的内容发生了变化。程序3void fun(int *p){  int b=100;  p=&b;       // 等同于第一个问题, b的地址并没有被返回}程序4void fun(int *p){  *p=100; // okay}结论:1.函数的返回值是指针类型的,检查是静态内存指针还是堆内存指针还是栈内存指针,栈内存指针是绝对要不得滴!2.函数需要使用指针参数进行传入传出的,在函数中只能对指针的指向的值(*p)进行修改,而不能修改指针指向,也就是指针地址!(函数中不得修改指针参数的地址,否则请使用指针的指针!)

推荐好的总结:
http://blog.chinaunix.net/uid-20788636-id-1841283.html
其中总结非常棒:

一般在函数中定义一个对象有两种方法:     1、在栈上建立局部变量。注意,在栈上时!栈用于函数是为了返回时找得到调用点(在调用时压入栈的),那么,返回时要POP才能得到。函数体中建立的任何东西都消失了(返回值除外),你返回的指针指向的内容现在不知被用作什么用途了,如果你还要修改的话,那么后果不能确定。     2、在堆中分配。返回时不会摧毁,因为堆是全局存在的。但函数的调用者要记得delete回来的指针。

指针的引用

void func(int *&p){    p = &m_value;    // 也可以根据你的需求分配内存    /*p = new int;    *p = 5;*/}int main(int argc, char *argv[]){    int n = 2;    int *pn = &n;    cout << *pn << endl;    func(pn);    cout << *pn << endl;    return 0;}

结果是:2,1
此时传递过去就是指针p,因为引用!

看一下func(int *&p)方法
p: 是指针的引用,main()方法里的 *pn
*p:是main()方法里的pn指向的内容。

0 0