w3resource

C Programming Functions: Declarations, Definitions & Examples

Functions in C Programming

Overview:

Functions allow us to break our program into smaller, more manageable subprocedures. Before using a function, we need to define it and optionally declare it explicitly. Although function declaration isn’t strictly required, omitting it may generate compiler warnings if the default declaration does not match the function’s actual signature. Every C program must have at least one function, which is main. The execution of a program begins with the main function.

Why use Functions?

Functions help in:

  • Breaking down complex problems.
  • Reducing code redundancy.
  • Improving readability and maintainability.
  • Encouraging code reuse across different parts of the program.

Key Topics:

Function Declarations

Function declarations specify the name of the function, its parameters, and its return type. These declarations end with a semicolon (;). The general syntax for a function declaration is:

return-type function-name(parameter-list);

Where:

  • return-type: Specifies the type of value the function will return. If the function doesn’t return a value, use void.
  • function-name: A valid identifier for the function.
  • parameter-list: This is a comma-separated list of zero or more parameters, each defined by its data type and an optional name. If no parameters are required, use void explicitly.

You can also declare functions with multiple parameters or no parameters at all. If there are no parameters, the parameter list should be void, though omitting the list also works. However, it is better to explicitly declare void for clarity.

Example:

int test(int x, int y, double z);

In this example, the function test accepts three parameters: two integers (x and y) and one double (z). Each parameter name must be unique within the function declaration.

Function Definitions

A function definition includes the function’s name, return type, parameter types and names, and the function body. The body consists of a series of statements enclosed in curly braces {}.

Identifiers, used for naming variables, functions, and other elements, are strings of characters that can contain letters, digits, and underscores (_). Identifiers must not start with a digit, and C is case-sensitive, so foo and FOO would be considered different.

Basic Function Example:

This example demonstrates a simple function that takes two integers as input, adds them, and returns the sum. It highlights function declaration, call, and definition.

Code:

#include <stdio.h>
// Function declaration
int add(int a, int b);  // Tells the compiler about the function
int main() {
    int x = 5, y = 10;
    int result = add(x, y);  // Function call with arguments
    printf("Sum: %d\n", result);
    return 0;
}
// Function definition
int add(int a, int b) {  
    return a + b;  // Performs addition and returns the result
}

Output:

Sum: 15

Explanation:

  • Function Declaration: The function add is declared before main(). It takes two integer arguments (int a, int b) and returns an integer (int).
  • Function Call: Inside main(), add is called with x and y as arguments.
  • Function Definition: The actual body of the function is defined after main(), where the addition happens.

Function without Return (void)

A function declared with the void return type in C does not return any value to the caller. This type of function is typically used to perform actions or tasks without needing to send any result back.

Example:

This example showcases a function without a return value, demonstrating how to declare and use functions that perform actions without returning anything.

Code:

#include <stdio.h>
// Function declaration
void greet();  // No return type, no parameters

int main() {
    greet();  // Function call
    return 0;
}

// Function definition
void greet() {
    printf("Hello, World!\n");  // Simple print statement
}

Output:

Hello, World!

Explanation:

  • The greet function is declared with void because it doesn't return any value.
  • It takes no arguments, and when called, it simply prints a message.

Pass by Value:

In pass by value, a copy of the variable is passed to the function. Any changes made inside the function do not affect the original variable.

Example:

This example shows the concept of pass by value, where a copy of the variable is passed to the function, and any changes inside the function do not affect the original variable.

Code:

 #include <stdio.h>
// Function declaration
void modifyValue(int num);
int main() {
    int a = 10;
    printf("Before: %d\n", a);
    modifyValue(a);  // Pass by value
    printf("After: %d\n", a);  // Value remains unchanged
    return 0;
}
// Function definition
void modifyValue(int num) {
    num = 20;  // Modifies local copy, not the original
}

Output:

Before: 10
After: 10

Explanation:

  • In this example, a is passed to modifyValue by value.
  • Inside modifyValue, the copy of a is modified, but the original variable in main() remains unchanged.

Pass by Reference:

In pass by reference, the actual memory address of the variable is passed, allowing the function to modify the original variable.

Example:

This example illustrates pass by reference, where the function modifies the original variable by passing its memory address using a pointer.

Code:

#include <stdio.h>
// Function declaration
void modifyValue(int *num);  // Pointer as parameter
int main() {
    int a = 10;
    printf("Before: %d\n", a);
    modifyValue(&a);  // Pass by reference using the address
    printf("After: %d\n", a);  // Value is changed
    return 0;
}
// Function definition
void modifyValue(int *num) {
    *num = 20;  // Modifies the original variable through the pointer
}

Output:

Before: 10
After: 20

Explanation:

  • Here, a pointer (int *num) is passed to the function.
  • The function uses *num to access and modify the actual variable a in main().
  • After the function call, the value of a is updated to 20.

Recursive Functions in C

A recursive function is a function that calls itself in order to solve smaller instances of a problem. Each recursive call works with a reduced or simplified version of the original problem until it reaches a base case, which stops the recursion.

Recursive functions are an important concept in C programming, especially for tasks like sorting, searching, or tree traversal.

Example:

This example demonstrates recursion by calculating the factorial of a number. Each function call reduces the problem size until the base case (n == 0) is reached.

Code:

#include <stdio.h>

// Function declaration
int factorial(int n);

int main() {
    int number = 5;
    printf("Factorial of %d is %d\n", number, factorial(number));
    return 0;
}

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

Output:

Factorial of 7 is 5040

Explanation:

  • The factorial function calls itself with n - 1 until n equals 0.
  • The base case returns 1, which stops the recursion and allows the results to be calculated.

Error Handling in C Functions

Error handling in C functions involves detecting and managing errors that occur during the execution of a function. Since C does not have built-in exception handling like some other languages, error handling is typically done using return values, global variables, or special error codes.

Common Approaches:

  • Return Values: Functions can return specific values to indicate errors. For example, a function might return -1 to signify an error, while returning a valid result for successful operations.

Code:

int divide(int a, int b) {
    if (b == 0) {
        return -1;  // Error: division by zero
    }
    return a / b;
}

Error Codes: Functions can use integer codes to represent various error states, which can be checked by the caller.

Code:

#define ERROR_DIV_BY_ZERO -1
int divide(int a, int b) {
    if (b == 0) {
        return ERROR_DIV_BY_ZERO;  // Return error code
    }
    return a / b;
}

Global Error Variables: For more complex programs, global variables can store error codes or messages, providing a way to report errors across different parts of the program.

Code:

int errorCode = 0;
int divide(int a, int b) {
    if (b == 0) {
        errorCode = 1;  // Set error code
        return 0;
    }
    return a / b;
}

Pointers to Functions (Function Pointers)

Function pointers allow you to dynamically assign different functions and improve the flexibility of your program. Including a brief introduction could be valuable for intermediate users.

Example:

Here a function pointer is declared and used to call the printMessage() function. This concept can be powerful for advanced function handling.

Code:

#include <stdio.h>
// Function declarations
void printMessage(char* message);

int main() {
    // Declare a function pointer
    void (*funcPtr)(char*);
    
    // Point it to printMessage function
    funcPtr = &printMessage;
    
    // Call the function via pointer
    funcPtr("Hello, Function Pointers!");
    
    return 0;
}

// Function definition
void printMessage(char* message) {
    printf("%s\n", message);
}

Output:

Hello, Function Pointers!

Edge Case

An edge case is a scenario that occurs at the extreme (boundary) of the input domain of a function or system. These cases often test the limits or constraints of the program and can reveal issues that are not apparent with typical inputs. Edge cases are crucial for ensuring the robustness and reliability of software.

Examples:

  • Zero or Negative Values: Passing zero or negative numbers to functions that expect positive values.
  • Large Inputs: Providing very large values to test the function's handling of integer overflows or memory usage.
  • Empty Inputs: Supplying empty strings or null pointers to functions that process data.

In the following example, the edge case is attempting to divide by zero, which is handled by returning an error code. Testing edge cases helps ensure that the function behaves correctly under all possible input conditions.

Code:

#include <stdio.h>
int divide(int a, int b) {
    if (b == 0) {
        return -1;  // Error: Division by zero
    }
    return a / b;
}

int main() {
    int result = divide(10, 0);  // Edge case: division by zero
    if (result == -1) {
        printf("Error: Division by zero.\n");
    } else {
        printf("Result: %d\n", result);
    }
    return 0;
}

Output:

Error: Division by zero.


Become a Patron!

Follow us on Facebook and Twitter for latest update.