말감로그

[C언어] 포인터 본문

언어/C

[C언어] 포인터

habbn 2024. 2. 9. 03:26
728x90

 

포인터(Pointer)란 데이터가 저장된 메모리의 주소값을 저장하는 변수이며, 포인터 변수라고도 한다.

-> 메모리의 주소, 즉 '어디'인지(=위치 정보)를 저장하는 전용 변수

 

int n = 100;	// 변수의 선언
int *ptr = &n;	// 포인터의 선언

 

-> int형 ptr 포인터는 n의 주소를 가리킨다.

 

주소 연산자(&) - 해당 변수의 주소값을 반환한다.

참조 연산자(*) - 포인터에 가리키는 주소에 저장된 값을 반환한다.

 

포인터의 연산

 

포인터는 값을 증가시키거나 감소시키는 등의 제한된 연산만을 할 수 있다.

포인터가 가리키고 있는 주소는 각각의 포인터 타입에 따라 달라진다.

char *ptr_char =0
int *ptr_int = NULL;
double *ptr_double = 0x00;

printf("포인터 ptr_char가 현재 가리키고 있는 주소값은 %#x입니다.\n",ptr_char);
printf("포인터 ptr_int가 현재 가리키고 있는 주소값은 %#x입니다.\n",ptr_int);
printf("포인터 ptr_double가 현재 가리키고 있는 주소값은 %#x입니다.\n",ptr_double);

printf("포인터 ptr_char가 1증가 후에 가리키고 있는 주소값은 %#x입니다.\n",++ptr_char);
printf("포인터 ptr_int가 1증가 후에 가리키고 있는 주소값은 %#x입니다.\n",++ptr_int);
printf("포인터 ptr_double이 1증가 후에 가리키고 있는 주소값은 %#x입니다.\n",++ptr_double);

 

