[C] void 포인터와 const 포인터

2023. 12. 26. 18:05C

Contents 접기

void 포인터

void라는 키워드는 보통 함수 앞에서 반환형이 없을 때 쓰인다.

그렇다면 (int *), (char *)형 포인터와 어떤 차이가 있을까?

 

다음 코드를 살펴보자

int a = 100;
int *b = &a;

이 코드가 의미하는 바는 다음과 같다.

b는 a의 주소를 저장하고 있고, b를 통해 a를 참조할 수 있는데, 그곳에는 int형 데이터가 있다.

 

 

그렇다면 void 포인터를 선언하는 코드 역시 다음과 같이 해석할 수 있을 것이다.

int a = 100;
void *b = &a;

b는 a의 주소를 저장하고 있고, b를 통해 a를 참조할 수 있는데, 그곳에는 void형 데이터가 있다.

 

그러나 void는 "빈공간'. 즉 자료형을 가지고 있지 않기 때문에 컴파일러는 데이터를 참조할 수 없을 것이다.

왜냐하면 자료형에 따라 참조해야 하는 byte가 달라지는데, void는 단순히 주소값만을 가지고 있으니까!

 

그러므로 void형 포인터는 어떤 자료형을 가리키든 간에 순수하게 메모리의 주소,

포인터의 크기 (4 byte)만을 가지고 있는 포인터이고 따라서 포인터 연산조차 할 수 없다.

#include <stdio.h>

int main() {
	int a = 10;
    void *b = &a;
    printf("%d\n", *b);
}

 

>>> Error:Expression must be a pointer to a complete object type

 

void형 포인터로 선언 후 연산을 하기 위해서는 반드시 명시적인 대상을 가리키는 포인터 타입으로 형변환 해야한다.

printf("%d\n", *(int *)b);
10

 

void포인터는 char형, 문자열 등 어떤 자료형이건 형변환을 통해 바로 참조할 수 있다.

 

여기서 문득 궁금증이 생긴다..

 

처음부터 자료형 지정해서 사용하면 되잖아!

 

좋은 지적이지만, void 포인터를 사용해야 하는 것의 몇 가지 장점이 존재한다.

 

1. 유연성과 일반성

void 포인터를 사용하면 어떤 타입의 포인터도 받을 수 있는 함수를 작성할 수 있다.

이는 특정 자료형에 구애받지 않고 여러 종류의 데이터를 가리키고 싶을 때 유용하다.

 

예를 들어, 함수 인자로 여러 종류의 데이터를 받아야 하는 경우 다음과 같이 사용될 수 있다.

void printData(void *ptr, int type) {
	// type에 따라 다른 자료형으로 캐스팅하여 데이터 출력
    switch (type) {
    	case 1:
        	printf("%d\n", *((int*)ptr));
            break;
		case 2:
        	printf("%d\n", *((float*)ptr));
            break;
        // 다른 자료형에 대한 처리도 추가 가능
	}
}

 

 

2. 메모리 할당과 반환

'malloc' 또는 'free'와 같은 메모리 관련 함수에서 반환되는 포인터는 주로 'void' 포인터 형태이다.

이렇게 하면 할당된 메모리의 유형을 나중에 결정할 수 있다.

void *allocateMemory(size_t size) {
	return malloc(size);
}



3. 다형성

'void' 포인터를 사용하면 함수나 구조체 등에서 다형성을 구현할 수 있다.

다형성은 같은 인터페이스를 가진 다양한 타입의 객체를 처리하는 데 유용하다.

struct Shape {
	int type;
   	void *data;
};

 

 

const 포인터

non-const 포인터

int value = 5;
int* ptr = &value;
*ptr = 6; // value 값을 6으로 변경

위 코드는 정상 컴파일 된다.

 

const 포인터

const 키워드는 변수를 상수화 시킬때 사용하는 키워드이다.

상수화된 변수 = 변경이 불가능, 포인터 변수에 붙여도 같은 동작을 한다.

 

