Then, when "child.c" includes "grandparent.h" the second time, the #ifndef test returns false, and the preprocessor skips down to the #endif, thus avoiding the second definition of struct foo.
Далее, когда к «child.c» подключается «grandfather.h» второй раз, проверка этого идентификатора на неопределенность директивой #ifndef не проходит, и препроцессор пропускает всё вплоть до директивы #endif, таким образом избегая второго определения struct foo.