포인터(Pointer)는 다른 변수의 메모리 주소를 저장하는 변수입니다. C 언어에서 포인터를 제대로 이해하면 배열, 문자열, 동적 메모리 관리 등을 훨씬 효율적으로 다룰 수 있습니다.
포인터의 주요 특징
- 포인터를 사용하면 메모리 공간을 절약할 수 있습니다.
- 메모리 위치에 직접 접근하기 때문에 실행 속도가 빠릅니다.
- 포인터를 활용하면 메모리를 효율적으로 접근할 수 있으며, 메모리를 동적으로 할당하고 해제할 수 있습니다.
- 연결 리스트, 트리 등 다양한 자료구조 구현에 필수적으로 사용됩니다.
포인터의 선언, 초기화 및 접근
다음 문장을 예로 들어 살펴보겠습니다.
int qty = 179;
이 변수는 메모리에서 아래 그림과 같이 표현됩니다.

1. 포인터 선언
포인터는 다음과 같이 선언할 수 있습니다.
int *p;
이 선언문의 의미는 'p'가 다른 정수(int) 변수의 주소를 저장하는 포인터 변수라는 것입니다.
2. 포인터 초기화
포인터 변수를 초기화할 때는 주소 연산자(&)를 사용합니다.
예를 들면 다음과 같습니다.
int qty = 175; int *p; p = &qty;
위 코드에서 p에는 변수 qty의 메모리 주소가 저장됩니다.
3. 포인터를 통해 변수 값 접근하기
포인터가 가리키는 변수의 실제 값을 읽거나 수정하려면 간접 참조 연산자(*), 즉 역참조(dereference) 연산자를 사용합니다.
예제: 포인터로 두 행렬 곱하기
다음은 포인터를 사용하여 두 개의 3x3 행렬을 곱하는 완전한 C 프로그램입니다. 배열 요소에 접근할 때 인덱스 대신 *(*(mat + row) + col) 형태의 포인터 연산을 사용한 점에 주목하세요.
#include <stdio.h>
#define ROW 3
#define COL 3
/* 함수 선언 */
void matrixInput(int mat[][COL]);
void matrixPrint(int mat[][COL]);
void matrixMultiply(int mat1[][COL], int mat2[][COL], int res[][COL]);
int main() {
int mat1[ROW][COL];
int mat2[ROW][COL];
int product[ROW][COL];
printf("Enter elements in first matrix of size %dx%d\n", ROW, COL);
matrixInput(mat1);
printf("Enter elements in second matrix of size %dx%d\n", ROW, COL);
matrixInput(mat2);
matrixMultiply(mat1, mat2, product);
printf("Product of both matrices is : \n");
matrixPrint(product);
return 0;
}
void matrixInput(int mat[][COL]) {
int row, col;
for (row = 0; row < ROW; row++) {
for (col = 0; col < COL; col++) {
scanf("%d", (*(mat + row) + col));
}
}
}
void matrixPrint(int mat[][COL]) {
int row, col;
for (row = 0; row < ROW; row++) {
for (col = 0; col < COL; col++) {
printf("%d ", *(*(mat + row) + col));
}
printf("\n");
}
}
void matrixMultiply(int mat1[][COL], int mat2[][COL], int res[][COL]) {
int row, col, i;
int sum;
for (row = 0; row < ROW; row++) {
for (col = 0; col < COL; col++) {
sum = 0;
for (i = 0; i < COL; i++) {
sum += (*(*(mat1 + row) + i)) * (*(*(mat2 + i) + col));
}
*(*(res + row) + col) = sum;
}
}
}코드 핵심 설명
- matrixInput():
*(*(mat + row) + col)대신(*(mat + row) + col)처럼 주소 자체를 전달하여 scanf로 값을 직접 입력받습니다. - matrixPrint(): 역참조 연산자를 이용해 각 행렬 요소를 화면에 출력합니다.
- matrixMultiply(): 행렬 곱셈의 기본 원리인 "첫 번째 행렬의 행(row)과 두 번째 행렬의 열(column)의 내적"을 삼중 반복문으로 계산합니다.
실행 결과
위 프로그램을 컴파일 후 실행하면 다음과 같은 출력을 확인할 수 있습니다.
Enter elements in first matrix of size 3x3 2 3 1 2 5 6 2 6 8 Enter elements in second matrix of size 3x3 1 2 1 2 3 4 5 6 7 Product of both matrices is : 13 19 21 42 55 64 54 70 82
이처럼 포인터 연산만으로도 일반적인 배열 인덱스 방식(mat[row][col])과 동일하게 행렬 입출력과 곱셈을 처리할 수 있습니다. 포인터와 2차원 배열의 관계를 이해하는 데 매우 좋은 예제이니 직접 코드를 작성해 보며 익혀보시기 바랍니다.