C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
28: Passing Arguments by Value
I once worked with a junior dev named Leo who was building a simple combat engine for a text-based RPG. He spent three hours staring at a screen, convinced he had found a compiler bug. He had written a function called take_damage that subtracted a value from the player's health. Inside the function, the math was perfect; he'd even put a print statement there showing the health dropping. But the moment the function ended and he printed the health in main, the player was back to full health. It was like the damage had never happened.
Leo was fighting against one of the most fundamental behaviors of the C language: passing arguments by value. In C, when you pass a variable into a function, you aren't giving the function access to the actual variable. Instead, you're giving it a copy.
The Copy-Paste Nature of C Functions
Think of it like this: if I have a piece of paper with a number written on it and I want you to do some math with it, I don't hand you my original paper. Instead, I take a photo of the paper and text it to you. You can scribble all over that photo, change the number, or even delete it entirely, but my original piece of paper remains exactly as it was. This is exactly what C does with every basic data type.
Take a look at this snippet, which is essentially what Leo wrote:
#include <stdio.h>
void take_damage(int health, int damage) {
health = health - damage;
printf("Inside function: Health is now %d\n", health);
}
int main() {
int playerHealth = 100;
printf("Before function: Health is %d\n", playerHealth);
take_damage(playerHealth, 20);
printf("After function: Health is %d\n", playerHealth);
return 0;
}
If you run this, you'll see that playerHealth stays 100. The health variable inside take_damage is a completely different slot of memory that just happens to start with the same value. Once the function hits that closing brace, that local copy is destroyed, and the changes vanish with it.
Tracking the Stack in Your Head
To really get this, you have to visualize the stack. Every time you call a function, C creates a new "stack frame." This frame holds all the local variables for that function. When you call take_damage(playerHealth, 20), the program looks at the value of playerHealth (which is 100), and it physically copies that number into a new location on the stack reserved for the take_damage function.
I'll be honest: this can feel restrictive when you just want to update a variable. But it's actually a massive safety feature. Imagine if every function you called could silently change any variable in your program. Debugging would be a nightmare. Pass-by-value ensures that a function can't accidentally mess up your data unless you explicitly tell it how to find the original memory address (which is where pointers come in, but that's a conversation for another time).
If you actually need a function to "change" a value using pass-by-value, your only option is to have the function return the new value and assign it back to the original variable in the calling function. It's a bit more verbose, but it's explicit.
📋 Practical Task
The Non-Destructive Health Modifier
Your task is to create a program that simulates a "Shield Buff" system. You need to write a function that calculates what a player's health would be if they received a shield boost, but without actually modifying the original health variable (since the player has to "accept" the buff first).
- Create an integer variable
currentHealthand initialize it to 50. - Write a function called
calculate_buffthat takes two integers:healthandboostAmount. - Inside
calculate_buff, add the boost to the health and print the result. - In your
mainfunction, callcalculate_buffand then printcurrentHealthimmediately after. - Verify that
currentHealthremains 50, proving that the function only worked on a copy.
There are no comments for now.