0Pricing
C Academy · Lección

Consideraciones sobre la seguridad de tipos

Riesgos de las funciones variádicas

Consideraciones sobre la seguridad de tipos es una lección gratuita de C Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de C Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de C Academy incluye 4 lecciones en total.

Los argumentos variádicos omiten la comprobación de tipos

Las funciones normales de C comprueban que los argumentos coincidan con los tipos de los parámetros. Los argumentos variádicos omiten por completo esta comprobación.

El compilador no puede verificar qué se pasa después de ..., lo que facilita los errores y los vuelve peligrosos.

#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 incorrecto en va_arg

Si lee un argumento con el tipo incorrecto, el resultado es un comportamiento indefinido.

Pasar un int y leerlo como double reinterpreta bytes que no corresponden y produce datos basura o un fallo.

#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;
}

Discrepancias en la cadena de formato

En las funciones de estilo printf, un especificador incorrecto es un error clásico. printf("%d", 3.14) lee un double como si fuera un int.

Los compiladores modernos advierten sobre discrepancias en literales, pero no pueden detectar cadenas de formato creadas en tiempo de ejecución.

#include <stdio.h>

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

Sorpresas de las promociones de enteros

Los tipos pequeños se promocionan a int en la parte variádica. Por tanto, aunque pase un char, debe leerlo de nuevo como un int.

Leerlo como char con va_arg(a, char) provoca un comportamiento 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 se convierte en double

Un argumento float siempre se promociona a double en los argumentos variádicos. Debe leerlo como double, nunca como float.

Este es uno de los errores variádicos más habituales.

#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;
}

Leer demasiados argumentos

Si su código llama a va_arg más veces de las que el llamador proporcionó argumentos, leerá datos basura de la pila.

Por eso es esencial contar con un número exacto de argumentos o un centinela.

#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;
}

Los centinelas de puntero NULL requieren cuidado

Usar NULL como centinela es arriesgado: en algunas plataformas, NULL es 0 como int, cuyo tamaño difiere del de un puntero.

Conviértalo explícitamente, por ejemplo con (char *)NULL, para asegurarse de que se inserta el tipo correcto.

#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 del compilador

GCC y Clang ofrecen __attribute__((format(printf, 1, 2))) para indicar al compilador que su función usa formato de estilo printf.

Así comprueba las cadenas de formato en tiempo de compilación y detecta muchos errores.

#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;
}

Preferir alternativas más seguras

Dado que los argumentos variádicos son propensos a errores, prefiera diseños con seguridad de tipos cuando sea posible: pase un array explícito, una estructura o parámetros fijos.

Reserve los argumentos variádicos para necesidades genuinas similares a 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;
}

Documentar el contrato

Como el compilador no puede imponer los tipos de los argumentos variádicos, una documentación clara es su red de seguridad.

Indique exactamente qué tipos y cuántos argumentos debe proporcionar el llamador, y cómo termina la lista.

#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;
}

Probar exhaustivamente

Como los errores pueden pasar inadvertidos, pruebe las funciones variádicas con muchas cantidades y tipos de argumentos.

Los casos límite, como cero argumentos o el número máximo, suelen revelar errores.

#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;
}

Comprobación rápida

Compruebe su comprensión de los riesgos de los tipos variádicos.

Resumen

Ha aprendido los peligros de las funciones variádicas:

  • El compilador no comprueba los tipos de los argumentos variádicos.
  • Los tipos incorrectos, las cantidades incorrectas y los centinelas defectuosos provocan un comportamiento indefinido.
  • Recuerde las promociones: los enteros pequeños se convierten en int y float en double.
  • Use atributos de formato del compilador, documente el contrato y prefiera alternativas con seguridad de tipos cuando pueda.

Preguntas frecuentes

¿La lección «Consideraciones sobre la seguridad de tipos» es gratis?

Sí — el texto completo de «Consideraciones sobre la seguridad de tipos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de C Academy, actualiza a CoddyKit PRO. El curso de C Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Consideraciones sobre la seguridad de tipos»?

Riesgos de las funciones variádicas Practicas C Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar C Academy?

No se requiere experiencia previa. C Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Consideraciones sobre la seguridad de tipos»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de C Academy?

Sí. Cada lección de C Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Macros de stdarg
  2. Escritura de funciones similares a printf
  3. Consideraciones sobre la seguridad de tipos
  4. Ejemplos prácticos
← Volver a C Academy