O número de matrícula dos alunos de uma universidade é composto por quatro números no padrão aaaa.s.cc.iii (onde: aaaa = ano de ingresso; \boldsymbol{s}= semestre de ingresso, que pode ser 1 ou 2 ; cc = número do curso, que pode ser qualquer valor; e iii = identificador do discente, também qualquer valor). (a) Defina um tipo (typedef) denominado matricula que seja composto de um registro (struct) contendo em cada campo um dos números que formam o número de matrícula. (b) Crie um programa que peça para o usuário digitar um número de matrícula no formato padronizado e verifique se os números digitados são aceitáveis. Caso o número de matrícula esteja com problemas, exiba mensagens identificando qual (ou quais) dos números digitados apresenta(m) problemas; caso contrário o número de matrícula deve ser atribuído a uma variável do tipo matricula (que você definiu no item a) e deve ser exibido na tela o número de matrícula o usuário, a partir da variável tipo matricula criada.

Passo 1

O enunciado pede duas coisas:
  1. Definir um tipo (typedef) chamado matricula que seja composto de um registro (struct) contendo, em cada campo, um dos números que formam o número de matrícula no formato aaaa.s.cc.iii.
  2. Escrever um programa que peça ao usuário para digitar um número de matrícula no formato aaaa.s.cc.iii, verifique se os números digitados são aceitáveis e:
    • se houver problema(s), exiba mensagens indicando qual(is) campo(s) estão inválidos;
    • se estiver tudo certo, atribua o valor a uma variável do tipo matricula e exiba na tela a matrícula a partir dessa variável.
Informações relevantes dadas no enunciado:
  • O formato é aaaa.s.cc.iii, onde:
    • aaaa = ano de ingresso (quatro dígitos),
    • s = semestre de ingresso (pode ser 1 ou 2),
    • cc = número do curso (o enunciado diz “pode ser qualquer valor” — interpretarei como um número inteiro com uma ou mais casas),
    • iii = identificador do discente (também “qualquer valor” — interpretarei como um número inteiro com uma ou mais casas).
  • O programa deve verificar a aceitabilidade (validação) de cada campo e informar erros específicos.

Passo 2

Decisão de método (explicando pedagogicamente):
  • Para validar uma string no formato aaaa.s.cc.iii de forma robusta em C, o melhor caminho para um programa de prova é:
    1. Ler a linha inteira como string (com fgets) para evitar problemas com espaços e quebras de linha.
    2. Separar a string nos quatro tokens usando o caractere '.' como separador (função strtok), porque a estrutura do formato usa pontos fixos.
    3. Para cada token, verificar que todos os caracteres são dígitos (usar isdigit) e checar comprimentos esperados: exigir que o token do ano tenha exatamente 4 dígitos, o token do semestre exatamente 1 dígito, e os tokens de curso e identificador tenham pelo menos 1 dígito (como o enunciado permitiu “qualquer valor”).
    4. Converter os tokens para inteiros com strtol (ou atoi) e, em seguida, validar semanticamente (por exemplo, semestre deve ser 1 ou 2; ano razoável — aqui vamos exigir que seja um número de 4 dígitos entre 1000 e 9999 para evitar entradas estranhas).
    5. Se tudo estiver OK, atribuir aos campos da struct e imprimir a matrícula a partir da struct.
  • Por que esse método? Porque é simples, fácil de implementar sem bibliotecas externas e confiável para provas: não depende de expressões regulares, nem de comportamento complexo de scanf. Além disso, é fácil explicar e reproduzir sem calculadora.

Passo 3

Definição do tipo matricula (a parte (a) pedida). Explicação do que cada campo representa e por que escolhi esses tipos:
  • Em C, podemos representar os quatro números como inteiros (int) dentro de uma struct. Cada campo da struct conterá um dos números: ano, semestre, curso, id.
  • Usei int porque são números inteiros e isso facilita operações e comparações (por exemplo, testar se semestre == 1 ou 2). Na impressão, formataremos ano com 4 dígitos (%04d) para manter o formato aaaa.
  • Aqui está a definição (este trecho corresponde à resposta do item (a)):
