The parameter valor
of function validaData
is passed by value. So when you do scanf("%d", &valor);
, you will change only the value of the local variable valor
, but this will not alter the content of paciente[i].dataNascimento.dia
, nor of paciente[i].dataNascimento.mes
nor paciente[i].dataNascimento.ano
. The right thing would be validaData
return the new value read.
Another detail is that a function called validaData
should, given her name do only the validation. But she is not doing this, she is doing the rereading if the date is incorrect. A more robust approach would be to do the function to read the date and reread as many times as necessary until the informed date is valid.
There is also a validation issue that your code accepts invalid dates such as 31/04/2020 or 29/02/2021, as not every month has days 29, 30 and 31.
In addition, there is the case that the user responds xyz
when the day is asked, and it will confuse the scanf
. Since this is a college project, it is best to read what the user type without making any kind of presumption about what he type and then try to build a date with it.
Another thing is that the fflush(stdin)
only works in windows. See that one and that one answers explaining how to solve.
In fact, the function scanf
It’s an old-fashioned, hard-to-use function and its biggest problems stem from it. This function should no longer be used. See here explanations about why not to use it and what to use in its place.
Therefore, I suggest this function to read a date:
// Autor: Victor Stafusa - /users/132
#include <stdio.h>
typedef struct {
int dia;
int mes;
int ano;
} data;
// Veja mais sobre isso nesses links:
// /a/231882/132
// /a/96012/132
#if defined(__MINGW32__) || defined(_MSC_VER)
#define limpar_input() fflush(stdin)
#else
#include <stdio_ext.h>
#define limpar_input() __fpurge(stdin)
#endif
// Monta um número de 2 dígitos a partir de 2 chars.
#define numero2(a, b) \
( (a - '0') * 10 \
+ (b - '0'))
// Monta um número de 4 dígitos a partir de 4 chars.
#define numero4(a, b, c, d) \
( (a - '0') * 1000 \
+ (b - '0') * 100 \
+ (c - '0') * 10 \
+ (d - '0'))
// Verifica se a string dada tem um formato NN/NN/NNNN.
// Mas não verifica nada após o 10o caractere.
int validarFormatoData(const char *digitado) {
for (int i = 0; i < 10; i++) {
char c = digitado[i];
if ((i == 2 || i == 5) ? c != '/' : (c < '0' || c > '9')) return 0;
}
return 1;
}
// Descobre quantos dias há no mês de um determinado ano.
int diasNoMes(int mes, int ano) {
// São bissextos os anos divisíveis por 4,
// com exceção dos que terminam com 00 e não são divisíveis por 400.
int bissexto = (ano % 4 == 0 && ano % 100 != 0) || ano % 400 == 0;
// Tabela com os dias de cada mês.
// Observe que fevereiro varia dependendo se o ano for ou não bissexto.
// Observe que a posição zero não é usada porque não há mês zero.
int dias[] = {0, 31, bissexto ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
return dias[mes];
}
// Descobre se a data do meio de fato está entre a data mínima e a máxima.
int comparaData3(const data *min, const data *meio, const data *max) {
// Monta um inteiro para cada data na forma AAAAMMDD.
int a = min->ano * 10000 + min->mes * 100 + min->dia;
int b = meio->ano * 10000 + meio->mes * 100 + meio->dia;
int c = max->ano * 10000 + max->mes * 100 + max->dia;
// Como as datas foram reduzidos a números inteiros,
// fica fácil saber se estão na ordem correta.
return a <= b && b <= c;
}
// Lê uma data do usuário no formato DD/MM/AAAA.
// Insiste até que uma data válida seja digitada.
// Inclui uma mensagem que pede para o usuário digitar alguma coisa.
// Inclui uma mensagem de erro para o caso de o usuário digitar algo inválido.
// Inclui as datas mínimas e máximas permitidas.
data lerData(const char *mensagem, const char *erro, const data *min, const data *max) {
char digitado[12];
int primeiraVez = 1;
// Repete quantas vezes for necessário, a menos que o return seja alcançado.
while (1) {
// Se não for a primeira vez, então é porque a vez anterior deu errado e uma
// mensagem de erro deve ser mostrada.
if (primeiraVez) {
primeiraVez = 0;
} else {
printf("%s\n", erro);
}
// Mostra a mensagem e lê o que o usuário digitar.
printf("%s", mensagem);
fgets(digitado, 12, stdin);
limpar_input(); // Limpa o input se ficou sujeira nele.
// Se os 10 primeiros caracteres não estiverem no formato NN/NN/NNNN,
// então a data é inválida. E se houver algo depois desse NN/NN/NNNN, ela
// também é.
if (!validarFormatoData(digitado) || digitado[10] != '\n') continue;
// Se chegou aqui, então a data tem um formato válido, o que ainda não
// necessariamente significa que ela é válida. Nesse ponto já podemos
// obter o número do dia, do mês e do ano ao montar inteiros a partir dos
// caracteres digitados.
int dia = numero2(digitado[0], digitado[1]);
int mes = numero2(digitado[3], digitado[4]);
int ano = numero4(digitado[6], digitado[7], digitado[8], digitado[9]);
// Verifica se o dia e o mês são válidos.
// Inclusive, graças a função diasNoMes, verifica se o mês tem 30 ou 31
// dias ou sse for fevereiro, se o ano é bissexto ou não.
if (mes < 1 || mes > 12 || dia < 1 || dia > diasNoMes(mes, ano)) continue;
// A data é válida. Podemos construí-la.
data d = {dia, mes, ano};
// Verifica se que a data montada está entre a data mínima e a máxima.
if (!comparaData3(min, &d, max)) continue;
// Se chegou até aqui, a data está ok.
return d;
};
}
// Teste do código todo.
// Pede para digitar uma data, insistindo até que uma data válida seja digitada
// e então exibe essa data.
int main(void) {
data min = {1, 1, 1900};
data max = {31, 12, 2020};
data d = lerData(
"Digite a data de nascimento (DD/MM/AAAA): ",
"Essa data não estava certa. Vamos tentar novamente.",
&min,
&max);
printf("A data que você digitou foi %d/%d/%d.", d.dia, d.mes, d.ano);
return 0;
}
The comments in the code explain what he is doing. Basically, it will give you a function lerData
that will ask the user to enter a date and will insist until the user enters a valid date.
See here the code working in repl.it.
Very good! 133 lines of code, month size validation, leap years, etc. Whenever I see these solutions to university problems my appreciation for modern Apis from Date/Time is renewed.
– Anthony Accioly
@Anthonyaccioly has the
struct tm
which would be the best type of data already prefabricated for this with several ready-made library functions that manipulate it. However, using it correctly also brings its challenges and complications. It was because of so much living with this kind of situation using so much low-level stuff in C and having to reinvent the square wheel every day, that I ended up specializing in Java and now in Python.– Victor Stafusa
@Victorstafusa Bro, thank you very much, helped me a lot.
– Rodrigo Diniz Monteiro