A impressão de ponteiros nulos com% p é um comportamento indefinido?




language-lawyer c99 (2)

A resposta curta

Sim A impressão de ponteiros nulos com o especificador de conversão %p tem um comportamento indefinido. Dito isto, não tenho conhecimento de qualquer implementação em conformidade existente que se comportasse mal.

A resposta se aplica a qualquer um dos padrões C (C89 / C99 / C11).

A longa resposta

O especificador de conversão %p espera que um argumento do tipo ponteiro seja anulado, a conversão do ponteiro em caracteres imprimíveis é definida pela implementação. Ele não indica que um ponteiro nulo é esperado.

A introdução às funções da biblioteca padrão indica que os ponteiros nulos como argumentos para funções (biblioteca padrão) são considerados valores inválidos, a menos que seja explicitamente indicado de outra forma.

C99 / C11 §7.1.4 p1

[...] Se um argumento para uma função tiver um valor inválido (como [...] um ponteiro nulo, [...] o comportamento é indefinido.

Exemplos para funções (biblioteca padrão) que esperam ponteiros nulos como argumentos válidos:

  • fflush() usa um ponteiro nulo para liberar "todos os fluxos" (que se aplicam).
  • freopen() usa um ponteiro nulo para indicar o arquivo "atualmente associado" ao fluxo.
  • snprintf() permite passar um ponteiro nulo quando 'n' é zero.
  • realloc() usa um ponteiro nulo para alocar um novo objeto.
  • free() permite passar um ponteiro nulo.
  • strtok() usa um ponteiro nulo para chamadas subseqüentes.

Se tomarmos o caso para snprintf() , faz sentido permitir a passagem de um ponteiro nulo quando 'n' for zero, mas este não é o caso de outras funções (biblioteca padrão) que permitem um zero 'n' similar. Por exemplo: memcpy() , memmove() , strncpy() , memset() , memcmp() .

Não é apenas especificado na introdução à biblioteca padrão, mas também na introdução a estas funções:

C99 §7.21.1 p2 / C11 §7.24.1 p2

Onde um argumento declarado como size_t n especifica o comprimento da matriz para uma função, n pode ter o valor zero em uma chamada para essa função. A menos que seja explicitamente indicado o contrário na descrição de uma função específica nesta subcláusula, os argumentos de ponteiro em tal chamada ainda terão valores válidos, conforme descrito em 7.1.4.

É intencional?

Eu não sei se o UB de %p com um ponteiro nulo é de fato intencional, mas desde que o padrão declara explicitamente que ponteiros nulos são considerados valores inválidos como argumentos para funções de biblioteca padrão, e então vai e explicitamente especifica os casos onde Um ponteiro nulo é um argumento válido (snprintf, free, etc), e então vai e mais uma vez repete o requisito para os argumentos serem válidos mesmo em casos 'n' memcpy ( memcpy , memmove , memset ), então eu acho que é É razoável supor que o comitê de padrões C não está muito preocupado em ter essas coisas indefinidas.

É um comportamento indefinido para imprimir ponteiros nulos com o especificador de conversão %p ?

#include <stdio.h>

int main(void) {
    void *p = NULL;

    printf("%p", p);

    return 0;
}

A questão se aplica ao padrão C e não às implementações C.


Os autores do Padrão C não fizeram nenhum esforço para listar exaustivamente todos os requisitos comportamentais que uma implementação deve satisfazer para ser adequada a qualquer propósito específico. Em vez disso, eles esperavam que as pessoas que escreviam compiladores exercitassem uma certa quantidade de bom senso, quer o Padrão o exigisse ou não.

A questão de se algo invoca UB raramente é útil em si. As verdadeiras questões de importância são:

  1. Alguém que está tentando escrever um compilador de qualidade faz com que ele se comporte de maneira previsível? Para o cenário descrito, a resposta é claramente sim.

  2. Os programadores devem ter o direito de esperar que compiladores de qualidade para qualquer coisa que se pareça com plataformas normais se comportarão de maneira previsível? No cenário descrito, eu diria que a resposta é sim.

  3. Poderiam alguns escritores de compiladores obtusos estender a interpretação do Padrão de modo a justificar fazer algo estranho? Eu espero que não, mas não descartaria isso.

  4. Os compiladores saneantes deveriam grasnar sobre o comportamento? Isso dependeria do nível de paranóia de seus usuários; um compilador saneante provavelmente não deveria ser o padrão para criticar tal comportamento, mas talvez fornecer uma opção de configuração para fazer caso os programas possam ser portados para compiladores "inteligentes" / burros que se comportam de maneira estranha.

Se uma interpretação razoável do Padrão implicaria que um comportamento é definido, mas alguns escritores de compiladores estendem a interpretação para justificar o contrário, será que realmente importa o que o Padrão diz?







c11