C언어에서 포인터를 어느 정도 이해하고 나면 많은 사람이 다음 단계에서 헷갈린다.
바로 배열과 포인터가 왜 비슷하게 동작하는가?, 그리고 둘은 같은 것인가? 라는 질문이다.
예를 들어 아래 코드를 보면 배열과 포인터가 같은 역할을 하는 것처럼 느껴진다.
uint8 Data[3] = {10U, 20U, 30U};
uint8 *ptr = Data;
(void)printf("%u", Data[0]);
(void)printf("%u", *ptr);
둘 다 첫 번째 값인 10을 출력한다.
그래서 많은 사람이 이렇게 생각한다.
* 배열 = 포인터 아닌가?
* 배열 이름은 왜 주소처럼 쓰이지?
* 둘이 뭐가 다른가?
결론부터 말하면, 배열과 포인터는 비슷하게 사용할 수는 있어도 완전히 다른 개념이다.
이번 글에서는 임베디드 실무 스타일 코드 기준으로 배열과 포인터 차이를 쉽게 설명한다.
배열이란 무엇인가?
배열은 같은 자료형 데이터를 여러 개 연속으로 저장하는 메모리 공간이다.
예:
uint8 TxData[4] = {0x11U, 0x22U, 0x33U, 0x44U};
의미:
TxData[0] = 0x11
TxData[1] = 0x22
TxData[2] = 0x33
TxData[3] = 0x44
즉, 배열은 여러 데이터를 한 번에 관리하기 위한 저장소다.
실무에서는 다음과 같이 많이 사용된다.
- CAN Tx/Rx 데이터
- 센서 샘플 데이터
- EEPROM 버퍼
- 진단 데이터 버퍼
포인터란 무엇인가?
이미 앞전에 포인터가 무엇인지 설명했지만 그래도 비교를 위해 간단하게 한 번 더 설명한다.
앞전 글을 보지 못했다면 아래 링크로 들어가서 천천히 읽어보면 된다.
https://jjongday.tistory.com/96
포인터는 메모리 주소를 저장하는 변수다.
예:
uint8 Value = 10U;
uint8 *ptr = &Value;
의미:
Value는 실제 값 10 저장
ptr은 Value의 주소 저장
즉, 배열은 데이터 저장 공간이고, 포인터는 위치(주소)를 저장하는 변수이다.
둘은 역할 자체가 다르다.
왜 배열과 포인터가 같아 보일까?
배열 이름은 특정 상황에서 첫 번째 요소의 주소처럼 사용되기 때문이다.
예:
uint8 RxData[3] = {1U, 2U, 3U};
uint8 *ptr = RxData;
여기서 RxData는 아래와 비슷하게 동작한다.
ptr = &RxData[0];
즉, 배열 이름이 첫 번째 요소 주소처럼 전달된다.
그래서 다음 코드가 가능하다.
uint8 A = *ptr;
uint8 B = *(ptr + 1);
uint8 C = *(ptr + 2);
결과:
A = 1
B = 2
C = 3
이 때문에 배열과 포인터가 같은 것처럼 느껴진다.
핵심 차이 1: 배열은 공간, 포인터는 변수
## 배열
uint8 Buffer[8];
이 코드는 8바이트 저장 공간을 만든다.
즉, 배열은 실제 데이터를 담는 메모리 공간이다.
## 포인터
uint8 *ptr;
이 코드는 주소를 저장할 변수를 만든다.
즉, 포인터는 데이터 자체가 아니라 위치 정보를 저장한다.
핵심 차이 2: 배열 이름은 변경 불가, 포인터는 변경 가능
## 배열
배열은 다른 주소를 가리킬 수 없다
uint8 Buffer[4];
Buffer = Buffer + 1U; /* 오류 */
배열 이름은 변수처럼 값을 바꾸는 대상이 아니다. (위 코드는 잘못된 코드)
## 포인터
포인터는 변경 가능하다
uint8 Data1 = 10U;
uint8 Data2 = 20U;
uint8 *ptr = &Data1;
ptr = &Data2;
이 코드의 의미는 처음에는 Data1 주소 저장하고, 이후 Data2 주소로 변경하는 코드이다.
이것이 배열과 포인터의 큰 차이다.
핵심 차이 3: sizeof 결과가 다르다
uint8 Buffer[8];
uint8 *ptr = Buffer;
의미:
sizeof(Buffer) = 8
sizeof(ptr) = 포인터 크기 (예: 4 또는 8)
즉, 배열은 전체 저장 공간 크기이고, 포인터는 주소 변수 크기이다.
메모리 구조가 완전히 다르다.
핵심 차이 4: 함수 인자에서는 배열이 포인터처럼 동작한다
실무에서 가장 자주 보는 패턴이다.
void ProcessData(uint8 *DataPtr)
{
uint8 Value;
Value = DataPtr[0];
}
사용:
uint8 RxBuffer[8];
ProcessData(RxBuffer);
설명:
RxBuffer는 배열
함수에 전달될 때 첫 번째 요소 주소 전달
함수 내부에서는 포인터로 사용
즉, 배열과 포인터가 만나는 지점이다.
정리
배열은 여러 데이터를 저장하는 공간이다.
포인터는 주소를 저장하는 변수다.
배열 이름은 특정 상황에서 첫 요소 주소처럼 동작한다.
그래서 비슷해 보이지만 실제로는 다르다.
실무에서는 배열과 포인터를 함께 사용한다.
'C 언어 > 실무' 카테고리의 다른 글
| C언어 비트 연산자 쉽게 설명하기 (임베디드 실무 관점에서 제대로 이해하기) (0) | 2026.04.14 |
|---|---|
| C언어 extern 변수 사용법 (파일 간 변수 공유 방법) (0) | 2026.04.13 |
| C언어 포인터 쉽게 설명하기 (개념부터 실무 예제까지 한 번에) (4) | 2026.04.11 |
| C언어 volatile은 언제 써야 하는가? (0) | 2026.04.11 |
| C언어 static 변수는 왜 쓰는가? (실무에서 안 쓰면 생기는 문제) (0) | 2026.04.11 |