La diferencia entre "referencia nula" y "puntero nulo" en C ++

Hay muchos artículos en Internet que discuten la diferencia entre "referencia" y "puntero" de C ++. Cuando se trata de la diferencia, uno de ellos es: "La referencia no puede ser NULL. La referencia debe estar asociada a una unidad de almacenamiento legal, y el puntero puede ser NULL". ) ", Pero en aplicaciones prácticas, a veces para mantener la coherencia, dejaremos de lado esta regla y crearemos artificialmente una" referencia nula ".

En muchos casos, las "referencias vacías" pueden funcionar, de modo que el consejo de que "las referencias no pueden estar vacías" es ridiculizado como formalismo, solo sensacionalismo por los creadores de estándares. Un ejemplo de una "referencia nula" es:

int * a = NULL;
int & b = * a;

Entonces, al acceder a b, apareció la excepción del programa:

nulo f ( int & p) 
{ 
    p = 0 ; 
} 
f (b);

Por supuesto, puede agregar algo de juicio para solucionar este problema:

nulo f ( int & p) 
{ 
    if (& p) p = 0 ; 
}

¿Cómo es, es un poco incómodo? Pero si se reemplaza con un puntero, la cantidad de caracteres que desea ingresar es exactamente la misma:

nulo f ( int * p) 
{ 
    if (p) * p = 0 ; 
}

 Entonces, si usar "referencia" o "puntero" parece ser una cuestión del sabio que ve al benevolente.

 

Sin embargo, sin embargo. . . . . .

 

¿Es esto realmente lo mismo?

 

Echemos un vistazo a un ejemplo más complicado:

// test.cpp 

#include <iostream> clase A 
{ int a; 
}; clase B 
{ int b; 
}; clase C 
: público A, público B 
{ int c; 
}; nulo fb (B & b) 
{ 
    std :: cout << & b << std :: endl; 
} vacío fb (B * b) 
{ 
    std :: cout << b << std :: endl; 
} int main ( int argc, char


    


    


    





* argv []) 
{ 
    C * c = NULL; 

    fb (c); 

    fb ( * c); 

    devuelve  0 ; 
}

Compila y corre para ver:

$ ./ prueba
 0 
0x4

Oye, por qué & b no es 0, es decir, no es una "referencia nula". En este momento, incluso si se agrega el juicio, si (& b) no es útil.

Es posible que haya notado que lo anterior es el entorno de Linux, entonces, ¿qué pasa con el entorno de Windows:

> test.exe
 00000000 
00000000

En este momento, la "referencia vacía" mantiene su atributo "vacío", solo los desarrolladores de C ++ en la plataforma Windows pueden relajarse.

¿Qué está pasando? ¿Te están engañando tus ojos? Tal vez lo sea, pero la CPU no nos engañará, podemos ver la esencia del código de ensamblaje. El siguiente es el código compilado en la plataforma Linux:

Volcado del código del ensamblador para la función main: 
0x0804870a <+ 0 >: push % ebp 
0x0804870b <+ 1 >: mov % esp,% ebp 
0x0804870d <+ 3 >: y $ 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>: agregar $ 0x4,% eax 
0x08048729 <+ 31 >: jmp 0x8048730 <main + 38 > 
0x0804872b <+ 33 >: mov $ 0x0,% eax 
0x08048730 <+ 38 >: mov % eax, (% esp) 
0x08048733 <+ 41 >: llame a 0x80486df <fb (B *)> 
0x08048738 <+ 46 >: mov 0x1c (% esp),% eax 
0x0804873c <+ 50 >: agregue $ 0x4,% eax 
0x0804873f <+ 53 >: mov % eax, (% esp) 
0x08048742 <++ 56>: llamar 0x80486b4 <fb (B &)> 
0x08048747 <+ 61 >: mov $ 0x0,% eax 
0x0804874c <+ 66 >: dejar  
0x0804874d <+ 67 >: ret 

Esto es para la plataforma de Windows:

wmain: 
004114D0  empuje ebp 
 004114D1  mov ebp, esp 
 004114D3  sub esp, 0DCh 
 004114D9  empuje ebx 
 004114DA  empuje esi 
 004114DB  empuje edi 
 004114DC  lea EDI, [ebp-0DCh] 
 004114E2  mov ecx, 37h 
 004114E7  mov eax, 0CCCCCCCCh 
 004114EC  rep stos dword ptr es: [edi] 
 004114EE  mov dword ptr [c], 0  
004114F5  mov eax, dword ptr [c] 
 004114F8  movdword ptr [rc], eax 
 004114FB  cmp dword ptr [c], 0  
004114FF  je wmain + 3Fh (41150Fh) 
 00411501  mov eax, dword ptr [c] 
 00411504  add eax, 4  
00411507  mov dword ptr [ebp-0DCh1150D eax 
 004  jmp wmain + 49h (411519h) 
 0041150F  mov dword ptr [ebp-0DCh], 0  
00411519  mov ecx, dword ptr [ebp-0DCh] 
 0041151F  push ecx 
 00411520  call fb (411118h) 
 00411525  add esp, 4  
00411528 cmp dword ptr [RC], 0  
0041152C  je wmain + 6Ch (41153Ch) 
 0041152E  mov eax, dword ptr [RC] 
 00411531  add eax, 4  
00411534  mov dword ptr [-0DCh ebp], eax 
 0041153A  JMP wmain + 76H (411546h) 
 0041153C  mov dword ptr [ebp-0DCh], 0  
00411546  mov ecx, dword ptr [ebp-0DCh] 
 0041154C  push ecx 
 0041154D  call fb (41108Ch) 
 00411552  add esp, 4  
00411555  xor eax, eax 
 00411557 pop edi 
 00411558  pop esi 
 00411559  pop ebx 
 0041155A  add esp, 0DCh 
 00411560  cmp ebp, esp 
 00411562  llamada @ ILT + 345 (__RTC_CheckEsp) (41115Eh) 
 00411567  mov esp, ebp 
 00411569  pop ebp 
 0041156A  ret 

El código de ensamblaje está interesado en estudiar solo, así que no entraré en detalles.


Mirando hacia atrás, los dos métodos de procesamiento de los compiladores de las dos plataformas tienen su razonabilidad: la plataforma Windows aumenta la tolerancia a fallas, mientras que la plataforma Linux reduce el juicio y aumenta el rendimiento al procesar referencias. Esto refleja vagamente los diferentes conceptos de desarrollo de Windows y Linux.

Finalmente, recuerde que las referencias no pueden ser nulas, y si puede haber objetos nulos, use punteros.


Enlace original: https://blog.csdn.net/luansxx/article/details/10134139

Supongo que te gusta

Origin www.cnblogs.com/2018shawn/p/12724525.html
Recomendado
Clasificación