Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Introducción a la programación en C

La sintaxis y programas básicos.

Universidad Nacional de Río Negro

Prerrequisitos: algoritmos, variables y decisiones elementales del capítulo anterior; GCC disponible.

Objetivos: 1. Escribir un programa C con main. 2. Compilarlo sin warnings y predecir su salida.

Comprobación de salida: modificá un ejemplo para leer un valor, compilalo con los flags de cátedra y explicá su salida.

Introducción a la programación en C

Introducción

Antes de iniciar el estudio de la programación en C, es fundamental comprender que programar no consiste únicamente en aprender un lenguaje, sino en desarrollar una forma de pensar. El lenguaje es la herramienta, pero el verdadero arte reside en la construcción de instrucciones lógicas para resolver problemas.

Programar es el acto de proporcionar instrucciones precisas a una computadora para que realice una tarea específica. Una diferencia clave con la comunicación humana es que la computadora no interpreta ambigüedades. No comprende conceptos como «más o menos». Cada paso debe estar perfectamente definido. En este sentido, C presenta ciertas ambigüedades que pueden conducir a resultados inesperados.

Durante la programación, la omisión de un solo detalle puede provocar que el programa no funcione. Por ello, es necesario aprender a pensar como una máquina, pero también a estructurar el pensamiento como un ser humano inteligente.

El lenguaje C es minimalista y provee acceso directo al hardware. Si bien promueve un estilo de programación explícito y prolijo para evitar bugs, C ofrece numerosos atajos sintácticos (tales como operadores de incremento ++, asignaciones compactas += y el operador condicional ternario ?:). La recomendación de evitarlos al principio responde a una decisión didáctica y no a una limitación técnica del lenguaje.


Desarrollo

¿Por qué aprender C?

El lenguaje C fue creado en 1972 por Dennis Ritchie y Brian Kernighan en los Bell Labs. A pesar de tener más de 50 años de existencia, se sigue utilizando ampliamente debido a sus características fundamentales:

Características Principales de C

Nivel de Abstracción

Aunque C es considerado un lenguaje de nivel medio, en el momento en el que fue creado se lo consideraba de alto nivel en comparación con el lenguaje ensamblador.

Hoy en día, y teniendo en cuenta que pasaron solo 50 años desde que fue creado, se lo sitúa en un nivel superior a los de bajo nivel, pero inferior a los que actualmente son considerados de alto nivel como Python o Java.

Esto le da un balance único:

C es un lenguaje compilado. El código fuente se traduce directamente a código máquina integramente antes de ejecutarse mediante un compilador, a diferencia de los lenguajes interpretados que son traducidos línea por línea en tiempo de ejecución.

Atributos Clave
Fortalezas del Lenguaje C
Acceso a conceptos de bajo nivel

C provee acceso a conceptos directamente relacionados con el hardware. Conceptos como el tamaño de la memoria, punteros y direccionamiento físico de memoria son muy similares a los que la CPU utiliza, de forma que los programas sean lo más rápidos posible.

C es un lenguaje pequeño

El lenguaje C posee un núcleo sintáctico y un conjunto de palabras clave reducido. Todo lo demás provisto por el entorno se cubre mediante la biblioteca estándar de C y funciones auxiliares.

C es un lenguaje permisivo

El lenguaje asume que el programador sabe lo que está haciendo, por lo que permite un control absoluto sobre el sistema, reduciendo las capas de validación del compilador, para bien y para mal.

Debilidades del Lenguaje C
Los programas en C pueden ser propensos a errores

La gran flexibilidad y permisividad de C facilitan la introducción de fallas de lógica o memoria que no siempre son detectadas por el compilador en tiempo de traducción. La mayoría de los errores de memoria (como el acceso fuera de límites o desreferencia de punteros nulos) se detectan recién en tiempo de ejecución.

Los programas en C pueden ser difíciles de entender

Debido a su diseño conciso e histórico (creado en una época donde la entrada de texto por consola era lenta), C utiliza una sintaxis compacta y operadores que pueden combinarse de forma críptica, exigiendo un cuidado extremo para mantener la legibilidad.

Los programas en C pueden ser difíciles de modificar

Los programas grandes escritos en C pueden ser difíciles de mantener si no se diseñan con cuidado. A diferencia de lenguajes orientados a objetos, C carece de conceptos como clases o paquetes integrados, delegando la modularidad a la estructuración de archivos del desarrollador.

La caja de herramientas

Preparación del entorno

