[C언어] void 포인터의 연산과 역참조

emplam27·2021년 1월 26일
0

C언어

목록 보기
2/2

자료형과 포인터를 알고 있을 때, 해당 포인터에서 원하는 만큼 떨어진 포인터에 접근하는 방법인 포인터 연산과 역참조에 대해서 포스팅 해보겠습니다. 자료형이 정해진 포인터의 경우에는 바로 포인터 연산을 사용할 수 있지만, void 포인터의 경우에는 자료형의 사이즈를 알 수 없기 때문에 포인터 연산을 할 수 없습니다. 이러한 문제점을 해결하는 방법인 형변환에 대해서도 같이 포스팅 하겠습니다.


자료형 포인터 연산

자료형이 정해진 포인터(int형 포인터, char형 포인터 등)의 경우에는 + 또는 - 연산자를 통해 포인터 연산을 수행할 수 있습니다. 주소값 + n 또는 주소값 - n을 하게 되면, (해당 자료형의 사이즈 * n) 만큼 포인터 값이 증가 또는 감소하는 것이 특징입니다. 구조체 역시 동일하게 적용됩니다. 포인터 연산을 통해서 배열의 원소에 바로 접근할 수 있습니다.

int형 포인터 연산

int 자료형의 크기인 4byte만큼 주소값이 커집니다.

int number = 1;
int *int_ptr1, *int_ptr2, *int_ptr3;

int_ptr1 = &number;		// 000000000061FE04
int_ptr2 = int_ptr1 + 1; 	// 000000000061FE04 + 4 = 000000000061FE08
int_ptr3 = int_ptr1 + 2;	// 000000000061FE04 + 8 = 000000000061FE0C

char형 포인터 연산

char 자료형의 크기인 1byte만큼 주소값이 커집니다.

char *str = "abc";
char *str_ptr1, *str_ptr2, *str_ptr3;
    
str_ptr1 = str;		// abc
str_ptr2 = str + 1;	// bc
str_ptr3 = str + 2;	// c

구조체 포인터 연산

해당 구조체의 경우에는 구조체 사이즈가 8byte입니다.

struct Object {
    int key;
    int value;
};

해당 구조체 포인터의 연산시 구조체 포인터의 크기만큼 증가 또는 감소합니다.

struct Object a = { 1, 10 };
struct Object *ptr = &a;
struct Object *struct_ptr1, *struct_ptr2, *struct_ptr3;

struct_ptr1 = ptr  	 // 000000000061FE10
struct_ptr2 = ptr + 1    // 000000000061FE18
struct_ptr3 = ptr + 2    // 000000000061FE20

void형 포인터 연산과 형변환

자료형을 알 수 없는 void형 포인터의 경우에는 포인터 연산을 수행할 수 없습니다. 자료형을 알 수 없으니 주소값을 얼마나 더하고 빼야 하는지 모르기 때문입니다. 이러한 경우에 사용하는 방법이 포인터 형변환입니다. (자료형 *) 주소값으로 활용 가능하며, 해당 void포인터에 자료형을 부여함으로써 포인터 연산을 사용할 수 있게 합니다.

void *ptr = malloc(5);
void *void_ptr1, *void_ptr2, *void_ptr3, *void_ptr4;

void_ptr1 = ptr;               // 0000000000A26BC0
void_ptr2 = ptr + 1;           // 에러! void포인터는 연산할 수 없음
void_ptr3 = (int *)ptr + 1;    // 0000000000A26BC4
void_ptr4 = (char *)ptr + 1;   // 0000000000A26BC1

free(ptr);

이중포인터 연산

이중포인터는 포인터의 포인터이기 때문에 산술연산시 포인터 사이즈(64bit 시스템에서는 8byte)만큼 이동합니다. 어떤 자료형의 이중 포인터이던 동일하게 적용됩니다.

void *void_ptr = malloc(5);
void **void_ptr_ptr = &void_ptr;
void **void_ptr_ptr1, **void_ptr_ptr2, **void_ptr_ptr3, **void_ptr_ptr4;

void_ptr_ptr1 = void_ptr_ptr;                // 000000000061FE10
void_ptr_ptr2 = void_ptr_ptr + 1;            // 000000000061FE18
void_ptr_ptr3 = (int **)void_ptr_ptr + 1;    // 000000000061FE18
void_ptr_ptr4 = (char **)void_ptr_ptr + 1;   // 000000000061FE18

free(void_ptr);

포인터 연산과 역참조

포인터 연산을 사용하여 특정 메모리 주소를 찾아내고, 역참조 연산자 *를 사용하여 메모리에 들어있는 값에 접근할 수 있습니다. 아래 예시는 구조체를 이용하여 배열을 만들었을 때와 malloc을 사용하여 메모리에 직접 값을 할당할 때를 비교한 것입니다.

struct Object {
    int key;
    int value;
};

위 구초제를 이용하여 배열을 만들었을 때의 값입니다.

struct Object array[3] = { { 1, 10 }, { 2, 20 }, { 3, 30 } };
struct Object *ptr = array;

printf("%d\n", ptr->key) 	  // 1
printf("%d\n", ptr->value) 	  // 10
printf("%d\n", (ptr + 1)->key) 	  // 2
printf("%d\n", (ptr + 1)->value)  // 20
printf("%d\n", (ptr + 2)->key)	  // 3
printf("%d\n", (ptr + 2)->value)  // 30

포인터 연산과 역참조를 이용해 구조체 배열과 동일하게 메모리에 값을 저장할 수 있습니다.

void *ptr = malloc(24);

*((int *)ptr) = 1;
*((int *)ptr + 1) = 10;
*((int *)ptr + 2) = 2;
*((int *)ptr + 3) = 20;
*((int *)ptr + 4) = 3;
*((int *)ptr + 5) = 30;

printf("%d\n", *((char *)ptr)); 	  // 1
printf("%d\n", *((char *)ptr + 4)); 	  // 10
printf("%d\n", *((char *)ptr + 8)); 	  // 2
printf("%d\n", *((char *)ptr + 12)); 	  // 20
printf("%d\n", *((char *)ptr + 16)); 	  // 3
printf("%d\n", *((char *)ptr + 20)); 	  // 30
profile
내가 다시 보고 싶은 글이어야 남들도 보고 싶은 글이라 생각하며 작성합니다. 공부한 내용들을 건강하게 공유하며 함께 성장하고자 합니다😊😊

0개의 댓글