C Programming Fundamentals: Functions, Data Types, Structures, Loops

Role of Functions in Programming

Functions are fundamental building blocks in programming that serve several important roles:

  1. Modularity: Functions allow programmers to break down complex problems into smaller, manageable pieces. Each function can handle a specific task, making the code easier to understand and maintain.
  2. Reusability: Once a function is defined, it can be reused multiple times throughout a program or even in different programs. This reduces code duplication and enhances maintainability.
  3. Abstraction: Functions provide a way to abstract away the details of implementation. Users of a function can focus on what the function does rather than how it does it.
  4. Organization: Functions help organize code logically. By grouping related operations into functions, the overall structure of the program becomes clearer.
  5. Testing and Debugging: Functions can be tested independently, making it easier to identify and fix bugs. This isolation helps ensure that changes in one part of the program do not inadvertently affect other parts.

Function Declaration, Definition, and Calling in C

1. Declaration (Prototype)

Informs the compiler about the function’s name, return type, and parameters.

Syntax:

return_type function_name(parameter_list);

Example:

int factorial(int n); // Declaration

2. Definition

Implements the function’s logic.

Syntax:

return_type function_name(parameter_list) {
    // Function body
}

Example:

int factorial(int n) { // Definition
    if (n == 0) return 1;
    else return n * factorial(n - 1);
}

3. Calling

Invokes the function to execute its code.

Syntax:

function_name(arguments);

Example:

int result = factorial(5); // Calling

Program: Factorial using Recursion (C)

#include <stdio.h>

// Function declaration
int factorial(int n);

int main() {
    int n, result;
    printf("Enter a positive integer: ");
    scanf("%d", &n);

    if (n < 0) {
        printf("Factorial of negative numbers is not defined.\n");
    } else {
        result = factorial(n); // Function call
        printf("Factorial of %d = %d\n", n, result);
    }
    return 0;
}

// Function definition (recursive)
int factorial(int n) {
    if (n == 0 || n == 1) { // Base case
        return 1;
    } else {
        return n * factorial(n - 1); // Recursive call
    }
}

Identifiers and Keywords in C

Identifier

An identifier is a name given to variables, functions, arrays, structures, etc., in a program. It is used to uniquely identify an element in the code.

Example: int age; → Here, age is an identifier.

Keyword

A keyword is a reserved word in C that has a predefined meaning and cannot be used as an identifier.

Example: int, if, else, return, etc.

