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.

Ejercicios: Compilación y Makefiles

Universidad Nacional de Río Negro

Acerca de

Comprender el proceso de compilación y saber cómo automatizarlo es una habilidad esencial para cualquier programador de C. Estos ejercicios te guiarán a través de la creación de proyectos multi-archivo, el uso de gcc para compilar y enlazar, y la escritura de Makefiles básicos para gestionar la compilación de forma eficiente.

Para profundizar en los conceptos teóricos, podés consultar los siguientes capítulos del apunte:

Proyecto Multi-archivo Manual

Ejercicio 6.1 - Separar un programa ⭐⭐☆☆☆

Tarea: Tomá un programa simple que tengas en un solo archivo .c (por ejemplo, una calculadora con funciones sumar y restar) y dividilo en tres archivos:

Ejercicio 6.2 - Compilación manual ⭐⭐☆☆☆

Tarea: Usando la terminal, compilá y enlazá los archivos del ejercicio anterior sin usar un Makefile.

Automatización con Makefile

Ejercicio 6.3 - Makefile básico ⭐☆☆☆☆

Tarea: Crear un Makefile para el proyecto del ejercicio anterior. El Makefile debe tener los siguientes objetivos (targets):

Compilación Condicional

Ejercicio 6.4 - Mensajes de depuración ⭐⭐☆☆☆

Tarea: Modificá tu proyecto para incluir mensajes de depuración que solo se impriman cuando se compile en “modo debug”.

Ejercicio 6.5 - Compilación Básica ⭐☆☆☆☆

Compilá un programa simple desde línea de comandos.

Orientación:

gcc -o programa programa.c
./programa

Ejercicio 6.6 - Warnings Habilitados ⭐☆☆☆☆

Compilá con warnings completos y corregí los problemas.

Orientación:

gcc -Wall -Wextra -std=c99 -pedantic programa.c -o programa

Ejercicio 6.7 - Dos Archivos Separados ⭐⭐☆☆☆

Separáun programa en main.c y funciones.c.

funciones.h:

#ifndef FUNCIONES_H
#define FUNCIONES_H

int sumar(int a, int b);

#endif

funciones.c:

#include "funciones.h"

int sumar(int a, int b) {
    return a + b;
}

main.c:

#include <stdio.h>
#include "funciones.h"

int main() {
    printf("%d\n", sumar(3, 4));
}

Compilación:

gcc -c funciones.c    # Genera funciones.o
gcc -c main.c         # Genera main.o
gcc funciones.o main.o -o programa

Ejercicio 6.8 - Include Guards ⭐⭐☆☆☆

Explicá por qué se necesitan include guards y implementalos.

Orientación:

#ifndef MI_HEADER_H
#define MI_HEADER_H

// Contenido del header

#endif  // MI_HEADER_H

Ejercicio 6.9 - Makefile Básico ⭐⭐⭐☆☆

Creá Makefile para compilar proyecto de ejercicio 3.

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CC = gcc
CFLAGS = -Wall -Wextra -std=c99

programa: main.o funciones.o
	$(CC) main.o funciones.o -o programa

main.o: main.c funciones.h
	$(CC) $(CFLAGS) -c main.c

funciones.o: funciones.c funciones.h
	$(CC) $(CFLAGS) -c funciones.c

clean:
	rm -f *.o programa

Uso:

make           # Compila
make clean     # Limpia

Ejercicio 6.10 - Biblioteca Matemática ⭐⭐⭐☆☆

Creá biblioteca con funciones matemáticas: factorial, potencia, fibonacci.

Estructura:

proyecto/
├── matematica.h
├── matematica.c
├── test.c
└── Makefile

Orientación:


Ejercicio 6.11 - Flags de Optimización ⭐⭐⭐☆☆

Compilá con diferentes niveles de optimización y medí diferencia.

Orientación:

gcc -O0 programa.c -o prog_O0  # Sin optimización
gcc -O1 programa.c -o prog_O1  # Básica
gcc -O2 programa.c -o prog_O2  # Recomendada
gcc -O3 programa.c -o prog_O3  # Agresiva

Ejercicio 6.12 - Compilación Condicional ⭐⭐⭐☆☆

Usá directivas de preprocesador para debug opcional.

Orientación:

#ifdef DEBUG
    printf("Debug: valor = %d\n", valor);
#endif

Compilación:

gcc -DDEBUG programa.c -o programa  # Con debug
gcc programa.c -o programa           # Sin debug

Ejercicio 6.13 - Módulo de Utilidades ⭐⭐⭐⭐☆

Creá módulo utils con funciones de uso general: intercambio, mínimo, máximo.

Estructura:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// utils.h
#ifndef UTILS_H
#define UTILS_H

void intercambiar(int *a, int *b);
int minimo(int a, int b);
int maximo(int a, int b);

#endif

// utils.c
#include "utils.h"
// Implementaciones...

// main.c
#include "utils.h"
// Uso...

Ejercicio 6.14 - Makefile con Variables ⭐⭐⭐⭐☆

Mejorá Makefile usando variables y patrones.

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CC = gcc
CFLAGS = -Wall -Wextra -std=c99 -O2
LDFLAGS = -lm

SRCS = main.c utils.c matematica.c
OBJS = $(SRCS:.c=.o)
TARGET = programa

$(TARGET): $(OBJS)
	$(CC) $(OBJS) $(LDFLAGS) -o $(TARGET)

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(OBJS) $(TARGET)

