자료구조

자료구조 - 포인터

tita 2024. 6. 6. 14:18

[ 포인터 (Pointer) ]

포인터는 다른 변수의 메모리 주소를 저장하는 변수입니다. 포인터를 사용하면 직접 메모리에 접근하여 값을 읽거나 쓸 수 있습니다. 포인터는 * 연산자와 & 연산자를 사용하여 선언하고 사용할 수 있습니다.

 

주요 연산자

  • 주소 연산자 (&): 변수의 주소를 반환합니다.
  • 간접 연산자 (*): 포인터가 가리키는 주소의 값을 참조합니다.

포인터의 장점

  • 메모리의 효율적 사용 및 직접적인 관리가 가능합니다.
  • 동적 메모리 할당과 다양한 데이터 구조 구현에 유용합니다.
  • 함수 인자로 사용하여 함수 호출 시 메모리 사용을 최적화할 수 있습니다.

포인터의 단점

  • 잘못된 메모리 접근으로 인한 오류와 버그가 발생할 수 있습니다.
  • 메모리 누수 및 안정성 문제가 발생할 수 있습니다.

 

포인터에 대한 코드 예시를 보겠습니다.

#include <iostream>

int main()
{
    int var = 10;          // 정수 변수 선언
    int *ptr = &var;       // 포인터 변수 선언 및 초기화 (var의 주소 저장)

    // 포인터를 사용한 접근
    std::cout << "변수 var의 값: " << var << std::endl;
    std::cout << "포인터 ptr이 가리키는 값: " << *ptr << std::endl;

    // 포인터를 사용하여 변수 값 변경
    *ptr = 20;
    std::cout << "포인터를 통해 변경된 변수 var의 값: " << var << std::endl;

    // 포인터의 주소 출력
    std::cout << "변수 var의 주소: " << &var << std::endl;
    std::cout << "포인터 ptr의 값(변수 var의 주소): " << ptr << std::endl;

    return 0;
}

 

 

[포인터와 배열]

포인터는 배열과 밀접한 관계가 있습니다. 배열의 이름은 배열의 첫 번째 요소의 주소를 가리키는 포인터와 같습니다.

 

코드 예시를 통해서 보겠습니다.

#include <iostream>

int main() 
{
    int arr[5] = {10, 20, 30, 40, 50}; // 배열 선언 및 초기화
    int *ptr = arr;                   // 배열 이름은 배열의 첫 번째 요소의 주소를 가리킴

    // 포인터를 사용하여 배열 요소 접근 및 출력
    std::cout << "포인터를 사용한 배열 요소 접근:" << std::endl;
    for (int i = 0; i < 5; ++i) 
    {
        std::cout << *(ptr + i) << " ";
    }
    std::cout << std::endl;

    // 배열 이름을 사용하여 배열 요소 접근 및 출력
    std::cout << "배열 이름을 사용한 배열 요소 접근:" << std::endl;
    for (int i = 0; i < 5; ++i) 
    {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;

    return 0;
}

 

 

 

[동적 메모리 할당 (Dynamic Memory Allocation) ]

동적 메모리 할당은 프로그램 실행 중에 필요에 따라 메모리를 할당하고 해제하는 방식입니다. C++에서는 동적 메모리 할당을 위해 new 연산자와 delete 연산자를 사용합니다. 이 방법을 통해 프로그램의 유연성과 효율성을 높일 수 있습니다.

 

 

개념

동적 메모리 할당은 컴파일 시점이 아닌 실행 시점에 메모리를 할당하는 방식입니다. 정적 메모리 할당과 달리, 동적 메모리 할당은 런타임에 메모리를 할당하므로 메모리 사용의 유연성이 증가합니다. 이를 통해 프로그램은 필요한 만큼의 메모리를 할당받고, 더 이상 필요하지 않은 메모리를 해제하여 효율적으로 자원을 관리할 수 있습니다.

 

 

 

new 연산자

 

  • 역할: 지정된 데이터 타입에 맞는 메모리를 할당하고, 해당 메모리의 주소를 반환합니다.
  • 문법: 포인터변수 = new 데이터타입;
int* ptr = new int; // 정수형 메모리 할당

 

 

 

delete 연산자

 

  • 역할: new 연산자로 할당된 메모리를 해제합니다.
  • 문법: delete 포인터변수;
delete ptr; // 할당된 메모리 해제

 

 

 

동적 배열 메모리 할당

 

  • 역할: 배열 형태로 메모리를 할당하고 해제합니다.
  • 문법:
    • 배열 할당: 포인터변수 = new 데이터타입[크기];
    • 배열 해제: delete[] 포인터변수;
int* arr = new int[5]; // 정수형 배열 메모리 할당
delete[] arr; // 할당된 배열 메모리 해제

 

 

 

[정수형 변수의 동적 메모리 할당]

#include <iostream>

int main() 
{
    // 정수형 변수의 동적 메모리 할당
    int* ptr = new int; // 정수형 포인터 선언 및 메모리 할당
    *ptr = 100;         // 할당된 메모리에 값 저장

    std::cout << "동적 메모리 할당된 변수의 값: " << *ptr << std::endl;

    delete ptr; // 동적 메모리 해제

    return 0;
}

 

 

[정수형 배열의 동적 메모리 할당]

#include <iostream>

int main()
{
    // 배열의 동적 메모리 할당
    int* arr = new int[5]; // 정수형 배열 포인터 선언 및 메모리 할당

    // 배열 요소 초기화
    for (int i = 0; i < 5; ++i) 
    {
        arr[i] = i * 10;
    }

    // 배열 요소 출력
    std::cout << "동적 메모리 할당된 배열의 요소: ";
    for (int i = 0; i < 5; ++i) 
    {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;

    delete[] arr; // 동적 배열 메모리 해제

    return 0;
}

 

 

 

[동적 메모리 할당의 중요성]

 

장점

 

  • 유연성: 프로그램 실행 중 필요한 만큼의 메모리를 할당받고 사용할 수 있습니다.
  • 효율성: 더 이상 필요하지 않은 메모리를 해제하여 자원을 효율적으로 관리할 수 있습니다.
  • 복잡한 데이터 구조: 링크드 리스트, 트리, 그래프와 같은 복잡한 데이터 구조를 구현할 때 유용합니다.

 

 

단점

 

  • 메모리 누수: 할당된 메모리를 해제하지 않으면 메모리 누수가 발생할 수 있습니다.
  • 잘못된 메모리 접근: 할당되지 않은 메모리나 이미 해제된 메모리에 접근하면 프로그램이 충돌할 수 있습니다.
  • 복잡성 증가: 포인터와 동적 메모리 관리는 코드의 복잡성을 증가시킬 수 있습니다.