09-1. 포인터의 기본 개념
포인터 : 메모리 주소 값을 저장하는 자료형.
[메모리]
: 데이터 저장, 추출을 하기 위해 사용되는 공간.
함수, 변수 등을 '선언'할 때 특정 크기의 메모리가 할당되고, 할당된 메모리 내에서 데이터를 처리할 수 있음.
어떤 함수를 선언한 후 해당 함수 내부에 여러 가지 변수를 선언할 경우, 변수에 할당되는 메모리는 처음 함수 선언 시 할당된 메모리의 일부. > 같은 이름의 변수를 사용하더라도 선언된 함수의 위치가 다르면 다른 변수로 사용될 수 있으며, '변수명'만으로 다른 함수의 메모리 공간에 접근할 수 없음.

- 메모리주소
: 함수나 변수를 선언할 때 할당되는 메모리 공간의 위치를 나타내는 값. 자료형의 크기에 따라 메모리 공간이 할당되고, 2바이트 이상의 변수는 여러 개의 주소 값에 걸쳐 할당됨. 한 변수(혹은 함수)에 대하여 할당된 메모리 전체는 변수(혹은 함수)의 이름으로 사용될 수 있음.
* 주소 연산자 (&, 엠퍼센트) : 변수 앞에 붙이면, 해당 변수에 할당된 메모리 공간의 주소를 나타내는 주소 값이 됨. 4
** 주소를 출력할 때는 "%p" 를 사용
[포인터]
: 메모리 주소 값을 저장하는 변수 자료형. 변수 앞에 *만 붙이면 포인터 변수로 선언할 수 있음.
< 자료형 *변수명;>
포인터 변수에는 '주소 값'만 저장할 수 있음. 포인터 변수 pa에 변수 a의 주소를 저장하면, '포인터 변수 pa가 변수 a를 가리킨다.' 라고 표현.
** 포인터를 화살표로 생각하면 이해하기 쉬움!!
- 간접 참조 연산자(*) : 포인터 변수에 저장된 변수 주소에 접근하여 데이터를 추출, 저장 등 데이터 처리가 가능한 연산자. 포인터 변수를 선언할 때는 <자료형 *변수명>의 형태로 선언하고, 간접 참조할 때는 자료형을 제외하고 <*변수명>의 형태로 사용함.
[const 포인터]
: const는 constant, 상수라는 뜻으로, 선언하면서 저장된 값을 변경할 수 없도록 함. const로 선언된 포인터 변수의 경우, 해당 포인터 변수를 통해 저장된 메모리 공간에 접근하여 값을 변경할 수 없다는 것을 의미함! > 데이터 변경 시도 시 에러 메시지 출력.
09-2 포인터 완전 정복을 위한 포인터 이해
* 포인터 ? 주소를 저장하는 일정한 크기의 메모리 공간. 다른 주소 저장, 포인터의 대입이 가능함.
[주소와 포인터의 차이]
주소 : 변수에 할당된 메모리 저장 공간의 시작 주소 값. 상수이므로 다른 값을 대입하거나 변경이 불가함.
포인터 : 변수에 할당된 메모리 저장 공간의 시작 주소 값, 즉 주소를 저장하는 또 다른 메모리 공간. const로 선언되지 않는 이상 변수이므로 다른 값을 대입하거나 변경이 자유로움.
즉, 프로그램 내에서 선언된 변수에 할당되는 메모리 공간의 주소는 코드가 실행된 이후로 절대 그 값이 바뀌지 않지만, 포인터 변수를 선언할 경우 코드를 어떻게 짜는지에 따라 저장된 주소 값이 바뀔 수 있음. 즉, 가리키는 변수(포인터 변수가 가리키는 메모리 공간 위치)가 달라질 수 있음.
[주소와 포인터의 크기]
- sizeof 연산자를 통해 구할 수 있음. 포인터와 주소의 경우, 메모리 공간의 특정 위치가 저장되므로 포인터의 크기와 변수의 주소 크기는 동일하지만, 해당 주소가 가리키는 변수의 크기는 달라질 수 있음!
[포인터 대입 규칙]
: 포인터는 크기가 모두 같지만, 포인터가 가리키는 변수의 형태가 같을 때만 대입이 가능함. > 일반 변수와 비슷함!
*형 변환을 사용한 포인터의 대입은 가능.
[포인터 사용 이유]
: 함수 안에 선언된 변수명은 사용 범위가 함수 내부로 제한되므로, 다른 함수에서 선언된 변수에 접근하거나 데이터를 처리할 수 없음. > 서로 다른 함수에서 선언된 변수에 접근해서 다양한 데이터 처리를 하기 위해 포인터가 필수!!
< 예제 코드 >
1.
#include <stdio.h>
int main(){
int a;
double b;
char c;
printf("int형 변수의 주소 : %u\n", &a);
printf("double형 변수의 주소 : %u\n", &b);
printf("char형 변수의 주소 : %u\n", &c);
}
2.
#include <stdio.h>
int main(){
int a;
int *pa;
pa = &a;
*pa = 10;
printf("포인터로 a값 출력 : %d\n", *pa);
printf("변수명으로 a값 출력 : %d\n", a);
}

3.
#include <stdio.h>
int main(){
int a = 10, b = 15, total;
double avg;
int *pa, *pb;
int *pt = &total;
double *pg = &avg;
pa = &a;
pb = &b;
*pt = *pa + *pb;
*pg = *pt / 2.0;
printf("두 정수의 값 : %d, %d\n", *pa, *pb);
printf("두 정수의 합 : %d\n", *pt);
printf("두 정수의 평균 : %.1lf\n", *pg);
}

5.
#include <stdio.h>
int main(){
char ch;
int in;
double db;
char *pc = &ch;
int *pi = ∈
double *pd = &db;
printf("char형 변수의 주소 크기 : %d\n", sizeof(&ch));
printf("int형 변수의 주소 크기 : %d\n", sizeof(&in));
printf("double형 변수의 주소 크기 : %d\n", sizeof(&db));
printf("char * 포인터의 크기 : %d\n", sizeof(pc));
printf("int * 포인터의 크기 : %d\n", sizeof(pi));
printf("double * 포인터의 크기 : %d\n", sizeof(pd));
printf("char * 포인터가 가리키는 변수의 크기 : %d\n", sizeof(*pc));
printf("int * 포인터가 가리키는 변수의 크기 : %d\n", sizeof(*pi));
printf("double * 포인터가 가리키는 변수의 크기 : %d\n", sizeof(*pd));
}
6.
#include <stdio.h>
void swap(int* pa, int* pb);
int main(){
int a = 10, b = 20;
swap(&a, &b);
printf("a : %d, b : %d\n", a, b);
}
void swap(int* pa, int* pb){
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}

* 포인터를 활용하면 외부 함수에서 메인 함수에 선언된 변수 메모리에 접근하여 그 안의 값을 수정할 수 있음!!
7. 도전 실전 예제
#include <stdio.h>
void swap(double *pa, double *pb);
void line_up(double *maxp, double *midp, double *minp);
int main(){
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);
}
void swap(double *pa, double *pb){
double temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}
void line_up(double *maxp, double *midp, double *minp){
if(*maxp < *midp){
swap(maxp, midp);
}
if(*maxp < *minp){
swap(maxp, minp);
}
if(*midp < *minp){
swap(midp, minp);
}
}