Problema Com a Função "strcmp" [RESOLVIDO]

1. Problema Com a Função "strcmp" [RESOLVIDO]

Yuri Coelho Rosário
v8yuricoelho

(usa Ubuntu)

Enviado em 08/03/2017 - 19:22h

Olá a todos.
Bem, estou criando um programa onde é necessário comparar uma string digitada pelo usuário com outra string elemento de uma struct. Para isso, utilizei a função "strcmp" da biblioteca <string.h>, vejam a sintaxe abaixo:

void separaEstilo (void)
{
char estilo[30]; // Variável de entrada para armazenar uma string e compara-la com a string "ritmo" da estrutura.
int i; // Contador.

printf ("Digite um estilo musical: ");
setbuf (stdin, NULL);
fgets (estilo, 30, stdin);

printf ("\n--- BANDAS LISTADAS NO SEGUINTE ESTILO ---\n");

for (i = 0; i < 5; i ++)
{
strcmp (estilo, vector[i].ritmo);
if (strcmp == 0)
{
printf ("\n%s", vector[i].nome);
}
}
}


Acontece que a função strcmp não está retornando 0 quando deveria, ou seja, quando as strings são iguais. Em caso de dúvidas, eis o código completo:

// Declarando bibliotecas auxiliares:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Declarando struct:

typedef struct
{
char nome[80];
char ritmo[30];
int integrantes;
int ranking;
} banda;

banda *vector;

// Declarando funções auxiliares:

void alocaBandas (banda **vector);
void leBandas (banda *vector);
void preferencia (void);
void separaEstilo (void);

// Início:

int main (void)
{
printf ("--- LISTE 5 DAS SUAS BANDAS PREFERIDAS ---\n");

alocaBandas (&vector);

leBandas (vector);

preferencia ();

separaEstilo ();

return 0; // Fim.
}

// Função responsável por alocar o vetor dinâmico que irá armazenar os elementos da struct:

void alocaBandas (banda **vector)
{
*vector = (banda *) malloc (5 * sizeof (banda));
if (*vector == NULL)
{
printf ("\aERRO DE MEMORIA!!! Programa sera encerrado.");
exit (-1);
}
}

// Função responsável por ler as entradas:

void leBandas (banda *vector)
{
int i; // Contador.

for (i = 0; i < 5; i ++)
{
printf ("\nDigite o nome da banda: ");
setbuf (stdin, NULL);
fgets (vector[i]. nome, 80, stdin);

printf ("\nDigite o tipo de musica que ela toca: ");
setbuf (stdin, NULL);
fgets (vector[i].ritmo, 80, stdin);

printf ("\nDigite o numero de integrantes da banda: ");
scanf ("%d", &vector[i].integrantes);

printf ("\nDigite a posicao no ranking dela entre as suas 5 bandas favoritas: ");;
scanf ("%d", &vector[i].ranking);

printf ("\n---------------------------------------------------------------------\n");
}
}

// Função responsável por solicitar a posição do ranking ao usuário e exibir a banda que está nesta posição:

void preferencia (void)
{
int n; // Variável de entrada para o número da banda.

printf ("\nDefina uma posicao do seu ranking: ");
scanf ("%d", &n);

n = n - 1;

printf ("\n-------------------------------------------\n");

printf ("\nNome da banda que se encontra nesta posicao: %s", vector[n].nome);
printf ("\nEstilo da banda: %s", vector[n].ritmo);
printf ("\nNumero de integrantes da banda: %d\n", vector[n].integrantes);
printf ("\nPosicao da banda no seu ranking: %d\n", vector[n].ranking);

printf ("\n-------------------------------------------\n");

printf ("\n");
}

// Função responsável por solicitar um estilo ao usuário e exibir as bandas listadas desse respectivo estilo:

void separaEstilo (void)
{
char estilo[30]; // Variável de entrada para armazenar uma string e compara-la com a string "ritmo" da estrutura.
int i; // Contador.

printf ("Digite um estilo musical: ");
setbuf (stdin, NULL);
fgets (estilo, 30, stdin);

printf ("\n--- BANDAS LISTADAS NO SEGUINTE ESTILO ---\n");

for (i = 0; i < 5; i ++)
{
strcmp (estilo, vector[i].ritmo);
if (strcmp == 0)
{
printf ("\n%s", vector[i].nome);
}
}
}




  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 08/03/2017 - 19:52h

Não olhei o segundo bloco de código, mas seguem comentários do primeiro.

É errado usar setbuf() num stream que já tenha sofrido operações de I/O, e esse certamente é o caso de stdin. Se o que você quer é garantir que o buffer de leitura está vazio (particularmente para evitar quebras de linha esquecidas) essa é A PIOR de todas as abordagens possíveis -- pior até mesmo que o infame fflush(stdin).

Minha sugestão: como logo a seguir você vai usar fgets(), possivelmente algo como “scanf(" ")” basta (um espaço no meio das aspas).

