当前位置:编程学习 > C/C++ >>

C++ 中“空引用”与“空指针”的区别

网络上有很多讨论C++ 的“引用”与“指针“的区别的文章,谈到区别,其中有一条:“引用不能为空(NULL),引用必须与合法的存储单元关联,指针则可以是NULL)”,但在实际应用中,有时候为了保持一致性,我们会抛开这个规则,人为创造出一个“空引用”。

很多情况下,“空引用”确实可以工作,以致于“引用不能为空”的忠告,被嘲笑为易做图,仅仅是标准制定者的耸人听闻。一个“空引用”的例子是:

[cpp]
int * a = NULL; 
int & b = *a; 

int * a = NULL;
int & b = *a;于是当访问b的时候,程序异常出现了:

[cpp]
void f(int & p) 

    p = 0; 

f(b); 

void f(int & p)
{
    p = 0;
}
f(b);当然,可以增加点判断,修正这个问题:

[cpp]
void f(int & p) 

    if (&p) p = 0; 

void f(int & p)
{
    if (&p) p = 0;
}
怎么样,是不是有点别扭?但是如果换成成指针,你要输入的字符数是一模一样的:

[cpp]
void f(int * p) 

    if (p) *p = 0; 
}  

void f(int * p)
{
    if (p) *p = 0;
}  于是,到底是使用“引用”还是“指针”,好像就是智者见智仁者见仁的事情了。

 

然而,然而。。。。。。

 

这真的一样吗?

 

我们来看看复杂一点的例子:

[cpp]
// test.cpp  
 
#include <iostream>  
 
class A 

    int a; 
}; 
 
class B 

    int b; 
}; 
 
class C 
    : public A, public B 

    int c; 
}; 
 
void fb(B & b) 

    std::cout << &b << std::endl; 

 
void fb(B * b) 

    std::cout << b << std::endl; 

 
int main(int argc, char* argv[]) 

    C * c = NULL; 
 
    fb(c); 
 
    fb(*c); 
 
    return 0; 

// test.cpp

#include <iostream>

class A
{
    int a;
};

class B
{
    int b;
};

class C
    : public A, public B
{
    int c;
};

void fb(B & b)
{
    std::cout << &b << std::endl;
}

void fb(B * b)
{
    std::cout << b << std::endl;
}

int main(int argc, char* argv[])
{
    C * c = NULL;

    fb(c);

    fb(*c);

    return 0;
}
编译运行一下看看:

[plain]
$ ./test 

0x4 

$ ./test
0
0x4咦,怎么&b不是0,也就是不是“空引用”了,这时候,即使加上判断,if (&b),也无济于事了。

大家也许注意到了,上面是linux环境运行,那么windows环境呢:

[plain]
>test.exe 
00000000 
00000000 

>test.exe
00000000
00000000这时候,“空引用”保持了他的“空”属性,仅在windows平台做C++的开发者,可以松口气了。

这是怎么回事呢,是你的眼睛欺骗了你?也许是,但是CPU不会欺骗我们,从汇编代码可以看出本质。下面是linux平台编译的代码:

[plain]
Dump of assembler code for function main: 
   0x0804870a <+0>:     push   %ebp 
   0x0804870b <+1>:     mov    %esp,%ebp 
   0x0804870d <+3>:     and    $0xfffffff0,%esp 
   0x08048710 <+6>:     sub    $0x20,%esp 
   0x08048713 <+9>:     movl   $0x0,0x1c(%esp) 
   0x0804871b <+17>:    cmpl   $0x0,0x1c(%esp) 
   0x08048720 <+22>:    je     0x804872b <main+33> 
   0x08048722 <+24>:    mov    0x1c(%esp),%eax 
   0x08048726 <+28>:    add    $0x4,%eax 
   0x08048729 <+31>:    jmp    0x8048730 <main+38> 
   0x0804872b <+33>:    mov    $0x0,%eax 
   0x08048730 <+38>:    mov    %eax,(%esp) 
   0x08048733 <+41>:    call   0x80486df <fb(B*)> 
   0x08048738 <+46>:    mov    0x1c(%esp),%eax 
   0x0804873c <+50>:    add    $0x4,%eax 
   0x0804873f <+53>:    mov    %eax,(%esp) 
   0x08048742 <+56>:    call   0x80486b4 <fb(B&)> 
   0x08048747 <+61>:    mov    $0x0,%eax 
   0x0804874c <+66>:    leave   
   0x0804874d <+67>:    ret     

Dump of assembler code for function main:
   0x0804870a <+0>:     push   %ebp
   0x0804870b <+1>:     mov    %esp,%ebp
   0x0804870d <+3>:     and    $0xfffffff0,%esp
   0x08048710 <+6>:     sub    $0x20,%esp
   0x08048713 <+9>:     movl   $0x0,0x1c(%esp)
   0x0804871b <+17>:    cmpl   $0x0,0x1c(%esp)
   0x08048720 <+22>:    je     0x804872b <main+33>
   0x08048722 <+24>:    mov    0x1c(%esp),%eax
   0x08048726 <+28>:    add    $0x4,%eax
   0x08048729 <+31>:    jmp    0x8048730 <main+38>
   0x0804872b <+33>:    mov    $0x0,%eax
   0x08048730 <+38>:    mov    %eax,(%esp)
   0x08048733 <+41>:    call   0x80486df <fb(B*)>
   0x08048738 <+46>:    mov    0x1c(%esp),%eax
   0x0804873c <+50>:    add    $0x4,%eax
   0x0804873f <+53>:    mov    %eax,(%esp)
   0x08048742 <+56>:    call   0x80486b4 <fb(B&)>
   0x08048747 <+61>:    mov    $0x0,%eax
   0x0804874c <+66>:    leave <

补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,