문타쿠, 공부하다.
[C언어 코딩 도장] Unit 34. 포인터 사용하기 본문
INTRO
"우리가 변수를 사용할 때, 변수는 어디에 생기는 것일까?"
- 변수는 컴퓨터의 메모리에 생성된다.
- 즉, 메모리에 일정한 공간을 확보해두고 원하는 값을 저장하거나 가져오는 방식
#include <stdio.h>
int main(void)
{
int num1 = 10;
printf("%p\n", &num1);
return 0;
}
- 변수는 num1과 같이 이름으로 사용하지만, 메모리의 특정 장소에 있으므로 메모리 주소로도 표현할 수 있다.
- 메모리 주소는 고정된 것이 아니라 컴퓨터마다, 실행할 때마다 달라짐
- 변수의 메모리 주소를 구할 때는 변수 앞에 &(주소 연산자)를 붙이면 된다.
- 서식 지정자로 %p(포인터) 사용
- 메모리 주소는 16진수 형태이므로 %x, %X를 사용해도 된다.
- 시스템이 32비트인지 64비트인지에 따라 메모리 주소의 범위도 달라진다.
34.1 포인터 변수 선언하기
C언어에서 메모리 주소는 포인터 변수에 저장한다.
#include <stdio.h>
int main(void)
{
int *numPtr;
int num1 = 10;
numPtr = &num1;
printf("%p\n", numPtr);
printf("%p\n", &num1);
return 0;
}
- 포인터와 메모리 주소는 같은 의미
- 포인터는 메모리의 특정 위치를 가리킬 때 사용
34.2 역참조 연산자 사용하기
포인터 변수에는 메모리 주소가 저장되어 있다.
메모리 주소가 있는 곳으로 이동해서 값을 가져오고싶다면 역참조 연산자 *를 사용한다.
#include <stdio.h>
int main(void)
{
int *numPtr;
int num1 = 10;
numPtr = &num1;
// 역참조 연산자: num1의 메모리 주소에 접근하여 값을 가져옴
printf("%d\n", *numPtr);
return 0;
}
- 포인터는 변수의 주소만 가리키며, 역참조 연산자는 주소에 있는 값에 접근한다.
포인터 선언과 역참조?
- 포인터를 선언할 때 *는 "이 변수가 포인터다 = 메모리 주소를 저장하는 변수다"라고 알려주는 역할
- 포인터에 사용할 때 *는 "포인터의 메모리 주소를 역참조하겠다 = 포인터가 가지고 있는 메모리 주소의 값을 가져오겠다"라는 뜻
"포인터 변수에 역참조 연산자를 사용한 뒤 값을 저장(할당)"
#include <stdio.h>
int main(void)
{
int *numPtr;
int num1 = 10;
numPtr = &num1;
*numPtr = 20;
printf("%d\n", *numPtr);
printf("%d\n", num1);
return 0;
}
역참조 연산자는 값을 가져올 수도 있고, 값을 저장할 수도 있다.
변수는 메모리 주소를 몰라도 값을 가져오거나 저장할 수 있다.
주소 연산자 &는 변수의 메모리 주소를 구한다.
포인터는 변수의 메모리 주소만 가리킨다.
역참조 연산자 *는 메모리 주소를 거쳐서 그 안에 있는 값을 가져오거나 저장한다.
34.3 디버거에서 포인터 확인하기
디버거를 사용하면 변수의 메모리 주소, 포인터, 역참조를 손쉽게 확인할 수 있다.
34.4 다양한 자료형의 포인터 선언하기
C언어에서 사용할 수 있는 모든 자료형은 포인터로 만들 수 있다.
#include <stdio.h>
int main(void)
{
long long* numPtr1;
float* numPtr2;
char* cPtr1;
long long num1 = 10;
float num2 = 3.5f;
char c1 = 'a';
numPtr1 = &num1;
numPtr2 = &num2;
cPtr1 = &c1;
printf("%lld\n", *numPtr1);
printf("%f\n", *numPtr2);
printf("%c\n", *cPtr1);
return 0;
}
34.5 void 포인터 선언하기
#include <stdio.h>
int main(void)
{
int num1 = 10;
char c1 = 'a';
int* numPtr1 = &num1;
char* cPtr1 = &c1;
void* ptr;
ptr = numPtr1;
ptr = cPtr1;
numPtr1 = ptr;
cPtr1 = ptr;
return 0;
}
- void 포인터는 자료형이 정해지지 않은 특성 때문에 어떤 자료형으로 된 포인터든 모두 저장할 수 있다.
- 반대로 다양한 자료형으로 된 포인터에도 void 포인터를 저장할 수 있다.
- 이러한 특성때문에 void 포인터는 범용 포인터라고 한다. -> 직접 자료형을 변환하지 않아도 암시적으로 자료형이 변환되는 방식
- 단, void 포인터는 자료형이 정해지지 않았으므로 값을 가져오거나 저장할 크기도 정해지지 않았기에, 역참조할 수 없다.
- 마찬가지로 void 키워드로는 변수를 선언할 수 없다.
34.6 이중 포인터 사용하기
포인터를 선언할 때 *를 두 번 사용하면 포인터의 포인터(이중 포인터)를 선언한다.
#include <stdio.h>
int main(void)
{
int* numPtr1;
int** numPtr2;
int num1 = 10;
numPtr1 = &num1;
numPtr2 = &numPtr1;
printf("num1의 주소\n");
printf("&num1: %p\n\n", &num1);
printf("num1의 값\n");
printf("num1: %d\n\n\n", num1);
printf("numPtr1의 주소\n");
printf("&numPtr1: %p\n\n", &numPtr1);
printf("numPtr1이 가지고 있는 값 = num1의 주소\n");
printf("numPtr1: %p\n\n", numPtr1);
printf("numPtr1이 가지고 있는 값(num1의 주소)의 값 = num1의 값\n");
printf("*numPtr1: %d\n\n\n", *numPtr1);
printf("numPtr2의 주소\n");
printf("&numPtr2: %p\n\n", &numPtr2);
printf("numPtr2가 가지고 있는 값 = numPtr1의 주소\n");
printf("numPtr2: %p\n\n", numPtr2);
printf("numPtr2가 가지고 있는 값(numPtr1의 주소)의 값 = num1의 주소\n");
printf("*numPtr2: %p\n\n", *numPtr2);
printf("numPtr2가 가지고 있는 값(numPtr1의 주소)의 값(num1의 주소)의 값(num1의 값)\n");
printf("**numPtr2: %d\n\n", **numPtr2);
return 0;
}
- 포인터도 실제로는 변수이기 때문에 메모리 주소를 구할 수 있다.
- 포인터의 메모리 주소는 일반 포인터에 저장할 수 없고 이중 포인터에 저장해야 한다. -> 포인터 변수끼리 서로 주소를 저장하는 것은 허용되지 않음
34.7 잘못된 포인터 사용
- 포인터는 메모리 주소를 저장하는 용도이므로 값을 직접 저장하면 안된다.
- 하지만 만약 실제로 존재하는 메모리 주소라면 포인터에 직접 저장할 수 있다.
'C언어 > C언어 코딩 도장' 카테고리의 다른 글
[C언어 코딩 도장] Unit 35. 메모리 사용하기 (0) | 2023.09.09 |
---|---|
[C언어 코딩 도장] Unit 34. 연습문제 및 심사문제 (0) | 2023.09.09 |
[C언어 코딩 도장] Unit 33. 연습문제 및 심사문제 (0) | 2023.09.05 |
[C언어 코딩 도장] Unit 33. FizzBuzz (0) | 2023.09.05 |
[C언어 코딩 도장] Unit 32. goto로 프로그램의 흐름을 원하는 대로 바꾸기 (0) | 2023.09.05 |