typedef struct {
    int ano;       /* ano de ingresso (ex.: 2023) */
    int semestre;  /* semestre de ingresso (1 ou 2) */
    int curso;     /* número do curso (qualquer inteiro não negativo) */
    int id;        /* identificador do discente (qualquer inteiro não negativo) */
} matricula;

Passo 4

Como ler e quebrar a string (detalhes passo a passo e mini-aula):
  • Ler a linha inteira: fgets(buffer, sizeof(buffer), stdin); — isso lê até a nova linha e evita overflow se usarmos um buffer com tamanho razoável (ex.: 100).
  • Remover o \n final (se existir): testar o último caractere e substituir por '\0'.
  • Separar pelos pontos: usar strtok(buffer, ".") para obter o primeiro token, depois strtok(NULL, ".") para os seguintes. Assim obtemos até 4 tokens; se houver mais ou menos tokens, a entrada não respeita o formato.
  • Por que não usar scanf("%d.%d.%d.%d", ...)? Porque scanf aceita espaços e sinais e pode aceitar entradas ambíguas; além disso, se quisermos detectar qual campo tem problema e validar tamanhos (ex.: ano com exatamente 4 dígitos), é muito mais direto tokenizar e inspecionar caractere a caractere.

Passo 5

Validação de cada token (mini-aula detalhada):
  • Verificar que cada token exista (ou seja, foram encontrados exatamente 4 tokens).
  • Verificar que cada caractere de um token é dígito: iterar sobre o token e usar isdigit((unsigned char) token[i]). Isso impede que apareçam letras ou sinais.
  • Regras específicas:
    • Token 1 (ano): deve ter comprimento exatamente 4 e todos dígitos. Depois de converter para inteiro, é razoável exigir 1000 \le \text{ano} \le 9999 para evitar anos com zeros à esquerda inválidos.
    • Token 2 (semestre): deve ter comprimento exatamente 1, ser dígito e convertido deve ser 1 ou 2.
    • Token 3 (curso): pelo enunciado “qualquer valor”, exigimos pelo menos 1 dígito e que a conversão resulte em inteiro não-negativo. (Você pode exigir tamanho fixo se o professor especificar, mas aqui mantemos flexível.)
    • Token 4 (id): idem ao curso — pelo menos 1 dígito e inteiro não-negativo.
  • Conversão: usar strtol(token, &endptr, 10) e checar se *endptr == '\0' (garante que toda a string foi convertida). Também é útil checar sinais (não permitimos -), então o primeiro caractere não deve ser '-'.
  • Se alguma verificação falhar, armazenar uma mensagem de erro específica (por exemplo: “Ano inválido: deve ter 4 dígitos” ou “Semestre inválido: deve ser 1 ou 2”).

Passo 6

Construção do programa e explicação do código principal (linha a linha, com comentários):
  • Cabeçalhos necessários: #include <stdio.h>, #include <stdlib.h>, #include <string.h>, #include <ctype.h>.
  • Variáveis: buffer de leitura (ex.: char linha[100];), ponteiros para tokens, flags/variáveis para armazenar erros e números convertidos.
  • Fluxo:
    1. Imprimir mensagem pedindo a matrícula.
    2. Ler com fgets.
    3. Remover \n.
    4. Usar strtok para separar e obter tokens.
    5. Para cada token, testar comprimento e isdigit.
    6. Converter com strtol.
    7. Verificar regras semânticas (semestre 1 ou 2, ano no intervalo).
    8. Se tudo válido, criar uma variável matricula m; e atribuir m.ano = ano_num; m.semestre = sem_num; m.curso = curso_num; m.id = id_num;.
    9. Exibir a matrícula a partir de m usando printf("%04d.%d.%d.%d\n", m.ano, m.semestre, m.curso, m.id);.
  • Observação sobre impressão: printf("%04d", m.ano) garante que ano apareça com 4 dígitos (ex.: 2023). Os campos curso e id são impressos como inteiros; se for necessário preservar zeros à esquerda originais, o struct teria que guardar strings. Aqui, imprimo como inteiros porque o enunciado pediu os números em cada campo.

Passo 7

Código completo do programa (implementação do item (b)). Leia com atenção os comentários — eu expliquei cada parte acima e, na prova, você pode reproduzir este padrão:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

typedef struct {
    int ano;
    int semestre;
    int curso;
    int id;
} matricula;

