C Classificar Caracteres (ctype.h)

"O CEP só tem algarismos?" "A senha tem pelo menos uma maiúscula?" "Esta resposta começa com S ou s?" Programas fazem perguntas assim o tempo todo, um caractere de cada vez. Dá para responder com contas na tabela ASCII, como em Caracteres (c >= '0' && c <= '9'), mas o C já traz as perguntas prontas, com nomes que se leem como frases: isdigit(c), "é algarismo?"; isupper(c), "é maiúscula?". Elas moram no cabeçalho <ctype.h> (de character type, "tipo de caractere"), junto com toupper e tolower, que trocam maiúscula e minúscula do jeito certo.

Você vai aprender
  • Como perguntar "este caractere é um algarismo?" com isdigit, e o que a função devolve.
  • A família completa: isalpha, isalnum, isspace, isupper, islower, ispunct, isxdigit…
  • Como contar letras, algarismos e espaços de um texto, e validar um CEP.
  • Como trocar maiúscula e minúscula com toupper e tolower.
  • Por que você nunca deve comparar o resultado com == 1.
  • Por que se escreve (unsigned char) antes do caractere, e por que ç e ã não são letras para o ctype.h.

Todos os programas foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa. Antes desta aula, veja Caracteres e Strings: aqui vamos percorrer strings letra por letra.


A ideia: a triagem dos Correios

Na agência dos Correios, cada pacote passa por uma esteira, e um funcionário faz perguntas simples: "é carta ou caixa?", "é para o Sul?", "é registrado?". Cada pergunta tem resposta sim ou não, e ele não precisa abrir o pacote para responder.

As funções is… do ctype.h são esse funcionário. Você entrega um caractere e faz uma pergunta. O nome de cada função é a pergunta em inglês: is ("é") + a categoria. isdigit = "is digit?", "é algarismo?". A resposta é um número: 0 quer dizer não; qualquer outro valor quer dizer sim (como no if, aula if e else).


1. A primeira pergunta: isdigit

#include <ctype.h>
#include <stdio.h>

int main(void) {
    char c = '7';
    if (isdigit(c)) {
        printf("'%c' é um algarismo\n", c);
    } else {
        printf("'%c' não é um algarismo\n", c);
    }
    return 0;
}
#include <ctype.h>
Traz as funções de caractere. Sem ele, o compilador não conhece isdigit (veja "Quando dá errado").
isdigit(c)
"O caractere c é um dos algarismos '0' a '9'?" Devolve um número diferente de zero para sim e 0 para não.
if (isdigit(c))
O if testa direto o resultado: diferente de zero é verdadeiro. É assim que se usa, sem comparar com nada.
$ gcc -std=c23 -Wall -Wextra digito.c -o digito
$ ./digito
'7' é um algarismo

O "sim" não é 1

Que número é esse "diferente de zero"? Vamos olhar:

#include <ctype.h>
#include <stdio.h>

