resueltos - Mejores prácticas para evitar problemas con punteros
punteros y vectores en c (9)
Esta pregunta ya tiene una respuesta aquí:
¿Cuáles son los resultados realistas de los errores de programación relacionados con los punteros?
¿Qué ''efectos negativos'' suceden cuando los programadores crean errores de puntero?
Ejemplos prácticos con código son preferibles.
Cosas que pueden ir mal cuando los punteros son mal utilizados:
Fugas de memoria : asigna un puntero a un método y luego lo deja fuera del alcance sin desasignarlo correctamente. El puntero a la memoria en el montón ahora se pierde, pero la memoria permanece asignada. Liberar este recuerdo ahora es extremadamente difícil. Más información de Wikipedia.
Violaciones de acceso : crea un puntero que apunta a una dirección de memoria a la que no tiene acceso o que no existe. Los punteros son solo enteros después de todo, y pueden manipularse como cualquier otro número. Cuando intentes desreferenciar tu puntero no válido, tu programa se detendrá. Más información de Wikipedia.
Errores de puntero nulo : este es un caso especial de una infracción de acceso. La forma correcta de "estacionar" un puntero, para que no apunte a nada en particular, es establecer su valor en cero o nulo. Intentar desreferenciar un puntero nulo detendrá su programa. Más información de Wikipedia.
Desbordamientos de búfer : asigna un puntero a un búfer de caracteres de 30 caracteres. Luego procede a transmitir las entradas del usuario (desde un socket, archivo, consola, etc.) a este búfer. Si no implementa correctamente las comprobaciones de límite de búfer, su programa podría poner más de 30 caracteres en el búfer. Esto dañará cualquier dato almacenado adyacente al búfer en la memoria y posiblemente lo expondrá a un ataque de código malicioso. Más información de Wikipedia.
Corrupción de memoria : un puntero es solo un número entero que contiene la dirección de memoria de algo a lo que apunta. Como un entero, la aritmética de punteros se puede utilizar para manipular el valor del puntero en todo tipo de formas interesantes. Se pueden desarrollar errores sutiles si los cálculos de puntero salen mal. El puntero ahora apuntará a una ubicación desconocida en la memoria, y cualquier cosa podría suceder cuando se haga una referencia.
Problemas de cadena terminados en nulo : estos errores ocurren cuando las funciones de la biblioteca de cadenas que esperan cadenas terminadas en nulo se alimentan con punteros de caracteres que no terminan en nulo. Las funciones de la biblioteca de cadenas continuarán procesando caracteres, uno a la vez, hasta que se encuentre un nulo, donde sea que se encuentre. Una broma ilustra mejor este error.
El solo hecho de inicializar las variables de puntero y una buena limpieza eliminará el 99% de sus problemas . Por buena limpieza, quiero decir; desasignar memoria y establecer variables de puntero a nulo.
De lo contrario, necesita un diseño claro con respecto a pasar punteros alrededor y qué código es responsable de limpiar esa memoria. Si terminas en una situación en la que no sabes qué código será el último en usar la memoria y deberías estar limpiando, entonces tienes un olor a diseño, que querrás arreglar para mantener tu cordura.
La mejor práctica es evitar utilizar los punteros tanto como sea posible. En su lugar, use un lenguaje administrado para la mayor parte de su software, y solo caiga a C para piezas pequeñas donde sea necesario para acceder a los recursos o la eficiencia del sistema. En otras palabras, C debería ser considerado como el lenguaje ensamblador.
(La pregunta original que ayudé a cerrar como "no es una pregunta real" era bastante diferente y demasiado amplia para ser útil).
Los punteros crudos son malos. No es una forma de saber si son válidos o no (punteros colgantes), si se han inicializado (si no se configuraron como NULL en la inicialización, pueden aparecer como que realmente apuntan a algo) y no está claro quién tiene la responsabilidad de liberar los recursos. apuntan a (por ejemplo, la recuperación de la persona que llama o la función que devuelve un puntero).
No viviría un día sin punteros inteligentes. std :: auto_ptr cuando transfiero la propiedad (sea claro acerca de la responsabilidad), boost :: shared_ptr cuando la propiedad se comparte, boost :: weak_ptr cuando alguien solo está "observando" el recurso.
Los resultados al eliminar la referencia de un puntero incorrecto no están definidos, por lo que, por definición, cualquier cosa puede suceder cuando se estropea con un puntero. Por eso debes evitar usarlos cuando sea posible.
Los lenguajes C-ish están diseñados para el uso de punteros, y son dominantes en este momento, por lo que esto parecerá un consejo para algunos. Recomiendo a la gente que estudie los idiomas que están diseñados para minimizar el uso de los punteros y para detectar errores comunes, como Ada.
Mi puntero anectode favorito es el siguiente: una vez trabajaba para un grupo en Florida que mantenía una simulación de vuelo en red de 3 heliocpoters en Kurtland AFB en Nuevo México (la mayor parte del camino en el otro lado del continente). Hubo un error de bloqueo que apareció un día. El técnico del sitio local no pudo arreglarlo, así que después de aproximadamente un mes, uno de nuestros ingenieros voló para verlo. Dos semanas después, quedó desconcertado, por lo que otro fue detenido. Después de otro mes, nuestro ingeniero superior también fue a ayudarlo.
Un mes más tarde (todo el tiempo con la compañía pagando por 3 personas que viven en hoteles, alquilan automóviles y vuelven en avión cada dos fines de semana), detectaron el problema. Resultó que alguien estaba indexando uno más allá del final de una matriz (C tampoco tiene comprobación de índice). Luego estaban agarrando la basura que estaba en ese lugar, pasándola a una segunda máquina a través de la red, y estaba usando ese valor como un índice de matriz. Dado que ese código también estaba en C, de nuevo sin verificación. Agarró la basura en esa ubicación y la envió a una tercera máquina. Esa máquina usó la basura como un puntero y trató de desreferirla. boom
Por lo tanto, un error en el código de una máquina estaba causando un fallo en dos máquinas eliminadas en la red. Miles de dólares y varios meses de tiempo precioso se desperdiciaron rastreando. Todo porque utilizaron un idioma sin controles de rango.
No sé si todavía puedes hacer eso, pero recuerdo hace unos años cuando haríamos un script para bloquear un sistema eliminando toda la memoria RAM. Esta es la forma en que lo hicimos.
int *i;
while(1){
*i = 0;
i++;
}
Al menos así es como recuerdo que lo hicimos. Aunque creo que no funcionará ahora.
Todo se reduce a acceder a áreas de memoria no designadas para ello. Lectura / escritura fuera del área asignada, desreferenciación de punteros no inicializados. Eso es básicamente eso.
También hay una mala interpretación del tipo de objeto apuntado, pero esto normalmente requiere un poco de esfuerzo para salirse con la suya sin que el compilador le grite.
Y las pérdidas de memoria, pero esa es una historia diferente, se trata de la asignación, no de los punteros en sí.
Desde http://xkcd.com
Supongo que estoy tomando la solicitud de ilustración literalmente .
- Nunca ignore ninguna advertencia.
Utilice herramientas de análisis estático como Splint .
Lo más importante: use herramientas de análisis dinámico : a menudo alertan sobre el uso incorrecto de los punteros, rompiendo los límites de la matriz, etc. ''Me aseguro de que no haya errores en ellos, incluso si el programa parece estar funcionando ...