농담곰담곰이의곰담농

크래프톤 정글 6기 TIL - Day 18 | 포인터와 배열

by 브이담곰

포.............이.ㄴ.터..............깡!

포인터 산술

포인터는 배열의 원소를 가리킬 수 있다.

int a[10];
int* p;

 

p가 a[0]를 가리키게 만들어 줄 수 있다.

p = &a[0];

 

a[0]에 5라는 값을 저장하고 싶으면 다음과 같이 해주면 된다.

*p = 5;

 

p에 포인터 산술(pointer arithmetic) 혹은 주소 산술(address arithmetic)을 사용하게 되면 a의 다른 원소들에 접근 할 수 있게 된다!

 

C는 세 가지의 포인터 산술을 지원한다.

* 포인터에 정수 더하기

* 포인터에 정수 빼기

* 포인터에 다른 포인터 빼기

 

포인터에 정수 더하기
int a[10];
int* p;
int* q;
int i;

 

 

포인터에서 정수 빼기

 

포인터끼리 빼기

포인터끼리의 뺄셈의 결과는 포인터간의(배열 원소끼리의) 거리이다. 그러므로 p는 a[i]를 가리키고 q는 a[j]를 가리킨다고 하면 p-q 는 i - j와 같다.

 

[!!!]

배열의 원소를 가리키지 않는 포인터간의 산술은 정의되지 않은 행동을 야기한다. 같은 배열의 원소를 가리키지 않는 포인터간의 뺄셈 또한 정의되지 않은 행동이다.

 

포인터 간 비교

포인터도 관계 연산자( <. <=, >, >=)와 동등 연산자(==와 !=)을 사용해서 비교를 할 수 있다. 

두 포인터를 관계 연산자로 비교하는 것은 오로지 그 포인터들이 같은 배열의 원소들을 가리킬 때만 의미가 있다.

비교한 결과는 배열의 두 원소들의 상대적 위치에 따라 결정된다.

p = &a[5];
q = &a[1];

 

2 포인터를 통한 배열처리

포인터 산술을 이용해 포인터 변수의 값을 반복해서 증가시켜주면 배열의 원소들을 전부 참조할 수 있다!

#define N (10)
…
int a[N];
int sum;
int* p;
…
sum = 0;
for (p = &a[0]; p < &a[N]; ++p) {
    sum += *p;
}

 

* 연산자와 ++연산자 섞어 쓰기

배열에 원소를 처리하는 구문에 *(참조)와 ++연산자를 섞어 쓰고는 한다. 배열 원소에 값을 저장하고 다음 원소로 넘어가는 간단한 코드를 보자면,

a[i++] = j;

만약 p가 배열의 원소를 가리키고 있다면 위와 동일한 구문은 다음과 같다.

*p++ = j;

++ 후위 연산자는 *보다 우선순위를 갖기 때문에 컴파일러는 이를 다음과 같이 인식한다.

*(p++) = j;

 

p++의 값은 (++ 후위 연산자 이므로 표현식이 평가되기 전까지는 증가되지 않음) p이다. 그러므로 *(++p)의 값은 *p p가 가르키는 그 오브젝트이다!!!!!!!

 

ex_ 배열 a의 원소들의 합을 구할 때 다음과 같이 코드를 작성할 수 있다.

for (p = &a[0];, p < &a[N]; ++p) {
    sum += *p;
}

위와 같이 작성하는 것 대신, 아래와 같이 작성해 줄 수도 있다.

p = &a[0];
while (p < &a[N]) {
    sum += *p++;
}

 

 3 배열 이름 포인터로서 쓰기

배열의 이름이 배열의 첫번째 원소를 가리키는 포인터로서 사용할 수 있다.

a가 다음과 같이 선언되어있다고 하면

int a[10];

a를 배열의 첫번째 원소를 가리키는 포인터로서 a[0]의 값을 바꿀 수 있다.

*a = 7;    /* a[0]에 7을 저장한다 */

포인터 a+1을 통해 a[1]의 값을 바꿔줄 수도 있다.

*(a + 1) = 12;    /* a[1]에 12를 저장한다 */

 a[i] = *(a+i) 이다.

 