Mas o problema que faz sua comparação falhar é uma característica de fgets(): ela não extirpa o caráter de quebra de linha recebido na entrada, mas o lê e o coloca na string lida. Isso é útil para diagnóstico de situações como uma linha longa demais para o tamanho do buffer ou para fim prematuro do arquivo.

Assim sendo, após ler a linha com fgets(), você tem manualmente de remover o caráter de fim de linha, se ele estiver presente, antes de compará-lo com strings que não contenham também um '\n'.

3. Re: Problema Com a Função

Uilian Ries
uilianries

(usa Linux Mint)

Enviado em 08/03/2017 - 21:18h

Considere utilizar strncmp ao invés de strcmp.
O motivo pelo qual você deve considerar isso, é o fato de strcmp percorrer as dadas strings até encontrar o batente NULL.
Se a sua string não for terminada em NULL, strcmp continuará a percorrer até ultrapassar o buffer.
Note que strncmp não é mais segura, ela apenas possibilita prevenir tal comportamento, desde que o tamanho da string dada, seja conhecido.


#include <string.h>
#include <assert.h>

int main() {
const char name_1 [3] = {'b', 'a', 'r'};
const char name_2 [3] = {'b', 'a', 'r'};
assert(strcmp(name_1, name_2) == 0);
}

Embora as strings name_1 e name_2 sejam idênticas, strcmp irá retornar falso, pois as strings não possuem '\0' em seu final.


#include <string.h>
#include <assert.h>

int main() {
const char name_1 [3] = {'b', 'a', 'r'};
const char name_2 [3] = {'b', 'a', 'r'};
assert(strncmp(name_1, name_2, 3) == 0);
}

Agora observe que strncmp, tem uma parâmetro a mais, no final. O valor 3, indica que somente os 3 primeiros caracteres devem ser comparados,
mas se esse valor fosse populado acima do limite, teriamos o mesmo comportamento anterior.

Obs: Não pude deixar de notar que o Paulo trocou a foto do perfil :)

--
Uilian Ries
Linux Counter: 521986


4. Re: Problema Com a Função

Paulo
paulo1205

(usa Ubuntu)

Enviado em 08/03/2017 - 23:16h

Começando pelo fim, de fato eu troquei a foto do perfil, até porque a imagem anterior não era uma foto, mas um símbolo de protesto contra as coisas que me entristeciam e envergonhavam como brasileiro. (Não que agora essas coisas tenham cessado, mas os revoltados da hora têm algumas causas muito estranhas, e eu não quero ser confundido com eles.)

Com relação ao uso de strncmp(), eu concordo que é melhor no geral, mas não sei se vale para o caso particular. Eu não olhei o código todo, mas no que diz respeito à string lida com fgets(), se não ocorrer nenhuma falha de leitura, o tamanho da string lida com certeza caberá no espaço reservado de 30 bytes, logo não haverá o risco de passar do espaço alocado. Quanto às strings que são comparadas com ela, não vi como são geradas, então pode ser que precisem mesmo da segurança de strncmp(). Mas se forem strings fixas ou se forem palavras lidas anteriormente com sucesso e integralmente copiadas (incluindo o terminador) para um dicionário de palavras, possivelmente strcmp() será plenamente suficiente -- e provavelmente mais eficiente.

Note que eu destaquei que fgets() teria de executar com sucesso. Se ocorrer um erro de leitura ou uma situação de fim prematuro de dados na entrada, o conteúdo da string (não-)lida será imprevisível. Porém, a forma correta de evitar a comparação desse conteúdo imprevisível é testar o valor de retorno de fgets(), não tentar a sorte com uma string indefinida e colocar uma limitação de tamanho como salvaguarda. E se a string indefinida for menor que o tamanho limite, e ainda coincidir com uma das strings com as quais é comparada?

Aliás, todas as operações de leitura, em qualquer programa, deveriam sempre ser verificadas quanto a falhas.

Em tempo: no seu exemplo de comparação, é importante notar que aqueles arrays de caracteres não são strings. Strings em C são convencionalmente e necessariamente arrays de caracteres de tamanho finito e contendo um caráter nulo, que indica onde a string termina. Por outro lado, um array contendo um byte nulo não é necessariamente uma string (por exemplo, se eu quiser ter um vetor de caracteres individuais, ou um array de bytes).


5. Re: Problema Com a Função "strcmp" [RESOLVIDO]

Uilian Ries
uilianries

(usa Linux Mint)

Enviado em 09/03/2017 - 09:13h

Bem lembrado quanto ao conteúdo dos arrays que utilizei no exemplo, deixei passar batido esse detalhe.

Como eu disse, considere utilizar e não colocar como regra, strncmp não é mais segura, apenas dá a oportunidade de limitar a comparação.

