Minha estrutura de repetição não esta funcionando, alguém pode ajudar ?

1. Minha estrutura de repetição não esta funcionando, alguém pode ajudar ?

Daniel Vinicius
Daniel2003

(usa Linux Mint)

Enviado em 04/11/2022 - 14:04h

Boa tarde pessoal,

O código abaixo tem como objetivo mostrar qual quadrante que esta os determinados números em o usuário digitar, o meu problema esta no final do código, quando digitado "S" ou "s" o código não roda novamente.

Segue código:

#include <stdio.h>

int main() {

float x,y;
char d;

printf("Digite o primeiro numero x:");
scanf("%f", &x);
printf("Digite o segundo numero y:");
scanf("%f", &y);

do {
if (( x > 0) && ( y > 0)) {

printf("\n Os numeros(%.2f, %.2f), encontra-se no primeiro quadrante\n", x, y);

}
else if ( ( x > -4) && ( y > 0) ) {

printf("\n Os numeros(%f, %f), encontra-se no segundo quadrante\n", x, y);

}
else if ( ( x < 0 ) && ( y < 0 )){

printf("\nOs numeros(%.2f, %.2f), encontra-se no terceiro quadrante\n", x, y);

}
else {

printf("\nOs numeros(%.2f, %.2f, encontra-se no quarto quadrante\n", x, y);

}

printf("\nDeseja continuar com outra operacao (S) ou (N):\n");
scanf("%c", &d);

} while( (d == "S") || (d =="s") );
}



  


2. Re: Minha estrutura de repetição não esta funcionando, alguém pode ajudar ?

Perfil removido
removido

(usa Nenhuma)

Enviado em 04/11/2022 - 15:32h

isso parece ser um problema de buffer
use "__fpurge(stdin);" antes do ultimo scanf
ou use "d = getchar();" no lugar de scanf
e você está usando aspas duplas no if para indicar um char sendo que o certo é usar aspas simples


3. Re: Minha estrutura de repetição não esta funcionando, alguém pode ajudar ?

Paulo
paulo1205

(usa Ubuntu)

Enviado em 05/11/2022 - 05:18h

Daniel2003 escreveu:

Boa tarde pessoal,

O código abaixo tem como objetivo mostrar qual quadrante que esta os determinados números em o usuário digitar, o meu problema esta no final do código, quando digitado "S" ou "s" o código não roda novamente.

Segue código:

#include <stdio.h>

