Не знаю как другие, но лично я стараюсь не использовать extern'ы из-за того, что возникает путаница со структурами и переменными, когда она определена где то в дебрях файла foo.c, а используется в bar.c.
Я стараюсь придерживаться модульного подхода, который может иметь примерно следующий вид.
// point.c
/* определение */
struct point {
int x;
int y;
};
void init_point(struct point** p, const int x, const int y)
{
(*p)->x = x;
(*p)->y = y;
}
int get_x(struct point* p)
{
return p->x;
}
int get_y(struct point* p)
{
return p->y;
}
// ... тут может быть много логики ...
// point.h
/* объявление */
/* тут можно подумать, что это инкапсуляция, но это обман зрения.
* на самом деле это просто аля сокрытие данных */
struct point;
// интерфейс
void init_point(struct point** p, const int x, const int y);
int get_x(struct point* p);
int get_y(struct point* p);
// ... ect ...
// main.c
#include "point.h"
int main()
{
struct point* p;
init_point(&p, 1, 1);
p->x; // ошибка
// в других файлах так же изменение и чтение данных производим с помощью функций, а напрямую поля структуры недоступны
const int x = get_x(p);
}