정보처리기사 - c언어

AnalytiCode·2025년 10월 17일

매개변수 전달 방법

call by value

int addTen(int a) {
    a = 7;
    return a;
}

int main() {
    int a = 5;
    addTen(a);
    printf("함수 밖: a = %d\n", a);
    return 0;
}

이러면 출력값 5

main()에서 a는 5로 선언됨.

addTen(a)를 호출할 때,
→ a의 값 5가 복사되어 함수 매개변수 a(다른 변수) 에 전달됨.

main의 a = 5
addTen의 a = 5 (복사본)

함수 안에서 a = 7;을 해도, 복사본(addTen의 a) 을 바꾸는 것.
→ main의 a는 그대로 5인 상태로 남음

addTen 함수가 return a;로 7을 반환하지만, main()에서 그 반환값을 변수에 저장하지 않음
addTen(a); -> 이렇게만 써놨기 때문에 리턴값이 버려짐

만약 7로 바꾸고 싶다면?

✅: 리턴값을 받아서 저장

int main() {
    int a = 5;
    a = addTen(a);   // 리턴값(7)을 저장
    printf("함수 밖: a = %d\n", a);
    return 0;
}

➡️ 출력: 함수 밖: a = 7

#include <stdio.h>

int addTen(int a) {
    a = 7;
    printf("%d ", a);   // 따옴표("") 안에 %d 넣기, 뒤에 공백 하나
    return a;
}

int main() {
    int a = 5;
    addTen(a);
    printf("%d\n", a);  // "함수 밖: " 대신 숫자만 출력하도록
    return 0;
}

하면 출력값 7 5

#include <stdio.h>

int change(int x) {
    x = x + 5;
    printf("함수 안: x = %d\n", x);
    return x;
}

int main() {
    int a = 10;
    change(a);
    printf("함수 밖: a = %d\n", a);
    return 0;
}

함수 안 15, 함수 밖 10

value vs reference

call by value vs call by reference

void add(int *x) {
    *x = *x + 1;   // x가 가리키는 "원본 int"를 1 증가
}

int calc(int x, int *y) {
    x = x + 2;     // x는 값 복사본
    add(y);        // y는 이미 "주소(int*)"다. 그대로 전달
    printf("calc 안: x = %d, *y = %d\n", x, *y);
    return x + *y;
}

int main() {
    int a = 5, b = 10;
    int result = calc(a, &b);  // &b: b의 주소를 넘김
    printf("main: a = %d, b = %d, result = %d\n", a, b, result);
}

흐름 추적

  1. main: a=5, b=10.

  2. calc(a, &b) 호출

    • x값 복사x = 5
    • y포인터(주소) → y = &b
  3. x = x + 2;x = 7 (원본 a는 그대로 5)

  4. add(y) 호출

    • add의 매개변수 타입은 int *x
    • 지금 y는 이미 int*이므로 그대로 넘기는 게 맞음 (add(y))
    • add 내부에서 *x = *x + 1;*(&b) = b = 10 + 1 = 11
    • b가 11이 됨
  5. 그래서 calc 안에서 *y*(&b) = b = 11
    출력: calc 안: x = 7, *y = 11

  6. return x + *y7 + 11 = 18

  7. main 출력: a = 5(안 바뀜), b = 11(주소로 바뀜), result = 18

헷갈린 포인트

  • & 없으면 주소로 안 들어가는 거 아냐?”

    • 맞아. 주소를 처음 만들 때(원본 int의 주소가 필요할 때)는 &가 필요해. 그래서 calc(a, &b)에서 &b를 쓴 거야.
  • 그 다음 단계에서 y는 이미 주소(int*) 를 담고 있어.

    • addint*를 받으니까 그냥 add(y)로 호출하는 게 정답.
    • 만약 add(&y)라고 했다면 `int(이중 포인터)를 넘기는 꼴**이 되고, 함수 시그니처도 void add(int **x)` 여야 해. 전혀 다른 이야기.

한 줄 요약

  • 주소를 처음 생성할 때만 &원본변수가 필요.
  • 이미 포인터(주소) 변수라면 그 포인터 자체를 넘긴다. (add(y))

이중포인터

#include <stdio.h>

void addOne(int *x) {
    *x = *x + 1;
}

void changePtr(int **p) {
    **p = **p + 2;
    printf("changePtr 안: **p = %d\n", **p);
}

