![[C언어] 포인터의 기본 개념](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVWrZA%2FbtsM6CkSaB8%2Fu9kvsIlqRei1cwBDtktRIk%2Fimg.png)
[C언어] 포인터의 기본 개념Dev/C2025. 4. 4. 01:41
Table of Contents
C언어의 핵심이자 가장 중요한 개념 중 하나인 포인터의 기본 개념에 대해 정리한 글입니다.
변수와 메모리, 그리고 주소의 개념
변수와 메모리
변수 (Variable)
- 특정 데이터를 저장해두는 이름 (label)
- ex)
int a = 10
➡️ 변수 이름 =a
| 값 =10
- ex)
- 모든 변수들은 주소가 존재 ➡️ 변수 이름은 개발자(사람)을 위한 것이라면 주소는 컴퓨터를 위한 값
메모리 (Memory)
- 프로그램에서 사용하는 모든 변수들이 저장되는 위치 ➡️ 컴퓨터 메모리(램, RAM) 어딘가에 실제로 저장되어 있음
- 변수가 선언되면, 해당 변수는 메모리 어딘가에 자료형의 크기만큼 공간을 차지하고 값을 저장하고 있음
주소 (Address)
- 메모리는 매우 많은 칸들로 구성되어 있고, 각 칸마다 고유의 변호가 있으며 이를 주소(address) 혹은 메모리 주소라고 함
- C언어에서는 어떤 변수에 할당된 메모리 위치를 주소값으로 표현할 수 있으며,
&
(앰퍼샌드)를 붙여 표현 (변수의 메모리 주소 = 변수가 저장된 메모리의 시작 주소)- ex)
int a = 10
➡️ 변수a
의 메모리 주소 =&a
- ex)
- OS는 메모리 주소를 byte (1 byte = 8 bit) 단위로 관리
- 주소는 일반적으로 16진수 형태(ex.
0x005FFEC4
)로 표현되며, 시스템이나 컴파일러에 따라 달라질 수 있고, 실행 때마다 바뀌는 경우가 많음
주소값 관리
- 운영체제와 컴파일러가 프로그램이 시작될 때, 변수들이 사용할 메모리 영역을 배정 ➡️ C언어의 코드 레벨에서는 개발자가 구체적으로 메모리 주소를 결정하지 않음
&
연산자를 통해 변수에 할당된 실제 메모리 주소를 가져와 사용할 수 있음
포인터의 개본 개념
포인터의 개요
포인터 (pointer)
- 포인터는 "메모리 주소를 저장하는 변수"
- 포인터 = 포인터 변수 = 메모리 주소를 보관하는 변수
- 포인터 자체도 변수이기 때문에 메모리에 저장되지만, 변수에 담긴 값이 일반적인 데이터 혹은 값이 아니라 "주소"라는 것이 핵심
- 메모리 주소를 가지고 직접 메모리의 내용에 접근해서 조작 가능 ➡️ 동적 메모리를 할당하거나 연결 리스트와 같은 향상된 자료구조를 생성할 때 사용
포인터를 사용하는 이유
데이터의 복사를 피하고 데이터를 공유하며 작업할 수 있다.
📌 데이터를 복사하지 않는다.➡️ 추가 메모리 공간이 필요하지 않다.
📌 데이터를 공유한다. ➡️ 여러 부분에서 동일한 데이터를 참조한다.
포인터의 선언
#include <stdio.h>
int main() {
int a = 10;
int *pa = &a;
return 0;
}
- 자료형 뒤에
*
을 붙여서 "포인터 변수"임을 표기하고, 변수명을 작성int* pa
: 변수pa
가 "int
타입을 가리키는 포인터"라는 선언*
의 위치는 표기 스타일에 따라int* pa
혹은int *pa
등으로 작성 가능 (의미는 동일)
- 포인터가 주소를 저장할 때, 포인터가 해당 변수(혹은 변수의 메모리 주소)를 "가리킨다"라고 표현 (화살표로 표현)
*pa
는 포인터 변수인pa
가 가리키고 있는 메모리 주소에 저장된 값을 의미하며,pa
는 해당 메모리 주소를 의미- 위 코드에서
*pa
는 변수a
와 동일하게 취급할 수 있고,pa
는 변수a
가 저장된 메모리 주소값을 의미한다.
- 위 코드에서
주소 연산자와 간접 연산자
- 주소 연산자 (
&
, address operator) : 변수 앞에 붙여 그 변수의 주소 값을 가져옴 (ex.&a
) - 간접 연산자 (
*
, dereference operator)- 변수 선언부(ex.
int* pa
)에서*
은 해당 변수가 포인터라는 것을 의미 - 표현식(
*pa
)에서*
은 포인터가 가리키는 대상(값)을 의미
- 변수 선언부(ex.
포인터 변수pa
가 있을 때, 변수pa
자체는 주소를 담고 있고,*pa
는 그 주소에 저장된 실제 데이터를 의미한다.
포인터와 메모리 구조
- 운영체제나 컴파일러 등에 따라 달라질 수 있지만, C 프로그램은 대략적으로 위 그림과 같은 메모리 구조를 가진다.
- 텍스트(코드) 영역 : 실행할 프로그램의 기계어 코드가 저장되는 영역
- 데이터 영역 : 전역 변수, 정적(static) 변수 등이 저장되는 영역
- 힙(heap) 영역 : 동적 메모리 할당(
malloc
,free
등)을 통해 사용할 수 있는 영역 - 스택(stack) 영역 : 지역 변수 등이 저장되는 영역으로, 함수가 호출될 때마다 새로운 스택 프레임이 생기고 함수가 끝나면 제거
- 포인터는 어떤 영역에 있는 변수를 가리키든 "주소"를 담고 있다는 점은 동일
- 단, 해당 변수가 전역 변수인지, 지역 변수인지, 동적으로 할당한 메모리인지 등에 따라 사용 시 주의할 점이 달라짐
포인터 크기와 자료형
포인터 변수의 크기
- 포인터의 크기는 해당 시스템 아키텍처에 따라 달라짐
- 32비트 시스템 : 포인터 사이즈 = 4바이트
- 64비트 시스템 : 포인터 사이즈 = 8바이트
int*
,char*
,double*
등 동일한 아키텍처에서 포인터 타입의 크기는 모두 동일 (단, 포인터가 가리키는 대상의 타입은 다름)- 타입이 다른 포인터들은 내부적으로 "가리키는 대상의 크기"가 다름 ➡️ 이를 이용해 포인터 연산 등을 수행할 때 사용
#include <stdio.h>
int main() {
printf("int* 의 크기 : %d byte\n", sizeof(int*));
printf("char* 의 크기 : %d byte\n", sizeof(char*));
printf("double* 의 크기 : %d byte\n", sizeof(double*));
printf("\n");
printf("int 의 크기 : %d byte\n", sizeof(int));
printf("char 의 크기 : %d byte\n", sizeof(char));
printf("double 의 크기 : %d byte\n", sizeof(double));
return 0;
}
포인터 사용시 주의할 점
- 초기화되지 않은 포인터는 위험
- 포인터를 선언만 하고 주소를 넣어주지 않으면 쓰레기 값(garbage value)이 들어가 있을 수 있음
- 포인터를 선언하는 즉시 올바른 주소를 대입(
int* pa = &a
)하거나NULL
로 초기화(int* pa = NULL
)해야 함
- 이미 해제한 메모리를 가리키는 포인터 사용 금지 (dangling pointer)
- 동적 할당 후,
free()
가 호출된 메모리 주소를 계속 사용하면 안됨
- 동적 할당 후,
- 잘못된 형 변환 주의
- 포인터는 타입이 매우 중요하기 때문에 가리키는 타입에 맞춰서 사용해야 하며, 필요한 경우에만 올바른 형변환 수행
- 스택 변수의 주소를 함수 밖에서 사용 주의
- 지역 변수가 선언된 함수가 끝나면 스택에 있는 그 변수의 메모리는 유효하지 않음
요약 및 정리
- 포인터는 "메모리 주소를 저장하는 변수"
- 포인터를 통해 변수의 실제 위치(메모리 주소)에 접근할 수 있으며, 이를 통해 값을 읽거나 쓸 수 있음
- 주소는 운영체제와 컴파일러가 관리하며, C 코드 레벨에서는
&
연산자를 통해 얻고, 그 값을pa
와 같은 포인터 변수에 저장하여 사용 - 메모리 관리와 타입에 대한 정확한 이해가 필요
'Dev > C' 카테고리의 다른 글
[C언어] 배열과 포인터의 관계 (1차원 배열 & 2차원 배열) (0) | 2025.04.08 |
---|
@청월누리 :: DevKuk 개발 블로그
since 2025.01.27. ~ 개발자를 향해....🔥