Compilar y ejecutar
Guarda el programa como caesar.c y compílalo con gcc o clang. En Windows, el segundo comando es caesar.exe.
gcc caesar.c -o caesar./caesarEl programa imprime el texto cifrado. Si el cifrado en sí es nuevo para ti, la guía lo explica sin código.
Cómo funciona el código
Un char en C es un número pequeño, así que puedes operar con él: 'H' - 'A' da 7, la posición de la letra en el alfabeto. Tras sumar el desplazamiento, el módulo mantiene el resultado entre 0 y 25, de modo que con un desplazamiento de 3 la Z cae en la C. base + index devuelve el número a su forma de letra.
Una función en C no puede devolver una cadena como en Python o Java, por lo que quien la llama pasa result, un búfer donde escribir. Debe caber el texto y un byte más, ya que la última línea de la función añade el '\0' con el que termina toda cadena en C.
Espacios, signos y cifras pasan por la rama else y se copian sin cambios. Lo mismo ocurre con la ñ: en UTF-8 ocupa dos bytes fuera de los rangos de la A a la Z, y ambos se copian tal cual.
Descifrar en C
No hace falta una segunda función. Descifrar es desplazar en sentido contrario, así que en main basta con pasar un número negativo:
caesar("¡Krod, pxqgr!", -3, result);printf("%s\n", result);// ¡Hola, mundo!De ahí esa fórmula tan rara. En C, % conserva el signo del número de la izquierda: -3 % 26 da -3, y esa posición no existe. Si sumas 26 y vuelves a aplicar el módulo sale 23, y la A vuelve a la X. En la versión en Python, el propio % ya lo resuelve.
Leer texto del teclado
Si el ejercicio pide datos por teclado, sustituye main por esta versión. fgets lee la línea entera, con espacios, y nunca escribe más del tamaño indicado, algo que scanf("%s") no garantiza.
#include <string.h> int main(void) { char text[100], result[100]; int shift; printf("Texto: "); fgets(text, sizeof text, stdin); text[strcspn(text, "\n")] = '\0'; printf("Desplazamiento: "); scanf("%d", &shift); caesar(text, shift, result); printf("%s\n", result); return 0;}fgets conserva la tecla Intro en la cadena. La línea con strcspn la elimina y funciona también con una línea vacía.
La clave por línea de comandos
A veces el programa debe recibir los datos como argumentos. Aquí el desplazamiento y el texto vienen de argv:
#include <stdlib.h>#include <string.h> int main(int argc, char *argv[]) { if (argc != 3) { printf("Uso: %s <desplazamiento> <texto>\n", argv[0]); return 1; } char result[strlen(argv[2]) + 1]; caesar(argv[2], atoi(argv[1]), result); printf("%s\n", result); return 0;} // ./caesar 3 "¡Hola, mundo!"// ¡Krod, pxqgr!argv[1] sigue siendo texto, y por eso atoi lo convierte en número. El tamaño del búfer depende de la entrada, de modo que un argumento largo no lo desborda.
Romper el cifrado por fuerza bruta
Con 26 letras solo hay 25 desplazamientos posibles, y un bucle corto basta para leer el mensaje sin la clave. Busca en la salida la línea que tenga sentido:
void crack(const char *text) { char result[100]; for (int shift = 1; shift < 26; shift++) { caesar(text, -shift, result); printf("%d %s\n", shift, result); }} int main(void) { crack("¡Krod, pxqgr!"); // 1 ¡Jqnc, owpfq! // 2 ¡Ipmb, nvoep! // 3 ¡Hola, mundo! // ... return 0;}El artículo sobre el ataque de fuerza bruta entra en más detalle, y el descifrador en línea hace lo mismo en tu navegador.
Cifras y la letra ñ
Para desplazar algo más que de la A a la Z, guarda los caracteres en una cadena y busca cada uno con strchr. Esta versión desplaza también las cifras. El unsigned char es importante, porque pasar un char negativo a tolower o isupper es comportamiento indefinido.
#include <ctype.h>#include <string.h> void caesar_alphabet(const char *text, int shift, char *result) { const char *alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"; int size = strlen(alphabet); int i; for (i = 0; text[i] != '\0'; i++) { unsigned char c = text[i]; const char *found = strchr(alphabet, tolower(c)); if (found == NULL) { result[i] = c; } else { int index = ((found - alphabet + shift) % size + size) % size; result[i] = isupper(c) ? toupper(alphabet[index]) : alphabet[index]; } } result[i] = '\0';} int main(void) { char result[100]; caesar_alphabet("Sala 101", 3, result); printf("%s\n", result); // Vdod 434 return 0;}Esto solo sirve para caracteres de un byte. La ñ ocupa dos bytes en UTF-8, y necesitarías wchar_t y una configuración regional que cambia de un sistema a otro. Si la necesitas, es mucho más sencillo en la versión en Python, y nuestra herramienta de cifrado César ya incluye el alfabeto español.
Cifrar un archivo de texto
Lee el archivo línea a línea y escribe cada resultado en un archivo nuevo. Comprueba siempre fopen, que devuelve NULL cuando no puede abrir un archivo.
int main(void) { char line[1000], result[1000]; FILE *in = fopen("message.txt", "r"); FILE *out = fopen("encrypted.txt", "w"); if (in == NULL || out == NULL) { perror("fopen"); return 1; } while (fgets(line, sizeof line, in) != NULL) { caesar(line, 3, result); fputs(result, out); } fclose(in); fclose(out); return 0;}Probar el código
Compara las cadenas con strcmp, porque == solo compara sus direcciones. Un assert que falla detiene el programa e indica la línea. Si todo va bien, imprime OK.
#include <assert.h>#include <string.h> int main(void) { char r[100]; caesar("ABC", 3, r); assert(strcmp(r, "DEF") == 0); caesar("xyz", 3, r); assert(strcmp(r, "abc") == 0); caesar("Hello, World!", 5, r); assert(strcmp(r, "Mjqqt, Btwqi!") == 0); caesar("A", -27, r); assert(strcmp(r, "Z") == 0); caesar("é", 3, r); assert(strcmp(r, "é") == 0); printf("OK\n"); return 0;}El mismo cifrado en C++
En C++, std::string gestiona su propia memoria, así que la función puede devolver el resultado sin que tengas que calcular ningún búfer. La aritmética no cambia, ni tampoco el paso extra para los negativos, porque % funciona en C++ igual que en C.
#include <iostream>#include <string> std::string caesar(std::string text, int shift) { for (char &c : text) { if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { char base = (c <= 'Z') ? 'A' : 'a'; c = base + ((c - base + shift) % 26 + 26) % 26; } } return text;} int main() { std::cout << caesar("¡Hola, mundo!", 3) << "\n"; // ¡Krod, pxqgr!}Errores frecuentes
El clásico es olvidar el '\0', y entonces printf sigue leyendo más allá del texto. Casi igual de habitual es un búfer un byte más corto de lo necesario. Quitar el salto de línea con text[strlen(text) - 1] también falla, ya que con una cadena vacía lee antes de su inicio. Y sin el + 26 adicional, al descifrar salen símbolos en lugar de letras.
Preguntas frecuentes
¿Qué compilador y versión de C necesito?
Sirve cualquier compilador compatible con C99, como gcc o clang. Hemos compilado el código de esta página con clang en modo C99, y la versión en C++ como C++17, sin advertencias.
¿Sirve para proteger datos reales?
No. El bucle de arriba lo rompe en segundos. Úsalo para aprender y para pasatiempos, y protege los datos reales con una librería mantenida como libsodium.
¿Cómo compruebo que mi resultado es correcto?
Introduce el mismo texto y desplazamiento en nuestra herramienta de cifrado César y compara. También puedes llamar a la API REST gratuita desde tus pruebas.