혼자 공부하는 C - 포인터



포인터의 기본 개념


메모리의 주소

프로그램에서는 데이터를 메모리에 저장하는데, 메모리에서 어디에 저장되었는지는 주소를 이용해 알 수 있습니다. 메모리의 위치는 바이트 단위이며 0부터 시작합니다.

int 형은 4바이트 이므로 변수에 값을 저장하면, 메모리의 4 바이트가 할당되게 됩니다. 주소값은 OS나 메모링 사용 현황에 따라 다르게 배정됩니다.

예를 들어, int a를 지정했고 메모리에서 100 번지에 배정되었다면 100, 101, 102, 103 은 a가 할당된 영역이 됩니다.


주소 연산자

변수의 주소는 &를 사용해 구할 수 있습니다.


1
2
3
int a;
printf("%u\n", &a); // a의 주소를 10진수로 출력
printf("%p\n", &a); // a의 주소를 16진수로 출력

포인터와 간접 참조 연산자

포인터는 변수의 메모리 주소를 저장하는 변수입니다. 선언할 변수명 앞에 *를 붙여서 선언합니다.

*는 포인터임을 표시하는 기호이며, 간접 참조 연산자 혹은 포인터 연산자 라고 합니다.


1
2
3
4
5
6
7
8
9
int a;
int *pa; // 자료형 *변수명;
pa = &a; // pa에 a의 주소를 대입함. pa가 a를 가리킴. pa -> a

/*
포인터 변수를 *와 함께 사용하면 가리키는 변수의 데이터에 접근한다.
따라서 가리키는 변수의 값을 포인터 변수에서 대입할 수 있고, a의 값도 바뀐다.
*/
*pa = 10;

포인터 변수 *pa를 대입연산자의

  • 왼쪽에 위치하면 pa가 가리키는 변수의 저장공간으로 사용됩니다. → l-value
  • 오른쪽에 위치하면 pa가 가리키는 변수의 값으로 사용됩니다. → r-value

scanf 함수에서 변수에 지정할 때 &를 사용하는 것도 변수의 주소를 알려주기 위함이며, 포인터가 존재하면 포인터를 직접 & 없이 넣어줄 수 있습니다.


1
2
3
4
int a;
int *pa = &a;
scanf("%d", &a);
scanf("%d", pa); // 위와 같은 동작을 한다.

const를 사용한 포인터

포인터를 const로 지정한다면, 포인터 간접 참조해 변수의 값을 바꿀 수 없습니다. 단, 포인터 가리키는 변수를 바꾸는 것은 가능합니다.


1
2
3
4
5
int a = 1, b = 2;
const int *p = &a;

p = &b; // 가능
*p = 3; // 에러 발생

주소와 포인터 차이

주소는 변수의 메모리 저장공간의 주소 값 자체이며, 포인터는 변수의 주소 값을 저장하는 변수입니다. 주소는 상수이며 포인터는 변수이므로, 하나의 주소를 여러 포인터가 가리킬 수도 있습니다.


주소와 포인터 크기

포인터는 변수의 주소를 저장하고 있기 때문에, 포인터 자체도 크기가 있으며 이는 가리키는 변수의 자료형과는 상관없이 일정합니다.


포인터의 대입 규칙


아래 예시를 보면 double 형 포인터에 int형 포인터의 주소를 입력한 예시입니다.


1
2
3
4
5
int a;
int *p = &a;
double *pd;

pd = p; // 포인터의 자료형이 다르다.

a의 주소가 100을 가리키고 있다면, 메모리에는 100~103을 차지하게 됩니다.

double형 포인터는 a의 주소를 가질 수 있으나, double은 8바이트의 크기를 가지므로 100 ~ 107의 값을 사용하게 되므로, 의도한 결과가 나타나지 않게됩니다.


포인터를 사용하는 이유

메모리에 직접 접근하는 경우나, 동적할당한 메모리를 사용하는 경우에 필요합니다.

두 변수의 값을 바꾸는 함수의 예시입니다. swap 함수에 int형 변수의 주소를 2개 넣어주면 두 값을 바꾸는 기능을 합니다. 이처럼 변수의 주소를 이용하면 함수간에 데이터를 공유하는 것이 편리해집니다.


1
2
3
4
5
6
7
void swap(int *pa, int *pb)
{
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}

다음은 3개의 실수를 입력 받고 정렬될 값을 출력하는 예시입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>

void swap(double *pa, double *pb);
void line_up(double *maxp, double *midp, double *minp);

int main(void)
{
double max, mid, min;
printf("실수값 3개를 입력하세요: ");
scanf("%lf%lf%lf", &max, &mid, &min);
line_up(&max, &mid, &min);
printf("정렬된 값: %.1lf %.1lf %.1lf\n", max, mid, min);

return 0;
}

void swap(double *pa, double *pb)
{
double temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}

void line_up(double *maxp, double *midp, double *minp)
{
if (*minp > *midp)
{
swap(minp, midp);
}
if (*minp > *maxp)
{
swap(minp, maxp);
}
if (*midp > *maxp)
{
swap(midp, maxp);
}
}

오늘은 포인터 기본에 대해 정리해 보았습니다. 감사합니다~~👋👋