티스토리 뷰

728x90

포인터메모리 주소를 저장하는 변수입니다. 일반적인 변수는 데이터를 직접 저장하지만, 포인터는 다른 변수나 데이터가 저장된 메모리 주소를 저장합니다. 포인터는 C 언어에서 매우 중요한 개념으로, 메모리 관리, 함수의 인자 전달, 동적 메모리 할당 등을 수행할 때 자주 사용됩니다.

포인터의 기본 개념

1. 포인터 선언

포인터는 메모리 주소를 저장하기 위한 변수입니다. 선언할 때는 변수가 가리킬 데이터 타입을 지정해야 하며, 변수 이름 앞에 *를 붙여 포인터임을 표시합니다.

int* ptr;
  • 여기서 ptr은 int형 데이터를 가리키는 포인터입니다. 즉, ptr은 어떤 int형 변수가 저장된 메모리 주소를 저장할 수 있습니다.

2. 포인터에 값을 할당 (주소 대입)

포인터 변수는 다른 변수의 메모리 주소를 저장합니다. 주소를 구하려면 변수 이름 앞에 주소 연산자(&)를 붙입니다.

int num = 10;
int* ptr = #  // num의 주소를 ptr에 저장
  • num이라는 변수의 값은 10이고, ptr은 num이 저장된 메모리 주소를 가리킵니다.

3. 포인터를 통한 값 참조 (역참조)

포인터를 사용해 가리키고 있는 변수의 값을 가져오려면, 역참조 연산자(*)를 사용합니다.

int num = 10;
int* ptr = #

printf("%d\n", *ptr);  // ptr이 가리키는 num의 값을 출력 (10)
  • *ptr은 ptr이 가리키고 있는 변수(num)의 값을 참조합니다. 이때 *ptr은 num의 값인 10을 의미합니다.

4. 포인터 연산

포인터는 메모리 주소를 가리키기 때문에 연산을 통해 가리키는 주소를 변경할 수 있습니다. 포인터에 숫자를 더하거나 빼면, 해당 자료형의 크기만큼 메모리 주소가 이동합니다.

int arr[3] = {10, 20, 30};
int* ptr = arr;  // 배열의 첫 번째 요소를 가리킴

printf("%d\n", *ptr);     // 출력: 10 (arr[0])
ptr++;                    // 포인터를 다음 요소로 이동
printf("%d\n", *ptr);     // 출력: 20 (arr[1])
  • ptr++은 포인터를 배열의 다음 요소로 이동시키는 연산입니다. 포인터가 int형일 때는 4바이트(32비트 시스템 기준)씩 이동합니다.

포인터의 활용

1. 함수에서 포인터 사용

포인터는 함수 인자 전달 시 매우 유용하게 사용됩니다. C 언어에서 함수는 기본적으로 값에 의한 복사로 인자를 전달하므로, 함수 내부에서 인자의 값을 변경해도 원본 값에는 영향을 미치지 않습니다. 하지만 포인터를 사용하면 원본 데이터에 접근해 직접 값을 변경할 수 있습니다.

void changeValue(int* ptr) {
    *ptr = 100;  // ptr이 가리키는 값을 변경
}

int main() {
    int num = 10;
    changeValue(&num);  // num의 주소를 전달
    printf("%d\n", num);  // 출력: 100 (num의 값이 변경됨)
}
  • changeValue 함수는 포인터를 통해 num의 주소를 받아, num의 값을 직접 변경할 수 있습니다.

2. 배열과 포인터

배열 이름은 포인터처럼 배열의 첫 번째 요소의 주소를 가리킵니다. 따라서 배열과 포인터는 밀접한 관련이 있으며, 배열을 다룰 때 포인터를 자주 사용합니다.

int arr[3] = {1, 2, 3};
int* ptr = arr;  // 배열 이름은 첫 번째 요소의 주소

printf("%d\n", *ptr);     // 출력: 1 (arr[0])
printf("%d\n", *(ptr+1)); // 출력: 2 (arr[1])
  • arr는 배열의 첫 번째 요소를 가리키며, ptr도 같은 주소를 가리킵니다. 따라서 포인터 연산을 통해 배열의 각 요소에 접근할 수 있습니다.

3. 동적 메모리 할당

포인터는 동적 메모리 할당에서 필수적으로 사용됩니다. malloc, calloc, realloc 등을 사용해 동적으로 메모리를 할당하면, 이 메모리의 주소를 포인터가 저장하여 나중에 사용할 수 있습니다.

int* ptr = (int*)malloc(sizeof(int) * 5);  // int형 5개를 저장할 메모리 할당
ptr[0] = 1;
ptr[1] = 2;
free(ptr);  // 메모리 해제
  • malloc은 메모리를 할당하고, 그 메모리의 시작 주소를 포인터로 반환합니다. 할당한 메모리를 다 사용한 후에는 반드시 free를 사용해 메모리를 해제해야 합니다.

포인터의 종류

1. 널 포인터 (NULL Pointer)

  • 널 포인터는 가리키는 대상이 없는 포인터로, 일반적으로 초기화되지 않은 포인터는 NULL로 설정합니다.
int* ptr = NULL;
  • NULL 포인터를 역참조하려 하면 프로그램이 충돌하거나 오류가 발생할 수 있습니다.

2. 이중 포인터 (Pointer to Pointer)

  • 이중 포인터는 포인터를 가리키는 포인터입니다. 즉, 이중 포인터는 포인터의 주소를 저장합니다.
int num = 10;
int* ptr = #
int** pptr = &ptr;

printf("%d\n", **pptr);  // 출력: 10 (num의 값)
  • pptr는 ptr의 주소를 가리키고, *pptr은 ptr을 역참조합니다. **pptr은 최종적으로 num의 값을 가리킵니다.

포인터의 장점과 주의사항

장점:

  1. 메모리 직접 제어: 포인터를 통해 메모리 주소를 직접 조작할 수 있습니다.
  2. 효율적인 함수 호출: 포인터를 사용하면 대용량 데이터를 복사하지 않고, 주소만 전달함으로써 성능을 향상시킬 수 있습니다.
  3. 동적 메모리 할당: 프로그램 실행 중에 필요한 만큼의 메모리를 동적으로 할당할 수 있습니다.

주의사항:

  1. 포인터 연산 오류: 잘못된 포인터 연산은 프로그램 충돌이나 예기치 않은 동작을 초래할 수 있습니다.
  2. 메모리 누수: 동적 메모리 할당 후 메모리를 해제하지 않으면 메모리 누수가 발생할 수 있습니다.
  3. 널 포인터 참조: 널 포인터를 역참조하려 하면 프로그램이 비정상 종료될 수 있습니다.

요약

  • 포인터는 메모리 주소를 저장하는 변수입니다.
  • 포인터는 변수의 값을 참조하거나, 함수에 값을 참조 방식으로 전달할 때 유용하게 사용됩니다.
  • 배열과 포인터는 밀접하게 연관되어 있으며, 동적 메모리 할당에서도 포인터가 필요합니다.
  • 포인터는 매우 강력한 도구이지만, 올바르게 사용하지 않으면 프로그램 오류나 충돌을 일으킬 수 있으므로 주의해야 합니다.

포인터는 C 언어의 중요한 개념 중 하나로, 이를 잘 이해하면 메모리 관리와 최적화된 프로그램 설계가 가능합니다.

728x90