[WEEK05] 포인터와 포인터 연산
이미 지난 주차에 포인터에 대해 정리한 글을 투고했지만, C에서 포인터 개념이 중요하게 사용되기 때문에 다시 한 번 다루게 되었다. 오늘은 C언어에서 정말 중요한 개념인 포인터pointer, 역참조dereferencing, 그리고 이중 포인터double pointer에 대해 정리해봤다. 이 개념들은 자료구조를 구현할 때 거의 필수로 사용되기 때문에 제대로 이해하고 넘어가는 게 중요하다.
🔹 포인터
포인터는 간단히 말하면 "주소를 저장하는 변수"다. 일반 변수는 데이터를 저장하지만, 포인터는 그 데이터가 저장된 메모리 주소를 저장한다.
int a = 10;
int *p = &a; // p는 a의 주소를 저장
여기서 *p는 포인터이고, &a는 변수 a의 주소다. 즉, 포인터 p는 a를 가리키고 있다고 볼 수 있다.
🔸 역참조
포인터가 주소를 저장하고 있다면, 그 주소에 있는 실제 값에 접근하려면 어떻게 해야 할까? 그때 사용하는 것이 역참조 연산자 * 다.
printf("%d\n", *p); // a의 값인 10이 출력됨
*p는 p가 가리키는 주소의 실제 값을 의미한다.
정리하자면, *는 선언할 때는 포인터임을 나타내고, 사용할 때는 역참조 연산자로 쓰인다. 문맥에 따라 다르게 쓰이므로 헷갈리지 않도록 주의해야 한다!
🔹 이중 포인터
이중 포인터는 말 그대로 "포인터를 가리키는 포인터"다. 즉, 포인터의 주소를 저장하는 포인터라고 생각하면 된다.
int a = 10;
int *p = &a;
int **pp = &p; // pp는 p의 주소를 저장
여기서 구조는 이렇게 된다:
- a: 실제 값 (10)
- p: a의 주소를 가리킴
- pp: p의 주소를 가리킴
그래서 *pp는 p이고, **pp는 a다.
printf("%d\n", **pp); // 결과: 10
🔸 이중 포인터를 언제 사용할까?
1. 함수에서 포인터를 수정하고 싶을 때
void setPointer(int **pp) {
*pp = (int *)malloc(sizeof(int));
**pp = 100;
}
이중 포인터를 통해 함수 바깥의 포인터 변수 자체를 바꿀 수 있다.
2. 이차원 배열 구현
int **matrix;
matrix = (int **)malloc(sizeof(int*) * rows);
for (int i = 0; i < rows; i++)
matrix[i] = (int *)malloc(sizeof(int) * cols);
3. 포인터 배열 (예: char **argv)
- main 함수의 매개변수 char **argv도 사실 이중 포인터!
💻 이중 포인터 예제 코드
1. 함수에서 포인터 값을 변경하기
C언어에서 함수는 값에 의한 호출call by value이기 때문에, 포인터 변수 자체를 변경하려면 이중 포인터가 필요하다.
#include <stdio.h>
#include <stdlib.h>
void allocateAndSet(int **ptr) {
*ptr = (int *)malloc(sizeof(int)); // 포인터에 동적 메모리 할당
if (*ptr != NULL) {
**ptr = 42; // 역참조해서 값 설정
}
}
int main() {
int *p = NULL;
allocateAndSet(&p); // 이중 포인터로 전달
if (p != NULL) {
printf("할당된 값: %d\n", *p); // 결과: 42
free(p); // 동적 메모리 해제
}
return 0;
}
- allocateAndSet() 함수는 포인터의 포인터 int **ptr을 통해 메모리 할당 + 값 설정을 한다.
- 이중 포인터가 아니면 함수에서 p 자체를 바꿀 수 없다.
2. 이중 포인터로 2차원 배열 동적 할당
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4;
int **matrix;
// 행 포인터 배열 동적 할당
matrix = (int **)malloc(sizeof(int*) * rows);
// 각 행마다 열 배열 동적 할당
for (int i = 0; i < rows; i++) {
matrix[i] = (int *)malloc(sizeof(int) * cols);
}
// 배열 값 초기화 및 출력
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j;
printf("%2d ", matrix[i][j]);
}
printf("\n");
}
// 메모리 해제
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
- int **matrix는 2차원 배열처럼 사용할 수 있다.
- 행마다 별도로 메모리 할당한 후, 2차원 배열처럼 접근 가능 (matrix[i][j])
💡 아래 코드 쌍들은 서로 동일한 동작을 실행하는 코드이다.
matrix[i][j] = ++idx;
*((*(matrix + i)) + j) = ++idx;
printf("%d\n", matrix[2][3]);
printf("%d\n", *(*(matrix + 2)) + 3);
3. 포인터를 이용한 문자열 처리
문자열은 사실 문자 배열char array이고, 문자열의 시작 주소를 가리키는 char 포인터를 통해 다룰 수 있다.
#include <stdio.h>
void printString(char *str) {
while (*str != '\0') {
putchar(*str); // 현재 문자 출력
str++; // 다음 문자로 이동
}
putchar('\n');
}
int main() {
char message[] = "Hello, pointer!";
printString(message); // 문자열의 첫 주소를 전달
return 0;
}
- char *str은 문자열의 시작 주소를 받는다.
- 포인터를 증가시키며 문자열을 순회한다. (str++)
- 배열 이름(message)은 포인터처럼 첫 번째 문자의 주소를 의미함.
4. 함수 포인터 사용하기
함수 포인터는 함수의 주소를 저장하는 포인터다. 상황에 따라 함수를 매개변수로 넘기거나, 조건에 따라 함수 호출할 때 유용하게 쓰인다.
#include <stdio.h>
// 두 개의 함수 정의
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
// 함수 포인터를 매개변수로 받는 함수
void calculate(int x, int y, int (*func)(int, int)) {
printf("결과: %d\n", func(x, y));
}
int main() {
calculate(3, 4, add); // 결과: 7
calculate(3, 4, multiply); // 결과: 12
return 0;
}
(Tip) 함수 포인터 배열도 가능!
int (*operations[])(int, int) = { add, multiply };
operations[0](5, 6); // add
operations[1](5, 6); // multiply
함수 선택 메뉴를 만들 때 유용하게 사용될 수 있다.
🔹 포인터 연산
포인터 연산Pointer Arithmetic이란 포인터끼리 덧셈, 뺄셈, 비교 등을 수행하는 것이다. C언어에서는 포인터가 가리키는 자료형의 크기를 기준으로 연산이 일어난다. 예를 들어 int는 보통 4바이트니까, p + 1은 다음 int 위치(4바이트 뒤)를 가리킨다.
🔸 기본 예제: 배열과 포인터
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;
printf("%d\n", *p); // 10
printf("%d\n", *(p+1)); // 20
printf("%d\n", *(p+2)); // 30
return 0;
}
- p는 arr[0]을 가리킴
- p + 1은 arr[1]을 가리킴
- 포인터 연산은 배열 인덱스처럼 동작함
🔹 사용할 수 있는 연산들
| 연산 | 의미 |
| p + n | 포인터를 n개의 요소만큼 앞으로 이동 |
| p - n | 포인터를 n개의 요소만큼 뒤로 이동 |
| p1 - p2 | 두 포인터 사이의 거리(요소 개수 차이) |
| p++, p-- | 포인터를 다음/이전 요소로 이동 |
| p == q | 두 포인터가 같은 주소를 가리키는지 비교 |
🔸 예제: 포인터 이동
#include <stdio.h>
int main() {
char str[] = "Hello";
char *p = str;
while (*p != '\0') {
printf("%c ", *p);
p++; // 다음 문자로 이동
}
return 0;
}
// 출력 : H e l l o
🔸 예제: 포인터 간 뺄셈
int arr[5] = {1, 2, 3, 4, 5};
int *start = &arr[0];
int *end = &arr[4];
int distance = end - start; // 결과: 4
- 같은 배열 내 포인터 간 뺄셈은 몇 개 떨어져 있는지를 알 수 있다.
🔸 예제: 배열 순회
#include <stdio.h>
int main() {
int nums[] = {10, 20, 30, 40, 50};
int *p = nums;
for (int i = 0; i < 5; i++) {
printf("%d ", *(p + i)); // 포인터 + i
}
return 0;
}
// 출력 : 10 20 30 40 50
🔸 예제: 포인터로 중간 요소 접근 및 수정
#include <stdio.h>
int main() {
int arr[] = {100, 200, 300, 400, 500};
int *p = arr;
*(p + 2) = 999; // 세 번째 요소(300 → 999)
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
return 0;
}
// 출력 : 100 200 999 400 500
🚫 주의할 점
- 포인터 연산은 같은 배열 내에서만 안전하게 해야 한다.
- 배열 범위를 벗어나면 정의되지 않은 동작undefined behavior이 발생할 수 있다.
- 포인터끼리 덧셈은 불가능하다. (p + q ← ❌)
정리
| 내용 | 설명 |
| 포인터 + 정수 | 메모리 상 다음 요소로 이동 |
| 포인터 - 포인터 | 두 포인터 간 거리(요소 개수) |
| 포인터 증가/감소 | 배열이나 문자열 순회에 유용 |
| 주의사항 | 배열 범위 밖 접근 금지, 다른 포인터끼리 덧셈 불가 |
[참고자료]
COS Pro 2급 C 언어: 44.1 포인터 연산으로 메모리 주소 조작하기
포인터 연산은 포인터 변수에 +, - 연산자를 사용하여 값을 더하거나 뺍니다. 또는, ++, -- 연산자를 사용하여 값을 증가, 감소시킵니다. 단, *, / 연산자와 실수 값은 사용할 수 없습니다. 포인터 +
dojang.io
[C언어] 포인터를 사용하는 이유
포인터 없이 두 변수의 값을 바꾸는 함수는 불가능할까? 우선 swap 함수에서 main 함수의 a, b를 이름으로 직접 사용하는 방법을 생각해 보겠습니다. 함수 안에 선언된 변수명은 사용 범위가 함수
hongong.hanbit.co.kr
C언어 문법 - 포인터(Pointer)
Python과 결이 매우 다른 C언어.. 그냥저냥 할 만했지만, 이제는 어렵다.바로 '포인터'의 개념이 처음 등장했기 때문.그래서 Pointer에 대해 공부한 내용을 정리하고자 글을 썼다. 프로그래밍 언어에
velog.io
09-09. 포인터와 함수
[TOC] ## 예제 1: 값에 의한 함수 호출 (Call-by-value) ```{.c} #include int func(int i); int main(void) { …
wikidocs.net
왜 C언어 포인터는 이해하기 어려울까? - 인프런 | 스토리
교수님! 진도가 너무 빠릅니다 😭 C언어 입문의 최종보스, 들어는 봤나 포인터! C언어 배워보신 분 손! 프로그래밍에 관심이 있다면 한 번쯤은 ‘C언어에서 포인터가 그렇게 어렵다더라’ 하는
www.inflearn.com
[크래프톤 정글 ] 포인터
C 언어에서 포인터는 변수의 메모리 주소를 저장하는 변수이다.즉, 포인터는 다른 변수나 메모리 상의 데이터의 위치를 가리키는 역할을 한다. 이로 인해 직접적으로 메모리 주소를 다룰 수 있
developsvai5096.tistory.com