int main(void) {
    printf("isdigit('7') = %d\n", isdigit('7'));
    printf("isdigit('x') = %d\n", isdigit('x'));
    printf("isalpha('x') = %d\n", isalpha('x'));
    printf("isupper('A') = %d\n", isupper('A'));
    printf("isspace(' ') = %d\n", isspace(' '));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra valor.c -o valor
$ ./valor
isdigit('7') = 2048
isdigit('x') = 0
isalpha('x') = 1024
isupper('A') = 256
isspace(' ') = 8192

2048, 1024, 256… A norma do C só promete "zero ou diferente de zero". A biblioteca do Linux (a glibc) guarda as categorias de cada caractere como bits de um número (aula Máscaras de Bits) e devolve o bit da pergunta. No editor do guia, a biblioteca é outra e o mesmo programa mostra 1 em todas as respostas "sim". Os dois estão certos. A lição:

Nunca escreva if (isdigit(c) == 1)

No notebook da Ana, isdigit('7') == 1 é 2048 == 1, falso: o programa diria que '7' não é algarismo. No editor, daria certo, e o erro ficaria escondido até alguém compilar com o gcc. Escreva if (isdigit(c)) para "sim" e if (!isdigit(c)) para "não". Se precisar guardar a resposta como 0 ou 1, use isdigit(c) != 0.

Missão: o programa pergunta se 'k' é um algarismo. Troque a pergunta: use isalpha ("is alphabetic?", "é uma letra?") e ajuste as duas mensagens, para a saída ser 'k' é uma letra. Depois, teste com 'K', '5' e '?'.

#include <ctype.h>
#include <stdio.h>

int main(void) {
    char c = 'k';
    if (isdigit((unsigned char) c)) {
        printf("'%c' é um algarismo\n", c);
    } else {
        printf("'%c' não é um algarismo\n", c);
    }
    return 0;
}
#include <ctype.h>
#include <stdio.h>

int main(void) {
    char c = 'k';
    if (isalpha((unsigned char) c)) {
        printf("'%c' é uma letra\n", c);
    } else {
        printf("'%c' não é uma letra\n", c);
    }
    return 0;
}

O (unsigned char) antes do c é um cuidado que todo programa com ctype.h deve ter. Por enquanto, aceite; a seção 5 explica o porquê.


2. A família completa

Todas as funções is… recebem um caractere e devolvem zero (não) ou diferente de zero (sim). Para os caracteres da tabela ASCII, as respostas são estas:

FunçãoPergunta (em inglês → português)"Sim" para
isdigit(c)is digit? → é algarismo?0 a 9
isalpha(c)is alphabetic? → é letra?a a z e A a Z
isalnum(c)is alphanumeric? → é letra ou algarismo?o que é isalpha ou isdigit
isupper(c)is upper case? → é maiúscula?A a Z
islower(c)is lower case? → é minúscula?a a z
isspace(c)is space? → é espaço em branco?espaço, \t (tab), \n (Enter), \r, \v, \f
isblank(c) C99is blank? → é espaço na linha?só o espaço e o \t
ispunct(c)is punctuation? → é pontuação?! " # , . ; ? @ _ - e os outros símbolos visíveis
isxdigit(c)is hex digit? → é algarismo hexadecimal?0–9, a–f, A–F
isprint(c)is printable? → aparece na tela?tudo que é visível, mais o espaço
isgraph(c)is graphic? → tem desenho?o mesmo que isprint, menos o espaço
iscntrl(c)is control? → é caractere de controle?os invisíveis: \n, \t, '\0'… (códigos 0 a 31 e 127)

Para ver várias perguntas de uma vez, este programa passa por cada caractere de um texto e responde sete delas:

#include <ctype.h>
#include <stdio.h>

int main(void) {
    char texto[] = "Ana, 29 anos!";
    printf("char alpha digit alnum space upper lower punct\n");
    for (int i = 0; texto[i] != '\0'; i++) {
        unsigned char c = texto[i];
        printf(" '%c'  %3s   %3s   %3s   %3s   %3s   %3s   %3s\n", c,
               isalpha(c) ? "sim" : "-",
               isdigit(c) ? "sim" : "-",
               isalnum(c) ? "sim" : "-",
               isspace(c) ? "sim" : "-",
               isupper(c) ? "sim" : "-",
               islower(c) ? "sim" : "-",
               ispunct(c) ? "sim" : "-");
    }
    return 0;
}
for (int i = 0; texto[i] != '\0'; i++)
Anda pela string até a marca de fim, como em Strings.
unsigned char c = texto[i];
Copia o caractere para um unsigned char (o mesmo cuidado do (unsigned char), escrito de outro jeito: seção 5).
isalpha(c) ? "sim" : "-"
O operador ternário (aula Ternário): se a resposta é diferente de zero, mostra "sim"; se é zero, "-".
$ gcc -std=c23 -Wall -Wextra tabela.c -o tabela
$ ./tabela
char alpha digit alnum space upper lower punct
 'A'  sim     -   sim     -   sim     -     -
 'n'  sim     -   sim     -     -   sim     -
 'a'  sim     -   sim     -     -   sim     -
 ','    -     -     -     -     -     -   sim
 ' '    -     -     -   sim     -     -     -
 '2'    -   sim   sim     -     -     -     -
 '9'    -   sim   sim     -     -     -     -
 ' '    -     -     -   sim     -     -     -
 'a'  sim     -   sim     -     -   sim     -
 'n'  sim     -   sim     -     -   sim     -
 'o'  sim     -   sim     -     -   sim     -
 's'  sim     -   sim     -     -   sim     -
 '!'    -     -     -     -     -     -   sim

Repare: cada caractere do ASCII visível cai em exatamente uma destas três: letra, algarismo ou pontuação. O espaço não é nenhuma das três. E isalnum é só "letra ou algarismo", muito usado para validar nomes de usuário.

Experimente: troque o texto por "#1A2b3G" (uma cor em hexadecimal, com um erro no fim) e acrescente uma coluna com isxdigit(c). Qual caractere não é hexadecimal? Teste também um texto com \t no meio: o isspace responde "sim", e o %c mostra um tab.

#include <ctype.h>
#include <stdio.h>

int main(void) {
    char texto[] = "Ana, 29 anos!";
    printf("char alpha digit space punct\n");
    for (int i = 0; texto[i] != '\0'; i++) {
        unsigned char c = texto[i];
        printf(" '%c'  %3s   %3s   %3s   %3s\n", c,
               isalpha(c) ? "sim" : "-",
               isdigit(c) ? "sim" : "-",
               isspace(c) ? "sim" : "-",
               ispunct(c) ? "sim" : "-");
    }
    return 0;
}

3. Contar e validar

Com uma pergunta por caractere e um laço, dá para contar e para conferir textos inteiros.

Contar letras, algarismos e espaços

#include <ctype.h>
#include <stdio.h>

int main(void) {
    char frase[] = "Pedido 42: 3 cafes e 2 bolos, por favor!";
    int letras = 0, algarismos = 0, espacos = 0, outros = 0;
    for (int i = 0; frase[i] != '\0'; i++) {
        unsigned char c = frase[i];
        if (isalpha(c)) {
            letras++;
        } else if (isdigit(c)) {
            algarismos++;
        } else if (isspace(c)) {
            espacos++;
        } else {
            outros++;
        }
    }
    printf("%s\n", frase);
    printf("letras: %d, algarismos: %d, espaços: %d, outros: %d\n",
           letras, algarismos, espacos, outros);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra contar.c -o contar
$ ./contar
Pedido 42: 3 cafes e 2 bolos, por favor!
letras: 25, algarismos: 4, espaços: 8, outros: 3     « os outros: ':' ',' '!'

O else if (aula else if) garante que cada caractere entra numa conta só. "cafes" está sem acento de propósito: a seção 6 mostra o que o ctype.h faz com o é.

Validar um CEP

Na lojinha, o CEP de entrega tem de ter o formato 01310-100: 9 caracteres, um traço na posição 5 e algarismos no resto.

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

int main(void) {
    char cep[] = "01310-100";
    int ok = strlen(cep) == 9 && cep[5] == '-';
    for (int i = 0; ok && i < 9; i++) {
        if (i != 5 && !isdigit((unsigned char) cep[i])) {
            ok = 0;
        }
    }
    printf("%s: %s\n", cep, ok ? "CEP válido" : "CEP inválido");
    return 0;
}
int ok = strlen(cep) == 9 && cep[5] == '-';
Começa conferindo o tamanho (strlen, aula Funções de String) e o traço. O resultado da comparação é 1 ou 0. Se o tamanho estiver errado, o && nem olha o cep[5] (aula Operadores Lógicos).
ok && i < 9
O laço para assim que algo der errado: não adianta continuar olhando.
i != 5 && !isdigit(…)
"Não é a posição do traço e não é algarismo": então o CEP é inválido. O ! inverte a resposta do isdigit.
$ gcc -std=c23 -Wall -Wextra cep.c -o cep
$ ./cep
01310-100: CEP válido

Troque o CEP por "0131O-100" (com a letra O no lugar do zero) e o programa diz CEP inválido. Para transformar o CEP já validado em número, use strtol (aula Texto ↔ Número).


4. Trocar maiúscula e minúscula: toupper e tolower

toupper ("to upper case", "para maiúscula") recebe um caractere e devolve a versão maiúscula. tolower faz o contrário. O detalhe que importa: se o caractere não é letra, elas devolvem o mesmo caractere, sem mexer.

#include <ctype.h>
#include <stdio.h>

int main(void) {
    char nome[] = "ana maria, 29!";
    for (int i = 0; nome[i] != '\0'; i++) {
        nome[i] = (char) toupper((unsigned char) nome[i]);
    }
    printf("%s\n", nome);
    printf("tolower('Q') = %c\n", tolower('Q'));
    printf("toupper('!') = %c\n", toupper('!'));
    printf("'!' - 32     = %c\n", '!' - 32);
    return 0;
}
toupper((unsigned char) nome[i])
Calcula a maiúscula da letra da posição i. A função não muda a string: ela só devolve um valor novo.
nome[i] = (char) …
Por isso o resultado tem de ser guardado de volta na posição. O toupper devolve um int; o (char) deixa claro que você sabe que ele vai virar char.
toupper('!')
Não é letra: volta o próprio '!'. Os espaços, a vírgula e o 29 do nome também passaram intactos.
'!' - 32
O truque da aula Caracteres, subtrair 32, aplicado a algo que não é letra: 33 − 32 = 1, um caractere de controle invisível.
$ gcc -std=c23 -Wall -Wextra maiusculas.c -o maiusculas
$ ./maiusculas
ANA MARIA, 29!
tolower('Q') = q
toupper('!') = !
'!' - 32     =       « o código 1: invisível no terminal

Esse é o motivo de preferir toupper à conta com 32: a conta só funciona se você tiver certeza de que o caractere é uma letra minúscula. O toupper confere sozinho.

Missão: o cadastro da lojinha recebeu o nome todo em minúsculas. Deixe maiúscula só a primeira letra de cada palavra, para mostrar Ana Maria Souza. Dica: um caractere é começo de palavra quando é o primeiro (i == 0) ou quando o anterior (nome[i - 1]) é um espaço.

#include <ctype.h>
#include <stdio.h>

int main(void) {
    char nome[] = "ana maria souza";
    for (int i = 0; nome[i] != '\0'; i++) {
        // se nome[i] começa uma palavra, troque por maiúscula

    }
    printf("%s\n", nome);
    return 0;
}
#include <ctype.h>
#include <stdio.h>

int main(void) {
    char nome[] = "ana maria souza";
    for (int i = 0; nome[i] != '\0'; i++) {
        if (i == 0 || nome[i - 1] == ' ') {
            nome[i] = (char) toupper((unsigned char) nome[i]);
        }
    }
    printf("%s\n", nome);
    return 0;
}

O i == 0 || vem primeiro de propósito: quando i é 0, o || já tem a resposta e não calcula nome[i - 1], que seria nome[-1], fora do array (aula Arrays).

Comparar sem ligar para maiúsculas

Um uso muito comum: aceitar S ou s como "sim". Em vez de if (r == 's' || r == 'S'), converta antes: if (tolower((unsigned char) r) == 's'). A aula Validar a Entrada usa esse truque para ler respostas do teclado.


5. Por que (unsigned char)

Até aqui, todo caractere passou por um (unsigned char) antes de entrar numa função do ctype.h. O motivo está na regra da norma do C: essas funções só aceitam um valor que caiba num unsigned char (de 0 a 255) ou o valor especial EOF (o "fim de arquivo" do getchar, que vale −1). Qualquer outro número negativo é comportamento indefinido.

E um char comum pode ser negativo. No Linux de PC (e no editor), ele vai de −128 a 127 (aula Inteiros). As letras do ASCII são todas positivas, mas os bytes de uma letra acentuada passam de 127 e, num char, viram negativos:

#include <ctype.h>
#include <stdio.h>

int main(void) {
    char palavra[] = "é";
    char c = palavra[0];
    printf("como char: %d\n", c);
    printf("como unsigned char: %d\n", (unsigned char) c);
    printf("isalpha(c) = %d\n", isalpha(c));
    printf("toupper(c) = %d\n", toupper(c));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra negativo.c -o negativo     « nenhum aviso
$ ./negativo
como char: -61
como unsigned char: 195
isalpha(c) = 0
toupper(c) = 195

O primeiro byte do é é o 195; num char, ele aparece como −61. O toupper(-61) devolveu 195 no gcc. No editor do guia, o mesmo programa mostrou toupper(c) = -61. Duas bibliotecas, dois resultados, porque a norma não diz o que acontece com −61: é comportamento indefinido (aula Comportamento Indefinido). Na biblioteca do Windows, em modo de depuração, um valor negativo chega a interromper o programa com um alerta.

O conserto é sempre o mesmo: converter para unsigned char antes. Com (unsigned char) c, a função recebe 195, um valor permitido, e todas as bibliotecas dão a mesma resposta. Há dois jeitos de escrever:

JeitoExemploQuando usar
Converter na chamadaisdigit((unsigned char) s[i])Uma pergunta só, no meio de um if
Guardar numa variávelunsigned char c = s[i]; e depois isdigit(c)Várias perguntas sobre o mesmo caractere (como no contar.c)
Sem converterisdigit('7')Só com um caractere fixo escrito entre aspas simples, que você sabe que é ASCII
E o getchar?

O getchar (aula Caracteres) já devolve um int no formato certo: de 0 a 255, ou EOF. Se você guardar o resultado num int c, pode passar direto: isdigit(c). O (unsigned char) é para caracteres que vêm de um array de char, como uma string.


6. Acentos não são letras para o ctype.h

A Ana tentou contar as letras de "maçã":

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

int main(void) {
    char palavra[] = "maçã";
    int letras = 0;
    for (int i = 0; palavra[i] != '\0'; i++) {
        unsigned char c = palavra[i];
        printf("byte %d: %3d  isalpha: %s\n", i, c, isalpha(c) ? "sim" : "não");
        if (isalpha(c)) {
            letras++;
        }
    }
    printf("isalpha contou %d letras em \"%s\" (%zu bytes)\n", letras, palavra, strlen(palavra));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra acento.c -o acento
$ ./acento
byte 0: 109  isalpha: sim
byte 1:  97  isalpha: sim
byte 2: 195  isalpha: não     « primeira metade do ç
byte 3: 167  isalpha: não     « segunda metade do ç
byte 4: 195  isalpha: não     « primeira metade do ã
byte 5: 163  isalpha: não     « segunda metade do ã
isalpha contou 2 letras em "maçã" (6 bytes)

"maçã" tem 4 letras, mas 6 bytes: o ç e o ã ocupam 2 bytes cada na codificação UTF-8, a usada pelos arquivos e terminais de hoje. As funções do ctype.h olham um byte por vez, e nenhuma metade de letra é uma letra. O editor dá exatamente a mesma resposta. Na prática:

  • o ctype.h é perfeito para o que é ASCII: algarismos, CEP, CPF, códigos de produto, senhas, comandos, respostas "s/n";
  • para contar ou converter letras acentuadas de português, ele não serve sozinho. O toupper não transforma ç em Ç, nem ã em Ã.

Como o C lida com acentos de verdade (bytes, strlen que conta "a mais", caracteres largos e setlocale) é o assunto da próxima aula, Acentos e UTF-8.


Formas diferentes: contas com a tabela ou ctype.h?

As duas formas abaixo respondem igual para caracteres ASCII:

PerguntaCom contasCom ctype.h
É algarismo?c >= '0' && c <= '9'isdigit(c)
É minúscula?c >= 'a' && c <= 'z'islower(c)
É letra?(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')isalpha(c)
É espaço em branco?c == ' ' || c == '\t' || c == '\n' || …isspace(c)
Para maiúsculaif (c >= 'a' && c <= 'z') c = c - 32;c = toupper(c);

Qual usar: o ctype.h. O código fica mais curto, se lê como uma frase, e não há como esquecer um caso (quem lembra do \v no "é espaço?"). A conta manual faz sentido em dois lugares: c - '0' para transformar o algarismo em número (depois de conferir com isdigit), e quando você quer exatamente um intervalo que não é uma categoria pronta, como "é uma letra de a a f?".

As funções dependem do locale

O locale (as "configurações regionais": idioma, formato de número, de data) pode mudar as respostas para os bytes acima de 127. Um programa começa sempre no locale "C", em que só o ASCII tem categoria, e só muda se chamar setlocale. Mesmo com o locale português em UTF-8 ligado, um byte sozinho de uma letra acentuada continua não sendo letra (seção 6). A aula Acentos e UTF-8 mostra o setlocale.


Quando dá errado

error: implicit declaration of function ‘isdigit’ [-Wimplicit-function-declaration]

O que significa: faltou o #include <ctype.h>. O compilador não conhece a função. O gcc 15 até diz o que fazer, na linha seguinte: note: include ‘<ctype.h>’ or provide a declaration of ‘isdigit’ ("inclua o <ctype.h> ou declare isdigit"), e mostra onde a linha entraria (+#include <ctype.h>). No editor, o Clang diz call to undeclared library function 'isdigit' with type 'int (int)' e dá a mesma dica.

Como consertar: #include <ctype.h> no topo do arquivo.

A string não mudou depois do toupper; e o aviso warning: statement with no effect [-Wunused-value]

O que significa: você escreveu toupper((unsigned char) nome[i]); sozinho na linha. A função calculou a maiúscula e o resultado foi jogado fora: o programa mostrou ana, em minúsculas. O gcc avisa "comando sem efeito"; o Clang do editor diz ignoring return value of function declared with pure attribute ("ignorando o valor devolvido por uma função que só calcula").

Como consertar: guarde o resultado: nome[i] = (char) toupper((unsigned char) nome[i]);.

O if (isdigit(c) == 1) diz "não" para um algarismo

O que significa: no gcc (glibc), isdigit('7') devolve 2048, e não 1. O "sim" é qualquer valor diferente de zero. No editor, o mesmo código funciona por acaso, porque lá o "sim" é 1.

Como consertar: if (isdigit(c)) ou if (isdigit(c) != 0). Nunca == 1 nem == true.

Falha de segmentação com isdigit("7")

O que significa: aspas duplas criam uma string (um endereço na memória), não um caractere. No gcc do laboratório, if (isdigit("7")) compilou sem nenhum aviso (na glibc, isdigit é uma macro que esconde o problema) e, ao rodar, caiu com Falha de segmentação, código 139. O Clang do editor recusa na compilação: error: incompatible pointer to integer conversion passing 'char[2]' to parameter of type 'int' ("conversão de ponteiro para inteiro incompatível").

Como consertar: aspas simples para um caractere: isdigit('7'). Para conferir um texto inteiro, use um laço, como no cep.c.

Letras acentuadas não são contadas como letras, ou não viram maiúsculas

O que significa: em UTF-8, cada acentuada ocupa 2 bytes, e o ctype.h olha um byte por vez (seção 6).

Como consertar: para dados ASCII (códigos, números, senhas), está certo como está. Para texto em português, veja Acentos e UTF-8.


Referência rápida

FunçãoDevolve "sim" (diferente de zero) para
isdigit(c)'0' a '9'
isxdigit(c)'0'–'9', 'a'–'f', 'A'–'F'
isalpha(c)letras (no locale "C": só a–z, A–Z)
isalnum(c)letras e algarismos
isupper(c) / islower(c)maiúsculas / minúsculas
isspace(c)' ', '\t', '\n', '\r', '\v', '\f'
isblank(c)' ' e '\t' (C99)
ispunct(c)símbolos visíveis que não são letra nem algarismo
isprint(c) / isgraph(c)visíveis, com / sem o espaço
iscntrl(c)caracteres de controle (códigos 0–31 e 127)
toupper(c) / tolower(c)Devolvem a maiúscula / minúscula; o que não é letra volta igual
(unsigned char) s[i]Sempre antes de passar um char de uma string
if (isdigit(c))O jeito certo de testar (nunca == 1)

Exercícios

1. O cabeçalho

Complete para poder usar isdigit e toupper:

#include <>

2. A pergunta certa

Qual função responde "é espaço, tab ou Enter?"

(c)

3. Para minúscula

Complete para guardar em r a versão minúscula de resposta:

char r = (char) ((unsigned char) resposta);

4. O que toupper('7') devolve?

5. No gcc do Linux, isdigit('5') pode devolver 2048. O que isso quer dizer?

6. Por que se escreve isalpha((unsigned char) s[i]) e não só isalpha(s[i])?

7. Para o ctype.h, quantas letras tem a string "pão", em UTF-8?

Missão final: a senha forte

Missão: o sistema da lojinha só aceita senhas com pelo menos 8 caracteres, uma maiúscula, uma minúscula e um algarismo. Complete o laço para contar as maiúsculas (isupper), as minúsculas (islower) e os algarismos (isdigit) de senha. O resto do programa já decide se a senha é forte. Sem avisos. Depois, teste com "lojinha" e com "LOJINHA2026".

Maiúsculas: 1
Minúsculas: 6
Algarismos: 4
Senha forte
#include <ctype.h>
#include <stdio.h>

int main(void) {
    char senha[] = "Lojinha2026";
    int maiusculas = 0, minusculas = 0, algarismos = 0, tamanho = 0;
    for (int i = 0; senha[i] != '\0'; i++) {
        unsigned char c = senha[i];
        // conte aqui o tipo do caractere c

        tamanho++;
    }
    printf("Maiúsculas: %d\n", maiusculas);
    printf("Minúsculas: %d\n", minusculas);
    printf("Algarismos: %d\n", algarismos);
    if (tamanho >= 8 && maiusculas > 0 && minusculas > 0 && algarismos > 0) {
        printf("Senha forte\n");
    } else {
        printf("Senha fraca\n");
    }
    return 0;
}
#include <ctype.h>
#include <stdio.h>

int main(void) {
    char senha[] = "Lojinha2026";
    int maiusculas = 0, minusculas = 0, algarismos = 0, tamanho = 0;
    for (int i = 0; senha[i] != '\0'; i++) {
        unsigned char c = senha[i];
        if (isupper(c)) {
            maiusculas++;
        } else if (islower(c)) {
            minusculas++;
        } else if (isdigit(c)) {
            algarismos++;
        }
        tamanho++;
    }
    printf("Maiúsculas: %d\n", maiusculas);
    printf("Minúsculas: %d\n", minusculas);
    printf("Algarismos: %d\n", algarismos);
    if (tamanho >= 8 && maiusculas > 0 && minusculas > 0 && algarismos > 0) {
        printf("Senha forte\n");
    } else {
        printf("Senha fraca\n");
    }
    return 0;
}

A solução testa com if direto (nunca == 1) e usa else if, porque um caractere não pode ser maiúscula e algarismo ao mesmo tempo. Um sistema de verdade também exigiria um símbolo: seria mais um contador com ispunct.


Resumo

  • O <ctype.h> traz perguntas prontas sobre um caractere: isdigit, isalpha, isalnum, isspace, isupper, islower, ispunct, isxdigit, isprint, iscntrl…
  • A resposta é 0 para "não" e qualquer outro valor para "sim" (2048 no gcc, 1 no editor). Teste com if (isdigit(c)), nunca com == 1.
  • toupper e tolower devolvem a letra convertida e deixam o resto igual. Elas não mudam a string: guarde o resultado (s[i] = (char) toupper(…)).
  • Sempre (unsigned char) antes de passar um char de uma string: valores negativos são comportamento indefinido, e o resultado muda de uma biblioteca para outra.
  • Com um laço, as perguntas contam e validam textos: CEP, senha, códigos.
  • O ctype.h olha um byte por vez: os acentos do UTF-8 (2 bytes) não são letras para ele. Para isso, veja Acentos e UTF-8.