- Pass by Value/Copy: A copy of the value is made and passed to the function. Changes inside the function don't affect the original variable.
- Pass by Reference: The function receives a reference (alias) to the original variable. Changes directly modify the original.
C uses pass by value for all function parameters:
void modify(int x) {
x = 100; // Only modifies the local copy
}
int main() {
int num = 5;
modify(num);
printf("%d", num); // Still prints 5
return 0;
}To achieve reference-like behavior, explicitly pass pointers:
void modify(int* x) {
*x = 100; // Modifies the value at the address
}
int main() {
int num = 5;
modify(&num); // Pass address of num
printf("%d", num); // Now prints 100
return 0;
}uint,int,bool,address,bytes32, etc.- Always copied - original unchanged
function modify(uint x) internal {
x = 100; // Only modifies local copy
}
uint num = 5;
modify(num); // num is still 5arrays,structs,mappings,strings- Passed by reference for
storageandmemory
function modifyArray(uint[] memory arr) internal {
arr[0] = 100; // Modifies the original array
}
uint[] memory numbers = new uint[](3);
numbers[0] = 5;
modifyArray(numbers); // numbers[0] is now 100// Storage reference - modifies original state
function modifyStorage(uint[] storage arr) internal {
arr[0] = 100; // Changes blockchain state
}// Memory reference - modifies temporary data
function modifyMemory(uint[] memory arr) internal pure {
arr[0] = 100; // Modifies memory copy
}// Calldata - read-only reference
function readCalldata(uint[] calldata arr) external pure {
// arr[0] = 100; // Error: cannot modify calldata
uint value = arr[0]; // Can only read
}function tryReassignStorage() public {
uint[] storage localRef = storageArray;
localRef[0] = 100; // ✅ Works - modifying contents
// localRef = new uint[](5); // ❌ Compilation error!
}Error: "TypeError: Type uint256[] memory is not implicitly convertible to expected type uint256[] storage pointer."
function reassignMemory(uint[] memory arr) public pure returns (uint[] memory) {
arr[0] = 100; // Modify original contents
arr = new uint[](5); // ✅ Reassign to new array
arr[0] = 200;
return arr; // Returns the new array
}function tryReassignCalldata(uint[] calldata arr) external pure {
// arr[0] = 100; // ❌ Cannot modify
// arr = new uint[](5); // ❌ Cannot reassign
uint value = arr[0]; // ✅ Can only read
}function demonstrateReassignment() public pure returns (uint[] memory, uint[] memory) {
// Create initial array
uint[] memory arr = new uint[](3);
arr[0] = 100;
arr[1] = 200;
arr[2] = 300;
// Save reference to original location
uint[] memory originalRef = arr;
// Reassign arr to point to new memory
arr = new uint[](2);
arr[0] = 999;
arr[1] = 888;
// Both memory locations are still valid!
return (originalRef, arr); // Returns ([100, 200, 300], [999, 888])
}function complexMemoryTest() public pure returns (
uint[] memory,
uint[] memory,
uint[] memory
) {
uint[] memory first = new uint[](2);
first[0] = 111;
first[1] = 222;
uint[] memory second = first; // Same memory location
uint[] memory third = first; // Same memory location
second[0] = 999; // Changes first[0] and third[0] too!
second = new uint[](1); // Only second points elsewhere now
second[0] = 777;
return (first, second, third);
// Returns: ([999, 222], [777], [999, 222])
}| Aspect | C | Solidity |
|---|---|---|
| Default behavior | Always pass by value | Depends on data type |
| Reference semantics | Manual (via pointers) | Automatic for reference types |
| Memory management | Manual | Automatic with gas costs |
| Data location | Not specified | storage, memory, calldata |
| Reassignment | Pointer can be reassigned | Depends on data location |
| Data Location | Can Reassign? | Can Modify Contents? | Notes |
|---|---|---|---|
| Storage | ❌ No | ✅ Yes | Fixed aliases to storage slots |
| Memory | ✅ Yes | ✅ Yes | Movable pointers in memory |
| Calldata | ❌ No | ❌ No | Read-only function parameters |
-
C keeps it simple: Uniform pass-by-value with manual pointer management for reference behavior.
-
Solidity is type-aware: Automatically chooses passing mechanism based on data types.
-
Memory safety: Unlike C, Solidity prevents dangling references and memory corruption.
-
Storage references are fixed: Cannot be reassigned to prevent accidental state modifications.
-
Memory persistence: Reassigning a memory reference doesn't invalidate the original memory location.
-
Gas implications: Understanding reference behavior is crucial for gas optimization in Solidity.
The fundamental difference is that C gives you complete control with manual memory management, while Solidity provides automatic memory safety with explicit data location controls for optimization and security.