int main() {
    int a = 5;
    int *ptr = &a;

    addOne(ptr);
    changePtr(&ptr);

    printf("main: a = %d\n", a);
    return 0;
}

전체 흐름

int a = 5;
int *ptr = &a;

addOne(ptr);     // (*ptr) = (*ptr) + 1 → a = 6
changePtr(&ptr); // (**p) = (**p) + 2 → a = 8

printf("main: a = %d\n", a); // ??? 왜 8이지?

addOne() 함수 내부

void addOne(int *x) {
    *x = *x + 1;
}
  • 매개변수 xptr의 복사본(주소를 복사)
  • 하지만 x가 가리키는 곳은 여전히 a의 메모리 주소
  • 그래서 *x = *x + 1; → a 값이 직접 바뀜
    💡 결과: a = 6

changePtr() 함수 내부

void changePtr(int **p) {
    **p = **p + 2;
    printf("changePtr 안: **p = %d\n", **p);
}
  • &ptr을 넘겼으니
    p는 “ptr의 주소”를 받음.
  • 하지만 ptr 자체가 가리키는 건 여전히 a.

그림으로 보면

main 영역
 ┌───────────────┐
 │ a = 6         │
 │ ptr ───────┐  │
 └────────────┼──┘
              │
              ▼
          (주소) → [ a의 메모리 6 ]

이때 &ptr이 넘어가면
pptr을 가리키는 포인터의 주소를 가짐.

그래서 **p는 결국 p → ptr → a → 값(6)

**p = **p + 2;a = a + 2;,
💡 a = 8


changePtr() 함수 종료 후

함수가 끝나도, a는 여전히 메모리에 8로 저장되어 있음.
왜냐면 **pa의 실제 메모리 공간을 바꾼 거니까.

a: 8
ptr: 여전히 &a

main()의 printf 실행 시점

이제 printf("main: a = %d\n", a); 하면 바뀐 a(8)를 그대로 출력
최종 출력은

changePtr 안: **p = 8
main: a = 8

🧠 요약 포인트

함수매개변수실제로 바뀌는 대상결과
addOne(int *x)xa의 주소 복사a 값 직접 바뀜a = 6
changePtr(int **p)pptr의 주소 복사, **p → aa 값 직접 바뀜a = 8

배열과 포인터

int arr[3] = {10, 20, 30};
int *p;

arr(배열이름, 상수) = p; ❌ 오류
p(포인터변수) = arr; ✅ 가능

p = arr; → ✅ 가능
이건 배열의 시작 주소(1000) 를 포인터 변수 p에 저장하는 것

  • arr → 1000 (상수)
  • p = 1000 (복사됨)

arr = p; → ❌ 오류
포인터의 주소값을 배열 이름에 넣으려는 시도, 배열 이름은 상수이기 때문에 값을 바꿀 수 없음

컴파일러 입장:
“상수(주소)를 다른 값으로 대입하려 하네? 말이 안 돼!”

배열 이름 arr은 변수(variable) 이 아니라 “고정된 메모리 이름(label)”
= 주소값을 바꾸는 대입 연산(=)은 불가능

예시실제 의미
p = arr;“변수 p에 5를 넣어라.” — 가능
arr = p;“5 = 7;” — 말이 안 됨 ❌

arr = p; 이건 “5 = 7;” 같은 말도 안 되는 문장
왼쪽이 ‘값을 담는 변수’가 아니라 ‘이미 정해진 상수’이기 때문

상황설명
p는 “택배 기사 이름표”기사 이름표(p)는 다른 집 주소로 옮길 수 있음
arr은 “집 주소 자체”집 주소(arr)는 영구적으로 고정돼 있음

풀이하면

표현실제 의미
p는 “택배 기사 이름표”포인터 변수는 주소를 저장할 수 있는 변수다. 즉, “지금 어느 집에 가야 하는지 적어둘 수 있다.”
p는 다른 집으로 옮길 수 있다p = 다른주소; 가능. 즉, “기사 이름표에 새 주소를 적을 수 있다.”
arr은 “집 주소 자체”배열 이름은 이미 그 집(메모리)의 고정된 주소다.
arr은 영구적으로 고정돼 있다arr = ...; 불가능. 집 주소는 바뀌지 않는다.

0개의 댓글