수업 목표
- 주소 연산자와 역참조 연산자에 대해 배워본다.
- 참조와 역참조에 대해 배워본다.
- Swap() 함수를 배워본다.
지난 시간에 메모리 주소를 저장하는 방법에 대해 배웠습니다.
그럼 메모리 주소는 어떻게 얻을까요?
주소 연산자(address-of operator) &
피연산자의 메모리 주소를 반환하는 연산자. 기호 &(Ampersand)를 사용합니다.
사실 scanf() 함수에서 사용 했었던 주소 연산자
~에”라고 해석하자고 배웠었습니다. 사실은 변수 앞에 &를 붙혀서 그 변수의 메모리 주소를 얻은 것입니다. 해당 메모리 주소에 입력 받은 값을 저장할 수 있게된 것입니다.
메모리 주소를 얻을 수 있는 두 가지 방법
1. 주소 연산자
2. 배열의 이름
메모리 주소는 어떻게 얻는지 배웠습니다.
해당 메모리 주소에 저장 되어 있는 값을 알고 싶다면 어떻게 해야할까요?
역참조 연산자(indirection operator) *
피연산자로 포인터를 받아서, 해당 메모리 주소에 저장된 값을 읽거나 값을 수정할 때 사용하는 연산자.
[좋은습관] “메모리 그리기”
포인터 관련 예제를 풀 때는 “메모리 그리기”를 해봅시다. 사각형을 그린 다음, 변수들을 적고 각 변수에 메모리 주소를 0x100번지부터 적어줍니다. 그 상태로 한 줄씩 소스코드를 분석해봅시다. “천천히 읽기” + “메모리 그리기”
Ex070201) 주소 연산자와 역참조 연산자 1 [중요 샘플 코드]
아래 소스코드를 따라서 작성해봅시다.
따라 적은 코드의 출력 결과를 예측해보고, 예측 결과와 실행 결과를 비교해봅시다.
// Main.c
#include <stdio.h>
int main(void)
{
int X = 10;
int* PtrToX = &X;
printf("%d\n", *PtrToX);
*PtrToX = 100;
printf("%d\n", *PtrToX);
return 0;
}
<예측 결과>
10
100
<실행 결과>
10
100
Ex070202) 주소 연산자와 역참조 연산자 2
아래 소스코드를 따라서 작성해봅시다. 따라 적은 코드의 출력 결과를 예측해보고, 예측 결과와 실행 결과를 비교해봅시다.
// Main.c
#include <stdio.h>
int main(void)
{
int X = 10;
int* PtrToX = &X;
printf("%d\n", *PtrToX);
*PtrToX = *PtrToX * 2;
printf("%d\n", *PtrToX);
return 0;
}
<예측 결과>
20
Ex070203) 주소 연산자와 역참조 연산자 3
아래 소스코드를 따라서 작성해봅시다.
따라 적은 코드의 출력 결과를 예측해보고, 예측 결과와 실행 결과를 비교해봅시다.
// Main.c
#include <stdio.h>
int main(void)
{
int X = 10, Y = 20;
int* PtrToX = &X;
printf("%d\n", *PtrToX);
*PtrToX = *PtrToX * 2;
PtrToX = &Y;
printf("%d\n", *PtrToX);
return 0;
}
Ex070204) 주소 연산자와 역참조 연산자 4
아래 소스코드를 따라서 작성해봅시다.
따라 적은 코드의 출력 결과를 예측해보고, 예측 결과와 실행 결과를 비교해봅시다.
[여기]에서 연산자 우선순위와 결합법칙 표를 볼 수 있습니다.
// Main.c
#include <stdio.h>
int main(void)
{
int X = 10;
int* PtrToX = &X;
printf("%d\n", X);
printf("%d\n", (*PtrToX)++);
printf("%d\n", X);
printf("%d\n", *PtrToX++);
// 문제 속의 문제. 계산될 연산자의 순서를 맞추시오.
return 0;
}
포인터 Vs. 역참조 연산자 Vs. 곱셈 연산자
셋은 모두 *(Asterisk) 기호를 사용합니다.
포인터: 자료형 오른쪽에 붙습니다.
역참조 연산자: 피연산자가 한 개. 특히 피연산자로 메모리 주소를 받습니다.
곱셈 연산자: 피연산자가 두 개.
Ex070205) 주소 출력
메모리 주소를 출력할 때 %X로 출력하는 것은 올바르지 않습니다.
형식 지정자 %p로 메모리 주소를 출력하는 게 올바르나, void* 자료형으로 형변환이 필요합니다.
아래 소스코드를 따라서 작성 후 실행 해봅시다.
// Main.c
#include <stdio.h>
int main(void)
{
int Num = 100;
int* PointerToNum = &Num;
printf("Num: %d\n", Num);
printf("&Num: %X\n", &Num);
printf("&Num: %p\n", (void*)&Num);
printf("PointerToNum: %X\n", PointerToNum);
printf("PointerToNum: %p\n", (void*)PointerToNum);
printf("*PointerToNum: %d\n", *PointerToNum);
printf("*PointerToNum: %d\n", *(&Num)); // 올바르진 않지만, 소거하는 식으로라도 이해해 보자.
printf("(*PointerToNum)*2: %d\n", (*PointerToNum) * 2);
return 0;
}
<?> (void*)의 의미를 모르겠다.
포인터의 단점
큰 데이터들의 시작 메모리 주소만으로 가볍게 공유할 수 있다는 점은 좋습니다.
다만, 공유 받은 사람이 착한 사람인지는 모릅니다.
해당 주소로 가서 들어있는 값을 마음대로 수정 할수도 있습니다.
아주 강력한 기능임과 동시에 그만큼 잘못쓰면 큰일 날 수도 있습니다.
Ex070206) 다양한 자료형과 포인터
아래 소스코드를 따라서 작성해봅시다.
따라 적은 코드의 출력 결과를 예측해보고, 예측 결과와 실행 결과를 비교해봅시다.
// Main.c
#include <stdio.h>
int main(void)
{
char Character = 'C';
char* PtrToChar = &Character;
short Num1 = 10;
short* PtrToShort = &Num1;
int Num2 = 11;
int* PtrToInt = &Num2;
float Num3 = 3.14f;
float* PtrToFloat = &Num3;
double Num4 = 3.141592;
double* PtrToDouble = &Num4;
printf("%p: %c(%d)\n", (void*)PtrToChar, Character, Character);
printf("%p: %d\n", (void*)PtrToShort, Num1);
printf("%p: %d\n", (void*)PtrToInt, Num2);
printf("%p: %f\n", (void*)PtrToFloat, Num3);
printf("%p: %f\n", (void*)PtrToDouble, Num4);
return 0;
}
Ex070207) char 자료형과 포인터
아래 소스코드를 따라서 작성해봅시다.
따라 적은 코드의 출력 결과를 예측해보고, 예측 결과와 실행 결과를 비교해봅시다.
// Main.c
#include <stdio.h>
int main(void)
{
char Char1 = 'L';
char Char2 = 'O';
char* PtrToChar = &Char1;
printf("%p: %c\n", (void*)PtrToChar, *PtrToChar);
PtrToChar = &Char2;
printf("%p: %c\n", (void*)PtrToChar, *PtrToChar);
PtrToChar = &Char1;
printf("%p: %c\n", (void*)PtrToChar, *PtrToChar);
return 0;
}
Ex070208) 뒤섞기 1
아래 소스코드를 따라서 작성해봅시다.
따라 적은 코드의 출력 결과를 예측해보고, 예측 결과와 실행 결과를 비교해봅시다.
// Main.c
#include <stdio.h>
int main(void)
{
int Num1 = 10;
int Num2 = 20;
int Num3 = 30;
int* Ptr1 = &Num1;
int* Ptr2 = &Num2;
int* Ptr3 = &Num3;
Ptr3 = Ptr2;
Ptr2 = Ptr1;
Ptr1 = Ptr2;
/* Q. num1을 가리키는 포인터는? */
printf("%p: %d\n", (void*)Ptr1, *Ptr1);
printf("%p: %d\n", (void*)Ptr2, *Ptr2);
printf("%p: %d\n", (void*)Ptr3, *Ptr3);
return 0;
}
Ex070209) 뒤섞기 2
아래 소스코드를 따라서 작성해봅시다.
따라 적은 코드의 출력 결과를 예측해보고, 예측 결과와 실행 결과를 비교해봅시다.
// Main.c
#include <stdio.h>
int main(void)
{
int Num1 = 15;
int Num2 = 30;
int Num3 = 45;
int* Ptr1 = &Num1;
int* Ptr2 = &Num2;
int* Ptr3 = &Num3;
Ptr1 = Ptr2;
Ptr2 = Ptr3;
*Ptr3 *= 2;
*Ptr1 += *Ptr3;
*Ptr2 *= 2;
/* Q. 출력 결과는? */
printf("%d %d %d", Num1, Num2, Num3);
return 0;
}
90
120
180
참조에 의한 호출 Vs. 값에 의한 호출
참조에 의한 호출 Vs. 값에 의한 호출
원본값이 바뀌냐 Vs. 안 바뀌냐.
함수 A가 함수 B를 호출 할 때, 인자를 전달하면서 호출한다고 가정해봅시다.
함수 B가 종료될 때 인자의 원본값도 바뀐다면 참조에 의한 호출입니다.
인자의 원본값이 바뀔 수 없다면 값에 의한 호출이라고 부릅니다.
'C언어 공부 > C언어 강의' 카테고리의 다른 글
| 챕터 7-1 : 포인터 (0) | 2026.03.06 |
|---|---|
| 챕터 6-5 : 변수의 종류 (0) | 2026.03.06 |
| 챕터 6-4 : 전방 선언 (0) | 2026.03.06 |
| 챕터 6-3 : 스코프 (0) | 2026.03.06 |
| 챕터 6-2 : [심화] 스택 프레임 (0) | 2026.03.06 |