Computer >> 컴퓨터 >  >> 프로그래밍 >> C 프로그래밍

C 언어 포인터 종류 총정리 – 널 포인터부터 허즈 포인터까지

포인터(pointer)는 다른 변수의 메모리 주소를 저장하는 변수입니다. C 언어에서 포인터는 메모리를 직접 다룰 수 있게 해주는 강력한 도구로, 배열·문자열 처리, 동적 메모리 할당, 함수 간 데이터 전달 등에 폭넓게 활용됩니다.

포인터 선언의 기본 문법은 다음과 같습니다.

pointer = &variable;

여기서 &(앰퍼샌드)는 변수의 주소를 의미하며, 이 주소가 포인터 변수에 저장됩니다.

포인터의 8가지 유형

C 언어의 포인터는 크게 여덟 가지 유형으로 나눌 수 있습니다.

  • 널 포인터(Null Pointer)

  • 보이드 포인터(Void Pointer)

  • 와일드 포인터(Wild Pointer)

  • 댕글링 포인터(Dangling Pointer)

  • 컴플렉스 포인터(Complex Pointer)

  • 니어 포인터(Near Pointer)

  • 파 포인터(Far Pointer)

  • 허즈 포인터(Huge Pointer)

1. 널 포인터 (Null Pointer)

널 포인터는 포인터를 선언할 때 NULL 값을 대입하여 만듭니다. 어떤 주소도 가리키지 않는 상태를 명시적으로 표현하는 방법으로, 항상 값 0을 가집니다.

선언 시점에 유효한 주소를 지정할 수 없거나, 아직 할당하지 않은 경우 널 포인터로 초기화하는 것이 안전한 프로그래밍 습관입니다.

예제

다음은 널 포인터를 사용하는 C 프로그램입니다.

#include <stdio.h>
int main(){
    int *ptr = NULL; //null pointer
    printf("The value inside variable ptr is:\n%d",ptr);
    return 0;
}

실행 결과

위 프로그램을 실행하면 다음과 같은 결과가 출력됩니다.

The value inside variable ptr is:
0

2. 보이드 포인터 (Void Pointer)

보이드 포인터는 특정 자료형에 연결되지 않은 포인터입니다. 어떤 타입의 주소든 저장할 수 있고, 필요할 때 원하는 타입으로 형 변환(typecast)하여 사용할 수 있습니다.

표준 자료형이 없기 때문에 '제네릭 포인터(generic pointer)'라고도 불리며, void 키워드를 사용해 생성합니다. 단, 역참조(dereference) 전에는 반드시 적절한 타입으로 변환해야 한다는 점에 유의하세요.

예제

다음은 보이드 포인터를 사용하는 C 프로그램입니다.

#include <stdio.h>
int main(){
    void *p = NULL; //void pointer
    printf("The size of pointer is:%d\n",sizeof(p)); //size of p depends on compiler
    return 0;
}

실행 결과

위 프로그램을 실행하면 다음과 같은 결과가 출력됩니다. 포인터의 크기는 컴파일러와 시스템 환경에 따라 달라질 수 있습니다.

The size of pointer is:8

3. 와일드 포인터 (Wild Pointer)

와일드 포인터는 '초기화되지 않은 포인터(uninitialized pointer)'라고도 부릅니다. 선언만 하고 초기화하지 않으면 임의의 메모리 위치를 가리키게 되어 프로그램 충돌이나 비정상 동작을 일으킬 수 있습니다.

이런 포인터는 알 수 없는 메모리 영역을 참조할 위험이 있어 매우 비효율적이며, 심각한 경우 프로그램이 크래시(crash)될 수 있습니다. 따라서 포인터는 반드시 선언 즉시 유효한 주소나 NULL로 초기화하는 것이 좋습니다.

예제

다음은 와일드 포인터를 사용하는 C 프로그램입니다.

#include <stdio.h>
int main(){
    int *p; //wild pointer
    printf("\n%d",*p);
    return 0;
}

실행 결과

초기화되지 않은 포인터를 역참조하기 때문에 정상적인 출력이 나오지 않으며, 컴파일러에 따라 접근 오류(access violation)가 발생합니다.

Process returned -1073741819 (0xC0000005) execution time : 1.206 s
Press any key to continue

즉, 출력값을 얻을 수 없고 일부 컴파일러는 에러 메시지를 표시합니다.

4. 댕글링 포인터 (Dangling Pointer)

댕글링 포인터는 이미 해제되었거나 더 이상 유효하지 않은 메모리 위치를 계속 가리키는 포인터입니다. free() 함수로 동적 메모리를 해제한 후 그 주소를 참조하거나, 함수 내 지역 변수의 주소를 반환할 때 발생합니다. 미정의 동작(undefined behavior)의 주요 원인이므로 메모리 해제 후에는 포인터에 NULL을 대입하는 것이 안전합니다.

5. 컴플렉스 포인터 (Complex Pointer)

컴플렉스 포인터는 여러 번의 간접 참조(indirection)가 결합된 포인터를 말합니다. 예를 들어 int **ptr;처럼 이중 포인터(double pointer)가 대표적이며, 포인터의 주소를 저장하는 용도로 사용됩니다. 별표(*)의 개수가 많아질수록 참조 단계가 깊어져 코드 해석이 어려워지므로 주의해서 사용해야 합니다.

6. 니어 포인터 / 파 포인터 / 허즈 포인터

이 세 가지 포인터는 과거 16비트 DOS 환경의 세그먼트 기반 메모리 구조에서 사용되던 포인터입니다.

  • 니어 포인터(Near Pointer): 16비트 오프셋만 저장하며, 같은 세그먼트(64KB) 내의 주소만 접근할 수 있습니다.

  • 파 포인터(Far Pointer): 세그먼트 주소와 오프셋을 함께 저장(32비트)하여 세그먼트를 넘어선 주소에 접근할 수 있습니다.

  • 허즈 포인터(Huge Pointer): 파 포인터처럼 세그먼트+오프셋 방식이지만, 주소를 정규화(normalize)하여 세그먼트 경계를 넘는 연산에서도 올바른 결과를 보장합니다.

현재 32비트·64비트 시스템에서는 통합된 평면(flat) 메모리 모델을 사용하므로 이들 포인터는 거의 사용되지 않지만, 임베디드 시스템이나 레거시 코드 분석 시 등장할 수 있는 개념입니다.