정말 좋은 질문이에요!
**"for문 안에 변수 선언 vs 밖에서 선언"**의 차이는
결국 **그 변수의 ‘수명(lifetime)’과 ‘저장된 값의 지속성’**에 영향을 줍니다.
지금 바로 아래처럼, 비교 예제 2개를 통해
값이 왜 다르게 나오는지 디버깅하듯이 완전 상세하게 설명드릴게요.
#include <stdio.h>
int main() {
int sum = 0;
for (int i = 0; i < 3; i++) {
int temp = 0; // 🔴 매 반복마다 '새로운 temp' 변수가 생성됨
temp += i; // temp = 0 + i;
sum += temp; // sum에 지금 temp를 더함
}
printf("sum = %d\n", sum);
return 0;
}
| 반복 i | temp 생성 위치 | temp 값 계산 | sum 누적 |
|---|---|---|---|
| 0 | temp = 0 | temp += 0 → 0 | sum = 0 |
| 1 | temp = 0 | temp += 1 → 1 | sum = 1 |
| 2 | temp = 0 | temp += 2 → 2 | sum = 3 |
// 각 반복마다 temp = 0 으로 새로 만들어짐
👉 sum = 3이 최종 결과
#include <stdio.h>
int main() {
int sum = 0;
int temp = 0; // 🟢 한번만 선언되고, 값이 누적됨
for (int i = 0; i < 3; i++) {
temp += i; // 이전 temp 값에 계속 i를 더함
sum += temp;
}
printf("sum = %d\n", sum);
return 0;
}
| 반복 i | temp 누적 방식 | sum 누적 |
|---|---|---|
| 0 | temp = 0 + 0 = 0 | sum = 0 |
| 1 | temp = 0 + 1 = 1 | sum = 1 |
| 2 | temp = 1 + 2 = 3 | sum = 1 + 3 = 4 |
👉 sum = 4이 최종 결과
[for문 안에 temp 있을 때]
i=0 temp=0 sum=0
i=1 temp=0→1 sum=0+1=1
i=2 temp=0→2 sum=1+2=3
[for문 밖에 temp 있을 때]
i=0 temp=0+0=0 sum=0
i=1 temp=0+1=1 sum=0+1=1
i=2 temp=1+2=3 sum=1+3=4
| 선언 위치 | 효과 | sum 값 |
|---|---|---|
| for문 안 | temp가 매 반복마다 새로 만들어짐 | 3 |
| for문 밖 | temp 값이 반복하며 누적됨 | 4 |
"for문 안에서 선언된 변수는 반복할 때마다 새로 만들어진다.
밖에서 선언하면 이전 값이 남아 있다."
**"변수가 사라지는 타이밍"**과 **"이전 값을 기억하느냐"**의 문제예요.
int temp = 0;이 for문 안에 있으면 매번 새로 생성 → 이전 값 유지 ❌int temp = 0;이 밖에 있으면 한 번만 생성 → 이전 값 계속 사용됨 ✅아래 코드의 출력은 무엇일까요?
#include <stdio.h>
int main() {
for (int i = 0; i < 3; i++) {
int x = 0;
x++;
printf("x = %d\n", x);
}
return 0;
}
좋습니다!
지금 우리가 볼 코드는 반복문 안에서 변수 x가 어떻게 작동하는지,
즉 초기화 위치에 따라 값이 매번 리셋되는지 유지되는지를 확인하는 좋은 예제예요.
#include <stdio.h>
int main() {
for (int i = 0; i < 3; i++) {
int x = 0;
x++;
printf("x = %d\n", x);
}
return 0;
}
int main() 시작for (int i = 0; i < 3; i++) → i는 0부터 2까지 반복됨 (총 3번)int x = 0;
x++;
printf("x = %d\n", x);
x = 0; → 새로운 지역 변수 x가 만들어지고 0으로 초기화됨x++; → x = 0 + 1 → x는 1이 됨x = 1이때 이
x는 이 블록(for문 한 사이클)이 끝나면 사라짐!
int x = 0; → 새로운 x가 만들어짐, 이전 x와 완전 다른 메모리x++ → x는 0 → 1x = 1int x = 0; → 새 xx++ → x는 1x = 1x = 1
x = 1
x = 1
| 선언 위치 | 효과 | 값 유지 여부 |
|---|---|---|
| for문 안 | 매번 새 변수 생성 → 초기화됨 | ❌ 유지 안 됨 |
| for문 밖 | 한 번 생성된 변수 사용 → 누적됨 | ✅ 유지됨 |
#include <stdio.h>
int main() {
int x = 0;
for (int i = 0; i < 3; i++) {
x++;
printf("x = %d\n", x);
}
return 0;
}
x = 1
x = 2
x = 3
이유: x가 한 번만 만들어지고, x++으로 계속 누적되니까!
반복문 안에서 변수를 선언하면 매번 새로 만들어지므로, 값이 유지되지 않는다!
맞아요! 아주 핵심을 잘 짚으셨어요 👏
이제 그 내용을 완전하게 정리해드릴게요.
지금 궁금하신 건 바로 함수에 값을 전달할 때 원본이 바뀌느냐?
즉 “값 전달 vs 참조 전달” 개념입니다.
| 자료형 | 전달 방식 | 원본 값이 바뀌는가? |
|---|---|---|
int, char 등 기본형 | 값 전달 (Call by Value) | ❌ 아니요 (복사본만 수정됨) |
int[], 포인터 등 | 참조 전달처럼 동작 (주소 전달) | ✅ 예, 원본 값이 바뀜 |
void change(int x) {
x = 100;
}
int main() {
int a = 10;
change(a);
printf("a = %d\n", a); // 🔸 출력: 10
}
change(a)는 a의 복사본 x를 만든 것a와는 아무 관련 없음void change(int arr[]) {
arr[0] = 100;
}
int main() {
int a[3] = {1, 2, 3};
change(a);
printf("a[0] = %d\n", a[0]); // 🔸 출력: 100
}
a는 함수에 넘길 때 주소로 전달됨arr[0] = 100은 곧 a[0] = 100과 같음void change(int* p) {
*p = 100;
}
int main() {
int a = 10;
change(&a);
printf("a = %d\n", a); // 🔸 출력: 100
}
&a는 a의 주소p는 그 주소를 받아서 *p = 100; → a에 직접 대입[ int x = 10 ] → 값 복사 → x = 10 (함수 안에서만 사용됨)
[ int arr[] = {1, 2, 3} ] → 주소 전달 → arr[0] = 원본에 직접 영향
[ int *p = &a ] → 주소 전달 → *p = a 직접 바꿈
✔️ 기본형은 복사해서 전달 → 원본 영향 ❌
✔️ 배열, 포인터는 주소를 전달 → 원본 영향 ✅
C 언어는 엄밀히 말하면 참조(reference) 전달 문법이 없고,
모든 건 값으로 전달되는데,
배열과 포인터는 주소 자체가 값이기 때문에 원본에 영향을 줄 수 있는 것입니다.
| 인자 타입 | 전달되는 것 | 원본 변경됨? | 예시 함수 |
|---|---|---|---|
int a | 값 (복사본) | ❌ | void f(int x) |
int a[] | 주소 | ✅ | void f(int arr[]) |
int* p | 주소 | ✅ | void f(int* p) |
정말 날카로운 질문이에요! 👏
지금 궁금하신 부분은:
“
change(&a);에서 10이라는 값이 들어가는 거 아니야?
그럼p = 10이 되는 게 아닌가?”
이건 C 언어의 포인터 개념과 주소 전달 vs 값 전달 차이를 아주 잘 이해해야 하는 부분이에요.
지금 바로 디버깅 수준으로 완전히 해부해서 설명해드릴게요.
void change(int* p) {
*p = 100;
}
int main() {
int a = 10;
change(&a);
printf("a = %d\n", a);
}
int a = 10;a는 정수형이며, 값은 10입니다.a가 저장되어 있고, 예를 들어 주소가 0x1000이라 가정해봅시다.[메모리 예시]
주소: 0x1000
내용: 10 (a의 값)
change(&a);&a는 a의 주소이므로, 실제로는 change(0x1000);처럼 동작합니다.10이 전달되는 게 아니라 → a가 저장된 주소값이 전달됩니다.void change(int* p) {
*p = 100;
}
p는 포인터이고, 그 안에는 0x1000이라는 주소가 들어 있음*p = 100;은 무슨 의미냐면?→
p가 가리키는 주소(= a가 저장된 주소)에100을 써넣으라는 뜻입니다
[p = 0x1000]
[*p = 100] → 즉, 0x1000 주소에 있는 값을 100으로 바꿔라
주소: 0x1000
내용: 100 ← 원래 10이었지만 이제는 바뀜!
printf("a = %d\n", a); // a는 실제로 100으로 바뀌었음
→ 출력: a = 100
❓ "그럼
change(&a)에서 a가 10이면, 10이 전달되는 거 아냐?"
👉 아니에요!
전달되는 것은 10이라는 값이 아니라 a의 주소입니다.
int* p는 그 주소를 받아서,
그 주소에 저장된 값을 100으로 바꾸는 작업을 합니다.
| 호출 형태 | 함수에서 의미 | 원본 바뀌나? |
|---|---|---|
change(a); | 10이 복사되어 전달됨 | ❌ X |
change(&a); | a의 주소가 전달됨 | ✅ O |
| 함수 매개변수 | int p (값) | ❌ |
int* p (주소/참조) | ✅ |
a = 10은 "책상 위에 10이라는 숫자가 적힌 종이"change(a)는 → 그 종이를 복사해서 갖다주는 것 (원본 안 바뀜)change(&a)는 → **종이의 위치(책상 주소)**를 알려주는 것*p = 100; → 그 위치에 가서 직접 값을 고쳐 쓰는 것좋아요!
이번엔 당신이 직접 눈으로 정확한 차이를 비교할 수 있게,
다음 두 함수를 같은 코드 흐름 안에서 비교해서 보여드릴게요.
int p vs int* p#include <stdio.h>
// 값으로 전달 (복사본)
void changeByValue(int p) {
p = 100;
}
// 주소로 전달 (참조)
void changeByPointer(int* p) {
*p = 200;
}
int main() {
int a = 10;
printf("초기값 a = %d\n", a);
changeByValue(a); // 🔹 값만 넘김 (복사본)
printf("changeByValue 후 a = %d\n", a); // a는 그대로 10
changeByPointer(&a); // 🔹 주소 넘김 (a를 직접 건드림)
printf("changeByPointer 후 a = %d\n", a); // a는 200으로 바뀜
return 0;
}
초기값 a = 10
changeByValue 후 a = 10
changeByPointer 후 a = 200
changeByValue(a);a = 10 → 복사본 p = 10 전달됨p = 100; 은 지역 변수 p만 바꿈a는 건드려지지 않음a ──┬──▶ 10
│
└──→ changeByValue(p = 10) → p = 100 ❌ a에 영향 없음
changeByPointer(&a);&a → a의 주소 (예: 0x1000) 전달됨*p = 200; → a가 저장된 메모리 위치에 직접 200 저장!a ──▶ 10
▲
└── changeByPointer(p = &a) → *p = 200 ✅ a 직접 변경
| 함수 호출 방식 | 내부 매개변수 | 값 변경? | 이유 |
|---|---|---|---|
changeByValue(a) | int p | ❌ | 값 복사, 원본과 무관 |
changeByPointer(&a) | int* p | ✅ | 주소 전달, 원본 직접 접근 |
값 전달(
int)은 복사본만 바꾸고,
참조 전달(int*)은 원본을 직접 바꾼다.
다음 코드에서 b의 최종 값은?
#include <stdio.h>
void test(int b) {
b += 5;
}
int main() {
int b = 20;
test(b);
printf("b = %d\n", b); // ???
return 0;
}
오! 그렇게 답할 수 있는 이유를 갖고 계신 건 아주 좋지만,
이 문제의 정답은 사실… 20입니다.
그 이유를 디버깅하듯 완전 해부해서 설명해드릴게요!
#include <stdio.h>
void test(int b) {
b += 5;
}
int main() {
int b = 20;
test(b);
printf("b = %d\n", b); // ✅ 실제 출력은?
return 0;
}
main() 시작int b = 20;
b는 메모리 어딘가에 저장되고, 값은 20입니다.test(b); 호출b의 값 20이 복사되어 함수로 전달됩니다.void test(int b) {
b += 5; // 이건 "지역변수 b = 20 + 5 = 25"일 뿐!
}
printf("b = %d\n", b);
b = 20b = 20| 구분 | 설명 |
|---|---|
test(int b) | b는 복사본 → main의 b와 무관 |
b += 5; | 지역 b가 25가 될 뿐, 원본은 그대로 |
| 출력 결과 | 원본 b는 변하지 않았으므로 20 출력 |
void test(int* p) {
*p += 5;
}
int main() {
int b = 20;
test(&b);
printf("b = %d\n", b); // ✅ 출력: 25
}
&b → b의 주소를 넘김*p += 5 → b의 값을 직접 변경🔸 기본형 변수는 복사되어 전달되므로 원본에 영향 ❌
🔸 주소를 전달해야만 원본에 직접 영향 줄 수 있음 ✅