int so_digitos(const char *s) {
    if (s == NULL || *s == '\0') return 0;
    for (size_t i = 0; s[i] != '\0'; ++i) {
        if (!isdigit((unsigned char)s[i])) return 0;
    }
    return 1;
}

int main(void) {
    char linha[200];
    char *token;
    char *rest;
    char *tokens[4] = {NULL, NULL, NULL, NULL};
    int i = 0;
    int erros = 0;

    printf("Digite a matrícula no formato aaaa.s.cc.iii : ");
    if (!fgets(linha, sizeof(linha), stdin)) {
        printf("Erro ao ler entrada.\n");
        return 1;
    }
    /* remover newline */
    rest = strchr(linha, '\n');
    if (rest) *rest = '\0';

    /* tokenizar por '.' */
    token = strtok(linha, ".");
    while (token != NULL && i < 4) {
        tokens[i++] = token;
        token = strtok(NULL, ".");
    }

    if (i != 4) {
        printf("Formato inválido: deve conter exatamente 4 campos separados por '.'\n");
        return 1;
    }

    /* Validar ano (tokens[0]) */
    if (strlen(tokens[0]) != 4 || !so_digitos(tokens[0])) {
        printf("Erro: 'ano' inválido. Deve ter exatamente 4 dígitos.\n");
        erros++;
    }

    /* Validar semestre (tokens[1]) */
    if (strlen(tokens[1]) != 1 || !so_digitos(tokens[1])) {
        printf("Erro: 'semestre' inválido. Deve ser 1 dígito (1 ou 2).\n");
        erros++;
    }

    /* Validar curso (tokens[2]) */
    if (strlen(tokens[2]) < 1 || !so_digitos(tokens[2])) {
        printf("Erro: 'curso' inválido. Deve conter pelo menos 1 dígito e apenas dígitos.\n");
        erros++;
    }

    /* Validar id (tokens[3]) */
    if (strlen(tokens[3]) < 1 || !so_digitos(tokens[3])) {
        printf("Erro: 'identificador' inválido. Deve conter pelo menos 1 dígito e apenas dígitos.\n");
        erros++;
    }

    if (erros > 0) {
        /* já imprimimos mensagens específicas */
        return 1;
    }

    /* Converter para inteiros com strtol e validar semanticamente */
    char *endptr;
    long ano_l = strtol(tokens[0], &endptr, 10);
    if (*endptr != '\0' || ano_l < 1000 || ano_l > 9999) {
        printf("Erro: 'ano' semanticamente inválido (espera-se 4 dígitos, ex: 2023).\n");
        return 1;
    }
    long semestre_l = strtol(tokens[1], &endptr, 10);
    if (*endptr != '\0' || (semestre_l != 1 && semestre_l != 2)) {
        printf("Erro: 'semestre' inválido. Só são permitidos 1 ou 2.\n");
        return 1;
    }
    long curso_l = strtol(tokens[2], &endptr, 10);
    if (*endptr != '\0' || curso_l < 0) {
        printf("Erro: 'curso' inválido.\n");
        return 1;
    }
    long id_l = strtol(tokens[3], &endptr, 10);
    if (*endptr != '\0' || id_l < 0) {
        printf("Erro: 'identificador' inválido.\n");
        return 1;
    }

    /* Atribuir à struct matricula e imprimir */
    matricula m;
    m.ano = (int) ano_l;
    m.semestre = (int) semestre_l;
    m.curso = (int) curso_l;
    m.id = (int) id_l;

    printf("Matrícula válida atribuída à variável do tipo 'matricula'.\n");
    printf("Matrícula (a partir da struct): %04d.%d.%d.%d\n", m.ano, m.semestre, m.curso, m.id);

    return 0;
}

Passo 8

Dicas e observações para a prova (como usar esse método rapidamente):
  • Na prova, escreva primeiro a struct — isso garante que você cumpra o item (a).
  • Depois, no main, implemente leitura com fgets e tokenização com strtok. Esses três passos (ler, separar, validar) são suficientes para resolver a maior parte dos exercícios parecidos.
  • Se o enunciado da prova exigir manter zeros à esquerda em cc ou iii, guarde esses campos como strings na struct em vez de inteiros (ex.: char curso[10]; char id[10];) para depois imprimi-los exatamente como digitados.
  • Sempre verifique e explique cada validação — o corretor costuma valorizar mensagens de erro claras.