int main() {

float x,y;
char d;

printf("Digite o primeiro numero x:");
scanf("%f", &x);
printf("Digite o segundo numero y:");
scanf("%f", &y);

do {
if (( x > 0) && ( y > 0)) {

printf("\n Os numeros(%.2f, %.2f), encontra-se no primeiro quadrante\n", x, y);

}
else if ( ( x > -4) && ( y > 0) ) {


Por que aqui você está comparando x com -4?


printf("\n Os numeros(%f, %f), encontra-se no segundo quadrante\n", x, y);

}
else if ( ( x < 0 ) && ( y < 0 )){

printf("\nOs numeros(%.2f, %.2f), encontra-se no terceiro quadrante\n", x, y);

}
else {


Eu diria que este else pode imprimir informações erradas. Por exemplo se x valer qualquer coisa negativa, e y for exatamente zero, eu não sei se a coordenada estará no segundo ou no terceiro quadrante, mas com certeza o quadrante não será o quarto. Semelhantemente, se x for exatamente zero e y for positivo, eu não sei se a coordenada pode ser chamada de no primeiro ou no segundo quadrante, mas com certeza não será do quarto quadrante.

Recomendo rever as condições testadas acima. Infelizmente eu não lembro como devem ser considerados os casos em que um dos componentes do par ordenado é igual a zero. Segundo este site (https://brasilescola.uol.com.br/matematica/sistema-cartesiano-ortogonal.htm) os eixos das abcissas (y=0) e o eixo das ordenadas (x=0) não fazem parte de nenhum quadrante. E creio ainda que a origem, que faz parte dos dois eixos (x=0 e y=0 ao mesmo tempo) é um caso ainda mias especial.


printf("\nOs numeros(%.2f, %.2f, encontra-se no quarto quadrante\n", x, y);

}

printf("\nDeseja continuar com outra operacao (S) ou (N):\n");
scanf("%c", &d);


A função scanf() (e suas correlatas fscanf(), sscanf() etc.) é uma das mais complexas da biblioteca padrão de funções do C. Eu recomendo que você leia a documentação da função com calma.

Resumidamente. algumas conversões, tais como as que leem números (%d, %i, %x, %f, entre outras) ou blocos de caracteres que não são de espaço (%s e relacionadas) fazem o descarte automático de espaços (espaço simples, tabulações e quebras de linha) que porventura antecedam o valor que provavelmente se quer ler, inciando a conversão propriamente dita assim que aparecer um caráter que não seja espaço, e interrompendo a leitura assim que aparecer novamente algum espaço ou caráter inválido no contexto da conversão (ou, caso você tenha especificado uma largura máxima de caracteres a serem convertidos, quando esse número de caracteres tiver sido consumido pela conversão). É importante lembrar que o eventual caráter que fez a conversão parar (ou aquele que vem após se ter alcançado a largura máxima da conversão) não são consumidos pela conversão, mas ficarão presentes no fluxo de leitura para a próxima operação de leitura ou conversão que porventura for solicitada pelo programa.

Já as conversões que leem caracteres individuais (como %c) ou blocos de texto que potencialmente conterão espaços (%[ e correlatas) não fazem este mesmo descarte automático, justamente para que você tenha a possibilidade de extrair espaços que talvez lhe interessem como parte de dados relevantes de entrada.

A função permite que você indique que quer provocar manualmente o descarte de uma quantidade qualquer de espaços em branco. Para tanto, basta você colocar um espaço em branco como parte da string de formatação que você passa como parâmetro à função. Esse recurso normalmente é usando antes de alguma conversão ou outro caráter não-espaço fixo que tenha de aparecer na entrada.


Quando, durante uma operação de leitura qualquer no terminal, você aperta a tecla Enter (ou Return, para quem usa Apple), isso provoca o aparecimento de um caráter de quebra de linha no fluxo de entrada. Esse caráter de quebra de linha é considerado na categoria de espaços em branco.

Se você tiver, por exemplo, um programa que, usando scanf() com a conversão "%d", interage com o usuário pedindo, a cada interação, que ele digite um valor inteiro, e ele digitar, em cada uma dessas interações, um valor inteiro qualquer e imediatamente em seguida apertar Enter, cada uma das operações de leitura vai começar fazendo o descarte automático de espaços que sobraram da interação anterior, vai converter o valor digitado pelo usuário, e vai deixar a quebra de linha correspondente à tecla Enter para a próxima interação.

Esse mesmo programa, após ler todos os valores numéricos que lhe interessam, tem de lembrar que para a próxima operação de leitura provavelmente haverá uma quebra de linha pendente no fluxo de entrada. Se esse espaço não interessar e a próxima leitura não tiver descarte automático, o programador terá de providenciar o descarte explícito.


} while( (d == "S") || (d =="s") );


Conforme já indicado por um colega nosso em outra postagem, as comparações acima estão erradas, porque você está comparando um caráter (o valor de d) com valores de ponteiros para caracteres (i.e. os endereços em si, não o conteúdo que está armazenado nesses endereços).

Lembre sempre que caracteres individuais são representados entre apóstrofos (por exemplo: 's'), ao passo que aspas são usadas para indicar sequências de caracteres, e que, quando essas sequências aparecem no programa, seu valor tem o tipo de dados de ponteiro para caracteres, usado para representar endereços de memória, não o conteúdo residente nesses endereços.


Esse tipo de erro, de comparar dados de tipos distintos de uma forma que não faz sentido, costuma ser indicado por compiladores modernos, desde que você não suprima nem ignore as mensagens que o compilador pode lhe enviar ao encontrar situações assim.

Caso você esteja usando um compilador semelhante ao GCC ou CLang, eu recomendo que você use sempre as opções de compilação -Wextra -Werror -pedantic-errors -O2, assim explicadas:

  • -Wextra: o compilador vai ser extremamente criterioso no diagnóstico e alerta sobre código suspeito.
  • -Werror: o compilador vai tratar alertas como erros (forçando você a corrigir as situações causadoras).
  • -pedantic-errors: o compilador vai forçar a compilação a aderir mais rigidamente àquilo que o padrão da linguagem permite, limitando o uso de extensões específicas do compilador e construções especificamente vedadas no padrão, mesmo que o compilador de outro modo as aceitasse.
  • -O2: habilita o segundo nível de otimização de código pelo compilador. Além de geralmente produzir código mais eficiente, tal eficiência frequentemente depende de análise do ciclo de vida de variáveis, e isso permite ao compilador identificar e alertar sobre situações em que uma variável pode acabar sendo usada sem ter sido previamente inicializada. Habilita também a identificação e o alerta sobre trechos de código que podem nunca ser alcançados (por exemplo: um bloco referente a if, while ou for cuja condição de controle seja sempre falsa).


Tendo dito isso, note que a lógica de avaliação acima (mesmo depois de você corrigir de string para caráter individual) não reflete a pergunta que é apresentada ao usuário. Esta dá a entender que o programa só aceitará S ou N (ou s ou n) como resposta, mas na prática você aceita qualquer valor, mas só repete a execução caso a resposta seja S ou s, interrompendo o programa com qualquer outra coisa que tenha sido digitada.

Minha sugestão é que ou você mude a pergunta (por exemplo, para algo como “Digite 's' para executar novamente, ou qualquer outra coisa para sair.)”) ou que mantenha a pergunta, mas mude a lógica de validação da resposta, a fim de limitá-la apenas às alternativas que você indicou como respostas válidas, ciente de que essa eventual mudança pode implicar ter de repetir a pergunta, caso o usuário não responda de modo adequado.

} 


Um comentário geral sobre o programa é que você construiu um programa interativo, mas não se preocupou muito com a validação dos dados lidos em cada interação com o usuário. Tudo bem que, nesta altura do seu aprendizado, o objetivo maior é treinar lógica e construção de algoritmos, mas é bom você já ir se acostumando com a dura realidade de que usuários cometem erros, e às vezes são deliberadamente mal-intencionados.

Considere o seguinte caso: com o seu programa do jeito como está imagine que, na primeira pergunta, o usuário responda digitando S seguindo da tecla Enter. O que vai acontecer? Eu não executei seu código aqui, mas eis o que a análise dele mostra (e fique à vontade para me corrigir, caso eu tenha me equivocado no que vai abaixo).

  • Por terem sido declarados como variáveis automáticas dentro de main, os valores iniciais de x e de y são desconhecidos.
  • A primeira operação de leitura tenta converte um valor e armazená-lo em x, mas como o usuário digitou um caráter inválido, esse caráter vai interromper a leitura sem que o valor de x seja alterado, e o caráter inválido vai permanecer no fluxo de entrada.
  • A segunda operação de leitura vai falhar pelo mesmo motivo, deixando y com o mesmo valor desconhecido que tinha antes, e o caráter inválido vai permanecer no fluxo de entrada.
  • A lógica de diagnóstico de quadrantes (que tem de ser corrigida, lembra?) vai imprimir um diagnóstico completamente espúrio, com base em dados numéricos de valores indeterminados.
  • A leitura com a conversão %c vai finalmente consumir a letra S digitada pelo usuário. A quebra de linha vai permanecer no fluxo de leitura.
  • Ao comparar o caráter lido com endereços de memória, provavelmente (certeza quase absoluta, pois muito dificilmente alguma das strings vai residir num endereço de memória 83, que é o valor inteiro correspondente ao caráter S, de acordo com o ASCII) o resultado da comparação será falso, e o programa será encerrado.

A função scanf() lhe fornece meios de perceber se os dados recebidos atendem ou não aquilo que você gostaria de ter. O principal mecanismo de validação de conversões bem sucedidas é o valor retornado pela função. Convém que você sempre o teste, para confirmar se alguma das conversões (pois pode haver mais de uma, num caso geral) pedidas na string de formatação suas conversões pode ter falhado. Além disso, há maniras de embutir na string formatação elementos que forçam a presença (ou a ausência) de caracteres específicos, bem como de lhe informar a contagem de caracteres consumidos até o momento.

Por exemplo, se eu quiser que o usuário digite apenas um valor numérico de ponto flutuante, sem ser precedido nem sucedido de nenhum outro caráter (nem mesmo espaços), a não ser uma quebra de linha imediatamente o número, eis uma possível maneira de fazer (ainda não perfeita, mas só para dar uma ideia). Se parece trabalhosa, é porque é complicado mesmo validar dados de entrada, especialmente quando o mesmo fluxo pode ser usado para ler diferentes elementos.
double x;
do {
int r; // Resultado de scanf().
int a, b, c; // Contadores de caracteres consumidos.

c=b=a=0; // Inicializa os contadores com zero antes da leitura.

printf("Digite o valor da abcissa: ");
r=scanf(" %n%lf%n%*1[\n]%n", &a, &x, &b, &c);
if(r==EOF){ // Erro de entrada, provavelmente irrecuperável.
fprintf(stderr, "ERRO FATAL: não é possível ler dados da entrada padrão.\n");
exit(1);
}
if(
r<1 // Conversão "%lf" falhou (pode ter sido digitada uma letra, por exemplo).
||
a>0 // Houve espaços antes do número, não quero que o usuário tenha essa liberdade! ;)
||
c<b // Número não foi imediatamente seguido por uma quebra de linha (portanto a conversão com atribuição suprimida "%*1[\n]" falhou, e a função retornou imediatamente, sem alterar o valor de c.
){
fprintf(stderr, "ERRO: favor digitar apenas o valor numérico, imediatamente seguido de <Enter>.\n\n");

// Verifica se tem de remover caracteres na entrada antes de tentar ler novamente e, se for o caso, efetua a remoção.
if(c==0){
b=a=0;
r=scanf("%*[^\n]%n%*1[\n]%n", &a, &b);
if(r==EOF || b==0){
fprintf(stderr, "ERRO FATAL: não é possível ler dados da entrada padrão.\n");
exit(1);
}
}
}
} while(r!=1);


Note que %n não é considerada uma conversão, embora provoque atribuição de valor de outro argumento, e portanto não afeta o valor retornado por scanf() (no trecho acima, os valores retornados nas duas chamadas são atribuídos a r). Também conversões com atribuição de valor suprimida (i.e., aquelas com um asterisco após o sinal porcento ("%*")) também não incrementam o valor de retorno de scanf().

De novo a dica: leia a documentação de scanf().

Algo que pode ser feito para não ter de repetir essa complicação toda a cada leitura é encapsulá-la numa função que receba a mensagem a ser exibida e um ponteiro para a variável a ser modificada, e cuide, dentro de si, de fazer a validação, entregando ao usuário apenas a informação de se a leitura foi bem sucedida ou não.
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


bool read_double(const char *message, double *p_value, size_t max_retries, FILE *input){
bool success=false; // Presume falha.

char *line=NULL;
size_t linesize=0;
size_t n_attempts=0;
do {
if(message && message[0]){
fputs(message, stdout);
fflush(stdout);
}

ssize_t linelen;
if((linelen=getline(&line, &linesize, input))==-1)
break; // Erro de leitura. Não adianta nem tentar novamente.

int r, a=0, b=0;
r=sscanf(line, " %n%lf%n", &a, p_value, &b);
++n_attempts;
success=(r==1 && a==0 && b>0 && line[b]=='\n');
if(!success){
errno=EINVAL;
fprintf(stderr, "ERRO: O texto “%.*s” is não é um valor de ponto flutuante válido", (int)(linelen-1), line);
if(max_retries>0)
fprintf(stderr, " (tentativa %zu de %zu)", n_attempts, max_retries);
fputs(".\n", stderr);
}
} while(!success && (max_retries==0 || n_attempts<max_retries));

free(line);

return success;
}

static const char *positions[]={
"no 3º quadrante",
"na parte negativa do eixo das ordenadas",
"no 4º quadrante",
"na parte negativa do eixo das abcissas",
"na origem",
"na parte positiva do eixo das abcissas",
"no 2º quadrante",
"na parte positiva do eixo das ordenadas",
"no 1º quadrante"
};

int main(void){
double x, y;
if(!read_double("Entre o valor da abcissa: ", &x, 0, stdin) || !read_double("Entre o valor da ordenada: ", &y, 0, stdin)){
fprintf(
stderr, "\nERRO FATAL: ao ler valores do par ordenado: %s.\n",
feof(stdin)? "fim prematuro dos dados": strerror(errno)
);
return 1;
}
printf("\nO par ordenado (%f,%f) está %s.\n", x, y, positions[(x>=0)+(x>0)+3*((y>=0)+(y>0))]);
}



... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


4. Re: Minha estrutura de repetição não esta funcionando, alguém pode ajudar ?

Paulo
paulo1205

(usa Ubuntu)

Enviado em 06/11/2022 - 10:38h

Brinquei mais um pouco com o programa. Segue para quem se interessar.
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


// Buffer dinâmico para uso com a função getline() (POSIX.1-2008), usada em read_double() e main().
static char *line=NULL;
static size_t linesize=0;


bool strict_strtod(const char *str, double *p_value){
char *eon;
return str && (isdigit(str[0]) || (str[0] && strchr(".+-IiNn", str[0]))) && (*p_value=strtod(str, &eon), *eon=='\0');
}

bool read_double(const char *message, double *p_value, size_t max_retries, FILE *input){
bool success=false; // Presume falha.
size_t n_attempts=0;
do {
if(message && message[0]){
fputs(message, stdout);
fflush(stdout);
}

ssize_t linelen=getline(&line, &linesize, input);
if(linelen==-1)
break; // Erro de leitura. Não adianta nem tentar novamente.
if(line[linelen-1]=='\n')
line[--linelen]='\0'; // Remove o caráter de quebra de linha, caso exista.

++n_attempts;

if((success=strict_strtod(line, p_value)))
break; // Converteu com sucesso: pode interromper o laço de repetição de leituras.

errno=EINVAL;
fprintf(stderr, "ERRO: O texto “%s” is não é um valor de ponto flutuante válido", line);
if(max_retries>0)
fprintf(stderr, " (tentativa %zu de %zu)", n_attempts, max_retries);
fputs(".\n", stderr);
} while(max_retries==0 || n_attempts<max_retries);

return success;
}

const char *position(double x, double y){
static const char *const positions[][3]={
{ "no 2º quadrante", "na parte positiva do eixo das ordenadas", "no 1º quadrante" },
{ "na parte negativa do eixo das abcissas", "na origem", "na parte positiva do eixo das abcissas" },
{ "no 3º quadrante", "na parte negativa do eixo das ordenadas", "no 4º quadrante" }
};
return positions[(y<=0)+(y<0)][(x>=0)+(x>0)];
}

const char *errorcause(FILE *f, int errnumber){
return feof(f)? "fim prematuro dos dados": strerror(errnumber);
}


int main(int argc, char **argv){
double x, y;
unsigned n_pairs=0, n_errors=0;

if(argc<2){
// Modo interativo.
bool again;
do {
if(
!read_double("Entre o valor da abcissa: ", &x, 0, stdin) ||
!read_double("Entre o valor da ordenada: ", &y, 0, stdin)
){
fprintf(
stderr, "\nERRO FATAL: ao ler valores do par ordenado: %s.\n",
errorcause(stdin, errno)
);
++n_errors;
break;
}
printf("\nO par ordenado (%f,%f) está %s.\n", x, y, position(x, y));
++n_pairs;
do {
fputs("\nDeseja testar um novo par ordenado (S/N)? ", stdout);
if(getline(&line, &linesize, stdin)==-1){
// Erro de leitura.
fprintf(
stderr, "\nERRO FATAL: Falha de leitura: %s.\n",
errorcause(stdin, errno)
);
++n_errors;
again=false;
break;
}
else {
char c=tolower((unsigned char)line[0]);
if((again=(c=='s')) || c=='n')
break; // Resposta válida: interrompe laço de repetição da leitura.
fputs("Resposta inválida.\n", stderr);
}
} while(1);
} while(again);
}
else{
// Pega coordenadas de cada par ordenado a partir de argumentos da linha de comando.
int n, m;
for(n=1, m=2; m<argc; n+=2, m+=2){
if(++n_pairs>1)
putchar('\n'); // Do segundo par odenado em diante, pula uma linha para separar da informação do par anterior.

bool goodpair=true;
if(!strict_strtod(argv[n], &x)){
goodpair=false;
fprintf(stderr, "O valor “%s” não é válido como abcissa do %dº par ordenando.\n", argv[n], n_pairs);
}
if(!strict_strtod(argv[m], &y)){
goodpair=false;
fprintf(stderr, "O valor “%s” não é válido como ordenada do %dº par ordenado.\n", argv[m], n_pairs);
}
if(goodpair)
printf("O par ordenado (%f,%f) está %s.\n", x, y, position(x, y));
else
++n_errors;
}
if(n<argc){
if(++n_pairs>1)
putchar('\n');
if(!strict_strtod(argv[n], &x))
fprintf(stderr, "O valor “%s” não é válido como abcissa do %dº par ordenando.\n", argv[n], n_pairs);
fprintf(stderr, "Falta o valor da ordenada do %dº par ordenado.\n", n_pairs);
++n_errors;
}
}

return n_errors==0? 0: 1+(n_pairs>n_errors); // 0: sucesso total; 1: falha total; 2: sucesso parcial (alguns pares OK, mas com erros).
}


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts