Practical Insights on C Language: A Delivery Guide from the Return Courier

Scan the code to follow Chip Dynamics and say goodbye to “chip” congestion!

Practical Insights on C Language: A Delivery Guide from the Return CourierPractical Insights on C Language: A Delivery Guide from the Return CourierSearch WeChatPractical Insights on C Language: A Delivery Guide from the Return CourierChip DynamicsPractical Insights on C Language: A Delivery Guide from the Return Courier

Imagine you (the main function) are the boss of a company, with a group of diligent employees (other functions) under you. You assign employee Xiao Zhang (function calculateSum) a task: “Go, help me calculate how much 1 plus 100 equals!” After Xiao Zhang finishes calculating, how does he tell you the result? Shouting at the top of his lungs? That’s too unrefined! At this moment, the return courier shines! Xiao Zhang hands the calculation result (an integer, like 5050) to return, and return immediately packages it and instantly delivers it back to your office (the calling point of the main function). You just need to say:

int total = calculateSum(); // The boss receives the delivery

Look, return has completed its core mission: safely and accurately delivering the result of the function’s calculation back to the place that called it (the delivery address).

The basic syntax of return1.1 Return Value

  • This is the main job of return. A function is like a small processing factory, taking in raw materials (parameters), processing them, and finally, it must deliver the product (result), right? Return is the logistics officer.

  • Return syntax: return [expression];

    • The expression is the “goods” you want to send out. Its type must match the return value type specified in the function declaration!

// Function declaration: This function returns an int type productint add(int a, int b) {    int sum = a + b; // Processed product    return sum;     // Return courier, please deliver this box of sum to the calling point!    // You can also write directly: return a + b; (a more concise packaging method)}int main() {     // Call the add factory, waiting for return courier to deliver the goods (8) to the result warehouse    int result = add(5, 3);    printf("5 + 3 = %d\n", result); // Output: 5 + 3 = 8    return 0; // The main function also sends a delivery (status code) to the operating system}

1.2 Terminating Function Execution

  • As soon as the return courier is dispatched, regardless of how many lines of code are left to execute in the function, it immediately stops! The function ends right away and departs (returns to the calling point).

  • Imagine Xiao Zhang is halfway through the calculation and suddenly realizes the raw materials are bad (for example, the parameters are invalid). He doesn’t need to foolishly finish the calculation; he can directly:

