C언어 공부

C언어 기초 - 8. 포인터의 개념

Client Side 2025. 8. 14. 18:04

1. 포인터(Pointer)란 무엇인가?

포인터는 메모리 주소(Memory Address)를 값으로 저장하는 변수임.  일반 변수가 정수나 문자 같은 실제 데이터를 저장하는 반면, 포인터 변수는 데이터가 저장된 메모리의 '위치' 즉, 주소값을 보관함. 포인터 변수 또한 하나의 변수이므로, 이 변수도 메모리 어딘가에 저장되며 그 자체로 주소값을 가짐.

 

포인터를 사용하는 이유

1) 포인터를 사용하면 메모리를 낭비하지 않고 효율적으로 사용 가능하다.

2) 포인터를 사용하여 함수에 데이터를 전달할 수 있다.

- 이를 통해 실제 데이터를 수정하거나 참조하는 것이 가능하다.

3) 포인터를 사용하면 개발자가 메모리를 직접 컨트롤 하는 것이 가능하다.

4) 포인터를 사용하면 메모리를 동적으로 사용 가능하다.

5) 포인터를 사용하면 문자열을 관리할 수 있다.

 

 

 

2. 포인터 변수 선언

포인터 변수를 선언할 때는 어떤 자료형의 메모리 주소를 저장할 것인지를 명시해야 함.

포인터 변수는 무조건 8byte다.

 

자료형* 포인터변수이름;
  • 자료형: 포인터가 가리킬 메모리 공간에 저장될 데이터의 타입 (예: int, char, double 등).
  • *: 해당 변수가 포인터임을 나타내는 표시. *는 포인터 변수를 선언할 때만 사용하며, 다른 곳에서는 다른 의미(역참조)로 사용됨.

예시:

int* ptr_int;       // 정수(int)를 가리킬 포인터 변수 ptr_int 선언
char* ptr_char;     // 문자(char)를 가리킬 포인터 변수 ptr_char 선언
double* ptr_double; // 실수(double)를 가리킬 포인터 변수 ptr_double 선언

 

 

 

 

3. 주소 연산자 (&): "어디에 있니?"

& 연산자는 주소 연산자(Address-of operator) 또는 참조 연산자라고 불림. 이 연산자는 변수의 메모리 주소값을 반환함. 어떤 변수가 메모리의 어디에 저장되어 있는지를 알고 싶을 때 사용함.

사용 예시:

int num = 10;
int* ptr;

ptr = # // num 변수의 메모리 주소를 ptr에 저장.
            // 즉, ptr은 이제 num의 위치를 가리킴.

4. 역참조 연산자 (*): "무엇이 있니?"

* 연산자는 역참조 연산자(Dereference operator) 또는 간접 참조 연산자라고 불림. 이 연산자는 포인터 변수가 가리키는 메모리 주소에 저장된 실제 값을 반환함.  포인터를 통해 해당 주소의 데이터를 읽거나 변경할 때 사용함.

사용 예시:

c

int num = 10;
int* ptr = # // ptr은 num의 주소를 가리킴.

printf("ptr이 가리키는 값: %d\n", *ptr); // *ptr은 num의 값인 10을 반환함.

*ptr = 20; // ptr이 가리키는 메모리 공간(num의 주소)의 값을 20으로 변경함.
           // 이제 num의 값도 20이 됨.

*ptr = 20;  num = 20; 과 동일한 결과를 가져옴!!

 

 

#include <stdio.h>
int main()
{

    int a = 100;
    int* p = &a;
    printf("%d\n",*p);  // 100
    printf("%p\n",p);	// 0x7ffe12526b3c
    printf("%p\n",&a);	// 0x7ffe12526b3c
    
    return 0;
}

 

p에는 a의 주소가 들어간다.

a의 주소는 &a 이다.

*p는 a의 주소에 들어있는 값을 나타낸다.

 

 

5. 포인터를 왜 사용하는가?

