¿Es un comportamiento indefinido imprimir punteros nulos con el %p
especificador de conversión?
#include <stdio.h>
int main(void) {
void *p = NULL;
printf("%p", p);
return 0;
}
La pregunta se aplica al estándar C y no a las implementaciones C.
c
language-lawyer
c99
undefined-behavior
c11
Dror K.
fuente
fuente
Respuestas:
Este es uno de esos casos raros en los que estamos sujetos a las limitaciones del idioma inglés y a la estructura inconsistente del estándar. Entonces, en el mejor de los casos, puedo hacer un contraargumento convincente, ya que es imposible probarlo :) 1
El código de la pregunta muestra un comportamiento bien definido.
Como [7.1.4] es la base de la pregunta, comencemos por ahí:
Este es un lenguaje torpe. Una interpretación es que los elementos de la lista son UB para todas las funciones de la biblioteca, a menos que se anulen por las descripciones individuales. Pero la lista comienza con "como", lo que indica que es ilustrativa, no exhaustiva. Por ejemplo, no menciona la terminación nula correcta de cadenas (crítica para el comportamiento de eg
strcpy
).Por lo tanto, está claro que la intención / alcance de 7.1.4 es simplemente que un "valor no válido" conduce a UB (a menos que se indique lo contrario ). Tenemos que mirar la descripción de cada función para determinar qué cuenta como "valor no válido".
Ejemplo 1 -
strcpy
[7.21.2.3] dice solo esto:
No hace mención explícita de punteros nulos, pero tampoco menciona terminadores nulos. En cambio, se infiere de "cadena apuntada por
s2
" que los únicos valores válidos son cadenas (es decir, punteros a matrices de caracteres terminadas en nulo).De hecho, este patrón se puede ver en todas las descripciones individuales. Algunos otros ejemplos:
Ejemplo 2 -
printf
[7.19.6.1] dice esto sobre
%p
:Null es un valor de puntero válido, y esta sección no menciona explícitamente que null es un caso especial, ni que el puntero tiene que apuntar a un objeto. Por tanto, se define como comportamiento.
1. A menos que se presente un autor de estándares, o que podamos encontrar algo similar a un documento de fundamento que aclare las cosas.
fuente
La respuesta corta
Sí . La impresión de punteros nulos con el
%p
especificador de conversión tiene un comportamiento indefinido. Habiendo dicho eso, no tengo conocimiento de ninguna implementación conforme existente que se comporte mal.La respuesta se aplica a cualquiera de los estándares C (C89 / C99 / C11).
La respuesta larga
El
%p
especificador de conversión espera que un argumento de tipo puntero se anule, la conversión del puntero a caracteres imprimibles está definida por la implementación. No indica que se espera un puntero nulo.La introducción a las funciones de la biblioteca estándar establece que los punteros nulos como argumentos de las funciones (de la biblioteca estándar) se consideran valores no válidos, a menos que se indique explícitamente lo contrario.
C99
/C11
§7.1.4 p1
Ejemplos de funciones (biblioteca estándar) que esperan punteros nulos como argumentos válidos:
fflush()
utiliza un puntero nulo para eliminar "todos los flujos" (que correspondan).freopen()
utiliza un puntero nulo para indicar el archivo "actualmente asociado" con la secuencia.snprintf()
permite pasar un puntero nulo cuando 'n' es cero.realloc()
utiliza un puntero nulo para asignar un nuevo objeto.free()
permite pasar un puntero nulo.strtok()
utiliza un puntero nulo para llamadas posteriores.Si aceptamos el caso
snprintf()
, tiene sentido permitir pasar un puntero nulo cuando 'n' es cero, pero este no es el caso de otras funciones (biblioteca estándar) que permiten un cero 'n' similar. Por ejemplo:memcpy()
,memmove()
,strncpy()
,memset()
,memcmp()
.No solo se especifica en la introducción a la biblioteca estándar, sino también una vez más en la introducción a estas funciones:
C99 §7.21.1 p2
/C11 §7.24.1 p2
¿Es intencional?
No sé si el UB de
%p
con un puntero nulo es de hecho intencional, pero dado que el estándar establece explícitamente que los punteros nulos se consideran valores no válidos como argumentos para funciones de biblioteca estándar, y luego especifica explícitamente los casos en los que un nulo puntero es un argumento válido (snprintf, libre, etc.), y luego se va y una vez más se repite el requisito de que los argumentos sean válidos incluso en cero 'n' casos (memcpy
,memmove
,memset
), entonces yo creo que es razonable suponer que la El comité de estándares de C no está demasiado preocupado por tener tales cosas sin definir.fuente
%p
no se supone que sea un comportamiento indefinidoLos autores de la Norma C no hicieron ningún esfuerzo por enumerar exhaustivamente todos los requisitos de comportamiento que una implementación debe cumplir para ser adecuada para un propósito en particular. En cambio, esperaban que las personas que escribían compiladores ejercieran una cierta cantidad de sentido común, ya sea que la Norma lo requiera o no.
La cuestión de si algo invoca a UB rara vez es útil en sí misma. Las verdaderas cuestiones de importancia son:
¿Alguien que esté intentando escribir un compilador de calidad debería hacer que se comporte de forma predecible? Para el escenario descrito, la respuesta es claramente sí.
¿Deberían los programadores tener derecho a esperar que los compiladores de calidad para cualquier cosa que se parezca a las plataformas normales se comporten de manera predecible? En el escenario descrito, diría que la respuesta es sí.
¿Podrían algunos escritores de compiladores obtusos estirar la interpretación del Estándar para justificar hacer algo extraño? Espero que no, pero no lo descartaría.
¿Deberían los compiladores de desinfección chillar sobre el comportamiento? Eso dependería del nivel de paranoia de sus usuarios; un compilador desinfectante probablemente no debería de forma predeterminada quejarse de tal comportamiento, pero quizás proporcione una opción de configuración para hacerlo en caso de que los programas puedan ser portados a compiladores "inteligentes" / tontos que se comportan de manera extraña.
Si una interpretación razonable del Estándar implicaría que se define un comportamiento, pero algunos redactores de compiladores estiran la interpretación para justificar hacer lo contrario, ¿realmente importa lo que diga el Estándar?
fuente
printf("%p", (void*) 0)
un comportamiento indefinido o no, según el Estándar? Las llamadas a funciones profundamente anidadas son tan relevantes para esto como el precio del té en China. Y sí, UB es muy común en programas del mundo real, ¿qué pasa con eso?%p
para cada posible significado de la pregunta.