이중포인터와 void 포인터
문자열배열은 이중포인터이다
아래와 같이
문자열 배열이 있고,
해당 문자열배열의 주소를 void 포인터에 초기화시켰다고 가정해보자.
char* arr[] = {"banana", "apple"};
void* vrr = arr;
여기서 다들 아는 내용을 정리해보자.
- 배열의 이름은 해당 배열의 첫 번째 요소의 주소를 나타내므로, arr는 &(arr[0])와 동일하다.
- arr는 char* 타입의 요소를 저장하는 배열이기 때문에, 이를 char** 타입으로 볼 수 있다.
즉, arr는 char* 타입의 배열의 첫 번째 요소의 주소를 가리키는 포인터이다. - 포인터 변수의 크기는 64비트 시스템 기준 8바이트이다. 메모리 주소를 모두 표현해야 하기 때문이다.
- arr 는 문자열포인터의 배열로, 각 리터럴들의 시작 주소가 담겨져 있다.
문자열 배열은 그 자체로 이중포인터인 것이다.
void 포인터와 arr 과의 관계는 다음과 같이 그려진다.
따라서 void 포인터를 통해 문자열을 출력하려면, char** 으로 캐스팅을 해주어야 한다.
vrr 은 '문자열을 가리키는 포인터' 를 가리키는 포인터 이기 때문이다.
printf("*(char**)vrr : %s\n", *(char**)vrr); //결과는 banana
여기서 산술연산도 추가해서 장난을 쳐보자.
*(char**)vrr 의 타입은 char* , 즉 '문자열' 을 의미하므로, 만약 +1을 해준다면,
char 의 크기에 맞게 주소가 1바이트만 더해져 anana 가 출력된다.
따라서 (char**)vrr 자체, 즉 '문자열을 가리키는 포인터' 에 1을 더해줘야 '포인터의 크기에 해당하는 8바이트'가 더해져 정상적으로 arr[1] : apple 을 출력할 수 있다.
printf("*(char**)vrr : %s\n", *(char**)vrr);
printf("*(char**)vrr+1 : %s\n", *(char**)vrr+1);
printf("*((char**)vrr+1): %s\n", *((char**)vrr+1));
이중포인터 예시
아래는 char** 타입을 인자로 받아 문자열을 출력하는 예시이다.
#include <stdio.h>
void print_array(char** arr, int idx){
printf("%s\n",arr[idx]);
}
int main(){
char* arr[] = {"banana", "apple"};
print_array(arr, 1);
return 0;
}
apple 이 정상적으로 출력된다.
void print_array_void(void* arr, int idx){
printf("%s\n", ((char**)arr)[idx]);
}
같은 주소를 가리키기만 한다면, void* 타입으로 배열을 받아도 문제 없다.
그런데 왜 도대체 이런 쓸대없는 짓을 해야하는 걸까?
그 이유는 void 포인터가 어떠한 데이터 타입의 포인터로도 캐스팅할 수 있는 범용성을 가지고 있기 때문이다.
따라서 , void 포인터는 다양한 함수와 라이브러리에서 타입에 독립적인 인터페이스를 제공할 때 핵심적으로 활용된다
void 포인터의 사용례 및 중요성
- 타입 독립성(Type Agnosticism):
- void 포인터는 특정 데이터 타입을 명시하지 않기 때문에, 어떠한 타입의 데이터도 가리킬 수 있다. 이 특성 덕분에, 다양한 데이터 타입을 처리할 수 있는 범용 함수나 라이브러리를 설계할 때 유용하다. 예를 들어, 표준 라이브러리 함수인 qsort()는 void*를 사용하여 어떤 타입의 배열도 정렬할 수 있다.
다음 게시글에서 이를 제대로 알아보자.
- void 포인터는 특정 데이터 타입을 명시하지 않기 때문에, 어떠한 타입의 데이터도 가리킬 수 있다. 이 특성 덕분에, 다양한 데이터 타입을 처리할 수 있는 범용 함수나 라이브러리를 설계할 때 유용하다. 예를 들어, 표준 라이브러리 함수인 qsort()는 void*를 사용하여 어떤 타입의 배열도 정렬할 수 있다.
- 메모리 관리의 유연성:
- void 포인터는 동적 메모리 할당과 관련된 함수들(malloc, free 등)에서 반환된 포인터 타입으로 사용된다. 이를 통해 할당된 메모리 블록을 원하는 타입의 데이터로 자유롭게 캐스팅하여 사용할 수 있다. 이는 프로그래머가 런타임에 다양한 데이터 타입의 메모리 요구사항을 동적으로 처리할 수 있게 해준다.
- 인터페이스 표준화:
- 여러 종류의 데이터 타입을 처리해야 하는 라이브러리 및 API에서 void 포인터를 사용하면 함수 인터페이스를 단순화시킬 수 있다. 이는 프로그래밍의 복잡성을 줄이고, 코드의 재사용성을 높이는 데 기여한다.
다음 게시글에서 void 포인터를 사용하여, 다양한 타입을 처리하는 함수를 만들어보자.
'SW > C,C++' 카테고리의 다른 글
[C/C++] 커널 드라이버에서의 함수포인터 (0) | 2024.04.22 |
---|---|
[C/C++] 콜백함수의 인자로써 void 포인터 (0) | 2024.04.21 |
[C/C++] 동적 2차원 배열과 가변길이배열(VLA) (0) | 2024.04.11 |
[C/C++] C++ 에서의 콜백함수 (0) | 2024.04.07 |
[C/C++] 콜백함수 (Callback Function) (0) | 2024.04.01 |
댓글