4 다차원 배열의 이름 포인터로서 쓰기

r개의 행을 갖는 배열은 다음과 같이 생겼다.

만약 포인터가 2차원 배열의 첫번째 원소를 가리키게 만들면, 이를 이용해서 p를 계속해서 증가시켜 배열의 모든 원소에 접근할 수 있게 된다.

int matrix[NUM_ROWS][NUM_COLS];

이를 이중for문 루프로 돌려서 접근할 수 도있겠지만, 2차원배열을 1차원 배열로 보고 아래와 같이 코드를 작성 할 수 있다.

int* p;
…
for (p = &matrix[0][0]; p <= &matrix[NUM_ROWS - 1][NUM_COLS - 1]; ++p) {
    *p = 0;
}

p는 matrix[0][0]를 가리키는것으로 시작해, matrix[0][NUM_COLS - 1]에 도달하게 된다면(0번 행의 마지막 원소) p는 matrix[1][0]을 가리키게된다. 따라서 이 과정은 matrix[NUM_ROWS - 1][NUM_COLS - 1]에 도달할때까지 계속된다.

 

다차원 배열의 행 처리

i 번째 행의 원소들을 보려면 p를 배열 matrix의 i번 행의 0번 원소를 가리키게 초기화를 해주어야 한다.

p = &matrix[i][0];

또는

p = matrix[i];

 

matrix[i]는 i번 행의 첫번째 원소를 가리키는 포인터이다.

&matrix[i][0] 은 &(*(matrix[i]+))와 같고, &*matrix[i]와 같다!!

 

다차원 배열의 열 처리

i 번째 열을 0으로 초기화해주는 코드이다.

int matrix[NUM_ROWS][NUM_COLS];
int (*p)[NUM_COLS];
int i;
…
for (p = &matrix[0]; p < &matrix[NUM_ROWS]; ++p) {
    (*p)[i] = 0;
}

✨ (*p)[i]에 소괄호가 없다면 컴파일러가 *p[i]를 *(p[i])로 인식해버린다.

 

다차원 배열의 이름을 포인터로서 사용하기

일차원 배열의 이름이 포인터로 쓰일 수 있듯, 차원의 개수와 무관하게 모든 배열도 그렇게 쓰일 수 있다.

int matrix[NUM_ROWS][NUM_COLS];

matrix는 matrix[0][0]를 가리키는 포인터가 아니다. 대신, matrix[0]을 가리키는 포인터이다.

C의 입장에서는 원소들이 일차원 배열인 또 다른 일차원 배열일 뿐이다.

matrix를 포인터로 사용하게 되면 matrix의 형은 int (*)[NUMS_COLS]   (NUM_COLS 개의 정수 원소를 갖는 배열을 가리키는 포인터)가 된다!

for (p = &matrix[0]; p < &matrix[NUM_ROWS]; ++p) {
    (*p)[i] = 0;
}

따라서 배열 matrix의 i번 행을 0으로 초기화 하는데 위와 같이 작성하는 대신 아래와 같이 작성해줄 수 있다.

for (p = matrix; p < matrix + NUM_ROWS; ++p) {
    (*p)[i] = 0;
}

 


예를 들어, find_largest 함수가 matrix의 최대값을 구하게 만들때, find_largest의 첫번째 입력변수로 matrix(배열의 주소)를 전달해주고, 두번째 입력변수로는 NUM_ROWS * NUM_COLS(matrix의 총 원소 개수)를 전달해준다면

largest = find_largest(matrix, NUM_ROWS * NUM_COLS);    /* 틀림 */

슬프게도...matrix의 형은 int*이 아닌 int (*)[NUM_COLS]이므로...컴파일러는 오류를 낸다..

제대로된 호출은 다음과 같다.

largest = find_largest(matrix[0], NUM_ROWS * NUM_COLS);

matrix[0]은 0번 행의 0번 원소를 가리키는 포인터이며, 형도 int*이기 때문에 아래의 경우 제대로 작동할 것이다.

블로그의 정보

농담곰담곰이의곰담농

브이담곰

활동하기