포인터는 C언어의 강력하고 유연한 특성을 가능하게 하는 핵심 개념임.  포인터를 사용하는 주된 이유는 다음과 같음.

  • 함수로 변수의 값 변경: 함수는 기본적으로 값을 복사하여 전달하지만, 포인터를 사용하면 함수의 매개변수로 변수의 주소를 전달하여 함수 내부에서 원본 변수의 값을 직접 변경할 수 있음.
  • 동적 메모리 할당: 프로그램 실행 중에 필요한 만큼의 메모리 공간을 할당받아 사용하고 해제하는 동적 메모리 할당(malloc, calloc, realloc, free)은 포인터를 통해서만 가능함. 이를 통해 메모리를 효율적으로 관리할 수 있음.
  • 배열 및 문자열 처리: C언어에서 배열 이름은 배열의 첫 번째 요소의 주소를 가리키는 포인터로 취급됨.  따라서 포인터 연산을 통해 배열의 요소에 효율적으로 접근하거나 문자열을 다룰 수 있음.
  • 복잡한 자료구조 구현: 연결 리스트, 트리, 그래프 등 복잡한 자료구조는 노드들이 메모리상에 흩어져 있고, 서로의 주소를 포인터로 연결하여 구성됨.
  • 하드웨어 제어: 임베디드 시스템 프로그래밍이나 운영체제 개발과 같이 하드웨어를 직접 제어해야 하는 상황에서 특정 메모리 주소에 접근하여 값을 읽거나 쓰는 데 포인터가 활용됨.

포인터는 C언어의 심장과 같음. 포인터의 정확한 이해와 활용은 C언어 프로그래밍 실력을 한 단계 더 끌어올리는 데 필수적임!!

 

 

-- 연습문제

  • i의 값이 50이 출력되도록 변수 i의 값을 해킹하시오
  • i의 값을 직접 바꾸는 것은 안됩니다
#include <stdio.h>

int main(void)
{
    int i = 30;

    // 수정가능지역 시작

        // i의 주소
        printf("&i = %p\n", &i);
        int* p = &i;  // 포인터 함수 (주소 값을 저장)
        printf("p = %p\n",p);
        *p = 50;  //  *p : p에 저장되어 있는 주소로 간다.

    // 수정가능지역 끝

	printf("i = %d\n", i);
    return 0;
}

 

출력값

&i = 0x7ffda1c5febc
p = 0x7ffda1c5febc
i = 50

 

 

 

포인터에 +1을 했을 때 주소가 얼마나 증가하는지

#include <stdio.h>

int main() 
{
    char c = 'A';
    int i = 10;
    double d = 3.14;
    
    char* cp = &c;
    int* ip = &i;
    double* dp = &d;
    
    printf("char 포인터 원래 주소: %p\n", cp);
    printf("char 포인터 +1 주소: %p\n", cp+1);
    printf("char 포인터 차이: %ld 바이트\n\n", (char*)(cp+1) - cp);
    
    printf("int 포인터 원래 주소: %p\n", ip);
    printf("int 포인터 +1 주소: %p\n", ip+1);
    printf("int 포인터 차이: %ld 바이트\n\n", (char*)(ip+1) - (char*)ip);
    
    printf("double 포인터 원래 주소: %p\n", dp);
    printf("double 포인터 +1 주소: %p\n", dp+1);
    printf("double 포인터 차이: %ld 바이트\n", (char*)(dp+1) - (char*)dp);
    
    return 0;
}

 

 

char 포인터 원래 주소: 0x7ffe3a248953
char 포인터 +1 주소: 0x7ffe3a248954
char 포인터 차이: 1 바이트

int 포인터 원래 주소: 0x7ffe3a248954
int 포인터 +1 주소: 0x7ffe3a248958
int 포인터 차이: 4 바이트

double 포인터 원래 주소: 0x7ffe3a248958
double 포인터 +1 주소: 0x7ffe3a248960
double 포인터 차이: 8 바이트