Para los usuarios de Windows, la cátedra ha preparado un programa para simplificar la instalación de las herramientas necesarias para editar, compilar y ejecutar los programas.

INGCOM-UNRN-P1/entorno

Esencialmente, instala gcc, VSCode, git y una terminal, también podés instalar manualmente todo siguiendo la guía: compilador.

En sistemas basados en Debian/Ubuntu:

sudo apt install build-essential
Primer programa: el «Hola Mundo» en C
hola.c
1
2
3
4
5
6
#include <stdio.h>              // directiva del preprocesador
int main(void)                  // punto de entrada del programa
{                               // inicio de un bloque de código
    printf("Hola mundo C.\n");  // llamada a función de biblioteca para salida
    return 0;                   // finalización del programa
} // fin del bloque de código

Program 1:El indispensable Hola Mundo!

Compilación y Ejecución

Para ejecutar un programa en C, primero hay que compilarlo. Esto se realiza desde la terminal traduciendo el código fuente en un ejecutable de código máquina.

Proceso de compilación en C

Figure 1:El proceso de compilación transforma el código fuente en un ejecutable que la máquina puede ejecutar directamente.

1
2
3
4
5
6
7
8
# Compila el archivo hola.c y crea un ejecutable por defecto llamado a.out (a.exe en Windows)
$> gcc hola.c

(ejecuta-el-programa)=
# Ejecuta el programa
$> ./a.out
Hola mundo C.
$>

Program 2:La salida por la terminal.

En la función printf, el carácter especial \n es una secuencia de control que indica un salto de línea en la consola de salida.

Más adelante, vamos a ver detalles de la compilación para crear programas más complejos.

Pieza por pieza
  1. #include <stdio.h>: Es una directiva del preprocesador. Le indica al preprocesador que inserte el contenido de la cabecera de la biblioteca estándar de entrada/salida (stdio.h), que contiene la declaración de la función printf.

  2. int main(void): Es la definición de la función principal y el punto de entrada del programa. Todo ejecutable en C comienza su ejecución en esta función. int indica que devolverá un valor numérico entero al sistema operativo.

  3. { ... }: Las llaves delimitan el bloque de código del cuerpo de la función.

  4. printf("Hola mundo C.\n");: Es una llamada a función de biblioteca para salida que imprime la cadena en pantalla.

  5. return 0;: Finaliza la ejecución de la función main devolviendo el estado 0 al entorno. Por convención, un retorno de 0 significa finalización exitosa.

De la Idea al Código en C

En el capítulo Fundamentos de Computación y Algoritmos se estudiaron los fundamentos lógicos y el diseño de algoritmos en pseudocódigo. Ahora daremos el paso crucial: traducir ese diseño abstracto a un programa real en C.

La Mentalidad del Programador
  1. Leé el problema, comprendelo y diseñalo.

  2. Dividilo en pasos simples e inequívocos (en papel o pseudocódigo) antes de tipear una sola línea.

  3. Escribí el código en C basándote estrictamente en el algoritmo diseñado, no en la improvisación.

Roll Safe thinking

Pensar es más importante que escribir.

Ejemplo: Sumar dos números enteros

Diseño algorítmico:

  1. Declarar variables para almacenar dos números.

  2. Solicitar y leer los números.

  3. Calcular la suma y asignarla a un destino.

  4. Mostrar el resultado de la suma por pantalla.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int main(void)
{
    int a = 0;
    int b = 0;
    int resultado = 0;
    printf("Ingresá dos números: ");
    scanf("%d %d", &a, &b);
    resultado = a + b;
    printf("La suma es: %d\n", resultado);
    return 0;
}

Sobre las reglas de estilo

El uso de reglas de estilo es fundamental para garantizar la consistencia y legibilidad del código. Al adherirse a normas uniformes (como nomenclatura, indentación y posición de llaves), se facilita la colaboración y el mantenimiento del software. Para más detalles, consultá la regla 0x0001h: La claridad y prolijidad son de máxima importancia.

Y cuando nos referimos a colaboración, esto incluye al profesor que tiene que leer su código.

Sintaxis y Semántica Básica

La sintaxis es el conjunto de reglas formales que definen cómo debe escribirse el código para que sea válido para el compilador. La semántica determina el significado lógico, la estructura y el comportamiento real que tiene el código durante su ejecución. Un código puede ser sintácticamente correcto pero semánticamente erróneo.

Identificadores y Palabras Reservadas

