O que segue é um breve resumo, aplicável a ambos C e C++, na medida do possível.
Em C e C++, há o conceito de translation unit (TU) (C, C++). Cada arquivo que vc compila para um arquivo-objeto (com extensao .o
, ou no Windows com extensao .obj
), é oriundo de uma TU.
Primeiro, sua TU é preprocessada -- é neste estágio que o seu arquivo externo é incluído, via diretiva de processador #include
(C, C++). Uma vez preprocessada, sua TU é compilada para um arquivo-objeto. Ao contrário do Python, que considera "arquivos externos" como módulos, a inclusão de arquivos no C++ é feita no pré-processamento, e por isso é uma inclusão meramente textual.
Depois o linker une todos os arquivos-objeto em um único binário, seja executável ou não.
No C/C++, também é possível separar a declaração (C, C++) da sua função de sua definição(C, C++). Como regra geral, tanto para o C quanto para o C++ (e apesar das muitas diferenças entre as duas linguagens), você pode declarar sua função quantas vezes forem necessárias, contanto que:
- Todas as suas declarações possuam a mesma assinatura; e
- Apenas uma definição seja visível pelo linker dentre todas as suas TUs (em C++, chamamos esta regra de ODR).
Para funções, a declaração é essencialmente a assinatura da função. Já a definição é onde vc coloca a implementação (o corpo) da função.
Tome como exemplo o seguinte cenário. Vc tem um arquivo que faz simplesmente a soma de dois números aleatórios e mostra-os na tela.
Arquivo main.c:
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main(void) {
int ra, rb, sum;
printf("ra = "); scanf("%d", &ra);
printf("rb = "); scanf("%d", &rb);
sum = add(ra, rb);
printf("%d + %d = %d\n", ra, rb, sum);
}
Neste caso, o main.c
é sua única TU. Um arquivo como estes pode ser facilmente compilado pelo e.g. GCC da seguinte forma:
gcc -o main.exe main.c
Ou de forma menos implícita:
# Gerar o arquivo-objeto para main.c
gcc -c -o main.o main.c
# Gerar o executável final
gcc -o main.exe main.o
Digamos então que vc queira migrar a função add
para um arquivo separado. Isso é simples: declare a assinatura da função na sua TU, e implemente-a em outra TU (outro arquivo). O linker tratará de encontrar a definição da sua função.
Arquivo main.c:
#include <stdio.h>
int add(int a, int b);
int main(void) {
int ra, rb, sum;
printf("ra = "); scanf("%d", &ra);
printf("rb = "); scanf("%d", &rb);
sum = add(ra, rb);
printf("%d + %d = %d\n", ra, rb, sum);
}
Arquivo add.c:
int add(int a, int b) {
return a + b;
}
Agora, vc precisará compilar os seus dois TUs separadamente, e fazer o link de ambos ao final:
# Gerar os arquivo-objeto para main.c e add.c
gcc -c -o main.o main.c
gcc -c -o add.o add.c
# Gerar o executável final
gcc -o main.exe main.o add.o
Opcionalmente, você poderá deixar a definição das suas funções em um arquivo separado, que será incluído em todos os lugares nos quais a sua função deva ser utilizada. Neste caso, criamos um arquivo header, que geralmente tem sufixo .h
. Vamos refatorar nossa solução:
Arquivo add.h:
int add(int a, int b);
Arquivo add.c:
#include "add.h"
int add(int a, int b) {
return a + b;
}
Arquivo main.c:
#include <stdio.h>
#include "add.h"
int main(void) {
int ra, rb, sum;
printf("ra = "); scanf("%d", &ra);
printf("rb = "); scanf("%d", &rb);
sum = add(ra, rb);
printf("%d + %d = %d\n", ra, rb, sum);
}
A compilação desta solução continua a mesma:
# Gerar os arquivo-objeto para main.c e add.c
gcc -c -o main.o main.c
gcc -c -o add.o add.c
# Gerar o executável final
gcc -o main.exe main.o add.o
Neste ponto, sua solução funciona perfeitamente: há um arquivo separado para a declaração da sua função, que é incluída em outro arquivo caso seja necessário usar a função.
Se fosse, no entanto, necessário incluir o seu arquivo-header mais de uma vez em arquivos diferentes (incluindo outros headers), recomendo usar um include guard. O include guard é um #if
do preprocessador usado para garantir que um arquivo não seja incluído mais de uma vez, e faz sentido apenas para arquivos-header. Alteramos apenas o add.h
todo o resto continua igual:
Arquivo add.h:
#ifndef ADD_H_
#define ADD_H_
int add(int a, int b);
# endif
Uma solução mais simples, mas menos portável (e talvez com alguns poréns), é utilizar #pragma once
:
Arquivo add.h:
#pragma once
int add(int a, int b);