.PHONY: clean

Ejercicio 6.15 - Dependencias Automáticas ⭐⭐⭐⭐☆

Generá dependencias automáticamente con gcc.

Orientación:

DEPS = $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
	$(CC) $(CFLAGS) -MMD -MP -c $< -o $@

Ejercicio 6.16 - Biblioteca Estática ⭐⭐⭐⭐⭐

Creá biblioteca estática (.a) y usala en programa.

Orientación:

1
2
3
4
5
6
7
8
# Compilar objetos
gcc -c modulo1.c modulo2.c

# Crear biblioteca estática
ar rcs libmibiblioteca.a modulo1.o modulo2.o

# Usar biblioteca
gcc main.c -L. -lmibiblioteca -o programa

Ejercicio 6.17 - Biblioteca Compartida ⭐⭐⭐⭐⭐

Creá biblioteca compartida (.so) y usala.

Orientación:

1
2
3
4
5
6
7
8
9
10
11
12
# Compilar con PIC (Position Independent Code)
gcc -fPIC -c modulo.c

# Crear biblioteca compartida
gcc -shared modulo.o -o libmodulo.so

# Compilar programa
gcc main.c -L. -lmodulo -o programa

# Ejecutar (necesita encontrar .so)
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./programa

Ejercicio 6.18 - Proyecto Multi-Directorio ⭐⭐⭐⭐⭐

Organizá proyecto en directorios: src/, include/, obj/, bin/.

Estructura:

proyecto/
├── src/
│   ├── main.c
│   └── modulo.c
├── include/
│   └── modulo.h
├── obj/        (generado)
├── bin/        (generado)
└── Makefile

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SRCDIR = src
INCDIR = include
OBJDIR = obj
BINDIR = bin

CFLAGS = -I$(INCDIR) -Wall -Wextra

SRCS = $(wildcard $(SRCDIR)/*.c)
OBJS = $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SRCS))

$(BINDIR)/programa: $(OBJS) | $(BINDIR)
	$(CC) $^ -o $@

$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
	$(CC) $(CFLAGS) -c $< -o $@

$(OBJDIR) $(BINDIR):
	mkdir -p $@

Ejercicio 6.19 - Targets Adicionales ⭐⭐⭐⭐☆

Agregá targets útiles: run, test, install, uninstall.

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
run: $(TARGET)
	./$(TARGET)

test: $(TARGET)
	./$(TARGET) test_input.txt

install: $(TARGET)
	install -m 755 $(TARGET) /usr/local/bin/

uninstall:
	rm -f /usr/local/bin/$(TARGET)

.PHONY: run test install uninstall

Ejercicio 6.20 - Compilación Debug/Release ⭐⭐⭐⭐⭐

Makefile con modo debug y release.

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MODE ?= release

ifeq ($(MODE),debug)
    CFLAGS += -g -O0 -DDEBUG
    TARGET = programa_debug
else
    CFLAGS += -O2 -DNDEBUG
    TARGET = programa
endif

all: $(TARGET)
	@echo "Compiled in $(MODE) mode"

# Uso:
# make              # Release
# make MODE=debug   # Debug

Ejercicio 6.21 - Análisis Estático ⭐⭐⭐⭐☆

Integrá herramientas de análisis en Makefile.

Makefile:

1
2
3
4
5
6
7
8
9
10
check: $(SRCS)
	cppcheck --enable=all $(SRCS)

format: $(SRCS)
	clang-format -i $(SRCS)

valgrind: $(TARGET)
	valgrind --leak-check=full ./$(TARGET)

.PHONY: check format valgrind

Ejercicio 6.22 - Cross-Compilation ⭐⭐⭐⭐⭐

Configurá cross-compilation para diferentes arquitecturas.

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Compilación nativa
CC_NATIVE = gcc
TARGET_NATIVE = programa

# Cross-compilation para ARM
CC_ARM = arm-linux-gnueabi-gcc
TARGET_ARM = programa_arm

arm: CC = $(CC_ARM)
arm: TARGET = $(TARGET_ARM)
arm: $(TARGET)

native: CC = $(CC_NATIVE)
native: TARGET = $(TARGET_NATIVE)
native: $(TARGET)

Ejercicio 6.23 - Sistema de Build Completo ⭐⭐⭐⭐⭐

Proyecto completo con múltiples bibliotecas y tests.

Estructura:

proyecto/
├── lib/
│   ├── util/
│   │   ├── util.h
│   │   └── util.c
│   └── math/
│       ├── math.h
│       └── math.c
├── src/
│   └── main.c
├── tests/
│   ├── test_util.c
│   └── test_math.c
└── Makefile

Orientación:


Ejercicio 6.24 - CMake Básico ⭐⭐⭐⭐⭐

Convertí proyecto a usar CMake en lugar de Makefile.

CMakeLists.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cmake_minimum_required(VERSION 3.10)
project(MiProyecto C)

set(CMAKE_C_STANDARD 99)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")

include_directories(include)

add_executable(programa
    src/main.c
    src/modulo.c
)

target_link_libraries(programa m)

# Tests
enable_testing()
add_executable(tests tests/test_main.c)
add_test(NAME tests COMMAND tests)

Uso:

mkdir build
cd build
cmake ..
make
./programa

Notas Finales

Estas consignas cubren compilación desde comandos básicos hasta proyectos complejos multi-archivo con automatización profesional.