ProgrammingC Developer

What is modularity in the C language, how is it achieved, and what are the difficulties in organizing a multi-module project?

Pass interviews with Hintsage AI assistant

Answer.

Historically, modularity emerged as a way to divide large projects into independent logical parts to improve readability, code reuse, and responsibility sharing among developers. In C, modularity is implemented at the file level — source (.c) and header (.h).

The problem that programmers face is how to organize interaction between parts of the code, avoid duplication of definitions, maintain encapsulation, and simplify compilation.

The solution is to use the separation of interface and implementation:

  • In the .h file, external functions, types, and structures are declared.
  • In the .c file, the implementation is provided.
  • The extern keyword is used for global variables.
  • For "private" entities — static.

Example structure of modular code:

// mymath.h #ifndef MYMATH_H #define MYMATH_H int add(int, int); #endif // mymath.c #include "mymath.h" int add(int a, int b) { return a + b; } // main.c #include "mymath.h" #include <stdio.h> int main() { printf("%d\n", add(3, 4)); return 0; }

Key features:

  • Clear separation of interface (.h) and implementation (.c).
  • Use of static to hide implementation.
  • extern allows sharing variables and functions between modules.

Tricky Questions.

Can you define a variable with extern in a header file and safely use it in multiple modules?

No! Global variables should only be defined in one .c file, and only declared through extern in headers. Otherwise, linker errors will occur due to "multiple definition".

Is it mandatory to include each header file through #include only once?

It is necessary to wrap each .h file with guard macros (#ifndef/#define/#endif), otherwise, conflicts in declarations and compilation errors will arise due to multiple inclusions.

Is it possible to achieve pure encapsulation of private data in a structure (opaque pointer) in C?

Yes. The so-called "opaque pointer" allows hiding the structure details from the user:

// mystruct.h typedef struct MyStruct MyStruct; MyStruct* create(void); void destroy(MyStruct*); // mystruct.c struct MyStruct { int a; };

Common mistakes and anti-patterns

  • Confusion between declaration and definition of variables.
  • Lack of include guards.
  • Violation of encapsulation (exposing private details in header).

Real-life example

Negative case

All logic is implemented in one long .c file, code is repeated, global variables overlap, leading to linker errors.

Pros:

  • Fast prototyping.

Cons:

  • Poor maintainability, risk of conflicts, complicated debugging.

Positive case

Code is decomposed into modules, include guards are used, private data is hidden via opaque pointers.

Pros:

  • Easy maintenance, module isolation, readability, scalability.

Cons:

  • Initially requires thoughtful architecture.