0Pricing
C Academy · Aula

Questões de segurança de tipos

Riscos dos argumentos variáveis.

Questões de segurança de tipos é uma aula grátis de C Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C Academy inclui 4 aulas no total.

Argumentos variádicos ignoram a verificação de tipos

Funções C normais verificam se os argumentos correspondem aos tipos dos parâmetros. Os argumentos variádicos ignoram completamente essa verificação.

O compilador não consegue verificar o que é passado depois de ..., o que torna os erros fáceis de cometer e perigosos.

#include <stdio.h>
#include <stdarg.h>

int add(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", add(2, 3, 4));
    return 0;
}

Tipo incorreto em va_arg

Se você ler um argumento com o tipo incorreto, o resultado será comportamento indefinido.

Passar um int e lê-lo como double reinterpretará bytes sem relação entre si e produzirá lixo ou uma falha.

#include <stdio.h>
#include <stdarg.h>

int read_int(int n, ...) {
    va_list a; va_start(a, n);
    int v = va_arg(a, int);
    va_end(a);
    return v;
}

int main(void) {
    printf("%d\n", read_int(1, 100));
    return 0;
}

Incompatibilidades na cadeia de formato

Em funções no estilo de printf, um especificador incorreto é um erro clássico. printf("%d", 3.14) lê um double como se fosse um int.

Compiladores modernos avisam sobre incompatibilidades literais, mas não conseguem detectar cadeias de formato criadas durante a execução.

#include <stdio.h>

int main(void) {
    double pi = 3.14;
    printf("%f\n", pi);
    return 0;
}

Surpresas das promoções de inteiros

Tipos pequenos são promovidos para int na parte variádica. Portanto, mesmo que você passe um char, deverá lê-lo de volta como um int.

Lê-lo como char com va_arg(a, char) é comportamento indefinido.

#include <stdio.h>
#include <stdarg.h>

void show(int n, ...) {
    va_list a; va_start(a, n);
    int c = va_arg(a, int);
    printf("char is %c\n", c);
    va_end(a);
}

int main(void) {
    show(1, 'A');
    return 0;
}

Float torna-se double

Um argumento float sempre é promovido para double em argumentos variádicos. Você deve lê-lo como double, nunca como float.

Esse é um dos erros variádicos mais comuns.

#include <stdio.h>
#include <stdarg.h>

void show(int n, ...) {
    va_list a; va_start(a, n);
    double d = va_arg(a, double);
    printf("%.2f\n", d);
    va_end(a);
}

int main(void) {
    float f = 1.5f;
    show(1, f);
    return 0;
}

Lendo argumentos demais

Se o seu código chamar va_arg mais vezes do que o número de argumentos fornecidos pelo chamador, você lerá lixo da pilha.

É por isso que uma contagem precisa ou uma sentinela é essencial.

#include <stdio.h>
#include <stdarg.h>

int sum(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", sum(3, 1, 2, 3));
    return 0;
}

Sentinelas com ponteiro NULL exigem cuidado

Usar NULL como sentinela é arriscado: em algumas plataformas, NULL é 0 como um int, o que tem tamanho diferente de um ponteiro.

Faça a conversão explicitamente, como em (char *)NULL, para garantir que o tipo correto seja colocado.

#include <stdio.h>
#include <stdarg.h>

void print_all(const char *first, ...) {
    va_list a; va_start(a, first);
    const char *s = first;
    while (s) { printf("%s\n", s); s = va_arg(a, const char *); }
    va_end(a);
}

int main(void) {
    print_all("a", "b", (char *)NULL);
    return 0;
}

Atributos de formato do compilador

GCC e Clang oferecem __attribute__((format(printf, 1, 2))) para informar ao compilador que sua função usa formatação no estilo de printf.

Assim, ele verifica suas cadeias de formato durante a compilação e detecta muitos erros.

#include <stdio.h>
#include <stdarg.h>

void logf(const char *fmt, ...)
    __attribute__((format(printf, 1, 2)));

void logf(const char *fmt, ...) {
    va_list a; va_start(a, fmt);
    vprintf(fmt, a);
    va_end(a);
}

int main(void) {
    logf("%d items\n", 3);
    return 0;
}

Prefira alternativas mais seguras

Como os argumentos variádicos estão sujeitos a erros, prefira projetos com segurança de tipos quando possível: passe uma matriz explícita, uma struct ou parâmetros fixos.

Reserve os argumentos variádicos para necessidades genuínas semelhantes às de printf.

#include <stdio.h>

int sum_array(const int *a, int n) {
    int s = 0;
    for (int i = 0; i < n; i++) s += a[i];
    return s;
}

int main(void) {
    int nums[] = {1, 2, 3, 4};
    printf("%d\n", sum_array(nums, 4));
    return 0;
}

Documente o contrato

Como o compilador não consegue impor os tipos dos argumentos variádicos, uma documentação clara é sua proteção.

Informe exatamente quais tipos e quantos argumentos o chamador deve fornecer, além de como a lista termina.

#include <stdio.h>
#include <stdarg.h>

/* sum_ints(n, ...): pass exactly n int values */
int sum_ints(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", sum_ints(2, 5, 5));
    return 0;
}

Testando minuciosamente

Como os erros podem permanecer ocultos, teste as funções variádicas com muitas quantidades e tipos de argumentos.

Casos-limite, como zero argumentos ou a quantidade máxima, frequentemente revelam erros.

#include <stdio.h>
#include <stdarg.h>

int sum(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", sum(0));
    printf("%d\n", sum(1, 9));
    return 0;
}

Verificação rápida

Teste sua compreensão dos riscos de tipos em argumentos variádicos.

Recapitulação

Você aprendeu os perigos das funções variádicas:

  • O compilador não verifica os tipos dos argumentos variádicos.
  • Tipos incorretos, quantidades incorretas e sentinelas inadequadas causam comportamento indefinido.
  • Lembre-se das promoções: inteiros pequenos tornam-se int e float torna-se double.
  • Use atributos de formato do compilador, documente o contrato e prefira alternativas com segurança de tipos quando possível.

Perguntas Frequentes

A aula “Questões de segurança de tipos” é grátis?

Sim — o texto completo de “Questões de segurança de tipos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C Academy, atualize para CoddyKit PRO. O curso de C Academy inclui 4 aulas no total.

O que vou aprender em “Questões de segurança de tipos”?

Riscos dos argumentos variáveis. Você pratica C Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar C Academy?

Nenhuma experiência prévia é necessária. C Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Questões de segurança de tipos”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de C Academy?

Sim. Cada aula de C Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. As macros stdarg
  2. Escrevendo funções semelhantes a printf
  3. Questões de segurança de tipos
  4. Exemplos práticos
← Voltar para C Academy