int calculateSum(int n) {    if (n <= 0)     {        printf("Error: n must be greater than 0!\n");        return -1;         // Found a problem with the raw materials, immediately package the error code (-1) and return, no more work!    }    // ... Normal calculation code ...    return sum;     // If the raw materials are fine, finish calculating and then package sum to return}

Here, return -1; not only sends back the error message (the goods are -1), but also immediately stops the function, avoiding subsequent invalid calculations. This is efficiency!

Special Scenarios of Return2.1 Void Functions

  • Some functions are more laid-back, like printWelcomeMessage, whose task is simply to print “Welcome!” without needing to bring back any calculation results for the boss. When declaring such functions, the return value type is written as void (empty).

  • At this point, the return courier has it easy; he doesn’t need to carry any goods, and his task becomes purely to “close the door”:

void printWelcomeMessage() {    printf("********************\n");    printf("*  Welcome to this system!  *\n");    printf("********************\n");    return;     // Returns empty-handed, just responsible for closing the door (ending the function). This return can even be omitted!

In a void function, return; can be used alone (without an expression following it), and if the function reaches the last line, this return; can be omitted (the compiler will silently close the door for you). But if you want to end the function early (for example, when a condition is not met), you must use it:

void login(char* username) {    if (username == NULL)     {        printf("Username cannot be empty!\n");        return; // Found an empty username, exit early!    }    // ... Normal login process ...    // Finally, the compiler will add an invisible return; here}

2.2 Main Function

The boss of the program, the main function, also needs to use return. The integer it returns is called the exit status code. This code is for the “big boss” (usually the operating system) that started it, telling the big boss: “I finished my work, how did I do?” However, in embedded systems, the main function generally enters a while(1) infinite loop and does not execute return.

  • return 0;: The most common, meaning “Report to the boss, everything is smooth, task completed successfully!”

  • Non-zero value (usually 1 or others): Means “Report to the boss, something went wrong!”, different non-zero values can represent different types of errors (though conventionally, there are no strict regulations).

  • Practical Considerations for Return3.1 Type Matching

    • This is the place where beginners are most likely to trip up! The result of the expression after return must strictly match the type declared in the function.

    • Tripping Scene:

    float getPi() {    double f = 3.14159;    return f; // f is a double type constant }// The compiler may warn: double to float may lose precision. The precision of double is too high to fit into the float's box!

    3.2 Returning Pointers to Local Variables

    int* createArray() {    int arr[5] = {1, 2, 3, 4, 5}; // arr is a local array, on the stack    return arr; // Big mistake!!! The return courier is trying to send back an address that will soon be destroyed!}// The function ends, and arr on the stack is destroyed. The returned address points to a wasteland (invalid memory), using it will lead to undefined behavior (crash/gibberish)!

    Correction (Dynamic Memory):

    int* createArray() {    int* arr = (int*)malloc(5 * sizeof(int)); // Allocate memory on the heap    if (arr != NULL)     {        arr[0]=1;         arr[1]=2; // ... Initialize    }    return arr; // Return the heap memory address, this memory remains valid until free is called}// The caller must remember to free() this pointer after use!

    3.3 Code After Return Does Not Execute

    Once return is executed, all subsequent code in the function becomes a “no man’s land” and will never be executed. Don’t write important code after return; that’s a waste of effort!

    int useless() {    return 42; // The courier takes 42 and leaves...    printf("This line will never be printed!\n");     // ... Other code is also just decoration ...}

    3.4 Multiple Returns

    • A function can have multiple return statements, usually appearing in different conditional branches (like if-else, switch). This is like arranging couriers at different intersections.

    • Advantages: It allows the code to exit earlier, avoiding deep nesting (commonly known as “arrow code”).

    • Disadvantages (controversial): Some people feel that multiple exits make the code logic less clear, violating the principle of “single entry, single exit”.

    • Suggestions:

      • For simple error checks/parameter validations, early return is clear and recommended (like the previous calculateSum example checking n).

      • For very complex logic, using multiple returns may make the flow difficult to track. In this case, consider using variables to temporarily store results and return them all at once, or refactor the function to make it simpler.

    3.5 Early Return vs Unified Return

    // Method 1: Early return (clear)int getValue(int option) {    if (option < 0 || option > MAX)     {        return INVALID; // Courier A: send invalid identifier    }    if (option == SPECIAL_CASE)     {        return calculateSpecial(); // Courier B: send special result    }    // ... Normal calculation ...    return normalResult; // Courier C: send normal result}// Method 2: Unified return (may fit some people's habits better)int getValue(int option) {    int result = DEFAULT;    if (option >= 0 && option <= MAX)     {        if (option == SPECIAL_CASE)         {            result = calculateSpecial();        }         else         {            // ... Normal calculation ...            result = normalResult;        }    }     else     {        result = INVALID;    }    return result; // Only one courier C, finally send the goods uniformly}

    Which is better? It depends on the specific situation and personal/team style. Early return is often clearer for error handling.

    Conclusion

    The three laws of return couriers:

    1. Mission Accomplished: For non-void functions, must carry type-matching “goods” (return value) back to the calling point.

    2. Immediate Action: Once executed, immediately stop all subsequent operations in the current function, carrying goods (or empty-handed) back.

    3. Compliance: Strictly adhere to type rules, beware of returning pointers/addresses of local variables (returning pointers to stack memory is a big taboo!).

    Though small, return carries significant responsibility—delivering the right goods and closing the door properly! Next time you write code, remember to treat this diligent courier well, and don’t let it deliver the wrong goods or take the wrong path; only then can your programming world be orderly!

    “Has your function returned today?” 😉

    Practical Insights on C Language: A Delivery Guide from the Return Courier

    If you find this article good, click “Share“, “Like“, or “Looking“!

    Leave a Comment