У вас получается один файл разными путями попадает в одно и то же
место // === file super.c
#include "my_foo.h"
#include "my_bar.h"
// === file my_foo.h
#include "my_bar.h"
//...
#define MY_MACRO(A, B) ( (A) * (B) )
// === file my_bar.h
//...
#define MY_MACRO(A, B) ( (A) + (B) )
При компиляции пожалуется на переопределение макроса MY_MACRO, потому что ... оставлю проследить связи как самостоятельную работу.
Чтобы такой фигни не было, следует обрамить файлы в директивы для предотвращения повторного включения (на примере my_bar.h):
#pragma once
#ifndef __MY_BAR_H__
#define __MY_BAR_H__
#define MY_MACRO(A, B) ( (A) + (B) )
#endif /* __MY_BAR_H__ */
С переменными работает подобным образом, только там декларации переменных. Как вариант - одно и то же имя переменной с разными типами, примерно:
//file1.h
unsigned int my_val;
//file2.h
uint32_t my_val;
Для компилятора это разные типы (если только не дефайн один от другого, но такой стиль - фу-фу-фу).
Как поведёт себя компилятор при дублирующемся объявлении - одни могут промолчать, другие ругаться будут или от ключей зависит. В любом случае такая декларация - нездоровая ситуация.
Так что правильно ругается. И правильно вас к букварю отсылают - видимо есть пробел знаний в этом конкретном месте.