Agora revisando o post, percebi que eu não havia considerado o fato de estar sendo utilizado fgets, até então eu pensei que fosse utilizado o velho e inseguro gets.

Nesse caso em especifico, seria mais prudente validar o retorno de fgets e strcmp funcionaria perfeitamente, como tu já comentaste anteriormente.


6. Re: Problema Com a Função "strcmp" [RESOLVIDO]

Yuri Coelho Rosário
v8yuricoelho

(usa Ubuntu)

Enviado em 09/03/2017 - 17:08h

Entendi o problema, já substitui o "setbuf" por "scanf (" ")". Bem, para solucionar a questão o Paulo propôs o seguinte:

Assim sendo, após ler a linha com fgets(), você tem manualmente de remover o caráter de fim de linha, se ele estiver presente, antes de compará-lo com strings que não contenham também um '\n'.


Como posso fazer isto, Paulo?


7. Re: Problema Com a Função

Yuri Coelho Rosário
v8yuricoelho

(usa Ubuntu)

Enviado em 10/03/2017 - 16:33h

Assim sendo, após ler a linha com fgets(), você tem manualmente de remover o caráter de fim de linha, se ele estiver presente, antes de compará-lo com strings que não contenham também um '\n'.


Paulo, eu tentei com a seguinte sintaxe:

string [strlen (string) - 1] = '\n'; 

O problema persiste.


8. Re: Problema Com a Função

Paulo
paulo1205

(usa Ubuntu)

Enviado em 10/03/2017 - 17:41h

char *p=str+strlen(str)-1;
if(p>=str && *p=='\n')
*p='\0';



9. Re: Problema Com a Função "strcmp" [RESOLVIDO]

Yuri Coelho Rosário
v8yuricoelho

(usa Ubuntu)

Enviado em 10/03/2017 - 18:06h

paulo1205 escreveu:

char *p=str+strlen(str)-1;
if(p>=str && *p=='\n')
*p='\0';


Não funcionou :(, não sei o que faço mais.




10. Re: Problema Com a Função

Paulo
paulo1205

(usa Ubuntu)

Enviado em 10/03/2017 - 18:27h

Agora que eu reparei:

		strcmp (estilo, vector[i].ritmo);
if (strcmp == 0)


Ainda está desse jeito? Se estiver, está errado. O que você faz é chamar a função que faz a comparação, mas descartando o valor de retorno, e depois você compara o endereço da função (que é o que se obtém quando se usa o nome da função sem os parênteses que indicariam uma chamada de função) com zero. Como a função tem um endereço não-nulo, essa comparação é sempre falsa.

O certo seria o seguinte.

		if(strcmp(estilo, vector[i].ritmo)==0) 


Eu sempre recomendo que se usem as opções de diagnóstico do compilador durante a compilação. Veja o que acontece se eu tentar compilar um trecho semelhante ao que você havia escrito empregando as opções “-Wall -Werror -pedantic-errors -O2” do GCC (chamei o arquivo fonte de w.c, e coloquei o código dentro de uma função chamada f()).

$ gcc -Wall -Werror -pedantic-errors -O2 -c w.c
w.c: In function ‘f’:
w.c:11:13: error: the comparison will always evaluate as ‘false’ for the address of ‘strcmp’ will never be NULL [-Werror=address]
if (strcmp == 0)
^
cc1: all warnings being treated as errors



11. Re: Problema Com a Função "strcmp" [RESOLVIDO]

Yuri Coelho Rosário
v8yuricoelho

(usa Ubuntu)

Enviado em 10/03/2017 - 21:04h

paulo1205 escreveu:

Agora que eu reparei:

		strcmp (estilo, vector[i].ritmo);
if (strcmp == 0)


Ainda está desse jeito? Se estiver, está errado. O que você faz é chamar a função que faz a comparação, mas descartando o valor de retorno, e depois você compara o endereço da função (que é o que se obtém quando se usa o nome da função sem os parênteses que indicariam uma chamada de função) com zero. Como a função tem um endereço não-nulo, essa comparação é sempre falsa.

O certo seria o seguinte.

		if(strcmp(estilo, vector[i].ritmo)==0) 


Eu sempre recomendo que se usem as opções de diagnóstico do compilador durante a compilação. Veja o que acontece se eu tentar compilar um trecho semelhante ao que você havia escrito empregando as opções “-Wall -Werror -pedantic-errors -O2” do GCC (chamei o arquivo fonte de w.c, e coloquei o código dentro de uma função chamada f()).

$ gcc -Wall -Werror -pedantic-errors -O2 -c w.c
w.c: In function ‘f’:
w.c:11:13: error: the comparison will always evaluate as ‘false’ for the address of ‘strcmp’ will never be NULL [-Werror=address]
if (strcmp == 0)
^
cc1: all warnings being treated as errors


Agora foi, finalmente! Obrigado pelo auxílio, mais uma vez.








Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts