본문 바로가기
컴퓨터 기초/C언어

[C언어]48일차 - 배열과 포인터

by 럭키길버트 2025. 11. 19.
반응형

 

1.배열과 포인터 표기법

2.배열 시작 주소

3.배열을 사용하는 포인터

4.배열과 포인터의 합체

 

 

1.배열과 포인터 표기법

 

포인터는 포인터 변수가 가리키는 메모리의 시작 주소를 기준으로 삼고,

 

배열도 해당 배열이 사용하는 메모리 그룹의 시작 주소를 기준으로 삼는다.

 

따라서 두 문법은 표기만 다를 뿐 문법 구조는 비슷하다.

 

 

배열 표기법의 한계

 

 

위와 같이 선언한 배열에서 data[0] 요소는 총 4바이트로 구성되어 있다.

 

그런데 첫번째 바이트에 있는 값 0x78을 0x22로 변경하고 싶어서 배열 표기법으로 data[0]에 0x22를 대입하면 어떻게 될까?

 

 

배열 표기법으로 data[0] 이라고 적으면 4바이트 크기의 메모리를 의미하기 때문에 0x22를 대입한다고 해서 data[0]의 일부 값만 변경되는 것이 아니다. 

 

아래와 같이 4바이트 값이 모두 변경되어 버려서 data[0]에는 0x00000022값을 대입한것 과 같다.

 

즉, 배열 표기법은 요소를 구성하는 모든 바이트 값을 한 번에 수정한다.

 

 

배열 표기법 대신 포인터 표기법을 사용하면?

 

위의 코드를 포인터 표기 법으로 변경하면 아래와 같다.

 

이렇게 변경하면 다음과 같이 * 연산자와 (data + 1) 사이에 형 변환 문법을 사용할 수 있다.

 

data 배열을 int형으로 선언했기 때문에 포인터로 표기법을 변경하면 int * 형이되는데

 

위치럼 형 변환하면 일시적으로 char * 형으로 변경하겠다는 뜻이다.

 

int* 형이 char* 형으로 변경된다는 것은 포인터가 가리키는 대상의 크기가 4바이트에서 1바이트로 변경된다는 의미이다.

 

따라서 0x22 값을 대입하면 4바이트 메모리 영역이 변경되는 것이 아니라 1바이트 메모리 영역만 변경된다.

 

즉, 아래 그림처럼 data[1] 영역의 첫 1바이트만 0x22 값으로 변경이 되는 것이다.

 

 

 

2.배열 시작 주소

 

배열 변수의 이름은 배열의 시작 주소

 

포인터는 일반 변수의 주소만 가질 수 있는 것이 아니라 배열과 같이 그룹으로 묶인 메모리의 주소도 가질 수 있다.

 

포인터 변수에 배열의 시작 주소를 대입할 때는 일반 변수와 마찬가지로 &연산자를 사용하면 된다.

 

그런데 배열의 경우에는 첫 요소인 data[0]의 시작 주소가 배열 전체의 시작 주소와 같기 때문에 다음과 같이 &연산자를 사용한다.

 

 

위 설명에서 사용한 &data[0]은 포인터 표기법을 사용하면 & * (data + 0)과 같이 표기할 수 있다.

 

그리고 + 0 은 생략할 수 있기 때문에 & * data라고 적어도 된다.

 

 

& * data의 의미는?

 

& 연산자와 * 연산자는 우선순위가 같다. 그래서 뒤쪽에서 앞쪽 방향으로 연산이 수행된다.

 

&(*data) 와 같다.

 

data가 가리키는 대상 (*data)의 주소를 얻겠다(&) 는 뜻이다.

 

data가 가리키는 대상의 주소라는 의미는 결국 data 변수가 저장된 메모리의 주소와 같다.

 

따라서 &(*data) 는 data 라고도 적을 수 있다.

 

 

 

이런 이유 때문에 '배열 변수의 이름은 배열의 시작 주소' 라고 말 할 수 있다.

 

 

 

배열은 포인터가 아니다.

 

배열이 포인터처럼 작동한다고 해서 완벽하게 포인터가 될 수는 없다.

 

문법적으로 포인터는 다른 변수의 주소를 저장할 수 있지만, 배열은 컴파일러가 제공하는 메모리 그룹화 기술이다.

 

즉, 배열 이름은 변수처럼 보이지만, 내부를 들여다 보면 실제로 상수화된 주소이기 때문에 변경할 수 없다.

 

즉, 배열은 일반 변수들을 묶어 놓은 개념이기 때문에 변수가 자신이 위치한 주소를 변경할 수 없듯이, 배열도 자신이 위치한 메모리 주소를 변경할 수 없다.

 

따라서 배열의 시작 주소도 변경할 수 없다.

 

 

 

 

3.배열을 사용하는 포인터

 

배열 예제를 포인터 사용해서 바꾸기

 

배열의 각 요소에 저장된 값을 합산하는 예제를 포인터를 사용해서 만들기.

 

포인터 변수 p가 data 배열의 시작 주소를 가지고 있기 때문에 *p를 사용하면 data 배열의 첫 번째 요소만 가리킨다.

반복문에서 p++를 추가하여 data 배열의 다음 요소로 이동할 수 있게 구성했다.

 

 

 

4.배열과 포인터의 합체

 

지금 부터는 배열문법과 포인터 문법을 결합해서 사용하는 법을 배운다.

서로 다른 두 문법이 만나면 어떤 문법이 기준이 되는지에 따라 의미가 달라질 수 있다.

 

배열을 기준으로 포인터와 합체하기

 

 

char*형 포인터가 5개 필요하다면 아래와 같이 선언하면 된다.

 

 

 

위와 같이 선언하면 포인터가 5개 선언된 것이기 때문에 p배열의 크기는 20바이트이다.

 

개별 포인터를 사용하고 싶다면 p[0] , p[1].. 이렇게 사용하면 되고, 

 

각 포인터가 가리키는 대상에 값을 읽거나 쓰고 싶다면 앞에 *연산자를 추가하여 *p[0] , *p[1] 라고 사용하면 된다.

 

 

 

 

 

포인터도 변수라서 배열을 이용하여 그룹으로 묶을 수 있다.

 

 

 

포인터를 배열 형식으로 선언할 때 char *p[5]; 라고 선언한 것은 char*(p[5]); 라고 선언한 것과 같다.

 

그 이유는 [ ] 연산자가 *연산자보다 연산자 우선순위가 높기 때문이다.

 

따라서 p[5]가 우선이기 때문에 p변수는 5개의 항목을 가진 배열이라는 뜻이 되고,

 

그 다음 조건에 의해 각 항목이 char*형 포인터라고 정해진다.

 

결과적으로 배열을 기준으로 결합되는 것이다.

 

 

 

 

포인터를 기준으로 배열과 합체하기 

 

 

 

이번에는 포인터 문법에 괄호를 사용했기 때문에 포인터가 기준이 된다. 

 

이렇게 선언하면 괄호 속에 있는 *p가 먼저 처리되기 때문에 p변수는 배열이 아니라 포인터라는 뜻이다.

 

따라서 p 변수의 크기는 4바이트다.

 

그리고 그 다음 조건인 char[5]에 의해서 포인터 변수 p가 가리키는 대상의 크기가 5바이트라는 뜻이 된다.

 

그리고 포인터 변수 p는 다음과 같이 주소 연산을 하면 p에 저장된 주소가 5씩 증가하게 된다.

 

p가 가리키는 대상의 크기가 char[5], 즉 5바이트이기 때문이다. 

 

예를 들어 p에 100번지가 저장되어 있었다면 주소 연산 후에 105번지가 된다.

 

 

 

그럼 char(*p)[5] ; 로 선언한 포인터는 어떤 상황에 사용할까?

 

1차원 포인터 (*) 와 1차원 배열 ([]) 이 결합된 형태이기 때문에 두 차원이 합쳐져 2차원의 개념을 가지게 된다.

 

따라서 이 포인터는 2차원 배열을 가리키는 용도로 사용하는 것이 적합하다.

 

 

아래 예는 2차원 배열로 선언한 char data[3][5]; 의 시작 주소를 char(*p)[5]; 포인터에 저장해서 포인터 p를 사용하여 data 배열의 각 요소 값을 변경하는 코드이다.

 

반응형