(%#x 는 0x를 붙이라는 뜻, %x, %p - 16진수로 표현)

 

실행 결과

포인터   ptr_char가 현재 가리키고 있는 주소값은 0입니다.
포인터    ptr_int가 현재 가리키고 있는 주소값은 0입니다.
포인터 ptr_double이 현재 가리키고 있는 주소값은 0입니다.
포인터   ptr_char가 1 증가 후에 가리키고 있는 주소값은 0x1입니다.
포인터    ptr_int가 1 증가 후에 가리키고 있는 주소값은 0x4입니다.
포인터 ptr_double이 1 증가 후에 가리키고 있는 주소값은 0x8입니다.

 


인수 전달 방법

1. 값에 의한 전달(call by value)

2. 참조에 의한 전달(call by reference)

 

값에 의한 전달(call by value)

- 인수로 전달되는 변수가 가지고 있는 값을 함수 내의 매개변수에 복사하는 방식이다.

- 이렇게 복사된 값으로 초기화된 매개변수는 인수로 전달된 변수와는 완전히 별개의 변수가 된다.

 -> 함수 내에서의 매개변수 조작은 인수로 전달되는 변수에 아무런 영향을 미치지 않는다.

 

#include <stdio.h>  

void local(int num)
{
    num += 10;
}

int main(void)
{
    int var = 10;
    printf("변수 var의 초깃값은 %d입니다.\n", var);  
    
    local(var);
    printf("local() 함수 호출 후 변수 var의 값은 %d입니다.\n", var);

    return 0;
}

 

실행 결과

변수 var의 초기값은 10입니다.
local() 함수 호출 후 변수 var의 값은 10입니다.

 

-> 함수 내에서 매개변수 num의 값을 아무리 변경하더라도 원래 인수로 전달된 변수 var 값은 절대 변경되지 않는다.


참조에 의한 전달(call by reference)

 

- 해당 변수의 주소값을 전달한다.

- 함수의 매개변수에 인수로 전달된 변수의 원래 주소값을 저장하는 것이다. 

-> 인수로 전달된 변수의 값을 함수 내에서 변경할 수 있게 된다.

 

#include <stdio.h>  

void local(int *num)
{
    *num += 10;
}

int main(void)
{
    int var = 10;
    printf("변수 var의 초깃값은 %d입니다.\n", var);  
    
    local(&var);
    printf("local() 함수 호출 후 변수 var의 값은 %d입니다.\n", var);

    return 0;
}

 

실행 결과

변수 var의 초기값은 10입니다.
local() 함수 호출 후 변수 var의 값은 20입니다.

 

num은 인수로 변수 var의 주소값을 전달 받는다. 따라서 함수 내에서 매개변수 num의 값을 변경하면, 원래 인수인 변수 var의 값도 같이 변경된다.

 


 

이중 포인터 

 - 포인터 변수를 가리키는 포인터

 

void 포인터

 - 데이터의 타입을 명시하지 않은 포인터

 - void 포인터는 주소값을 저장하는 것 이외에는 아무것도 할 수 없다.

 - 반드시 먼저 사용하고자 하는 타입으로 명시적 타입 변환 작업을 거친 후에 사용해야 한다.

int num = 10;         // 변수 선언
void* ptr_num = &num; // void 포인터 선언  

printf("변수 num가 저장하고 있는 값은 %d입니다.\n", num);
printf("void 포인터 ptr_num가 가리키는 주소에 저장된 값은 %d입니다.\n", *(int*)ptr_num);  

*(int*)ptr_num = 20;  // void 포인터를 통한 메모리 접근  
printf("void 포인터 ptr_num가 가리키는 주소에 저장된 값은 %d입니다.\n", *(int*)ptr_num);
실행 결과

변수 num가 저장하고 있는 값은 10입니다.
void 포인터 ptr_num가 가리키는 주소에 저장된 값은 10입니다.
void 포인터 ptr_num가 가리키는 주소에 저장된 값은 20입니다.

-> 사용할 때마다 명시적 타입 변환을 하고 난 뒤에 사용해야 한다.


 

함수 포인터

- 프로그램에서 정의된 함수는 프로그램이 실행될 때 모두 메인 메모리에 올라가게 된다.

- 이때 함수의 이름은 메모리에 올라간 함수의 시작 주소를 가리키는 포인터 상수가 된다.

- 이렇게 함수의 시작 주소를 가리키는 포인터 상수를 함수 포인터라고 한다.

 

- 함수 포인터의 포인터 타입은 함수의 반환값과 매개변수에 의해 결정된다.

 -> 함수의 원형을 알아야만 해당 함수에 맞는 함수 포인터를 만들 수 있다.

 

 

 


 

 

포인터를 사용하면 특정 데이터를 직접 조작하거나 다른 데이터로 전환할 수 있다.

게임 치트 엔진은 가동 중인 게임의 메모리에 직접 접근해서 16진수 형태로 이루어진 게임 내 변수값을 조작할 수 있는 도구이다.
꽤 많은 게임에서 프로그램 안에 있는 변수, 즉 메모리 주소는 프로그램을 껐다 켤 때마다 계속 바뀌게 되는데(동적 할당), 그런 게임을 재부팅할 때마다 어떤 값을 바꾸기 위한 메모리 주소를 매번 새로 찾아야 하니 번거롭다.

하지만 포인터를 찾는다면, 포인터가 그 메모리의 위치를 가리키기 때문에 매번 바뀌는 메모리 주소값을 다시 구하지 않아도 값을 수정할 수 있다.

 

매니지드 언어(Managed Language)

- 대표적인 언어로 C#, Java로 프로그래머가 별도로 조작하지 않아도 언어 자체적으로 메모리를 관리해주는 언어

 

언매니지드 언어(UnManaged Language)

- 대표적인 언어로 C,C++로 프로그래머가 주도적으로 메모리를 할당하고 해제하기 위해 malloc()이나 free()와 같은 메모리 제어 기능을 제공하는 언어


포인터가 어려운 이유?

 

1. 메모리에 직접 접근하는 위험성

  - 포인터를 잘못 사용하면 시스템에 치명적인 버그를 일으킬 수 있다.

 

2. 까다로운 메모리 관리

  - 메모리 누수 같은 복잡한 메모리 관리 문제 직접 관리해야 한다.

 

3. 복잡한 문법, 어려운 디버깅

 

포인터를 이해하면 뭐가 좋은가요?

 

1. 언어간 상호 운용성

  - 다양한 언어 간의 전환이나 운용성을 이해하는 데 도움이 된다.

 

2. 알고리즘 및 자료구조 이해

  - 일부 알고리즘 및 자료구조는 포인터를 사용, 포인터를 이해하면 더 효율적으로 프로그램 작성할 수 있다,

 

3. 보안 및 취약점 이해

  - 소프트웨어 보완을 강화하는데 도움이 된다.

 

4. 메모리 관리에 대한 이해

  - 컴퓨터의 동작 방식에 대해 더 깊은 이해를 얻을 수 있다.

 

5. 디버깅 능력 강화

  - 메모리 관련 오류나 메모리 누수 등의 문제를 식별하는 데 도움이 된다.

 

6. 더 넓은 취업 기회

 


 

 

포인터 한 줄 정리 짤

 

 

참고

https://www.inflearn.com/pages/dc-pointer-202310

 

 

728x90

'언어 > C' 카테고리의 다른 글

[C언어] 연결리스트(Linked List)  (0) 2024.02.10