Rules for Naming Valid Identifiers in C

  1. Must start with a letter (A-Z, a-z) or an underscore (_).
    • Valid: sum, _count
    • Invalid: 1num (starts with a digit)
  2. Can contain letters, digits (0-9), and underscores (_).
    • Valid: total_amount, student1
    • Invalid: user@name (contains special character @)
  3. Cannot be a keyword.
    • Valid: myInt
    • Invalid: int (keyword)
  4. Case-sensitive.
    • Age and age are different identifiers.
  5. No special symbols (like @, #, $, etc.) allowed.
    • Valid: average_score
    • Invalid: score#1
  6. No whitespace allowed.
    • Valid: studentName
    • Invalid: student Name

Examples of Valid and Invalid Identifiers

ValidInvalidReason
count1countStarts with a digit
_tempfloatfloat is a keyword
totalSumtotal-sumHyphen (-) not allowed
num1num 1Space not allowed

Program: Sum of Digits (C)

#include <stdio.h>

int main() {
    int num, sum = 0, remainder;
    printf("Enter a number: ");
    scanf("%d", &num);

    while (num != 0) {
        remainder = num % 10; // Extract the last digit
        sum += remainder; // Add the digit to sum
        num /= 10; // Remove the last digit
    }
    printf("Sum of digits = %d\n", sum);
    return 0;
}

Structure vs. Union in C

FeatureStructureUnion
Memory AllocationEach member has its own memory space.All members share the same memory space.
Total SizeSum of sizes of all members (plus padding).Equal to the size of the largest member.
Member AccessAll members can be accessed simultaneously.Only one member can be accessed at a time.
Member ChangesChanges to one member don’t affect other members.Changing one member affects all other members.

Program: Student Information using Structure (C)

#include <stdio.h>
#include <string.h>

// Define the student structure
struct Student {
    int roll;
    char name[50];
    char address[100];
    char email[50];
    char phone[15];
};

int main() {
    // Create a student variable
    struct Student student;

    // Store student information
    student.roll = 101;
    strcpy(student.name, "John Doe");
    strcpy(student.address, "123 Main St, Cityville");
    strcpy(student.email, "john.doe@example.com");
    strcpy(student.phone, "+1234567890");

    // Display student information
    printf("Student Information:\n");
    printf("Roll Number: %d\n", student.roll);
    printf("Name: %s\n", student.name);
    printf("Address: %s\n", student.address);
    printf("Email: %s\n", student.email);
    printf("Phone: %s\n", student.phone);

    return 0;
}

Expressions and Operators in C

Expression

An expression in programming is a combination of variables, constants, operators, and function calls that evaluates to a single value.

Examples:

  • 5 + 3 // Evaluates to 8
  • x * y + z // Depends on values of x, y, z
  • func(a, b) // Function call that returns a value

Operator

An operator is a symbol that performs an operation on operands (variables or values).

Examples:

  • Arithmetic: +, -, *, /, %
  • Relational: ==, !=, >, <, >=, <=
  • Logical: &&, ||, !
  • Assignment: =, +=, -=, *=, /=, %=

Operator Associativity in C

Operator associativity determines the order in which operators of the same precedence are evaluated in an expression.

Left-associative

Operators evaluate from left to right.

  • Example: a + b + c is evaluated as (a + b) + c.
  • Most operators in C (e.g., +, -, *, /) are left-associative.

Right-associative

Operators evaluate from right to left.

  • Example: a = b = c is evaluated as a = (b = c).
  • Assignment (=), ternary (?:), and unary (++, --) operators are right-associative.

Example:

int x = 5, y = 3, z = 2;
int result = x * y / z; // Left-associative: (x * y) / z → 15 / 2 → 7 (integer division)

Program: Factorial using Recursion (C)

#include <stdio.h>

// Recursive function to compute factorial
unsigned long long factorial(int n) {
    if (n == 0) { // Base case
        return 1;
    }
    return n * factorial(n - 1); // Recursive call
}

int main() {
    int num;
    printf("Enter a non-negative integer: ");
    scanf("%d", &num);

    if (num < 0) {
        printf("Error: Factorial of a negative number is undefined.\n");
    } else {
        unsigned long long result = factorial(num);
        printf("Factorial of %d = %llu\n", num, result);
    }
    return 0;
}

Data Types in C and Their Ranges

Basic Data Types in C

1. char

  • Size: 1 byte
  • Range: -128 to 127 (signed) or 0 to 255 (unsigned)
  • Format specifier: %c

2. int

  • Size: 2 or 4 bytes (compiler dependent)
  • Range: -32,768 to 32,767 (2-byte) or -2,147,483,648 to 2,147,483,647 (4-byte)
  • Format specifier: %d

3. float

  • Size: 4 bytes
  • Range: 1.2E-38 to 3.4E+38
  • Format specifier: %f

4. double

  • Size: 8 bytes
  • Range: 1.7E-308 to 1.7E+308
  • Format specifier: %lf

5. void

  • Represents absence of type

Program: Palindrome Check (C)

#include <stdio.h>

int isPalindrome(int num) {
    int original = num;
    int reversed = 0;
    // Handle negative numbers (they can't be palindromes)
    if (num < 0) {
        return 0;
    }
    // Reverse the number
    while (num > 0) {
        reversed = reversed * 10 + num % 10;
        num /= 10;
    }
    // Check if reversed number equals original
    return original == reversed;
}

int main() {
    int number;
    printf("Enter an integer: ");
    scanf("%d", &number);

    if (isPalindrome(number)) {
        printf("%d is a palindrome.\n", number);
    } else {
        printf("%d is not a palindrome.\n", number);
    }
    return 0;
}

Global Variables: Advantages and Disadvantages

Advantages:

  1. Easy to Use: Global variables can be accessed from any part of the program, making them convenient for sharing data across multiple functions.
  2. Avoids Passing Parameters: Reduces the need to pass variables as parameters between functions, which can simplify code in some cases.
  3. Persistent Data: Global variables retain their values throughout the program’s execution, which can be useful for maintaining state.

Disadvantages:

  1. Unintended Modifications: Since global variables can be modified anywhere in the program, it can lead to bugs that are hard to trace.
  2. Reduced Modularity: Overuse of global variables can make the code less modular and harder to maintain or debug.
  3. Namespace Pollution: Too many global variables can clutter the global namespace, leading to potential naming conflicts.
  4. Testing Difficulties: Functions that depend on global variables are harder to test in isolation.

Program: Merge and Sort Files (Python)

Note: The original prompt requested a C program, but the provided snippet was in Python. This is a completed Python implementation.

def merge_and_sort_files(file1, file2, output_file):
    # Read integers from the first file
    with open(file1, 'r') as f1:
        numbers1 = list(map(int, f1.read().split()))

    # Read integers from the second file
    with open(file2, 'r') as f2:
        numbers2 = list(map(int, f2.read().split()))

    # Merge and sort the numbers
    merged_numbers = sorted(numbers1 + numbers2)

    # Write the sorted numbers to the output file
    with open(output_file, 'w') as f3:
        for num in merged_numbers:
            f3.write(str(num) + '\n')

# Example usage (assuming num1.txt and num2.txt exist):
# Create dummy files for demonstration
# with open('num1.txt', 'w') as f: f.write('10\n20\n5\n')
# with open('num2.txt', 'w') as f: f.write('15\n3\n25\n')
# merge_and_sort_files('num1.txt', 'num2.txt', 'file3.txt')
# print('Files merged and sorted into file3.txt')

Nested Structures in C

A nested structure in programming is a structure that contains another structure as a member. This allows for organizing complex data types in a hierarchical manner. In C, for example, you can define a structure for a student and then include another structure for subjects or marks within it.

Program: Student Records with Nested Structure (C)

#include <stdio.h>
#include <string.h>

#define MAX_STUDENTS 10

struct Marks {
    float math;
    float c_programming;
    float english;
};

struct Student {
    char name[50];
    int roll_number;
    struct Marks marks; // Nested structure
};

int main() {
    struct Student students[MAX_STUDENTS];
    int i;

    // Input records for 10 students
    for (i = 0; i < MAX_STUDENTS; i++) {
        printf("Enter details for student %d:\n", i + 1);
        printf("Name: ");
        scanf(" %[^
]", students[i].name); // Read string with spaces
        printf("Roll Number: ");
        scanf("%d", &students[i].roll_number);
        printf("Marks in Math: ");
        scanf("%f", &students[i].marks.math);
        printf("Marks in C Programming: ");
        scanf("%f", &students[i].marks.c_programming);
        printf("Marks in English: ");
        scanf("%f", &students[i].marks.english);
        printf("\n");
    }

    // Display records of students who passed in C programming
    printf("Students who passed in C Programming:\n");
    for (i = 0; i < MAX_STUDENTS; i++) {
        if (students[i].marks.c_programming >= 50) {
            printf("Name: %s, Roll Number: %d, C Programming Marks: %.2f\n",
                   students[i].name, students[i].roll_number, students[i].marks.c_programming);
        }
    }
    return 0;
}

When to Use For vs. While Loops

For Loop

A for loop is typically used when the number of iterations is known beforehand. It is ideal for iterating over a range of values or through elements in a collection (like an array or list).

Syntax:

for initialization; condition; increment/decrement:
    # code block (C/C++ style syntax shown, Python uses 'for item in iterable:')

While Loop

A while loop is used when the number of iterations is not known in advance and depends on a condition being true. It continues to execute as long as the condition remains true.

Syntax:

while condition:
    # code block

Comparison of While and Do-While Loops

While Loop

The condition is checked before the execution of the loop body. If the condition is false initially, the loop body may not execute at all.

Example (Python):

count = 0
while count < 5:
    print(count)
    count += 1

Do-While Loop

The condition is checked after the execution of the loop body. This guarantees that the loop body will execute at least once, regardless of whether the condition is true or false initially.

Example (Python simulation, as Python lacks a built-in do-while):

count = 0
while True:
    print(count)
    count += 1
    if count >= 5:
        break

Program: Calculate Student Ages (Python)

# Initialize an empty list to store ages
ages = []

# Input ages of 20 students
for i in range(20):
    age = int(input(f"Enter the age of student {i + 1}: "))
    ages.append(age)

# Calculate average, minimum, and maximum age
average_age = sum(ages) / len(ages)
min_age = min(ages)
max_age = max(ages)

# Display the results
print(f"The average age is: {average_age:.2f}")
print(f"The minimum age is: {min_age}")
print(f"The maximum age is: {max_age}")