정말 좋은 질문이에요!
그리고 이 질문을 하신 걸 보면 C언어의 “값 전달”과 “변수의 생명 주기(scope)”를 거의 다 이해하셨다는 뜻이에요.
이제 이 개념을 완전히 마무리할 수 있도록,
아래 코드를 디버깅 하듯이 한 줄 한 줄 해부하면서
왜 temp 값이 누적되는지 완벽하게 설명드릴게요.
#include <stdio.h>
int main() {
int sum = 0;
int temp = 0;
for (int i = 0; i < 3; i++) {
temp += i;
sum += temp;
}
printf("sum = %d\n", sum);
return 0;
}
temp는int형이고,
아까int형은 값이 복사되기 때문에 원본에 영향이 없다고 했는데,
왜 여기서는 temp가 누적돼?
함수의 매개변수로
int x를 넘겼을 때, x는 복사본 → 원본 변수와는 연결 X
📌 이건 “함수 호출 시 전달 방식”에만 해당됩니다!
int temp = 0; // ⬅️ 이 변수는 main 함수 전체에서 살아있음
즉, temp는 main 함수의 지역 변수이고,
for문 안에서도 계속 같은 temp를 사용하고 있는 거예요.
int sum = 0;
int temp = 0;
sum, temp 모두 한 번만 선언됨for (int i = 0; i < 3; i++) {
temp += i;
sum += temp;
}
printf("sum = %d\n", sum); // 🔸 4 출력됨
| temp 선언 위치 | 의미 |
|---|---|
| for문 밖 | ➤ 단 한 번 생성됨 → 매 반복 때 기존 값 유지 |
| for문 안에 있으면 | ➤ 매 반복마다 새롭게 0으로 초기화됨 |
int형이 "값만 복사된다"는 건 함수에 전달할 때 이야기temp는 매번 누적됨!for (int i = 0; i < 3; i++) {
int temp = 0; // 매번 새로 만들어짐
temp += i;
sum += temp;
}
📌 “temp가 누적되는 이유는, 함수 호출이 아니라 같은 함수 내 반복문에서 사용되고,
그 temp 변수가 한 번만 선언되고 계속 재사용되기 때문입니다.”
네! 아주 핵심을 정확히 반 이상 이해하셨어요 👏
이제 남은 반을 예시 + 시각화 + 상황별 비교로 완전히 채워드릴게요.
✔️ 같은 함수(main 함수 포함) 안에 있는
int변수는
그냥 그 변수 하나만 계속 쓰기 때문에 값이 누적됨❌ 하지만, 다른 함수에서 int 변수 값을 바꾸려면,
→ 그냥 값만 넘기면 원본은 바뀌지 않음 → 복사본만 바뀜
temp가 누적되는 경우#include <stdio.h>
int main() {
int temp = 0;
for (int i = 0; i < 3; i++) {
temp += i;
}
printf("temp = %d\n", temp); // ✅ 0 + 1 + 2 = 3
return 0;
}
temp는 main 함수의 지역 변수#include <stdio.h>
void update(int temp) {
temp += 100;
}
int main() {
int temp = 0;
for (int i = 0; i < 3; i++) {
update(temp); // ❌ temp는 바뀌지 않음
}
printf("temp = %d\n", temp); // 🔸 여전히 0
return 0;
}
📌 이 경우는 temp가 함수로 넘겨질 때 값만 복사됨
함수 안에서 바꿔봤자 → main 함수의 temp에는 영향 없음
#include <stdio.h>
void update(int* p) {
*p += 100;
}
int main() {
int temp = 0;
for (int i = 0; i < 3; i++) {
update(&temp); // ✅ 주소를 넘겨서 직접 temp를 바꿈
}
printf("temp = %d\n", temp<); // 🔸 출력: 300
return 0;
}
update(&temp) → temp의 주소를 넘김*p += 100; → temp의 실제 메모리를 수정| 상황 | temp 위치 | 함수에서 전달 방식 | 결과 |
|---|---|---|---|
| main 함수 안 | 지역 변수 | 직접 누적 가능 | ✅ 누적됨 |
| 함수로 넘김 | 지역 변수 | 값만 복사 (int) | ❌ 안 바뀜 |
| 함수로 넘김 | 지역 변수 | 주소 넘김 (int*) | ✅ 바뀜 |
🔸 main 함수 안에 있으면 그냥 그 변수를 쓰기 때문에 누적됨
🔸 main 함수 밖의 함수에 값을 전달하면 복사본만 전달되기 때문에 안 바뀜
🔸 그 변수의 주소(=참조)를 넘기면 바뀐다!
✔️ “같은 함수 안에 있으면 변수는 계속 살아 있어서 누적되고,
다른 함수에서 바꾸려면 값을 넘기면 안 되고,
주소를 넘겨야 한다.”
네, 아주 좋은 질문입니다! 👍
결론부터 말하면:
✅ C 언어에서는
함수(function)라는 말만 씁니다.
✅update()는 함수입니다.
❌ C에는 **메서드(method)**라는 개념이 없습니다.
| 구분 | 함수 (Function) | 메서드 (Method) |
|---|---|---|
| 어디서 사용? | C, Python 등 | Java, C++, Python 등 |
| 특징 | 독립적인 코드 블록 | 클래스 내부에 소속됨 |
| this 포인터? | 없음 | 있음 (this 사용 가능) |
| 대표 언어 | C, Go, Python | Java, C++, Python |
void update(int* p) {
*p = 100;
}
class Example {
void update() {
System.out.println("Hello");
}
}
따라서 main(), update() 같은 건 모두 **"함수(function)"**입니다.
Java처럼 this로 객체 상태를 조작하는 방식은 아예 존재하지 않죠.
update()는 C 언어 기준에서 보면 함수입니다.와, 정말 날카로운 질문이에요! 👍
**“자바에서 메서드니까 값에 영향을 받는가?”**라는 질문은,
사실 이렇게 정리할 수 있어요:
❌ “메서드라서 값에 영향을 받는다”는 말은 정확하지 않아요.
✅ 자바도 값에 영향을 주느냐 마느냐는 '자료형'에 따라 달라져요.
| 구분 | 자바에서의 동작 |
|---|---|
int, double, boolean 등 기본형 | ✅ 값만 복사됨 → 원본 영향 ❌ |
객체 (String, ArrayList, 배열 등) | ✅ 주소(참조)가 복사됨 → 원본 영향 가능 ✅ |
다만 **"값 자체가 '참조(주소)'일 수 있다"**는 게 핵심이에요!
public class Test {
static void change(int x) {
x = 100;
}
public static void main(String[] args) {
int a = 10;
change(a);
System.out.println(a); // ✅ 출력: 10
}
}
a의 값 10이 복사돼서 x로 전달됨public class Test {
static void change(int[] arr) {
arr[0] = 100;
}
public static void main(String[] args) {
int[] a = {10, 20, 30};
change(a);
System.out.println(a[0]); // ✅ 출력: 100
}
}
arr에는 a의 주소가 복사되어 들어감arr[0] = 100 → 실제 a[0]을 수정함| 질문 | 답변 |
|---|---|
| 자바는 메서드니까 값이 바뀌는가? | ❌ 아니요. 메서드여도 기본형은 복사됨 (원본 영향 ❌) |
| 자바는 객체는 왜 바뀌는가? | ✅ 참조(주소)를 복사하므로 원본이 바뀔 수 있음 |
| 자바는 참조 전달인가요? | ❌ 아니요. 자바는 값 전달만 존재함. 단, 그 값이 참조(주소)일 수 있음 |
✔️ 자바는 항상 값 전달(pass-by-value)
✔️ 기본형: 값이 복사됨 → 영향 ❌
✔️ 참조형(배열, 객체 등): 주소가 복사됨 → 원본 영향 가능 ✅
좋아요! 지금까지 배운
값 전달 vs 참조 전달 (기본형 vs 참조형),
C와 Java에서의 차이,
변수가 영향을 받는가? 이 개념들을
완전히 복습할 수 있도록 문제를 단계별로 낼게요.
다음 코드의 출력 결과는?
public class Test {
static void change(int x) {
x += 5;
}
public static void main(String[] args) {
int a = 10;
change(a);
System.out.println("a = " + a);
}
}
Q: 출력 결과는 무엇인가요?
public class Test {
static void change(int[] arr) {
arr[1] = 999;
}
public static void main(String[] args) {
int[] nums = {1, 2, 3};
change(nums);
System.out.println<("nums[1] = " + nums[1]);
}
}
Q: 출력 결과는 무엇인가요?
#include <stdio.h>
void update(int x) {
x = x + 10;
}
int main() {
int a = 5;
update(a);
printf("a = %d\n", a);
return 0;
}
Q: 출력 결과는 무엇인가요?
#include <stdio.h>
void update(int* p) {
*p = *p + 10;
}
int main() {
int a = 5;
update(&a);
printf("a = %d\n", a);
return 0;
}
Q: 출력 결과는 무엇인가요?
문제 5. 다음 중 ‘값이 실제로 변경되는 경우’를 모두 고르세요.
void f(int x)로 int 전달void f(int[] arr)로 배열 전달void f(int x)로 int 전달void f(int* p)로 포인터 전달좋습니다! 각 문제에 대한 정답과 자세한 이유를 하나씩 디버깅 흐름처럼 설명해드릴게요.
int 전달public class Test {
static void change(int x) {
x += 5;
}
public static void main(String[] args) {
int a = 10;
change(a);
System.out.println("a = " + a);
}
}
change(a) → a의 값 10이 복사되어 x로 전달됨x += 5 → x는 15가 되지만, main의 a는 바뀌지 않음"a = 10"a = 10static void change(int[] arr) {
arr[1] = 999;
}
int[] nums = {1, 2, 3}change(nums) → 배열 주소가 전달됨arr[1] = 999 → nums[1]을 직접 바꿈"nums[1] = 999"nums[1] = 999void update(int x) {
x = x + 10;
}
update(a) → 값 5가 복사되어 x로 전달x = x + 10 → x는 15가 되지만, a는 그대로"a = 5"a = 5void update(int* p) {
<*p = *p + 10;
}
update(&a) → a의 주소 전달*p = *p + 10 → a의 값을 직접 수정"a = 15"a = 15다음 중 ‘값이 실제로 변경되는 경우’를 모두 고르세요.
| 선택지 | 상황 설명 | 값이 바뀌는가? |
|---|---|---|
| 1번 | Java에서 int 전달 | ❌ 복사본만 변경 |
| 2번 | Java에서 int[] 배열 전달 | ✅ 참조값 복사, 실제 배열 변경됨 |
| 3번 | C에서 int 값 전달 | ❌ 복사본만 변경 |
| 4번 | C에서 int* 포인터 전달 | ✅ 실제 변수의 메모리 직접 수정 |
| 언어 | 기본형(int 등) 전달 | 참조형(배열, 포인터 등) 전달 |
|---|---|---|
| Java | 값 복사 (원본 변경 ❌) | 참조 복사 (원본 변경 가능 ✅) |
| C | 값 복사 (원본 변경 ❌) | 주소 전달 (원본 변경 가능 ✅) |
좋아요! 😈
이번엔 정말 대충 이해했으면 틀릴 수밖에 없는,
헷갈리는 요소를 가득 넣은 문제를 드릴게요.
단순한 개념 암기만으로는 절대 못 푸는 문제로 구성했습니다.
(그러면서도 본질은 기본 개념입니다!)
public class Test {
static void change(int[] arr) {
arr = new int[]{9, 9, 9}; // 🔥 배열 자체를 새로 만듦!
arr[0] = 100;
}
public static void main(String[] args) {
int[] nums = {1, 2, 3};
change(nums);
System.out.println("nums[0] = " + nums[0]);
}
}
Q: nums[0]의 출력 결과는 무엇일까요?
#include <stdio.h>
void update(int* p) {
int x = *p;
x += 10;
p = &x;
}
int main() {
int a = 5;
update(&a);
printf("a = %d\n", a);
return 0;
}
Q: a의 최종 값은?
class Data {
int x;
}
public class Test {
static void change(Data d) {
d = new Data(); // 🔥 새로운 객체 생성
d.x = 999;
}
public static void main(String[] args) {
Data d = new Data();
d.x = 100;
change(d);
System.out.println("d.x = " + d.x);
}
}
Q: d.x의 최종 출력값은?
#include <stdio.h>
void change(int arr[]) {
arr = (int[]){9, 9, 9}; // ❌ 이건 컴파일 안 될 수도 있어요, 유사한 트릭입니다
arr[0] = 100;
}
int main() {
int nums[] = {1, 2, 3};
change(nums);
printf("nums[0] = %d\n", nums[0]);
return 0;
}
Q: nums[0]의 출력 결과는?
public class Test {
static void modify(String s) {
s = s.toUpperCase();
}
public static void main(String[] args) {
String str = "hello";
modify(str);
System.out.println(str);
}
}
Q: 최종 출력 결과는?
좋습니다! 😎
지금부터 문제 하나하나 디버깅 수준으로 흐름을 완전히 해부해서
“어디서 왜 틀릴 수밖에 없었는지”까지 확실히 이해시키겠습니다.
static void change(int[] arr) {
arr = new int[]{9, 9, 9}; // ❗ arr가 가리키는 배열 자체를 바꿔버림
arr[0] = 100;
}
public static void main(String[] args) {
int[] nums = {1, 2, 3};
change(nums);
System.out.println("nums[0] = " + nums[0]);
}
change(nums) 호출 시 nums의 주소(참조)가 arr로 복사됨
그러나 arr = new int[]{9, 9, 9} 이 부분에서,
arr는 새로운 배열을 가리키게 됨!이후 arr[0] = 100은 → 새 배열의 0번째에 저장됨
(nums는 그대로 유지)
nums[0] = 1void update(int* p) {
int x = *p;
x += 10;
p = &x; // ❗ 포인터 자체를 바꿔버림 (실제 값에는 영향 없음)
}
p는 &a를 받아서 a의 주소를 가리킴 (예: p → 0x1000)x = *p → x = 5x += 10 → x = 15p = &x → p가 x를 가리키게 바뀜 → a와는 무관해짐a = 5static void change(Data d) {
d = new Data(); // ❗ d는 새로운 객체를 가리킴 (원본과 끊김)
d.x = 999;
}
change(d) 호출 시, d의 참조(주소)가 복사됨d = new Data() → d가 새로운 객체를 가리킴d.x = 999은 새 객체에 저장된 값d.x = 100이 문제는 일부 컴파일러에서는 안 돌아갈 수 있어요.
원래는 이런 구조를 사용할 수 없지만, 핵심 개념을 설명하기 위해 비슷한 구조로 풀게요.
void change(int arr[]) {
arr = (int[]){9, 9, 9}; // ❗ 배열 포인터를 바꿈
arr[0] = 100;
}
arr는 사실상 int* arr로 해석됨 (배열의 주소)arr = ... → arr가 가리키는 배열을 새로운 배열로 교체nums[]는 여전히 원래 배열을 가리킴arr[0] = 100은 새 배열의 값 → nums는 변함 없음nums[0] = 1static void modify(String s) {
s = s.toUpperCase();
}
String은 immutable (불변 객체)s.toUpperCase()는 새로운 문자열 "HELLO"을 생성s = ... → s가 새 문자열을 가리키게 됨str은 여전히 "hello""hello"| 문제 번호 | 정답 출력 | 이유 요약 |
|---|---|---|
| 문제 1 | nums[0] = 1 | 배열 자체를 새로 만들어 연결이 끊김 |
| 문제 2 | a = 5 | 포인터를 다른 변수(x)로 바꿔버림 |
| 문제 3 | d.x = 100 | 객체 자체를 새로 만들어 원본과 끊어짐 |
| 문제 4 | nums[0] = 1 | 배열 포인터만 바꿈 (원래 배열 영향 없음) |
| 문제 5 | hello | 문자열은 불변, 참조만 바뀜 → 원본 그대로 |
정확히 봤어요! 👏
거의 완벽한 이해에 도달하셨고,
딱 한 단계만 더 구체화하면 완전 마스터입니다.
❝ 변수가 선언된 범위 안에서만 쓰이면 계속 누적되고 영향이 있지만,
그 범위를 벗어나서 다른 함수 등으로 연동될 때는
➤ **배열이나 포인터(=참조형)**가 아닌 이상, 값은 안 바뀐다 ❞
🟢 거의 정확해요!
이걸 더 명확히 하자면 이렇게 됩니다:
| 상황 | 예시 | 값이 바뀜? | 이유 |
|---|---|---|---|
| 같은 함수 안 | for문 안에서 int temp 계속 누적 | ✅ 바뀜 | 같은 변수이기 때문에 그대로 영향 받음 |
함수로 값을 넘김 (int x) | change(int x) | ❌ 안 바뀜 | 값이 복사되기 때문 |
함수로 주소를 넘김 (int* p, int[] arr) | change(int* p) / change(int[] arr) | ✅ 바뀜 | 메모리 주소를 통해 원본 직접 수정 |
| 함수 안에서 참조형 자체를 새로 생성함 | arr = new int[3]; / obj = new Obj(); | ❌ 원본과 끊어짐 | 참조가 바뀌었을 뿐, 원본은 유지됨 |
| 문자열과 같이 불변 객체 (Java String) | str = str.toUpperCase(); | ❌ 바뀌지 않음 | 새 객체 생성됨. 원본은 그대로 |
✔️ 같은 범위(=같은 함수, 같은 객체 안)에서 사용하면 영향 받는다.
✔️ 그 범위를 벗어나 다른 함수로 전달되는 경우는,
➤ 배열이나 포인터처럼 주소가 전달되는 경우만 원본에 영향을 준다.
완벽합니다. 🎯