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 바이트'C언어 공부' 카테고리의 다른 글
| C언어 기초 - 9. 배열의 개념 (0) | 2025.08.15 |
|---|---|
| C언어 기초 - 8-1. 포인터의 개념 연습문제 (0) | 2025.08.15 |
| C언어 기초 - 7-1. 함수의 개념(리턴) (4) | 2025.08.14 |
| C언어 기초 - 7. 함수의 개념 (매개변수) (5) | 2025.08.14 |
| C언어 기초 - 6-2. 중첩반복문 (0) | 2025.08.14 |