다만, const의 위치에 따라 의미가 다르다.

const int value = 5; // value = cosnt
int ptr = &value; // compile error: cannot convert const int* to int*
*ptr = 6;

위 코드는 컴파일 되지 않는다. 상수 변수는 값을 변경할 수 없기 때문에, 상수가 아닌 포인터가 상수 변수를 가리킨 다음

역참조하여 값을 바꾸는 것은 const의 의도를 위반하므로 상수가 아닌 포인터는 상수 변수를 가리킬 수 없다.

 

1. 상수를 가리키는 포인터

상수를 가리키는 포인터는 상수 변수의 주소를 가리키는 (non-const) 포인터이다.

const int value = 5;
const int* ptr = &value; // ptr is non-const pointer that is pointing to a "const int"
*ptr = 6; // not allowed

위 예제에서 ptr은 const int를 가리키지만, const value값은 바꿀 수 없다.

 

다음 예제를 살펴보자

int value = 5;
const int* ptr = &value;

# case 1
value = 6; // non-const 식별자를 통해 접근 -> 값 변경 가능

# case 2
*ptr = 6; // ptr은 value를 const로 처리하므로 ptr을 통해 value에 접근 불가능

# case 3
int value2 = 6;
ptr = &value2; // ptr은 value1 -> value2로 다른 const int를 가리킴

 

2. 상수 포인터

상수 포인터는 초기화 후에 가리키는 주소를 변경할 수 없는 포인터로 자료형 뒤에 const 키워드를 사용하면 된다.

int value1 = 5;
int value2 = 6;

# case 1
int* const ptr = &value1; // const 포인터는 value1의 주소를 가리킴
ptr = &value2; // 초기화 이후, const 포인터는 변경 X

# case 2
int value = 5;
int* const ptr = &value; // ptr은 항상 value를 가리킴
*ptr = 6; // ptr 포인터는 non-const int를 가리킴

일반 상수 변수와 마찬가지로 상수 포인터는 선언 시 초기화해야 한다.

즉, 상수 포인터는 항상 같은 주소를 가리킨다.

 

case 2와 같이 가리키는 변수는 상수가 아닌 경우, 포인터를 역참조하여 값을 변경하는 것은 가능하다.

 

 

3. 상수를 가리키는 상수 포인터

int value = 5;
const int *const ptr = &value;

상수를 가리키는 상수 포인터는 다른 주소를 가리키도록 수정할 수 없으며, 역참조를 통해 값을 수정하는 것도 불가능

 

 


Summary

  • non-const 포인터는 다른 주소를 가리키도록 수정 가능
  • 상수 포인터는 항상 같은 주소를 가리키며, 가리키는 주소 변경 x
  • 상수를 가리키지 않는 포인터는 역참조를 통해 값 변경 가능, But 상수값 가리킬 수 없음
  • 상수를 가리키는 포인터는 역참조를 통해 값 변경 x

상수를 가리키는 포인터는 배열을 함수에 전달하는 경우처럼

주로 함수가 전달된 인수를 실수로 변경하지 않기 위해 매개변수에서 사용된다.

 

 

참고

https://dojang.io/mod/page/view.php?id=1737

https://reakwon.tistory.com/19

https://boycoding.tistory.com/206

 

[C언어] void 포인터(void pointer)개념과 자유로운 형변환, malloc 반환형

void 포인터 void 포인터??이런 포인터는 처음 들어봤네요~ 분명 void라는 것은 함수 앞에서 반환형이 없을 때 쓰이는 키워드로 아는뎁;void main은 많이 봤는데... 지금부터 이야기할 주제가 바로 void

reakwon.tistory.com

 

COS Pro 2급 C 언어: 31.5 void 포인터 선언하기

long long *numPtr1;이나 float *numPtr2;는 자료형이 정해진 포인터입니다. 하지만 C 언어에서는 자료형이 정해지지 않은 포인터도 있습니다. void 포인터라는 포인터인데 다음과 같이 void 키워드와 *로 선

dojang.io