Identificadores: Son los nombres que asignamos a variables, constantes y funciones. Deben comenzar obligatoriamente con una letra o guion bajo (_) y pueden contener letras, dígitos y guiones bajos. C distingue entre mayúsculas y minúsculas (suma es un identificador distinto de Suma). No pueden coincidir con palabras reservadas del lenguaje.

Palabras Reservadas: Son palabras clave del lenguaje C que poseen un significado sintáctico especial predefinido y no pueden ser utilizadas como identificadores (ej: int, float, char, if, else, while, return).

Nuestros identificadores deben respetar las pautas de estilo (ver 0x0101h: Los identificadores deben ser descriptivos).

Tipos de operadores

Los operadores son símbolos que le indican al compilador realizar manipulaciones matemáticas o lógicas específicas sobre las variables. En C se clasifican según su función y la cantidad de operandos que requieren:

Variables y Tipos de Datos

¿Qué es una variable?

Una variable es un identificador asociado a una dirección física de memoria RAM que almacena un dato de un tipo específico.

Variables y memoria

Figure 3:Las variables abstraen ubicaciones físicas de memoria. Cada una tiene una dirección de memoria, un nombre y un tipo.

Tipos básicos de datos en C
Tipos de datos en C

Figure 4:Especificadores de formato de tipos básicos en C.

Declaración e Inicialización

Toda variable debe declararse antes de ser usada, indicando su tipo y su nombre:

1
2
3
4
5
#include <stdbool.h>
int edad = 42;
float pi = 3.14f;
char inicial = 'A';
bool activo = true;

Si declarás una variable sin inicializarla, su contenido inicial en memoria física es indeterminado (“basura”). Siempre inicializá tus variables a un valor conocido antes de utilizarlas (ver regla de estilo 0x7001h: Siempre debés inicializar las variables a un valor conocido).

Secuencia incorrecta de asignaciones

Las variables deben poseer datos válidos antes de ser leídas o manipuladas en expresiones o asignaciones.

Roles de las Variables

En programación, el rol de una variable se refiere al propósito específico que una variable desempeña dentro de un algoritmo. No es solo lo que la variable contiene, sino para qué la usamos. Identificar el rol de una variable nos permite diseñar algoritmos más claros, eficientes y fáciles de depurar. Es como asignar una tarea a cada miembro de un equipo; cada uno tiene su función y sabe qué hacer.

Establecer el rol, nos ayuda a elegir un buen identificador y salir de nombres como a, b y c y pasar a sumador, promedio y bandera. Por poner un ejemplo.

Rol de variable Auxiliar o Temporal

Una variable auxiliar o temporal se usa para almacenar un valor de forma transitoria, generalmente para facilitar una operación que requiere conservar un dato mientras se manipulan otros. El caso más común es el intercambio de valores entre dos variables sin perder ninguno.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int main(void)
{
    int a = 10;
    int b = 20;
    int auxiliar = 0; // Inicialización explícita según la regla {ref}`0x7001h`
    printf("Antes del intercambio: a = %d, b = %d\n", a, b);
    auxiliar = a; // Guarda el valor de 'a'
    a = b;        // 'a' toma el valor de 'b'
    b = auxiliar; // 'b' recupera el valor original de 'a' desde 'auxiliar'
    printf("Despues del intercambio: a = %d, b = %d\n", a, b);
    return 0;
}

Acá, auxiliar es crucial para no perder el valor original de a cuando se lo asignamos a b.

La Importancia de los Roles

Entender estos roles te da una perspectiva más rica sobre cómo funcionan tus programas. No se trata solo de saber qué tipo de dato almacena una variable, sino para qué la estás usando. Reconocer estos patrones te ayudará a:

Vamos a ir viendo más roles a medida que vamos aprendiendo las diferentes cosas que podemos hacer con C.

L-Values y R-Values (Asignación y Expresiones)

Para comprender cómo el compilador evalúa y almacena los datos durante una asignación, tenés que conocer las dos categorías de expresiones en C: L-values y R-values, según lo define formalmente el estándar del lenguaje.

L-Values (locator values / object locators)

Un L-value es una expresión que identifica o localiza un objeto persistente en memoria (es decir, una celda física de memoria direccionable).

R-Values (value of an expression)

Un R-value representa simplemente el valor de una expresión. No posee una ubicación de memoria direccionable de almacenamiento persistente; es un valor transitorio.

Restricciones del compilador