Resposta

(a)
typedef struct {
    int ano;       /* ano de ingresso (ex.: 2023) */
    int semestre;  /* semestre de ingresso (1 ou 2) */
    int curso;     /* número do curso (qualquer inteiro não negativo) */
    int id;        /* identificador do discente (qualquer inteiro não negativo) */
} matricula;
(b)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

typedef struct {
    int ano;
    int semestre;
    int curso;
    int id;
} matricula;

int so_digitos(const char *s) {
    if (s == NULL || *s == '\0') return 0;
    for (size_t i = 0; s[i] != '\0'; ++i) {
        if (!isdigit((unsigned char)s[i])) return 0;
    }
    return 1;
}

int main(void) {
    char linha[200];
    char *token;
    char *rest;
    char *tokens[4] = {NULL, NULL, NULL, NULL};
    int i = 0;
    int erros = 0;

    printf("Digite a matrícula no formato aaaa.s.cc.iii : ");
    if (!fgets(linha, sizeof(linha), stdin)) {
        printf("Erro ao ler entrada.\n");
        return 1;
    }
    /* remover newline */
    rest = strchr(linha, '\n');
    if (rest) *rest = '\0';

    /* tokenizar por '.' */
    token = strtok(linha, ".");
    while (token != NULL && i < 4) {
        tokens[i++] = token;
        token = strtok(NULL, ".");
    }

    if (i != 4) {
        printf("Formato inválido: deve conter exatamente 4 campos separados por '.'\n");
        return 1;
    }

    /* Validar ano (tokens[0]) */
    if (strlen(tokens[0]) != 4 || !so_digitos(tokens[0])) {
        printf("Erro: 'ano' inválido. Deve ter exatamente 4 dígitos.\n");
        erros++;
    }

    /* Validar semestre (tokens[1]) */
    if (strlen(tokens[1]) != 1 || !so_digitos(tokens[1])) {
        printf("Erro: 'semestre' inválido. Deve ser 1 dígito (1 ou 2).\n");
        erros++;
    }

    /* Validar curso (tokens[2]) */
    if (strlen(tokens[2]) < 1 || !so_digitos(tokens[2])) {
        printf("Erro: 'curso' inválido. Deve conter pelo menos 1 dígito e apenas dígitos.\n");
        erros++;
    }

    /* Validar id (tokens[3]) */
    if (strlen(tokens[3]) < 1 || !so_digitos(tokens[3])) {
        printf("Erro: 'identificador' inválido. Deve conter pelo menos 1 dígito e apenas dígitos.\n");
        erros++;
    }

    if (erros > 0) {
        /* já imprimimos mensagens específicas */
        return 1;
    }

    /* Converter para inteiros com strtol e validar semanticamente */
    char *endptr;
    long ano_l = strtol(tokens[0], &endptr, 10);
    if (*endptr != '\0' || ano_l < 1000 || ano_l > 9999) {
        printf("Erro: 'ano' semanticamente inválido (espera-se 4 dígitos, ex: 2023).\n");
        return 1;
    }
    long semestre_l = strtol(tokens[1], &endptr, 10);
    if (*endptr != '\0' || (semestre_l != 1 && semestre_l != 2)) {
        printf("Erro: 'semestre' inválido. Só são permitidos 1 ou 2.\n");
        return 1;
    }
    long curso_l = strtol(tokens[2], &endptr, 10);
    if (*endptr != '\0' || curso_l < 0) {
        printf("Erro: 'curso' inválido.\n");
        return 1;
    }
    long id_l = strtol(tokens[3], &endptr, 10);
    if (*endptr != '\0' || id_l < 0) {
        printf("Erro: 'identificador' inválido.\n");
        return 1;
    }

    /* Atribuir à struct matricula e imprimir */
    matricula m;
    m.ano = (int) ano_l;
    m.semestre = (int) semestre_l;
    m.curso = (int) curso_l;
    m.id = (int) id_l;

    printf("Matrícula válida atribuída à variável do tipo 'matricula'.\n");
    printf("Matrícula (a partir da struct): %04d.%d.%d.%d\n", m.ano, m.semestre, m.curso, m.id);

    return 0;
}

Exercícios relacionados