C 기본 문법 – 배열 – 3편: 포인터와 배열 완전 정복
안녕하세요, 코딩 동지 여러분! 😄
오늘은 C 언어 학습의 본격적인 핵심 구간,
바로 **”포인터와 배열의 관계”**에 대해 정리해드릴게요.
🧠 C 언어에서 **포인터(pointer)**와 **배열(array)**은 뗄래야 뗄 수 없는 사이입니다.
둘은 서로 다른 개념이지만, 실제 사용 방식에서는 서로 긴밀하게 연결되어 있고, 종종 비슷하게 동작하죠.
“포인터는 주소를 저장하는 변수”
“배열의 이름은 포인터처럼 동작한다”
이런 말들, 헷갈리셨죠?
오늘 싹 정리해드릴게요! 🔍
1. 배열 이름 = 시작 주소?
int arr[5] = {10, 20, 30, 40, 50};
위에서 arr
은 단순한 배열 이름이지만,
실제로는 arr
자체가 배열의 시작 주소를 가리키는 포인터처럼 동작합니다.
printf("%p
", arr); // 배열의 시작 주소
printf("%d
", *arr); // arr[0]과 같음 → 10
💡
arr
==&arr[0]
배열의 이름은 배열 첫 번째 요소의 주소를 의미해요!
2. 포인터로 배열 순회하기
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // == &arr[0]
for (int i = 0; i < 5; i++) {
printf("%d ", *(p + i)); // 포인터 연산으로 접근
}
비교: 배열 방식 vs 포인터 방식
배열 접근 방식 | 포인터 방식 |
---|---|
arr[i] |
*(arr + i) |
arr + i |
주소 연산 |
&arr[i] |
arr + i |
🎯 배열과 포인터는 인덱스 접근과 포인터 연산이 거의 동일하게 작동합니다!
3. 배열 함수에 전달 = 포인터 전달
예제:
void printArr(int *p, int size) {
for (int i = 0; i < size; i++) {
printf("%d ", *(p + i));
}
}
int main() {
int nums[5] = {10, 20, 30, 40, 50};
printArr(nums, 5); // 배열 전달 == 포인터 전달
}
✅ 배열을 함수에 전달하면 자동으로 포인터로 변환됩니다!
4. 포인터를 이용한 배열 값 변경
void doubleValues(int *arr, int size) {
for (int i = 0; i < size; i++) {
*(arr + i) *= 2;
}
}
int main() {
int nums[4] = {1, 2, 3, 4};
doubleValues(nums, 4);
for (int i = 0; i < 4; i++) {
printf("%d ", nums[i]);
}
}
🎯 포인터를 통해 함수 안에서 배열 요소를 직접 수정할 수 있어요!
5. 2차원 배열과 포인터
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
int *p = &matrix[0][0];
- 2차원 배열은 실제로는 연속된 메모리 공간
- 따라서
*p
,*(p+1)
… 로 순차적으로 접근 가능
예:
for (int i = 0; i < 6; i++) {
printf("%d ", *(p + i));
}
6. 포인터 배열 vs 배열 포인터
이 둘은 다릅니다!
(1) 포인터 배열
char *strs[3] = {"apple", "banana", "grape"};
- 문자열 포인터 3개가 들어있는 배열
- 각 요소는 문자열을 가리키는 포인터
(2) 배열 포인터
int (*p)[3]; // 3개의 정수를 갖는 배열을 가리키는 포인터
- 복잡해 보이지만, 포인터가 배열 전체를 가리킴
🧠 포인터 배열은 “많은 포인터를 담은 배열”
배열 포인터는 “하나의 배열을 가리키는 포인터”
7. 문자열과 포인터
char str1[] = "Hello"; // 배열
char *str2 = "World"; // 포인터
str1
은 배열 이름 = 주소str2
는 문자열 리터럴을 가리키는 포인터
str1[0] == *str1
str2[0] == *str2
완전 같은 방식으로 사용 가능!
✅ 배열 vs 포인터 요약 비교
항목 | 배열 | 포인터 |
---|---|---|
선언 | int arr[5]; |
int *ptr; |
메모리 | 고정 크기 연속 공간 | 동적으로 변경 가능 |
크기 | sizeof(arr) 는 전체 크기 |
sizeof(ptr) 는 주소 크기 |
함수 전달 시 | 포인터로 자동 변환 | 직접 포인터 전달 |
접근 방법 | arr[i] |
*(ptr + i) |
메모리 할당 | 정적 | 동적 할당 가능 (malloc ) |
⚠️ 포인터와 배열 사용 시 주의할 점
주의사항 | 설명 |
---|---|
배열 이름은 상수 | arr++ 처럼 배열 이름에 연산 불가 |
포인터는 초기화 필수 | 선언 후 주소를 반드시 지정해야 함 |
인덱스 범위 초과 | *(p + i) 사용 시 메모리 초과 주의 |
함수 인자 크기 정보 없음 | 포인터 전달 시 배열 크기 따로 전달해야 함 |
문자열 포인터 수정 금지 | char *s = "text"; 는 읽기 전용 리터럴을 가리킴 |
마무리하며 💬
오늘은 배열과 포인터의 관계를 낱낱이 파헤쳐 봤어요!
포인터를 제대로 이해하면 배열을 유연하게 다룰 수 있고,
배열을 잘 이해하면 포인터 개념이 더욱 쉽게 와닿습니다.
함수와 배열의 관계, 메모리 주소와 연산, 문자열 처리까지
실전 개발에서 굉장히 중요한 내용들이에요.
🎯 “배열을 마스터하려면 포인터를 이해해야 한다”
이 말, 오늘로서 실감하셨죠? 😊
다음에는 포인터를 활용한 **동적 메모리 할당(malloc, free)**에 대해서도 다뤄볼게요!
끝까지 읽어주셔서 감사합니다!
포인터와 배열, 이젠 자신 있으시죠? 💻🔥
계속해서 함께 성장해봐요!