본문 바로가기
C언어

C언어 포인터 & 배열

by godfeeling 2020. 7. 5.

포인터와 배열의 관계

포인터와 배열은 매우 긴밀한 관계를 맺고 있으며, 어떤 부분에서는 서로를 대체할 수도 있습니다.

 

 

 

배열의 이름은 그 값을 변경할 수 없는 상수라는 점을 제외하면 포인터와 같습니다.

 

따라서 배열의 이름은 포인터 상수(constant pointer)입니다.

 

포인터 상수(constant pointer)란 포인터 변수가 가리키고 있는 주소 값을 변경할 수 없는 포인터를 의미하며,

상수 포인터(pointer to constant)란 상수를 가르키는 포인터를 의미합니다.

 

 

 

예제

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));

 

위의 예제에서는 포인터에 배열의 이름을 대입한 후, 해당 포인터를 배열의 이름처럼 사용합니다.

 

이처럼 C언어에서는 배열의 이름을 포인터처럼 사용할 수 있을 뿐만 아니라, 포인터를 배열의 이름처럼 사용할 수도 있습니다.

 

 

 

하지만 배열의 크기를 계산할 때에는 배열의 이름과 포인터 사이에 차이가 발생합니다.

 

배열의 이름을 이용한 크기 계산에서는 배열의 크기가 int형 배열 요소 3개의 크기인 12바이트로 제대로 출력됩니다.

 

하지만 포인터를 이용한 크기 계산에서는 배열의 크기가 아닌 포인터 변수 자체의 크기가 출력되는 차이가 있습니다.

 

배열의 포인터 연산

다음 예제는 앞선 예제와는 반대로 배열의 이름을 포인터처럼 사용하는 예제입니다.

 

배열의 이름으로 포인터 연산을 수행하여 각각의 배열 요소에 접근합니다.

 

예제

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));

 

따라서 배열의 이름과 포인터 사이에는 다음과 같은 공식이 성립함을 알 수 있습니다.

 

공식

arr이 배열의 이름이거나 포인터이고 n이 정수일 때,

 

arr[n] == *(arr + n)

 

 

 

위의 공식은 1차원 배열뿐만 아니라 다차원 배열에서도 언제나 성립합니다.

 

배열에 관계된 연산을 할 때는 언제나 배열의 크기를 넘어서는 접근을 하지 않도록 주의해야 합니다.

 

포인터 연산을 이용하여 계산하다가 배열의 크기를 넘어서는 접근을 하는 경우, C 컴파일러는 어떠한 오류도 발생시키지 않습니다.

 

다만 잘못된 결과만을 반환하므로 C언어로 프로그래밍할 때에는 언제나 배열의 크기에도 주의해야 합니다.

 

포인터 배열

포인터 배열이란 배열 요소로 포인터 변수를 가지는 배열을 의미합니다.

 

, 포인터 변수를 저장할 수 있는 배열을 의미합니다.

 

 

 

다음 예제는 세 개의 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]);

 

}

 

 

배열 포인터

배열 포인터란 배열을 가리킬 수 있는 포인터를 의미합니다.

 

 

 

앞서 배열의 이름은 그 값을 변경할 수 없는 상수라는 점을 제외하면 포인터와 같다고 했습니다.

 

이렇게 배열 이름이 있는데도 따로 배열 포인터를 정의하여 사용하는 이유는 2차원 이상의 배열을 가리킬 때 포인터를 통해 배열과 같은 인덱싱을 할 수 있도록 하기 위함입니다.

 

, 포인터를 배열처럼 사용하기 위해서 배열 포인터를 정의하여 사용합니다.

 

따라서 배열 포인터는 1차원 배열에서는 아무런 의미가 없으며, 2차원 이상의 배열에서만 의미를 가집니다.

 

2차원 배열의 배열 이름 arr는 부분 배열 arr[0]와 같은 곳을 가리킵니다.

 

2차원 배열의 배열 이름으로 포인터 연산을 하면 배열의 행 단위로 이동하게 됩니다.

 

, (arr+1)arr[1]과 같은 곳을 가리키게 됩니다.

 

 

 

다음 예제는 2차원 배열에서 각 부분 배열의 시작 주소가 가리키는 메모리에 저장된 데이터를 출력하는 예제입니다.

 

예제

int arr[2][3] = {

 

{10, 20, 30},

 

{40, 50, 60}

 

};

 

 

 

printf("%d\n", *arr[0]);

 

printf("%d\n", *arr[1]);

 

 

2차원 배열에서는 포인터 연산 시 증가하는 값이 행의 길이에 따라 차이를 보이게 됩니다.

 

2차원 배열의 행의 길이란 부분 배열의 크기를 의미하며, 다음 수식으로 구할 수 있습니다.

 

수식

sizeof(arr[0]) / sizeof(타입)

 

 

 

다음 예제에서 포인터 연산 시 증감하는 값의 크기는 int형 타입의 크기인 4바이트에 배열 행의 길이인 3를 곱한 12바이트가 됩니다.

 

예제

int arr[2][3];

 

 

 

따라서 위의 예제에서 배열 이름 arr의 타입은 정확하게 다음과 같이 정의할 수 있습니다.

 

 

 

1. 배열의 이름 arrint형 데이터를 가리키는 배열 포인터입니다.

 

2. 이 배열 포인터는 포인터 연산 시 증감하는 값의 크기가 12바이트입니다.

 

 

 

따라서 위의 예제에서 배열 arr를 가리키는 배열 포인터는 다음과 같이 선언할 수 있습니다.

 

예제

int (*pArr)[3];

 

 

 

또한, 위 예제의 배열 포인터는 다음과 같은 배열들을 가리킬 수 있습니다.

 

예제

int arr01[2][3];

 

int arr02[3][3];

 

int arr03[4][3];

 

...

 

 

 

다음 예제는 배열 포인터를 사용하여 배열과 같은 인덱싱 방법으로 배열 요소를 참조하는 예제입니다.

 

예제

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]); // 배열 포인터로 참조

 

 

 

'C언어' 카테고리의 다른 글

C언어 문자  (0) 2020.07.05
C언어 메모리  (0) 2020.07.05
C언어 포인터  (0) 2020.07.05
C언어 배열  (0) 2020.07.05
C언어 함수  (0) 2020.07.04

댓글