Intentar realizar asignaciones sobre un R-value producirá un error inmediato en tiempo de compilación.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int x = 10;
int y = 20;
x = 50;    // VÁLIDO: 'x' es un L-value (ubicación modificable).
y = x + 5; // VÁLIDO: 'y' es un L-value, 'x + 5' evalúa a un R-value.
// Asignaciones inválidas que causan ERROR DE COMPILACIÓN:
// 100 = x;      // ERROR: el literal '100' es un R-value, 
                 //     no podés asignarle nada.
    // (x + y) = 15; // ERROR: la expresión 'x + y' es un R-value temporal 
                     //      sin dirección física
        . 
    // &x = &y;      // ERROR: la expresión de la izquierda 
                     //      no es un L-value asignable.
    // &(x + 5);     // ERROR: el operador de dirección (&) 
                     //      requiere un L-value.

Entrada y Salida Básica

printf() - Salida Formateada

Se utiliza para imprimir texto y valores de variables formateados en la salida estándar de consola. Podés consultar el apunte más detallado del tema.

1
printf("Tiene %d años\n", edad);

El par de símbolos \n se usan para indicar el ‘salto de línea’, para que no quede todo junto en una sola. Específicamente, cada vez que se ve una \, se indica que el siguiente carácter tiene un significado diferente del que se ve; esto se llama secuencias de escape.

Especificadores de formato de tipos básicos
Rol de variable de Salida

Las variables de salida se utilizan para almacenar los resultados finales de un cálculo o proceso, los cuales serán mostrados al usuario o guardados en algún medio (como un archivo).

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main(void)
{
    float radio = 5.0;
    float area = 0.0; // 'area' es una variable de salida (resultado)
    // Calculamos el área del círculo
    area = 3.14159 * radio * radio;
    printf("El area del circulo con radio %.2f es: %.2f\n", radio, area); // Se
    muestra el valor de 'area' return 0;
}

Aquí, area guarda el resultado del cálculo y luego se imprime.

scanf() - Entrada Formateada

Permite leer datos de entrada ingresados por teclado en la entrada estándar (stdin). Requiere pasar la dirección de la variable de destino anteponiendo el operador de dirección &.

1
2
3
int edad = 0;
printf("Ingrese su edad: ");
scanf("%d", &edad);
Rol de variable de Entrada

Una variable de entrada es aquella que se utiliza para almacenar datos que provienen de una fuente externa al programa, como la entrada del usuario a través del teclado, la lectura de un archivo o datos recibidos de una red.

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main(void)
{
    int edad = -1; // 'edad' es una variable de entrada
    printf("Por favor, ingresa tu edad: ");
    scanf("%d", &edad); // Aquí, el valor ingresado se guarda en 'edad'
    printf("Tu edad es: %d años.\n", edad);
    return 0;
}

En este ejemplo, edad toma el valor que el usuario introduce.

El Buffer de Entrada y la Lectura de Caracteres

Al presionar “Enter” para enviar datos en la consola, se agrega un carácter de salto de línea (\n) en el buffer de entrada stdin. Si la siguiente instrucción lee un carácter (scanf("%c")), leerá ese \n residual en lugar de la entrada esperada. Para evitar esto, se debe anteponer un espacio en blanco en el especificador (" %c"), lo cual instruye a scanf a descartar los espacios en blanco y saltos de línea residuales del buffer.

1
2
3
char inicial = ' ';
printf("Ingrese su inicial: ");
scanf(" %c", &inicial); // El espacio antes de %c limpia el buffer de stdin

Ejercicios de Autoevaluación

Historia y Características

Hola Mundo y Compilación

Algoritmo a Código

Sintaxis y Operadores

Variables y Tipos

Entrada y Salida


Glosario

Compilador
Herramienta de software que traduce un programa escrito en código fuente de alto nivel a instrucciones de código máquina nativo.
Sintaxis
Conjunto de reglas formales y gramaticales que definen la validez estructural de un programa para el compilador.
Semántica
Significado lógico y comportamiento operacional de las instrucciones de un programa en tiempo de ejecución.
L-value
Expresión que localiza un objeto persistente en memoria física, cuya dirección puede ser obtenida.
R-value
Expresión transitoria que representa únicamente un valor y carece de dirección física de almacenamiento direccionable.
Buffer de entrada
Área de almacenamiento temporal de memoria donde se retienen las entradas del usuario hasta ser consumidas por funciones como scanf.

Síntesis y Resumen

En esta unidad introductoria de C analizaste los siguientes fundamentos:

Para continuar con el control de flujo condicional y los lazos iterativos, consultá Control de Flujo Gradual.


Referencias y Lecturas Complementarias