Enviado em 20/02/2021 - 11:36h 
		SrKon escreveu:
Estou encontrando os seguintes erros no programa
- Primeira letra digitada, estando certa ou não, é contabilizada como erro.
- Indicar se a letra já foi digitada, não funciona. 
- Em alguns casos ele conta 2 erros na primeira tentativa.
Para mim, parece esta contundente. Não sei se esqueci de zerar algo ou adicionar algo.
O código que tenho:
#include <stdio.h>  
Se você vai considerar 
ç  como letra válida e diferente de 
c , então provavelmente não vai poder usar apenas 26 elementos para representar conjuntos de letras já digitadas ou que compõem a palavra.
Além disso, parece que faltou um 
n  na palavra.
};  
Eu acho meio feio misturar línguas diferentes nos nomes de variáveis de um mesmo programa, e pior ainda quando a mistura acontece na composição do nome de uma mesma variável.
Minha sugestão é que você escolha um padrão — tudo em Português ou tudo em Inglês — e que seja consistente ao longo de todo o programa.
Além disso, para variáveis estáticas, globais ou declaradas em escopo de arquivo, não é necessário atribuir valor inicial igual a zero, pois os valores são zerados por padrão.  Só é necessário declarar valores iniciais quando você quiser que eles 
não sejam  nulos.  Apenas as variáveis locais e de armazenamento automático, cujos valores 
default  são indeterminados, são as que precisam ser explícitas sobre valores iniciais que tenham de ser iguais a zero.
O fato de você ter chamado a função de 
sorteio () dá a entender que a função poderia ser chamada mais de uma vez ao longo do programa (você não fez isso neste programa ainda, mas, além de sugerido pelo nome, poderia vir a fazê-lo caso o programa fosse modificado para apresentar a opção de jogar novamente).  Se esse for o caso, então você deve saber que a forma típica de usar 
srand () é chamá-la apenas uma vez, no início do programa, não a cada sorteio realizado.  Recomendo, portanto, tirar a chamada de dentro da função.
Se você de maneira nenhuma pretende que a função seja chamada mais de uma vez, então não faz muito sentido criar uma função separada cujo interior é tão simples.
Por que essa forma de calcular?  Por que não fazer simplesmente “
rand()%5 ”?
Em tempo: muitas implementações de 
rand () têm baixa aleatoriedade nos bits de mais baixa ordem, de modo que simplesmente pegar o resto da divisão por um valor 
N  também pequeno pode não produzir muito bons resultados. Isso pode ser melhorado através da transferência informação contida nos bits de mais alta ordem, com uma expressão parecida com a seguinte: “
(int)((rand()/(1.0+RAND_MAX))*N) ”.
}  
Não entendi a lógica de tomar o tamanho de uma 
string  (
wordSortead ) para varrer e imprimeir os caracteres de outra 
string  (
wordDigitada ).  Eu perdi alguma coisa, ou foi você que se confundiu?  Porque, se eu não tiver perdido nada, muito provavelmente você poderia substituir essa função por um mero “
printf("\n%zu letras\n%s", strlen(wordSorteada), wordDigitada); ”, que, por ser tão simples, novamente não precisaria residir numa função separada.
Em todo caso, note que a cláusula de parada do laço de repetição, que é testada a cada iteração do laço, contém uma chamada a 
strlen ().  A não ser que o compilador seja muito, MUITO bom em otimização, ele possivelmente não tem como prever que o tamanho da 
string  não mudou entre uma iteração e outra, e possivelmente será obrigado a recalcular o comprimento de 
wordSorteada  a cada iteração, elevando o custo do laço de repetição de 
O(n)  para 
O(n²) .  Curiosamente, você já tinha definido uma variável (
tam ) com o valor de 
strlen(wordSorteada) , e eu não entendi por que você não usou essa variável na cláusula de parada, em lugar de repetir a chamada a 
strlen () a cada iteração.
}  
Outra função extremamente simples demais para justificar sua existência, podendo ser substituída simplesmente por “
strcpy(wordSorteada, banco[lin]); ”.
}  
De novo, eu vejo com muita suspeição essa chamada a 
strlen () na cláusula de parada, avaliada a cada iteração.  Esse foi um vício que você usou ao longo de todo o programa.
Se você não quiser usar uma variável auxiliar para não ter de invocar a função a cada iteração, pode mudar a forma e a direção do laço de repetição, chamado 
strlen () apenas uma vez, na cláusula de inicialização, e ir do fim para o início da 
string .
for(size_t r = strlen(wordSorteada); r-- > 0; ) 
}  
O 
break  não é necessário no último 
case label  de um 
switch  (tanto 
switch  quanto 
break  são formas disfarçadas de 
goto ).
A primeira linha impressa em todos os casos é exatamente a mesma. Assim sendo, ela poderia ser impressa antes do 
switch , e ser removida de cada um dos casos.
As demais linhas poderiam ser impressas com uma única chamada a 
printf () ou 
puts (), em vez de três.  Não sei se lhe foi ensinado mas, em C, a declaração 
const char *const str_array[5]={  produz um 
array  com cinco elementos que são 
strings  constantes com exatamente o mesmo conteúdo.  Então você também poderia trocar algo como 
            printf("|     o\n");  pelo seguinte (economizando a quantidade de chamadas a funções, sem perder a formatação visual do código fonte).
            printf("|     o\n" 
}  
De novo o 
strlen () na cláusula de parada.  E, de novo, para fazer uma operação que já existe na biblioteca padrão; você poderia ter usado simplesmente “
strchr(vetor, letra)!=NULL ”.
      
De novo...
    {  
... e de novo...
    {  
Toda esta função poderia ser substituída simplesmente por uma única chamada a 
printf ().
}  
... e de novo.
    {  
Em outras palavras, esta função testa se 
wordDigitada  é igual a 
wordSorteada .  Efeito semelhante poderia ser conseguido com “
strcmp(wordDigitada, wordSorteada)==0 ”.
}  
Evite usar 
system (), pois seu uso indiscriminado pode ser bastante ineficiente e inseguro.
Procure restringir seu uso a situações em que ele seja estritamente necessário, para executar operações que seriam inviáveis de fazer de outra forma.  Mesmo quando isso for necessário, tome medidas para minimizar a chance de dar problema de segurança, tais como usar o caminho completo do programa que você quer invocar (por exemplo: em vez de apenas 
"clear" , prefira 
"/usr/bin/clear" ) 
e  predefinir o valor da variável de ambiente 
PATH  para um conjunto mínimo e seguro de diretórios (fazendo, por exemplo, algo como “
setenv("PATH", "/bin:/usr/bin", 1); ” logo no início do programa), e possivelmente alterando também as variáveis de ambiente que definem aspectos de 
locale .
Veja as considerações sobre o uso de 
system )() e alternativas para limpeza de tela nas mensagens de nº 10 a 12 no tópico 
https://www.vivaolinux.com.br/topico/C-C++/Preciso-fazer-um-programa-em-C-para-cadastra-alunos-consu... .  Uma discussão ainda mais aprofundada de possíveis problemas com 
system () pode ser encontrada na 3ª mensagem no tópico 
https://www.vivaolinux.com.br/topico/C-C++/Duvida-com-realloc-em-C , a partir do 12º parágrafo.
        imprimirForca(contaErros);  
Esta operação de leitura possivelmente é uma parte importante do problema que você reportou, porque não basta que você aperte uma letra, mas tem de digitar uma letra seguida da tecla <Enter> (ou <Return>, se você estiver num computador da Apple), que provoca o aparecimento de dois caracteres na entrada: a letra em si, e um caráter de fim-de-linha (
'\n' ) correspondente ao <Enter>.  Cada um desses caracteres será consumido numa iteração diferente do laço de repetição, e você não fez nenhuma validação de se o valor lido realmente corresponde a uma letra ou a um caráter válido.
A leitura de dados de entrada a partir do terminal exige certos cuidados, porque ela é normalmente orientada a linha (i.e. o sistema procura ler sempre linhas inteiras; mesmo que você solicite apenas um caráter, o sistema vai verificar se esse caráter já existe num 
buffer  interno e, se existir, ele devolve o tal caráter imediatamente, mas, caso não exista, o sistema vai procurar encher o 
buffer  com tanto caracteres quantos forem possíveis, até encontrar uma marca de fim-de-linha ou até não haver mais espaço no tal 
buffer  interno antes de lhe entregar o único caráter que você pediu).  Além disso, a função 
scanf () é muito complexa (possivelmente a mais complexa de todas as funções da biblioteca padrão do C), e requer uma boa e cautelosa leitura de sua documentação — e possivelmente alguma prática — para ser usada de modo efetivo e competente, com o devido conhecimento de quando acontecem descartes automáticos de caracteres e quando eles não ocorrem, como usar conversões, conversões sem atribuição/alteração de valor ou textos fixos, limitações de largura ou de conjuntos de caracteres, alocação automática de memória, indicadores de quantos caracteres foram consumidos, alteração de ordem de argumentos que podem receber atribuições e sinalização de erros ou grau de sucesso das conversões, entre outros aspectos.  E eu não estou exagerando.
No mínimo, eu sugiro a você fazer as seguintes mudanças referentes a este pedaço:
  • teste o valor de retorno de 
scanf (), para ter certeza de que não ocorreu erro de leitura;
  • dentro da própria 
string  de formatação de 
scanf (), procure descartar todos os caracteres que não interessarem (especialmente espaços, incluindo quebras de linha, cujo tratamento é muito fácil);
  • após a leitura, verifique que fruto de leitura seja válido, e dê o tratamento adequado caso não seja.
Acima, uma das palavras do dicionário incluía um 
ç .  Lembre-se de que 
ç  e vogais acentuadas são caracteres distintos do 
c  comum e das vogais não-acentuadas.  Além disso, dependendo do conjunto de caracteres que esteja em uso tanto no momento da compilação quanto no momento da execução, a representação dos caracteres acentuados ou com cedilha pode ser diferente, inclusive com mais do que um 
byte  (ou caráter, pois em C cada 
char  corresponde exatamente a um 
byte ) por sinal gráfico (por exemplo, com o conjunto de caracteres europeu e codificação ISO-8859-1, o 
c  corresponde ao 
byte  com valor 99 e o 
c  corresponde ao valor 231; já com o Unicode e codificação UTF-8, que é a mais comum no Linux hoje em dia, o 
c  corresponde ao mesmo 
byte  com valor 99, mas o 
ç  corresponde a um par de 
bytes , sendo o primeiro com valor 195 e o segundo com o valor 167).  Se você quiser tratar caracteres com marcas diacríticas, seu programa certamente vai ter de ser mais sofisticado.
        letter = tolower(letter);   
Aqui já há um exemplo de possível problema com caracteres com marcas diacríticas.  Mesmo num conjunto de caracteres que usam estritamente apenas um 
byte , como o ISO-8859-1, todos os caracteres acentuados utilizam o oitavo 
bit  do 
byte , o que, nos nossos PCs, corresponde a um valor negativo.  A função 
tolower () não trata tais valores, a não ser que você os converta antes para uma representação de valor sem sinal.  Assim sendo, depois de ter certeza de que a leitura foi bem sucedida, você deveria fazer algo parecido com o seguinte.
letter=tolower((unsigned char)letter);  
        if(verifyLetter(letter, letraDig))  
Anteriormente você chamou 
system("clear") , dando a entender que estava num sistema UNIX-like, visto que no mundo Microsoft, o comando equivalente a 
clear  seria 
CLS .  Mas aqui você usa uma chamada a 
pause , que até onde eu sei, não existe no Linux ou sistemas UNIX em geral.  Não existindo, esse comando só serve para dar erro.
        } else  
