指针与引用

引用的一些规则如下:

(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。

(2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。

(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

(4)引用的创建和销毁并不会调用类的拷贝构造函数

(5)在语言层面,引用的用法和对象一样;在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮我们完成了转换.

总的来说:引用既具有指针的效率,又具有变量使用的方便性和直观性.
以下示例程序中,k被初始化为i的引用。语句k = j并不能将k修改成为j的引用,只是把k的值改变成为6。由于k是i的引用,所以i的值也变成了6。


  int i = 5;

  int j = 6;

  int &k = i;

  k = j;   // k和i的值都变成了6;

上面的程序看起来象在玩文字游戏,没有体现出引用的价值。引用的主要功能是传递函数的参数和返回值。C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。

以下是“值传递”的示例程序。由于Func1函数体内的x是外部变量n的一份拷贝,改变x的值不会影响n, 所以n的值仍然是0。


  void Func1(int x)

{

  x = x + 10;

}

…

int n = 0;

  Func1(n);

  cout << “n = ” << n << endl;   // n = 0

 

以下是“指针传递”的示例程序。由于Func2函数体内的x是指向外部变量n的指针,改变该指针的内容将导致n的值改变,所以n的值成为10。


  void Func2(int *x)

{

  (* x) = (* x) + 10;

}

…

int n = 0;

  Func2(&n);

  cout << “n = ” << n << endl;     // n = 10

 

以下是“引用传递”的示例程序。由于Func3函数体内的x是外部变量n的引用,x和n是同一个东西,改变x等于改变n,所以n的值成为10。


  void Func3(int &x)

{

  x = x + 10;

}

…

int n = 0;

  Func3(n);

  cout << “n = ” << n << endl;    // n = 10

 

对比上述三个示例程序,会发现“引用传递”的性质象“指针传递”,而书写方式象“值传递”。实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”这东西?

答案是“用适当的工具做恰如其分的工作”。

指针能够毫无约束地操作内存中的如何东西,尽管指针功能强大,但是非常危险。就象一把刀,它可以用来砍树、裁纸、修指甲、理发等等,谁敢这样用?

如果的确只需要借用一下某个对象的“别名”,那么就用“引用”,而不要用“指针”,以免发生意外。比如说,某人需要一份证明,本来在文件上盖上公章的印子就行了,如果把取公章的钥匙交给他,那么他就获得了不该有的权利。


void fun(int* b){  //用指针做形参
b = (int*)malloc(sizeof(int)*3);

for(int i=0; i<3; i++){
  b[i] = i;
}
}

void fun(int* &b){  //用引用做形参
b = (int*)malloc(sizeof(int)*3);

for(int i=0; i<3; i++){
  b[i] = i;
}
}

如果在main函数中定义了一个int型的空指针并分别作为实参传入,如下:


int main(){
int *a = NULL;

fun(a);

for(int i=0; i<3; i++){
  cout << a[i] << " ";
}
cout << "\n";

return 0;
}

结果用指针的函数会出现内存访问出错,用引用的函数则运行正常并正确输出1 2 3.

这是因为:

1.指针虽然是地址传递,但实际上也是在函数中又定义了一个新的指针让其与传入的指针指向同一地址。但两个指针本身作为变量在内存中的存放地址是不同的,就是说这是两个不同的变量,只是内容(即所指地址)相同。

2.在函数中对新定义的指针动态申请内存,但是当函数结束后,申请的内存的生命周期也就结束了,所以当回到主函数时,作为实参的指针地址和内容都没有变化。仍然是个空指针,对其进行访问自然出现了内存读错误了。

假如在main函数中这样写:


int *a = (int*)malloc(sizeof(int)*3);

就不会出现内存读错误了,但是输出结果还是错误的,道理也是一样的。
再看一个例子:


int *a = NULL;

char* b = (char*)a;

 

int *a = NULL;

char* &b = (char*)a;

这一次是在编译阶段的区别:

用指针可以通过编译,而用引用则不可以,提示类型转换出错。

通过这两个例子可以看出,指针比引用灵活,也更加危险。
指针与引用看上去完全不同(指针用操作符’*’和’->’,引用使用操作符’.’),但是它们似乎有相同的功能。指针与引用都是让你间接引用其他对象。你如何决定在什么时候使用指针,在什么时候使用引用呢?
首先,要认识到在任何情况下都不能用指向空值的引用。一个引用必须总是指向某些对象。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。

PS:引用在定义时不可加const,否则编译出错,在形参前面则可以加const以确保在函数中该变量不会被修改。
不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。

不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。


 void printDouble(const double& rd)
{
     cout << rd; // 不需要测试rd,它
} // 肯定指向一个double值
相反,指针则应该总是被测试,防止其为空:
void printDouble(const double *pd)
{
     if (pd)
 
     { // 检查是否为NULL
           cout << *pd;
     }
}

   指针与引用的另一个重要的不同是指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变。
int * pi = new int(10); int *& pa = pi; int *& pb = pi; 此时pa,pb为pi的别名。



#pragma once
#include <cstring>
#include <cstdio>
#include <cstdlib>

// -----------------------------------------------
void GetMemory1(char *p, int num)
{
    p = (char*)malloc(num);
}
void Test1(void)
{
    char *str = NULL;
    GetMemory1(str, 100);
    strcpy(str, "hello world");
    printf(str);
}

// -----------------------------------------------
void GetMemory2(char **p, int num)
{
    *p = (char*)malloc(num);
}
void Test2(void)
{
    char * str = NULL;
    GetMemory2(&str, 100);
    strcpy(str, "hello world");
    printf(str);
    free(str);
}

// -----------------------------------------------
char* GetMemory3(void)
{
    char p[] ="hello world";
    return p;
}
void Test3(void)
{
    char* str = NULL;
    str = GetMemory3();
    printf(str);
}

// -----------------------------------------------
char* GetMemory4(void)
{
    char *p = "hello world";
    return p;
}
void Test4()
{
    char* str = NULL;
    str = GetMemory4();
    printf(str);
}

// -----------------------------------------------
char* GetMemory5(void)
{
    char *p = (char*)malloc(100);
    strcpy(p,"hello world");
    return p;
}
void Test5()
{
    char* str = NULL;
    str = GetMemory5();
    printf(str);
    free(str);
}

// -----------------------------------------------
void  Test6( void )
{
    char * str = (char*)malloc(100);
    strcpy(str, "hello");
    free(str);
    if (str != NULL)
    {
        strcpy(str,  "world" );
        printf(str);
    }
}

// -----------------------------------------------
void TestPointerAndReference()
{
    // -----------------------------------------------
    // 请问运行Test1函数会有什么样的结果?
    //
    // 答:程序崩溃。同时有内存泄漏。
    //
    // 因为在GetMemory1函数的调用过程中,其实是对实参指针p做了拷贝,拷贝为局部变量,
    // 在函数内的操作是对局部变量的操作,局部变量与实参是两个不同的变量,相互不影响。
    //
    // 所以,当GetMemory1调用结束时,Test1函数中的 str一直都是 NULL。
    // strcpy(str, "hello world");将使程序崩溃。
    //
    //Test1();

    // -----------------------------------------------
    // 请问运行Test2函数会有什么样的结果?
    //
    // 答:(1)能够输出hello world; (2)但是调用结束要对内存释放,否则内存泄漏;
    //
    Test2();

    // -----------------------------------------------
    // 请问运行Test3函数会有什么样的结果?
    //
    // 答:可能是乱码。
    //
    // 因为GetMemory3返回的是指向“栈内存”的指针,
    // 该指针的地址不是 NULL,但其原现的内容已经被清除,新内容不可知。
    //
    Test3();

    // -----------------------------------------------
    // 请问运行Test4函数会有什么样的结果?
    //
    // 答:(1)能够输出hello world; (2) 此时的str指向了常量区,不需要程序员释放,程序结束自动释放。
    //
    Test4();

    // -----------------------------------------------
    // 请问运行Test5函数会有什么样的结果?
    //
    // 答:(1)能够输出hello world; (2)但是调用结束要对内存释放,否则内存泄漏;
    //
    Test5();

    // -----------------------------------------------
    // 请问运行Test6函数会有什么样的结果?
    //
    // 答:篡改动态内存区的内容,后果难以预料,非常危险。
    //
    // 因为free(str);之后,str成为野指针,
    // if(str != NULL)语句不起作用。
    //
    Test6();

} 

http://blog.chinaunix.net/space.php?uid=24328404&do=blog&id=70209

http://xinklabi.iteye.com/blog/653643

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>