B. 파생형 포인터와 배열
1. 포인터와 배열
a. 포인터와 배열의 관계
배열의 이름은 그 값을 변경할 수 없는 상수라는 점을 제외하면 포인터와 같음
따라서 배열의 이름 = 포인터 상수
💙 포인터 상수?
⦁ 포인터 상수(constant pointer) : 포인터 변수가 가리키고 있는 주소 값을 변경할 수 없는 포인터
⦁ 상수 포인터(pointer to constant) : 상수를 가르키는 포인터를 의미
🤓 예제 : 포인터에 배열의 이름을 대입
#include <stdio.h>
int main(void)
{
int arr[3] = {10, 20, 30}; // 배열 선언
int* ptr_arr = arr; // 포인터에 배열의 이름을 대입함
printf("배열의 이름을 이용하여 배열 요소에 접근 : %d %d %d\n", arr[0], arr[1], arr[2]);
printf(" 포인터를 이용하여 배열 요소에 접근 : %d %d %d\n", ptr_arr[0], ptr_arr[1], ptr_arr[2]);
printf("배열의 이름을 이용한 배열의 크기 계산 : %d\n", sizeof(arr));
printf(" 포인터를 이용한 배열의 크기 계산 : %d\n", sizeof(ptr_arr));
return 0;
}
✨ 실행결과
배열의 이름을 이용하여 배열 요소에 접근 : 10 20 30
포인터를 이용하여 배열 요소에 접근 : 10 20 30
배열의 이름을 이용한 배열의 크기 계산 : 12
포인터를 이용한 배열의 크기 계산 : 8
❕ 하지만 배열의 이름과 포인터에 크기 차이가 일어남 (배열은 배열의 크기만큼, 포인터는 포인터의 크기만큼)
b. 배열의 포인터 연산
배열의 이름과 포인터 사이에는 다음과 같은 공식이 성립
📘 공식
arr이 배열의 이름이거나 포인터이고 n이 정수일 때,
arr[n] == *(arr + n)
👉 1차원 배열뿐만 아니라 다차원 배열에서도 언제나 성립
🤓 예제 : 배열의 이름으로 포인터 연산을 수행하여 각각의 배열 요소에 접근
int arr[3] = {10, 20, 30}; // 배열 선언
printf(" 배열의 이름을 이용하여 배열 요소에 접근 : %d %d %d\n", arr[0], arr[1], arr[2]);
printf("배열의 이름으로 포인터 연산을 해 배열 요소에 접근 : %d %d %d\n", *(arr+0), *(arr+1), *(arr+2));
✨ 실행결과
배열의 이름을 이용하여 배열 요소에 접근 : 10 20 30
배열의 이름으로 포인터 연산을 해 배열 요소에 접근 : 10 20 30
📝 예제 설명
💥 WARNING
배열 연산을 할때는 배열의 크기를 넘어서는 접근을 하지 않도록 주의
포인터 연산을 이용하여 계산하다가 배열의 크기를 넘어서는 접근을 하는 경우
C 컴파일러는 오류 발생 ❌, 하지만 잘못된 결과만을 반환
따라서 C언어로 프로그래밍할 때에는 언제나 배열의 크기에 주의!
2. 포인터 배열과 배열 포인터
a. 포인터 배열
🌟 포인터 변수를 저장할 수 있는 배열
🤓 예제 : 세 개의 int형 포인터 변수를 저장할 수 있는 포인터 배열을 선언
int i, arr_len;
int num01 = 10, num02 = 20, num03 = 30;
int* arr[3] = {&num01, &num02, &num03}; // int형 포인터 배열 선언
arr_len = sizeof(arr)/sizeof(arr[0]);
for (i = 0; i < arr_len; i++)
{
printf("%d\n", *arr[i]);
}
✨ 실행결과
10
20
30
b. 배열 포인터
🌟 배열을 가리킬 수 있는 포인터
배열 이름 = 포인터 이지만 따로 포인터를 정의해서 사용하는 이유는, 2차원 이상 배열을 가리킬 때 포인터를 통해 배열과 같은 인덱싱을 하기 위함 (배열 포인터는 1차원 배열에서는 의미 ❌, 2차원 이상의 배열에서만 의미 ⭕)
즉, 포인터를 배열처럼 사용하기 위해 배열포인터를 정의해서 사용
🤓 예제 : 각 부분 배열의 시작 주소가 가리키는 메모리에 저장된 데이터를 출력
int arr[2][3] =
{
{10, 20, 30},
{40, 50, 60}
};
printf("%d\n", *arr[0]);
printf("%d\n", *arr[1]);
✨ 실행결과
10
40
c. 2차원 배열의 포인터 연산
2차원 배열에서 포인터 연산 시 증가하는 값 = 행의 크기
📘 문법: 부분 배열의 크기 구하는 수식
sizeof(arr[0]) / sizeof(타입)
🤓 예제 : 포인터 증감시 증가하는 값의 크기
int arr[2][3]; //4바이트에 배열 행의 길이인 3를 곱한 12바이트
int (*pArr)[3]; //위 배열 포인터 선언하기
📝 예제 설명
위의 예제에서 배열 이름 arr의 타입은 정확하게 다음과 같이 정의
1) 배열의 이름 arr는 int형 데이터를 가리키는 배열 포인터
2) 이 배열 포인터는 포인터 연산 시 증감하는 값의 크기가 12바이트
또한, 위 예제의 배열 포인터는 다음과 같은 배열들을 가리킬 수 있음
int arr01[2][3];
int arr02[3][3];
int arr03[4][3];
...
🤓 예제 : 배열 포인터를 사용하여 배열과 같은 인덱싱 방법으로 배열 요소를 참조하기
#include <stdio.h>
int main(void)
{
int arr[2][3] = // 배열의 선언
{
{10, 20, 30},
{40, 50, 60}
};
int (*pArr)[3] = arr; // 배열 포인터의 선언
printf("%d\n", arr[1][1]); // 배열 이름으로 참조
printf("%d\n", pArr[1][1]); // 배열 포인터로 참조
return 0;
}
✨ 실행결과
50
50
💙 포인터 배열과 배열 포인터의 구분
int (*pArr)[3]; //int형 데이터를 저장할 수 있는 2차원 배열을 가리키는 배열 포인터 int* pArr[3]; // int형 데이터를 가리킬 수 있는 포인터 변수를 모아 놓은 배열을 가리키는 포인터 배열
❕ 전혀 다른 의미가 되기 때문에 괄호 유무가 중요함
c. main() 함수의 인수 전달
main() : 프로그램이 실행되면 제일 먼저 자동으로 호출되는 함수
📘 main() 함수의 원형
void(또는 int) main(int argc, char *argv[]);
//첫 번째 인수 : int형 변수인 argc, main() 함수에 인수로 전달되는 문자열의 개수를 명시
//두 번째 인수 : char형 포인터 배열인 argv, main() 함수에 인수로 전달된 각각의 문자열이 저장된 배열
'프로그래밍 언어 > C' 카테고리의 다른 글
[자료형(data type)] C 구조체 배열과 구조체 포인터, 구조체 활용 (0) | 2023.08.17 |
---|---|
[자료형(data type)] C 파생형 구조체 기본 (0) | 2023.08.17 |
[자료형(data type)] C 파생형 포인터의 기본 (0) | 2023.08.17 |
[자료형(data type)] C 파생형 배열 총정리 (0) | 2023.08.17 |
[자료형(data type)] C 파생형 함수 총정리 (0) | 2023.08.17 |