Esse 
system("stop")  é algo que eu nunca vi, e não faria muito sentido, mesmo que existisse tal comando “
stop ”.
A forma de encerrar o programa em C é fazer como na linha que vem logo abaixo (i.e. através do comando 
return  dentro de 
main ()), ou por meio das funções da biblioteca 
exit () ou 
abort ().
            return 0;  
Outros problemas que eu acredito que você deveria considerar e corrigir ao longo do programa como um todo:
  • Várias constantes numéricas, tais como 
30 , 
26  ou 
6 , espalhadas pelo programa não são geralmente uma boa prática. O programa provavelmente ficaria mais simples de compreender se essas constantes numéricas fossem substituídas por nomes descritivos de seu propósito, tais como 
TAM_MAX_PALAVRA , 
N_CARACTERES_VALIDOS  e 
MAX_RODADAS , sendo cada um desses nomes associados apenas uma vez a seu respectivo valor numérico constante.  Fazer desse modo teria ainda a vantagem adicional de facilitar eventuais alterações nesses valores — por exemplo, caso você decida trabalhar com palavras com mais de 30 caracteres, poderia alterar somente a definição do valor de 
TAM_MAX_PALAVRA , e automaticamente todos os outros lugares em que esse nome aparecesse teriam o valor correto automaticamente substituído, o que é bem melhor do que sair procurando a constante 30 ao longo do programa, verificar se cada ocorrência corresponde ao comprimento de uma palavra (porque poderia ser um outro valor 30, usado para outro propósito e que não deveria ser alterado) e então trocar manualmente cada ocorrência.
  • Você não especificou se seu programa é em C ou em C++.  Parece ser em C, e eu vou considerar que realmente o seja, então eu acho importante que você saiba que, em C, uma lista de parâmetros vazia significa que a função poder receber uma quantidade qualquer de argumentos de quaisquer tipos; para declarar funções que não têm parâmetros (i.e. que não devem receber argumentos), a forma de fazer é declarar a função com a palavra-chave 
void  como única coisa dentro da lista de parâmetros.  No seu programa você tem várias funções, incluindo 
main , que são declaradas com lista de parâmetros vazias, mas que provavelmente deveriam ter sido declaradas explicitamente como não podendo receber argumentos.  Desse modo, você provavelmente deveria ter dito, por exemplo, “
int main(void) ”, em vez de “
int main() ”, e o mesmo para diversas outras funções.
  • Acho que a escolha entre variáveis globais e variáveis locais não ficou muito consistente, bem como casos em que as funções separadas de 
main () recebem argumentos ou usam variáveis globais.  Essa inconsistência sugere que você pode não ter planejado o programa de antemão, e pode ter juntado partes de modo meio improvisado na medida em que foi programando.  Se esse realmente tiver sido o caso, procure planejar melhor antes de começar a codificar.
... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)