c++ - para - bioestadistica daniel pdf
¿Se pueden usar ''/ 0'' y NULL indistintamente? (7)
NULL se usa a menudo en el contexto de punteros, y se define a través de macros en múltiples bibliotecas estándar (como
<iostream>
) para ser el número entero
0
.
''/0''
es el carácter nulo y tiene 8 bits de ceros.
Por cierto, 8 bits de ceros son equivalentes al entero
0
.
En algunos casos, aunque se considera que es un estilo horrible, estos dos pueden intercambiarse:
int *p=''/0'';
if (p==NULL) //evaluates to true
cout << "equal/n";
O
char a=NULL;
char b=''/0'';
if (a==b) //evaluates to true
cout << "equal again/n";
Ya hay muchas preguntas similares sobre SO solo; por ejemplo, la respuesta principal para esta pregunta ( ¿Cuál es la diferencia entre NULL, ''/ 0'' y 0 ) dice "realmente no son lo mismo".
¿Podría alguien dar un ejemplo de que
NULL
y
/0
no pueden intercambiarse (preferiblemente una aplicación real y no un caso patológico)?
¿Alguien podría dar un ejemplo de que NULL y / 0 no se pueden intercambiar?
La diferencia entre
NULL
y
''/0''
puede afectar la resolución de sobrecarga.
Ejemplo ( verifíquelo en Coliru ):
#include <iostream>
// The overloaded function under question can be a constructor or
// an overloaded operator, which would make this example less silly
void foo(char) { std::cout << "foo(char)" << std::endl; }
void foo(int) { std::cout << "foo(int)" << std::endl; }
void foo(long) { std::cout << "foo(long)" << std::endl; }
void foo(void*) { std::cout << "foo(void*)" << std::endl; }
int main()
{
foo(''/0''); // this will definitely call foo(char)
foo(NULL); // this, most probably, will not call foo(char)
}
Tenga en cuenta que el compilador gcc utilizado en Coliru define
NULL
como
0L
, lo que para este ejemplo significa que
foo(NULL)
resuelve
foo(long)
lugar de
foo(void*)
.
Esta respuesta
discute ese aspecto en detalle.
Definamos qué es NULL en C / C ++.
De acuerdo con la referencia C / C ++, NULL se define como una macro que se expande a una constante de puntero nulo. A continuación, podemos leer que una constante de puntero nulo se puede convertir a cualquier tipo de puntero (o tipo de puntero a miembro), que adquiere un valor de puntero nulo. Este es un valor especial que indica que el puntero no apunta a ningún objeto.
Definición que se refiere a C:
Una constante de puntero nulo es una expresión constante integral que se evalúa a cero (como 0 o 0L), o la conversión de dicho valor para escribir void * (como (void *) 0).
Definición que se refiere a C ++ 98:
Una constante de puntero nulo es una expresión constante integral que se evalúa a cero (como 0 o 0L).
Definición que se refiere a C ++ 11:
Una constante de puntero nulo es una expresión constante integral que se evalúa a cero (como 0 o 0L), o un valor de tipo nullptr_t (como nullptr).
Ejemplo de métodos de sobrecarga.
Supongamos que tenemos los siguientes métodos:
class Test {
public:
method1(char arg0);
method1(int arg0);
method1(void* arg0);
method1(bool arg0);
}
Llamar al método1 con el argumento
NULL
o
nullptr
debería llamar al
method1(void* arg0);
.
Sin embargo, si llamamos al método1 con el argumento
''/0''
o
0
debería ejecutar el
method1(char arg0);
y
method1(int arg0);
.
Definición de macro NULL en C ++
Leon tiene razón en
que cuando hay varias sobrecargas para la misma función,
/0
preferiría la que toma el parámetro de tipo
char
.
Sin embargo, es importante notar que en un compilador típico, ¡
NULL
preferiría la sobrecarga que toma el parámetro de tipo
int
, no de tipo
void*
!
Lo que probablemente causa esta confusión es que el lenguaje C permite definir
NULL
como
(void*)0
.
El estándar C ++ establece explícitamente
(borrador N3936, página 444)
:
Las posibles definiciones [de macro
NULL
] incluyen0
y0L
, pero no(void*)0
.
Esta restricción es necesaria porque, por ejemplo,
char *p = (void*)0
es válido C pero no válido C ++, mientras que
char *p = 0
es válido en ambos.
En C ++ 11 y
nullptr
posteriores, debe usar
nullptr
, si necesita una constante nula que se comporte como puntero.
Cómo funciona la sugerencia de Leon en la práctica
Este código define varias sobrecargas de una sola función. Cada sobrecarga genera el tipo de parámetro:
#include <iostream>
void f(int) {
std::cout << "int" << std::endl;
}
void f(long) {
std::cout << "long" << std::endl;
}
void f(char) {
std::cout << "char" << std::endl;
}
void f(void*) {
std::cout << "void*" << std::endl;
}
int main() {
f(0);
f(NULL);
f(''/0'');
f(nullptr);
}
En Ideone esto genera
int
int
char
void*
Por lo tanto, afirmaría que el problema con las sobrecargas no es una aplicación real sino un caso patológico.
La constante
NULL
se comportará mal de todos modos, y debe reemplazarse con
nullptr
en C ++ 11.
¿Qué pasa si NULL no es cero?
Andrew Keeton sugiere otro caso patológico en otra pregunta:
Tenga en cuenta que lo que es un puntero nulo en el lenguaje C. No importa en la arquitectura subyacente. Si la arquitectura subyacente tiene un valor de puntero nulo definido como la dirección 0xDEADBEEF, entonces depende del compilador resolver este desorden.
Como tal, incluso en esta arquitectura divertida, las siguientes formas siguen siendo formas válidas de verificar un puntero nulo:
if (!pointer) if (pointer == NULL) if (pointer == 0)
Las siguientes son formas inválidas de verificar un puntero nulo:
#define MYNULL (void *) 0xDEADBEEF if (pointer == MYNULL) if (pointer == 0xDEADBEEF)
ya que estos son vistos por un compilador como comparaciones normales.
Resumen
En general, diría que las diferencias son principalmente estilísticas.
Si tiene una función que toma
int
y sobrecarga que toma
char
, y funcionan de manera diferente, notará la diferencia cuando las llame con constantes
/0
y
NULL
.
Pero tan pronto como coloca esas constantes en variables, la diferencia desaparece, porque la función que se llama se deduce del tipo de la variable.
El uso de constantes correctas hace que el código sea más fácil de mantener y transmite un significado mejor.
Debe usar
0
cuando se refiere a un número,
/0
cuando se refiere a un carácter y
nullptr
cuando se
nullptr
a un puntero.
Matthieu M.
señala en los comentarios, que
GCC tenía un error
, en el que se comparaba un
char*
con
/0
, mientras que la intención era desreferenciar el puntero y comparar un
char
con
/0
.
Dichos errores son más fáciles de detectar si se usa un estilo adecuado en toda la base de código.
Para responder a su pregunta, no existe realmente un caso de uso real que le impida usar
/0
y
NULL
indistintamente.
Solo razones estilísticas y algunos casos extremos.
Extractos de C ++ 14 draft N3936:
18.2 Tipos [support.types]
3 La macro
NULL
es una constante de puntero nulo de C ++ definida por la implementación en esta Norma Internacional (4.10).4.10 Conversiones de puntero [conv.ptr]
1 Una constante de puntero nulo es un literal entero (2.14.2) con valor cero o un valor de tipo
std::nullptr_t
.
Una constante de puntero nulo se puede convertir a un tipo de puntero; el resultado es el valor de puntero nulo de ese tipo y es distinguible de cualquier otro valor de puntero de objeto o tipo de puntero de función.
Por lo tanto,
NULL
puede ser cualquier literal entero con valor cero, o un valor de tipo
std::nullptr_t
como
nullptr
, mientras que
''/0''
es siempre el literal de caracteres estrechos cero.
Por lo tanto, no es en general intercambiable, a pesar de que en un contexto de puntero no se puede ver ninguna diferencia estilística.
Un ejemplo sería:
#include <iostream>
#include <typeinfo>
int main() {
std::cout << typeid(''/0'').name << ''/n''
<< typeid(NULL).name << ''/n''
<< typeid(nullptr).name << ''/n'';
}
Los programas de computadora tienen dos tipos de lectores.
El primer tipo son los programas de computadora, como el compilador.
El segundo tipo son los humanos, como usted y sus compañeros de trabajo.
Los programas generalmente están bien para obtener un tipo de cero en lugar de otro. Hay excepciones, como han señalado las otras respuestas, pero eso no es realmente importante.
Lo importante es que estás jugando con los lectores humanos .
Los lectores humanos son muy sensibles al contexto. Al usar el cero incorrecto, estás mintiendo a tus lectores humanos. Te van a maldecir.
Un humano al que se le miente puede pasar por alto los errores más fácilmente.
Un humano al que se le miente puede ver "errores" que no están allí.
Cuando "arreglan" estos errores fantasmas, introducen errores reales.
No les mientas a tus humanos. Uno de los humanos a los que mientes es tu futuro yo. Tú también te maldecirás.
Por favor no hagas esto.
Es un antipatrón, y en realidad está mal.
NULL es para punteros NULL,
''/0''
es el carácter nulo.
Son cosas lógicamente diferentes.
No creo haber visto esto nunca:
int* pVal=''/0'';
Pero esto es bastante común:
char a=NULL;
Pero no es buena forma. Hace que el código sea menos portátil y, en mi opinión, menos legible. También es probable que cause problemas en entornos mixtos C / C ++.
Se basa en suposiciones sobre cómo cualquier implementación particular define NULL. Por ejemplo, algunas implementaciones usan un simple
#define NULL 0
Otros pueden usar:
#define NULL ((void*) 0)
Y he visto a otros definirse como un número entero, y todo tipo de tratamiento extraño.
NULL
, en mi opinión, debe usarse únicamente para indicar una dirección no válida.
Si desea un carácter nulo, use
''/0''
.
O defina esto como
NULLCHR
.
Pero eso no es tan limpio.
Esto hará que su código sea más portátil: no comenzará a recibir advertencias sobre los tipos, etc. si cambia la configuración del compilador / entorno / compilador. Esto podría ser más importante en un entorno C o C / C ++ mixto.
Un ejemplo de advertencias que pueden surgir: considere este código:
#define NULL 0
char str[8];
str[0]=NULL;
Esto es equivalente a:
#define NULL 0
char str[8];
str[0]=0;
Y estamos asignando un valor entero a un carácter. Esto puede causar una advertencia del compilador, y si hay suficientes ocurrencias de esto, muy pronto no podrá ver ninguna advertencia importante . Y para mí, este es el verdadero problema. Tener advertencias en el código tiene dos efectos secundarios:
- Dadas suficientes advertencias, no se ven nuevas.
- Da la señal de que las advertencias son aceptables.
En ambos casos, los errores reales pueden pasar, que el compilador podría detectar si nos hubiéramos molestado en leer las advertencias (o activar -Werror)
Sí, pueden exhibir un comportamiento diferente mientras resuelven funciones sobrecargadas.
func(''/0'')
invoca
func(char)
,
mientras
func(NULL)
invoca
func(integer_type)
.
Puede eliminar la confusión utilizando nullptr , que siempre es un tipo de puntero, no muestra ambigüedad al asignar / comparar valores o resolver la resolución de sobrecarga de funciones .
char a = nullptr; //error : cannot convert ''std::nullptr_t'' to ''char'' in initialization
int x = nullptr; //error : nullptr is a pointer not an integer
Tenga en cuenta que todavía es compatible con NULL:
int *p=nullptr;
if (p==NULL) //evaluates to true
Extracto del libro C ++ Programming Stroustrup 4th Edition:
En el código anterior, normalmente se usa 0 o NULL en lugar de nullptr (§7.2.2). Sin embargo, el uso de nullptr elimina la posible confusión entre enteros (como 0 o NULL) y punteros (como nullptr).