1. 배열 이름의 정체
배열과 포인터는 매우 밀접한 관계를 가지고 있습니다. 사실 배열의 이름은 포인터입니다!
배열 이름은 첫 번째 요소의 주소
int arr[3] = {10, 20, 30};
| arr[0] | arr[1] | arr[2] |
|---|---|---|
| 10 | 20 | 30 |
| 주소: 0x100 | 주소: 0x104 | 주소: 0x108 |
배열 이름 arr은 배열의 첫 번째 요소 arr[0]의 주소를 의미합니다.
arr == &arr[0] // 참
실습 1
#include <stdio.h>
int main() {
int arr[3] = {10, 20, 30};
printf("배열 이름 arr: %p\n", arr);
printf("첫 번째 요소의 주소 &arr[0]: %p\n", &arr[0]);
printf("두 번째 요소의 주소 &arr[1]: %p\n", &arr[1]);
printf("세 번째 요소의 주소 &arr[2]: %p\n", &arr[2]);
return 0;
}
실행 결과 보기 (예시)
배열 이름 arr: 000000D5BCDFF6E8 첫 번째 요소의 주소 &arr[0]: 000000D5BCDFF6E8 두 번째 요소의 주소 &arr[1]: 000000D5BCDFF6EC 세 번째 요소의 주소 &arr[2]: 000000D5BCDFF6F0
arr과&arr[0]이 같은 주소- int형은 4바이트이므로 주소가 4씩 증가
배열 이름과 포인터의 차이
배열 이름은 포인터처럼 동작하지만 중요한 차이점이 있습니다:
int arr[3] = {10, 20, 30};
int *ptr = arr; // 가능
ptr = ptr + 1; // 가능 (포인터는 값 변경 가능)
arr = arr + 1; // 불가능! (배열 이름은 상수)
⚠️ 중요한 차이점
• 포인터 변수: 값을 변경할 수 있음
• 배열 이름: 상수 포인터, 값 변경 불가
• 포인터 변수: 값을 변경할 수 있음
• 배열 이름: 상수 포인터, 값 변경 불가
2. 포인터 연산
포인터는 특별한 방식으로 산술 연산을 수행합니다.
포인터 증가 연산
포인터를 1 증가시키면, 가리키는 자료형의 크기만큼 주소가 증가합니다.
int arr[3] = {10, 20, 30};
int *ptr = arr; // ptr은 arr[0]을 가리킴
| 연산 | 가리키는 요소 | 주소 증가량 |
|---|---|---|
ptr |
arr[0] | - |
ptr + 1 |
arr[1] | +4 (int 크기) |
ptr + 2 |
arr[2] | +8 (int 크기) |
자료형별 포인터 증가
char *cptr; // char는 1바이트
cptr + 1; // 주소 +1
int *iptr; // int는 4바이트
iptr + 1; // 주소 +4
double *dptr; // double은 8바이트
dptr + 1; // 주소 +8
포인터 연산의 핵심
포인터 + 1 = 주소 + (자료형 크기 × 1)
포인터 + n = 주소 + (자료형 크기 × n)
포인터 + 1 = 주소 + (자료형 크기 × 1)
포인터 + n = 주소 + (자료형 크기 × n)
실습 2
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;
printf("=== 포인터 연산 ===\n");
printf("ptr: %p, 값: %d\n", ptr, *ptr);
printf("ptr+1: %p, 값: %d\n", ptr+1, *(ptr+1));
printf("ptr+2: %p, 값: %d\n", ptr+2, *(ptr+2));
printf("ptr+3: %p, 값: %d\n", ptr+3, *(ptr+3));
printf("ptr+4: %p, 값: %d\n", ptr+4, *(ptr+4));
return 0;
}
실행 결과 보기 (예시)
=== 포인터 연산 === ptr: 000000C8FFDFF700, 값: 10 ptr+1: 000000C8FFDFF704, 값: 20 ptr+2: 000000C8FFDFF708, 값: 30 ptr+3: 000000C8FFDFF70C, 값: 40 ptr+4: 000000C8FFDFF710, 값: 50
주소가 4바이트(int 크기)씩 증가합니다.
포인터 증감 연산자
int arr[3] = {10, 20, 30};
int *ptr = arr;
printf("%d\n", *ptr); // 10
ptr++; // 다음 요소로 이동
printf("%d\n", *ptr); // 20
ptr++; // 다음 요소로 이동
printf("%d\n", *ptr); // 30
💡 포인터 연산 활용
•
•
•
•
ptr++: 다음 요소로 이동•
ptr--: 이전 요소로 이동•
ptr += 2: 2칸 앞으로 이동
3. 포인터로 배열 접근하기
포인터를 사용하면 배열을 두 가지 방법으로 접근할 수 있습니다.
배열 표기법 vs 포인터 표기법
int arr[3] = {10, 20, 30};
int *ptr = arr;
같은 의미의 표현들:
| 의미 | 배열 표기법 | 포인터 표기법 |
|---|---|---|
| 첫 번째 요소 값 | arr[0] |
*arr 또는 *ptr |
| 두 번째 요소 값 | arr[1] |
*(arr+1) 또는 *(ptr+1) |
| 세 번째 요소 값 | arr[2] |
*(arr+2) 또는 *(ptr+2) |
실습 3
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int i;
printf("=== 배열 표기법 ===\n");
for (i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
printf("\n=== 포인터 표기법 ===\n");
for (i = 0; i < 5; i++) {
printf("*(arr+%d) = %d\n", i, *(arr+i));
}
return 0;
}
실행 결과 보기
=== 배열 표기법 === arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[3] = 4 arr[4] = 5 === 포인터 표기법 === *(arr+0) = 1 *(arr+1) = 2 *(arr+2) = 3 *(arr+3) = 4 *(arr+4) = 5
포인터로 배열 순회하기
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;
int i;
// 방법 1: 인덱스 사용
for (i = 0; i < 5; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// 방법 2: 포인터 연산
for (i = 0; i < 5; i++) {
printf("%d ", *(ptr + i));
}
printf("\n");
// 방법 3: 포인터 증가
ptr = arr; // 포인터 초기화
for (i = 0; i < 5; i++) {
printf("%d ", *ptr);
ptr++;
}
printf("\n");
return 0;
}
실행 결과 보기
10 20 30 40 50 10 20 30 40 50 10 20 30 40 50
4. 포인터 배열
포인터 배열은 포인터를 요소로 가지는 배열입니다.
포인터 배열 선언
int *parr[3]; // int형 포인터 3개를 저장하는 배열
구조:
parr[0] → int형 변수의 주소
parr[1] → int형 변수의 주소
parr[2] → int형 변수의 주소
포인터 배열 사용 예
#include <stdio.h>
int main() {
int a = 10, b = 20, c = 30;
// 포인터 배열 선언 및 초기화
int *parr[3] = {&a, &b, &c};
// 포인터 배열을 통한 접근
printf("첫 번째 값: %d\n", *parr[0]); // 10
printf("두 번째 값: %d\n", *parr[1]); // 20
printf("세 번째 값: %d\n", *parr[2]); // 30
// 값 변경
*parr[0] = 100;
printf("변경된 a: %d\n", a); // 100
return 0;
}
실행 결과 보기
첫 번째 값: 10 두 번째 값: 20 세 번째 값: 30 변경된 a: 100
문자열 배열
포인터 배열은 여러 문자열을 저장할 때 유용합니다.
#include <stdio.h>
int main() {
char *fruits[3] = {"Apple", "Banana", "Cherry"};
int i;
printf("=== 과일 목록 ===\n");
for (i = 0; i < 3; i++) {
printf("%d. %s\n", i+1, fruits[i]);
}
return 0;
}
실행 결과 보기
=== 과일 목록 === 1. Apple 2. Banana 3. Cherry
포인터 배열의 활용
• 여러 변수의 주소를 한 번에 관리
• 문자열 배열 구현
• 가변 길이 데이터 처리
• 여러 변수의 주소를 한 번에 관리
• 문자열 배열 구현
• 가변 길이 데이터 처리
5. 종합 실습
문제 1 - 배열과 포인터 (기초)
문제 1
다음 코드의 실행 결과는?
포인터와 배열
#include <stdio.h>
int main() {
int arr[3] = {5, 10, 15};
printf("%d", *arr);
return 0;
}
문제 2 - 포인터 연산 (기초)
문제 2
다음 코드의 실행 결과는?
포인터와 배열
#include <stdio.h>
int main() {
int arr[4] = {2, 4, 6, 8};
printf("%d", *(arr + 2));
return 0;
}
문제 3 - 포인터 표기법 (중급)
문제 3
다음 코드의 실행 결과는?
포인터와 배열
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr + 2;
printf("%d", *ptr + *(ptr + 1));
return 0;
}
문제 4 - 포인터 증가 (중급)
문제 4
다음 코드의 실행 결과는?
포인터와 배열
#include <stdio.h>
int main() {
int arr[4] = {1, 3, 5, 7};
int *ptr = arr;
ptr++;
ptr++;
printf("%d", *ptr);
return 0;
}
문제 5 - 포인터 배열 (중급)
문제 5
다음 코드의 실행 결과는?
포인터와 배열
#include <stdio.h>
int main() {
int a = 100, b = 200, c = 300;
int *parr[3] = {&a, &b, &c};
*parr[1] = 500;
printf("%d", b);
return 0;
}
문제 6 - 배열 합계 (고급)
문제 6
다음 코드의 실행 결과는?
포인터와 배열
#include <stdio.h>
int main() {
int arr[5] = {2, 4, 6, 8, 10};
int *ptr = arr;
int sum = 0;
int i;
for (i = 0; i < 5; i++) {
sum += *(ptr + i);
}
printf("%d", sum);
return 0;
}
핵심 요약
1. 배열 이름과 포인터
• 배열 이름 = 첫 번째 요소의 주소
•
• 배열 이름은 상수 포인터 (값 변경 불가)
2. 포인터 연산
•
• 주소 증가량 = 자료형 크기
•
3. 배열 접근
• 배열 표기법:
• 포인터 표기법:
• 두 방법은 완전히 동일
4. 포인터 배열
• 선언:
• 포인터를 요소로 가지는 배열
• 문자열 배열 구현에 활용
5. 핵심 공식
•
•
•
• 배열 이름 = 첫 번째 요소의 주소
•
arr == &arr[0]• 배열 이름은 상수 포인터 (값 변경 불가)
2. 포인터 연산
•
ptr + 1 = 다음 요소의 주소• 주소 증가량 = 자료형 크기
•
ptr++, ptr-- 사용 가능3. 배열 접근
• 배열 표기법:
arr[i]• 포인터 표기법:
*(arr + i)• 두 방법은 완전히 동일
4. 포인터 배열
• 선언:
int *parr[크기];• 포인터를 요소로 가지는 배열
• 문자열 배열 구현에 활용
5. 핵심 공식
•
arr[i] = *(arr + i)•
&arr[i] = arr + i•
ptr[i] = *(ptr + i)
PREVIOUS8. 포인터 소개
NEXT10. 다차원 배열