C++ Functions: Parameters, Return Values, Overloading,
이 글의 핵심
Functions are the building blocks of C++ programs. This guide covers everything beginners need: how to define and call functions, when to pass by value vs reference, how overloading works, and the pitfalls to avoid.
Why Functions?
Functions give a name to a block of code so you can reuse it without copying. They break large programs into smaller, testable pieces.
// Without functions — repeated code
double area1 = 3.14159 * 5.0 * 5.0;
double area2 = 3.14159 * 3.0 * 3.0;
// With a function — write once, use anywhere
double circleArea(double radius) {
return 3.14159 * radius * radius;
}
double area1 = circleArea(5.0);
double area2 = circleArea(3.0);
Function Anatomy
// Return type Name Parameters
double add (double a, double b) {
return a + b; // body
}
Every part matters:
- Return type: what type the function produces (
voidif nothing) - Name: what you call it
- Parameters: inputs (can have zero or many)
- Body: the code that runs
return: sends a value back to the caller
Declaration vs Definition
A declaration tells the compiler a function exists (its name, return type, parameter types). A definition provides the actual code. You can declare a function before defining it:
// Declaration (prototype) — put in header files or at top of file
double squareRoot(double x);
void printLine(const std::string& text);
// Use the function before the definition
int main() {
std::cout << squareRoot(16.0) << '\n'; // 4.0
printLine("Hello");
}
// Definition — the actual implementation
double squareRoot(double x) {
return std::sqrt(x);
}
void printLine(const std::string& text) {
std::cout << text << '\n';
}
In larger projects, declarations go in .h header files and definitions go in .cpp source files.
Parameters: Value, Reference, and Pointer
Pass by Value
A copy of the argument is made. Changes inside the function don’t affect the caller:
void doubleIt(int x) {
x *= 2; // modifies the copy, not the caller's variable
}
int main() {
int n = 5;
doubleIt(n);
std::cout << n; // still 5
}
Use when: the type is small (int, double, char, small structs), or you intentionally want a local copy to modify.
Pass by Reference
An alias to the caller’s variable — no copy, changes affect the caller:
void doubleInPlace(int& x) { // & means reference
x *= 2; // modifies the caller's variable
}
int main() {
int n = 5;
doubleInPlace(n);
std::cout << n; // 10
}
Use when: you need to modify the caller’s variable (output parameter).
Pass by const Reference
Efficient read-only access — no copy, but cannot be modified:
double sumVector(const std::vector<double>& v) { // const & — no copy, no modification
double total = 0;
for (double x : v) total += x;
return total;
}
Use when: the type is large (string, vector, any struct) and the function only reads it. This is the most common pattern for large types in C++.
Pass by Pointer
Similar to reference, but allows nullptr (no argument):
void process(const std::string* text) {
if (text == nullptr) {
std::cout << "(nothing)\n";
return;
}
std::cout << *text << '\n';
}
int main() {
std::string s = "hello";
process(&s); // pass address
process(nullptr); // no argument
}
Use when: null is a valid state (optional parameter). Prefer std::optional<T> in modern C++.
Quick Reference
| Type | Syntax | Copy? | Modifiable? | Use for |
|---|---|---|---|---|
| Value | int x | Yes | Local only | Small types |
| Const ref | const T& x | No | No | Large read-only types |
| Ref | T& x | No | Yes | Output parameters |
| Pointer | T* x | No | Yes | Optional parameters |
Return Values
Returning a Value
int add(int a, int b) {
return a + b;
}
std::string greet(const std::string& name) {
return "Hello, " + name + "!";
}
Every non-void function must return a value on all code paths. Modern compilers warn or error when a path exits without returning.
Returning Large Objects (RVO)
You can return large objects by value — the compiler eliminates the copy:
std::vector<int> makeRange(int start, int end) {
std::vector<int> result;
for (int i = start; i < end; i++) {
result.push_back(i);
}
return result; // RVO: constructed directly in caller's memory — no copy
}
int main() {
auto v = makeRange(0, 1000); // efficient — no extra copy
}
Never Return References to Locals
A local variable is destroyed when the function returns. Returning a reference to it is undefined behavior:
// WRONG — dangling reference
int& getLocal() {
int x = 42;
return x; // x is destroyed here — reference is dangling
}
// CORRECT — return by value
int getLocal() {
int x = 42;
return x; // copy returned, x destroyed — caller gets the copy
}
void Functions
Functions that perform actions but produce no value use void:
void printDivider(char ch, int count) {
for (int i = 0; i < count; i++) {
std::cout << ch;
}
std::cout << '\n';
}
// void functions use return; with no value to exit early
void processIfValid(int value) {
if (value < 0) return; // early exit
// ...process...
}
Default Arguments
Parameters can have default values when not provided:
void printBox(int width, int height = 5, char fill = '*') {
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
std::cout << fill;
}
std::cout << '\n';
}
}
int main() {
printBox(10); // width=10, height=5, fill='*'
printBox(8, 3); // width=8, height=3, fill='*'
printBox(6, 4, '#'); // width=6, height=4, fill='#'
}
Rules:
- Defaults apply right-to-left — all parameters to the right of a default must also have defaults
- Put defaults in the declaration (header), not in the definition
// Header — declaration with defaults
void connect(const std::string& host, int port = 8080, bool secure = false);
// Source — definition without defaults (they're already in the header)
void connect(const std::string& host, int port, bool secure) {
// ...
}
Function Overloading
Multiple functions with the same name but different parameter types:
// Three versions of print — different parameter types
void print(int value) {
std::cout << "int: " << value << '\n';
}
void print(double value) {
std::cout << "double: " << value << '\n';
}
void print(const std::string& value) {
std::cout << "string: " << value << '\n';
}
int main() {
print(42); // calls print(int)
print(3.14); // calls print(double)
print("hello"); // calls print(const string&)
}
You cannot overload on return type alone — the parameter list must differ:
int getValue(); // OK
double getValue(); // compile error — same name and parameters
Recursion
A function that calls itself. Classic examples:
// Factorial: n! = n * (n-1) * ... * 1
long long factorial(int n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1); // recursive case
}
// Fibonacci — note: O(2^n) time, impractical for large n
int fib(int n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
// Better: iterative Fibonacci — O(n) time
long long fibIterative(int n) {
if (n <= 1) return n;
long long a = 0, b = 1;
for (int i = 2; i <= n; i++) {
long long c = a + b;
a = b;
b = c;
}
return b;
}
Every recursive function needs a base case that stops the recursion. Without it, the program runs until the stack overflows.
Common Mistakes
Calling before declaring:
int main() {
result = add(2, 3); // error: add not declared yet
}
int add(int a, int b) { return a + b; }
// Fix: declare add before main, or define add before main
Missing return on some path:
int sign(int x) {
if (x > 0) return 1;
if (x < 0) return -1;
// compiler may warn: control reaches end without return
// Fix: add return 0;
}
Passing large types by value:
double sumAll(std::vector<int> v) { ... } // copies the entire vector
double sumAll(const std::vector<int>& v) { ... } // no copy — correct
Returning reference to local:
const std::string& getName() {
std::string name = "Alice"; // local variable
return name; // UB: name destroyed at end of function
}
Practical Example: String Utilities
#include <string>
#include <algorithm>
#include <sstream>
#include <vector>
// Count occurrences of a character
int countChar(const std::string& s, char target) {
int count = 0;
for (char c : s) {
if (c == target) count++;
}
return count;
}
// Check if string is a palindrome
bool isPalindrome(const std::string& s) {
std::string lower = s;
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
std::string reversed = lower;
std::reverse(reversed.begin(), reversed.end());
return lower == reversed;
}
// Split string by delimiter
std::vector<std::string> split(const std::string& s, char delimiter) {
std::vector<std::string> result;
std::stringstream ss(s);
std::string token;
while (std::getline(ss, token, delimiter)) {
result.push_back(token);
}
return result;
}
int main() {
std::cout << countChar("mississippi", 's') << '\n'; // 4
std::cout << isPalindrome("racecar") << '\n'; // 1 (true)
std::cout << isPalindrome("hello") << '\n'; // 0 (false)
auto words = split("one,two,three", ',');
for (const auto& w : words) {
std::cout << w << '\n'; // one / two / three
}
}
Key Takeaways
- Pass small types by value, large types by const reference — this is the most common function parameter pattern
- Const reference (
const T&) gives efficient read-only access without copying - Return by value for most things — RVO eliminates the copy overhead
- Never return a reference to a local variable — it’s undefined behavior (dangling reference)
- Overloading lets you use the same name for functions that do the same thing with different types
- Default arguments go in the declaration (header), not the definition
- Recursion needs a base case — always think about when it stops
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Complete C++ function guide for beginners: declaration vs definition, pass by value/reference/pointer, return rules, RVO… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ 조건문 | if/else/switch ‘완벽 정리’ [실수 방지 팁]
- [C++ Classes and Objects: Constructors, Access Control, and](/en/blog/cpp-class-object-beginner/
- C++ 반복문 | for/while/do-while ‘완벽 마스터’ [무한루프 탈출]
이 글에서 다루는 키워드 (관련 검색어)
C++, functions, parameters, return, beginner 등으로 검색하시면 이 글이 도움이 됩니다.