This repository is for my first-year students (Licence 1, Section 5) for the academic year 2025/2026.
It contains the code explained during our practical sessions for:
- TP5 — Functions in C (Library Version)
- TP6 — Structures in C
This repo serves as a reference for you to review and understand the implementations discussed in class.
The theoretical explanations (function delegation, DRY principle, struct memory layout, etc.) are detailed in your assignment reports and class notes. Here, you will find the implementations with practical guidance.
TP5-functions/
├── solve_quadratic.c → Old version: Final integrated solution
├── my_pow.c → Old version: my_pow() with test main
├── my_discr.c → Old version: compute_discr() with test main
├── my_sqrt.c → Old version: my_sqrt() with test main
└── lib_version/
├── main.c → New version: Entry point (single main)
├── my_math.c → New version: All implementations
└── my_math.h → New version: Function declarations
TP6_structures/
├── students.c → Complete student management program
└── test.c → Memory layout demonstration (padding vs packing)
| What do you want to learn? | Where to go? |
|---|---|
| How to write individual functions | TP5-functions/ (old version) |
| How to test functions separately | TP5-functions/my_*.c files |
| How to organize a C project | TP5-functions/lib_version/ |
| How to use header files | TP5-functions/lib_version/my_math.h |
| How to work with structures | TP6_structures/students.c |
| How memory alignment works | TP6_structures/test.c |
TP5 contains two different implementations of the same solution:
- Each function in its own
.cfile - Each file has its own
main()for independent testing - Files:
solve_quadratic.c,my_pow.c,my_discr.c,my_sqrt.c - Purpose: Learning to write and test functions independently
- Professional library structure
- Single
main()inmain.c - All implementations in
my_math.c - Declarations in
my_math.h - Purpose: Learning proper C project organization
Both! They teach different concepts:
| Old Version | Lib Version |
|---|---|
| ✓ Independent testing | ✓ Professional structure |
| ✓ One function = one file | ✓ Separation of interface/implementation |
| ✓ Easy debugging | ✓ Reusable library |
| ✓ Beginner-friendly | ✓ Industry standard |
Recommended Learning Path:
- Start with the old version to understand each function
- Move to lib version to see professional organization
- Understand why the lib version is better for real projects
The old version teaches you how to write functions.
The lib version teaches you how to organize a project.
Both are essential skills!
The old version demonstrates the learning phase approach:
- Each function is isolated in its own file
- Every file has a
main()function for testing - Easy to test and debug individual functions
- Good for understanding function responsibilities
The final integrated solution that:
- Calls
compute_discr() - Uses
sqrt()from<math.h>ormy_sqrt() - Determines number of real roots
- Returns results using pointers
Compilation:
gcc solve_quadratic.c -o solve -lmStandalone implementation of power function:
- Contains
double my_pow(double base, int exp) - Has its own
main()with tests - Can be compiled and tested independently
Compilation:
gcc my_pow.c -o pow_testDiscriminant calculation:
- Contains
double compute_discr(double a, double b, double c) - Uses
my_pow()(must be included) - Has its own test
main()
Compilation:
gcc my_discr.c -o discr_testSquare root using Newton's method:
- Contains
double my_sqrt(double S) - Has its own test comparing with standard
sqrt() - Independent testing and validation
Compilation:
gcc my_sqrt.c -o sqrt_test -lm✅ Easy to learn: One concept per file
✅ Independent testing: Test each function separately
✅ Easy debugging: Isolate problems quickly
✅ Clear focus: Each file has one responsibility
❌ Not scalable: Too many files for larger projects
❌ Code duplication: Each file might redefine helpers
❌ Not professional: Not how real projects are organized
❌ Multiple mains: Can't combine files easily
While the old version is primarily for learning, the pattern of "one file with test main" is actually used professionally for:
- Unit testing: Each module has its own test file
- Algorithm prototyping: Quick experimentation
- Code interviews: Self-contained solutions
- Documentation examples: Runnable code snippets
The difference is that in real projects, you'd separate test files from implementation files (e.g., my_sqrt.c + test_my_sqrt.c).
This version demonstrates proper C library structure with:
- Separation of interface (
.h) and implementation (.c) - Single
main()function inmain.c - All function implementations in
my_math.c - Function declarations in
my_math.h
This is the professional approach to organizing C code.
This header file contains:
- Function declarations (prototypes)
- Include guards (
#ifndef,#define,#endif) - No implementations, only signatures
Purpose: Tells other files what functions are available.
#ifndef MY_MATH_H
#define MY_MATH_H
double my_pow(double base, int exp);
double my_fabs(double nbr);
double my_sqrt(double S);
double compute_discr(double a, double b, double c);
void solve_quadratic(double a, double b, double c,
double *x1, double *x2, int *num_roots);
#endifContains the actual code for all functions:
-
my_pow()— Power calculation without usingpow()- Uses a loop to multiply base by itself exp times
- Handles edge cases (negative exponents, 0^0)
-
my_fabs()— Absolute value- Simple ternary operator implementation
- Used by
my_sqrt()for precision checking
-
my_sqrt()— Square root using Newton's method- Iterative approximation method
- Stops when precision (epsilon) is reached
-
compute_discr()— Discriminant calculation- Uses
my_pow()instead of rewritingb*b - Demonstrates function delegation
- Uses
-
solve_quadratic()— Complete quadratic equation solver- Uses
compute_discr()andmy_sqrt() - Returns multiple values using pointers
- Uses
- Contains only the
main()function - Calls
solve_quadratic() - Displays results
- Clean separation of concerns
gcc main.c my_math.c -o my_program -lmgcc *.c -o my_program -lmEven though we implemented our own my_sqrt(), the program might still need the math library for other operations. It's safe to always include it.
- Declare functions (tell the compiler they exist)
- Allow different
.cfiles to use the same functions - Prevent "implicit declaration" errors
- Contain the actual function code
- Include the corresponding
.hfile - Multiple
.cfiles can be compiled together
#ifndef MY_MATH_H
#define MY_MATH_H
// ... declarations ...
#endif- Prevent multiple inclusions of the same header
- Avoid "redefinition" errors
double compute_discr(double a, double b, double c) {
return my_pow(b, 2) - 4 * a * c; // Calls my_pow() instead of b*b
}- Don't Repeat Yourself (DRY principle)
- Each function has one clear responsibility
void solve_quadratic(double a, double b, double c,
double *x1, double *x2, int *num_roots)- A function can only
returnone value - Use pointers to "return" multiple values
- The function modifies the variables through pointers
Try to answer these questions:
- Why do we put declarations in
.hand implementations in.c? - What happens if you forget to include
my_math.hinmain.c? - Why does
compute_discr()callmy_pow()instead of usingb * b? - How does
solve_quadratic()return 3 values (x1, x2, num_roots)? - What would happen if you compiled only
main.cwithoutmy_math.c?
This TP introduces structures (struct) in C:
- Grouping related data together
- Arrays of structures
- Memory layout (padding vs packing)
- Real-world data modeling
Purpose: Demonstrate practical use of structures.
-
Defines a
Studentstructure with 4 fields:struct Student { int id; // 4 bytes char name[50]; // 50 bytes int age; // 4 bytes float grade; // 4 bytes };
-
Creates an array of structures:
struct Student students[50];
-
Reads student information from user input
-
Displays all students
-
Calculates average grade
Accessing struct fields:
students[i].id // Access id of student i
students[i].name // Access name of student i
students[i].grade // Access grade of student iThe dot operator (.):
- Used to access members of a structure
- Syntax:
structure_variable.field_name
Why use structures?
- Logically group related data
- Easier to manage than separate arrays
- Represents real-world entities clearly
Purpose: Understand how structures are stored in memory.
#pragma pack(0) // Default padding (optimized for speed)
struct abc {
char a; // 1 byte
int c; // 4 bytes
char b; // 1 byte
};Question: How much memory does this struct use?
- Expected: 1 + 4 + 1 = 6 bytes
- Actual: 12 bytes! Why?
32-bit CPUs read memory in 4-byte chunks (words)
Memory layout:
[a][ ][ ][ ] — 4 bytes (a + 3 padding bytes)
[c][c][c][c] — 4 bytes (int c)
[b][ ][ ][ ] — 4 bytes (b + 3 padding bytes)
Total: 12 bytes
CPU cycles: 3 (one per word) ✓ FAST
Memory layout:
[a][c][c][c] — 4 bytes
[c][b][ ][ ] — 4 bytes
Total: 6 bytes
CPU cycles: More than 3 ✗ SLOWER
Why? The CPU must perform extra operations to extract the int c that spans across two memory words.
| Padding (Default) | Packing |
|---|---|
| More memory (12B) | Less memory (6B) |
| Faster access | Slower access |
| Aligned to word | Not aligned |
In practice: Let the compiler handle it (use padding). Memory is cheap, speed matters.
struct Student {
int id;
char name[50];
int age;
float grade;
};- Groups related variables together
- Creates a new data type
struct Student student1; // Single student
struct Student students[50]; // Array of studentsstudent1.id = 12345;
printf("%s", students[0].name);struct Student s1 = {101, "Ahmed", 20, 15.5};- CPU reads memory in word-sized chunks
- Padding ensures efficient access
- Use
sizeof()to check actual size
- Navigate to
TP5-functions/(root) - Start with
my_pow.c- understand the simplest function - Move to
my_sqrt.c- see Newton's method - Study
my_discr.c- see function delegation - Finally read
solve_quadratic.c- the complete solution - Compile each file independently:
gcc my_pow.c -o pow_test gcc my_sqrt.c -o sqrt_test -lm gcc solve_quadratic.c -o solve -lm
- Test each function separately to understand it
- Navigate to
TP5-functions/lib_version/ - Read
my_math.hto understand available functions - Read
my_math.cto see implementations - Study
main.cto see how everything connects - Compile and test:
gcc *.c -o program -lm - Experiment: Try adding a new function to the library
- Compare: Notice how this is cleaner than having separate files
- Navigate to
TP6_structures/ - Compile
students.c:gcc students.c -o students - Run and test with different inputs
- Compile
test.c:gcc test.c -o test - Observe the
sizeof()output - Experiment: Try
#pragma pack(1)and see the difference
Make sure you understand:
- Why each function exists
- What its responsibility is
- How structures organize data
- How memory is laid out
- Writing your own header files
- Splitting code into multiple files
- Using structures for real problems
- Understanding memory trade-offs
❌ Forgetting to include the header file
❌ Forgetting -lm when compiling
❌ Not using & when passing pointers
❌ Confusing . (struct) with -> (pointer to struct)
❌ Not initializing structure variables
- What's the difference between
my_math.handmy_math.c? - Why does
compute_discr()callmy_pow()instead of usingb * b? - How does
solve_quadratic()return 3 values (x1, x2, num_roots)? - What's the difference between Newton's method and using the standard
sqrt()?
- Why can the old version have multiple
main()functions but lib version can't? - What are the advantages of having each function in a separate file (old version)?
- What are the advantages of the library structure (lib version)?
- When would you use the old version approach in real programming?
- How does the compiler handle multiple
.cfiles in the lib version? - What happens if you try to compile all old version files together?
- What's the size of
struct Student? (Hint: Usesizeof()) - How do you access the grade of the 3rd student in an array?
- Why is padding used in structures?
- What does
#pragma pack(1)do? - Why is padding faster even though it uses more memory?
# TP5 - Old Version (Individual Testing)
cd TP5-functions/
gcc my_pow.c -o pow_test
./pow_test
gcc my_sqrt.c -o sqrt_test -lm
./sqrt_test
gcc solve_quadratic.c -o solve -lm
./solve
# TP5 - Library Version (Professional Structure)
cd TP5-functions/lib_version/
gcc *.c -o quadratic -lm
./quadratic
# Alternative: Specify files explicitly
gcc main.c my_math.c -o quadratic -lm
./quadratic
# TP6 - Students
cd TP6_structures/
gcc students.c -o students
./students
# TP6 - Memory Test
gcc test.c -o test
./testThis repository is a learning resource, not just a solution.
Your goal is to:
✓ Write clean, modular code
✓ Understand function organization
✓ Master structures and memory concepts
✓ Build good programming habits
If something is unclear:
- Review your class notes
- Test the code yourself
- Experiment with modifications
- Ask specific questions during TP sessions
Remember: Understanding > Copying
Programming is learned by doing. Compile, run, modify, break things, fix them, and learn from the process.