É possível substituir o número 9
por uma macro para ter mais manutenibilidade de código nesta linha de código?
scanf("%9[^\n]s", str);
Tentei ler a documentação, mas não consigo encontrar os nomes exatos dessas operações:
"[^\n]s"
"%ns"
Eu tentei essas alternativas, mas Clion está marcando a primeira ocorrência str
como um erro em ambas as linhas:
scanf("%" str(MAX_LENGTH) "%[^\n]s", str);
scanf("%" str(MAX_LENGTH) "[^\n]%*c", str);
O motivo pelo qual você obtém um erro
str
não é uma função interna nem uma macro predefinida. Você pode definirstr
como uma macro e usar o operador de stringização#
para realizar a substituição, mas é complicado e confuso:str
deve ser definido como uma macro que invoca outra macroxstr
, que por sua vez stringize seu argumento com#x
:Observe, no entanto, que ambos os exemplos têm problemas:
scanf("%" str(MAX_LENGTH) "%[^\n]s", str);
tem um extras
no final do formato, que é inútil e indica uma confusão entre a%[...]
conversão e%s
, ambas requerem um prefixo de contagem de caracteres para evitar estouro de buffer. A segunda%
também está incorreta. Além disso, você não deve usar o mesmo identificadorstr
para a macro e o array de destino: embora não seja um erro, torna o código confuso desnecessariamente. O código deve ser escrito:scanf("%" str(MAX_LENGTH) "[^\n]%*c", str);
tem a forma correta, mas consumirá incondicionalmente o próximo byte após a correspondência, que não é o caractere de nova linha se a linha tiver mais deMAX_LENGTH
bytes antes da nova linha. Nenhuma indicação para isso é retornada ao chamador.%9[^\n]
falhará em linhas de entrada vazias porque nenhum caractere corresponde à especificação de conversão.scanf()
retornará0
e deixará a matriz de destino em um estado indeterminado.Aqui está um pequeno exemplo:
Se
str
fosse definido como#define str(x) #x
, invocarstr(MAX_LENGTH)
seria expandido para"MAX_LENGTH"
. A segunda chamada de macro realiza sua substituição após primeiro expandir o argumento inicial da macro, portanto,str(MAX_LENGTH)
expande paraxstr(9)
, que se expande para"9"
.Observe também que
MAX_LENGTH
não é o comprimento da matriz de destino: você deve adicionar um caractere extra para o terminador nulo e não há verificação de consistência na invocação da macro: a consistência entreMAX_LENGTH
e a definição debuf
dependem inteiramente do programador.Além disso, se a definição de
MAX_LENGTH
não for uma constante inteira sem um sufixo, esse truque de expansão de macro não produzirá umscanf
especificador de conversão correto.Uma abordagem mais confiável seria usada
snprintf
para construir ascanf()
string de formato:Esta versão funciona melhor, mas tem suas próprias deficiências: impede que o compilador verifique a consistência entre a string de formato e os
scanf()
argumentos restantes, o que causará um aviso nos níveis de aviso recomendados (-Wall -Wextra
) e essa verificação de consistência é bastante útil, enquanto a string de formato para construir a string de formato é fácil errar.No final, ambas as abordagens são complicadas e propensas a erros. É muito mais confiável usar
fgets()
para o seu propósito e remover manualmente a nova linha à direita:O comportamento é um pouco diferente:
fgets
consumirá a nova linha, a menos que a linha seja muito longa, o que dificulta a recuperação de erros.Uma solução melhor em geral parece usar uma função personalizada:
Observe que o comportamento ainda é sutilmente diferente da
scanf()
chamada original, mas potencialmente mais próximo de seus objetivos:get_line
lê uma linha completa, incluindo a nova linha, os caracteres em excesso são descartados.get_line
sempre armazena uma string C na matriz de destino sesize
não for 0, mesmo no final do arquivo ondebuf
haverá uma string vazia.scanf()
retornariaEOF
no final do arquivo e deixariabuf
inalterado.get_line
aceitará linhas vazias, enquantoscanf()
falharia, retornaria0
e sairiabuf
em um estado indeterminado, uma limitação que você provavelmente não sabia.Conclusão:
scanf()
é cheio de manias. Tentar evitar estouros de buffer com uma contagem de caracteres explícita é uma boa ideia, masscanf()
causará outros problemas que não são facilmente resolvidos. Muitas vezes, é necessário escrever um código personalizado para obter uma semântica precisa e consistente.Você só pode usar a stringificação se a macro a ser expandida for um número simples (não uma expressão mais geral envolvendo adição ou subtração, por exemplo).
Você então tem que usar a dança normal de macro dupla para obter a macro expandida corretamente:
e então: