A. 포인터
1. 포인터의 개념
a. 주소값의 이해
데이터의 주소값이란 해당 데이터가 저장된 메모리의 시작 주소를 의미
C언어에서는 주소값을 1바이트 크기의 메모리 공간으로 나누어 표현
b. 포인터란?
🌟 메모리의 주소값을 저장하는 변수
포인터 변수라고도 부름
💥 WARNING
포인터 연산자와 헷갈리면 안됨!
포인터 연산자는 포인터 변수에 있는 데이터를 가져오는 기호!
👉 포인터 연산자 공부하기 (기타)
c. 포인터의 선언
📘 문법
타입* 포인터이름; //선언만 하는법
or
타입* 포인터이름 = &변수이름; //1) 선언 & 초기화 하기
타입* 포인터이름 = 주소값; //2) 선언 & 초기화 하기
- 타입 : 포인터가 가리키고자 하는 변수의 타입
- 포인터 이름 : 포인터가 선언된 후에 포인터에 접근하기 위해 사용
👉 포인터 선언 후 참조 연산자를 사용하기 전에 포인터는 반드시 초기화 되어야 함 (문법 아래와같이)
💥 그렇지 않으면 의도하지 않은 메모리 값을 변경하게 되어 오류를 발생시킴
🤓 예제
int n = 100; // 변수의 선언
int *ptr = &n; // 포인터의 선언
🎨 도식화
포인터 변수 *ptr은 n의 주소를 저장함 (메모리에 있는 값 부터 주소를 저장한다)
d. 포인터의 참조
선언된 포인터는 참조 연산자(*)를 사용하여 참조
🤓 예제 1
int x = 7; // 변수의 선언
int *ptr = &x; // 포인터의 선언
int *pptr = &ptr; // 포인터의 참조
🎨 도식화
🤓 예제 2
int num01 = 1234;
double num02 = 3.14;
int* ptr_num01 = &num01;
double* ptr_num02 = &num02;
1) printf("포인터의 크기는 %d입니다.\n", sizeof(ptr_num01));
2) printf("포인터 ptr_num01이 가리키고 있는 주소값은 %#x입니다.\n", ptr_num01);
3) printf("포인터 ptr_num02가 가리키고 있는 주소값은 %#x입니다.\n", ptr_num02);
printf("포인터 ptr_num01이 가리키고 있는 주소에 저장된 값은 %d입니다.\n", *ptr_num01);
printf("포인터 ptr_num02가 가리키고 있는 주소에 저장된 값은 %f입니다.\n", *ptr_num02);
✨ 실행결과
1) 포인터의 크기는 8입니다.
2) 포인터 ptr_num01이 가리키고 있는 주소값은 0x7c255e4입니다.
3) 포인터 ptr_num02가 가리키고 있는 주소값은 0x7c255e8입니다.
포인터 ptr_num01이 가리키고 있는 주소에 저장된 값은 1234입니다.
포인터 ptr_num02가 가리키고 있는 주소에 저장된 값은 3.140000입니다.
🧡 1) 포인터의 크기가 8인 이유
포인터변수는 메모리에서 변수의 위치를 나타내는 주소를 다루는 변수 => 크기는 CPU에따라서 결정
32비트 CPU에서는 1word = 32bit = 4byte => 포인터 변수의 크기 또한 4byte
64비트 CPU에서는 2배라서 포인터의 크기가 8
2) 3) 에서는 포인터가 가리키는 변수의 타입에 따라 포인터의 타입도 같이 바꿈
👉 포인터의 타입은 참조 연산자를 통해 값을 참조할 때 참조할 메모리의 크기를 알려주는 역할
2. 포인터 연산
a. 한 포인터의 연산
- 포인터와 👉 정수 연산 가능 ⭕ (실수 ❌)
🤓 예제
char* ptr_char = 0;
int* ptr_int = NULL;
double* ptr_double = 0x00;
printf("포인터 ptr_char가 현재 가리키고 있는 주소값은 %#x입니다.\n", ptr_char);
printf("포인터 ptr_int가 현재 가리키고 있는 주소값은 %#x입니다.\n", ptr_int);
printf("포인터 ptr_double이 현재 가리키고 있는 주소값은 %#x입니다.\n", ptr_double);
printf("포인터 ptr_char가 1 증가 후에 가리키고 있는 주소값은 %#x입니다.\n", ++ptr_char);
printf("포인터 ptr_int가 1 증가 후에 가리키고 있는 주소값은 %#x입니다.\n", ++ptr_int);
printf("포인터 ptr_double이 1 증가 후에 가리키고 있는 주소값은 %#x입니다.\n", ++ptr_double);
✨ 실행결과
포인터 ptr_char가 현재 가리키고 있는 주소값은 0입니다.
포인터 ptr_int가 현재 가리키고 있는 주소값은 0입니다.
포인터 ptr_double이 현재 가리키고 있는 주소값은 0입니다.
포인터 ptr_char가 1 증가 후에 가리키고 있는 주소값은 0x1입니다.
포인터 ptr_int가 1 증가 후에 가리키고 있는 주소값은 0x4입니다.
포인터 ptr_double이 1 증가 후에 가리키고 있는 주소값은 0x8입니다.
📝 예제 설명
각 1을 증가시키는 포인터 연산을 하면 주소는 포인터 타입에 따라서 달라짐
증가폭은 포인터가 가리키는 변수의 타입과 크기가 같게 됨!
뺄셈도 동일하게 적용됨
🎨 도식화
b. 두 포인터의 연산
- 포인터끼리 + * / 👉 의미 ❌
- 포인터끼리 - 👉 상대적 거리 ↔
- 포인터끼리 대입 / 비교 ⭕
🤓 예제
int num01 = 10;
int num02 = 20;
int *ptr_num01 = &num01;
int *ptr_num02 = &num02;
if (ptr_num01 != ptr_num02) // 포인터끼리의 비교 연산
{
printf("포인터 ptr_num01이 가리키고 있는 주소에 저장된 값은 %d입니다.\n", *ptr_num01);
printf("포인터 ptr_num02가 가리키고 있는 주소에 저장된 값은 %d입니다.\n", *ptr_num02);
printf("포인터 ptr_num01과 ptr_num02는 현재 다른 주소를 가리키고 있습니다.\n\n");
ptr_num02 = ptr_num01; // 포인터끼리의 대입 연산
}
printf("포인터 ptr_num01이 가리키고 있는 주소에 저장된 값은 %d입니다.\n", *ptr_num01);
printf("포인터 ptr_num02가 가리키고 있는 주소에 저장된 값은 %d입니다.\n", *ptr_num02);
if (ptr_num01 == ptr_num02) // 포인터끼리의 비교 연산
{
printf("포인터 ptr_num01과 ptr_num02는 현재 같은 주소를 가리키고 있습니다.\n");
}
✨ 실행결과
포인터 ptr_num01이 가리키고 있는 주소에 저장된 값은 10입니다.
포인터 ptr_num02가 가리키고 있는 주소에 저장된 값은 20입니다.
포인터 ptr_num01과 ptr_num02는 현재 다른 주소를 가리키고 있습니다.
포인터 ptr_num01이 가리키고 있는 주소에 저장된 값은 10입니다.
포인터 ptr_num02가 가리키고 있는 주소에 저장된 값은 10입니다.
포인터 ptr_num01과 ptr_num02는 현재 같은 주소를 가리키고 있습니다.
3. 다양한 포인터
a. 포인터의 포인터
🌟 포인터 변수를 가리키는 포인터
참조 연산자(*)를 두 번 사용하여 표현, 이중 포인터라고 부름
🤓 예제
int num = 10; // 변수 선언
int* ptr_num = # // 포인터 선언
int** pptr_num = &ptr_num; // 포인터의 포인터 선언
printf("변수 num가 저장하고 있는 값은 %d입니다.\n", num);
printf("포인터 ptr_num가 가리키는 주소에 저장된 값은 %d입니다.\n", *ptr_num);
printf("포인터의 포인터 pptr_num가 가리키는 주소에 저장된 포인터가 가리키는 주소에 저장된 값은 %d입니다.\n",
**pptr_num);
✨ 실행결과
변수 num가 저장하고 있는 값은 10입니다.
포인터 ptr_num가 가리키는 주소에 저장된 값은 10입니다.
포인터의 포인터 pptr_num가 가리키는 주소에 저장된 포인터가 가리키는 주소에 저장된 값은 10입니다.
b. void 포인터(void pointer)
🌟 대상이 되는 데이터의 타입을 명시하지 않은 포인터 = 주소값을 저장하는 것 이외에는 아무것도 할 수 없는 포인터
변수, 함수, 포인터 등 어떠한 값도 가리킬 수 ⭕
포인터 연산이나 메모리 참조 ❌
void 포인터를 사용 ➡ 반드시 먼저 사용하고자 하는 타입으로 명시적 타입 변환 작업을 거친 후에 사용
🤓 예제
int num = 10; // 변수 선언
void* ptr_num = # // void 포인터 선언
printf("변수 num가 저장하고 있는 값은 %d입니다.\n", num);
printf("void 포인터 ptr_num가 가리키는 주소에 저장된 값은 %d입니다.\n", *(int*)ptr_num);
*(int*)ptr_num = 20; // void 포인터를 통한 메모리 접근
printf("void 포인터 ptr_num가 가리키는 주소에 저장된 값은 %d입니다.\n", *(int*)ptr_num);
✨ 실행결과
변수 num가 저장하고 있는 값은 10입니다.
void 포인터 ptr_num가 가리키는 주소에 저장된 값은 10입니다.
void 포인터 ptr_num가 가리키는 주소에 저장된 값은 20입니다.
c. 함수 포인터(function pointer)
프로그램에서 정의된 함수 => 실행될 때 모두 메인메모리에 올라감
이때 함수의 이름 = 메모리에 올라간 함수의 시작주소를 가리키는 포인터 상수가 됨
🌟 즉, 함수의 시작 주소를 가리키는 포인터 상수
포인터 타입은 함수의 반환값과 매개변수에 의해 결정 = 함수의 원형을 알아야 함
📘 문법
void func (int, int); //함수 원형
void (*ptr_func) (int, int); //함수 포인터
//함수 포인터 사용시 연산자의 우선순위 때문에 반드시 *ptr_func 부분을 괄호(())로 둘러싸야 함
d. 널 포인터(null pointer)
🌟 0이나 NULL을 대입하여 초기화한 포인터
아무것도 가리키지 않는 포인터라는 의미
'프로그래밍 언어 > C' 카테고리의 다른 글
[자료형(data type)] C 파생형 구조체 기본 (0) | 2023.08.17 |
---|---|
[자료형(data type)] C 파생형 포인터와 배열 (0) | 2023.08.17 |
[자료형(data type)] C 파생형 배열 총정리 (0) | 2023.08.17 |
[자료형(data type)] C 파생형 함수 총정리 (0) | 2023.08.17 |
C 제어문 총정리 (조건문, 반복문, 기타 제어문) (0) | 2023.08.17 |