728x90
반응형

포인터와 배열

l 배열 이름과 포인터의 관계

다음은 배열의 이름이 무엇인지를 나타내는 예제이다.

위의 소스에서 확인할 수 있는 부분은 배열의 이름 그 자체를 출력했을 때의 값과 배열의 [0]번째 요소의 주소 값이 같다는 점이다. , 배열의 이름은 해당 배열의 시작 주소 값을 의미한다.

결국 배열의 이름 또한 주소 값을 지니고 있다는 관점에서 포인터와 그 개념이 매우 유사하다. 그렇다면 포인터 변수처럼 배열의 이름 또한 주소를 대입해볼 수 없을까? 배열의 이름에 주소 값을 대입하는 예제이다.

8번 라인에 새로운 변수 number를 선언하고, 10번 라인에서 배열의 이름에 number의 주소 값을 넣었다. 왜 배열의 주소가 아니라 일반 변수의 주소를 넣었느냐? 왜냐하면 배열의 이름은 배열의 시작 주소하나를 저장하기 때문에 배열 이름의 입장에서 보면 어떤 하나의 변수의 주소 값을 대입한 것과 다를 바 없이 보이기 때문이다. 그렇기 때문에 배열의 이름에 하나의 변수 number의 주소 값이 대입되는 것은 문제가 되지 않는다. 이렇게 배열의 이름에 변수의 주소를 넣은 결과, 10번 라인에서 배열 타입의 표현 식에 할당하는 부분에서 에러가 발생한다. 그 이유는 배열의 이름은 주소 값을 저장하고 가리키지만 처음 정해진 배열 이름의 값은 변경이 불가능하다. , 배열의 이름은 상수 값이다. 그도 그럴 것이 특별하게 배열을 선언하여 만들었는데 그 배열의 이름을 대표하는 배열의 이름이 가리키는 요소를 마음대로 바꾸게 되면 무엇을 위해 배열 이름을 지정해가며 배열을 생성했겠는가?

다음은 위에서 배열과 포인터의 관계에 대해 내린 결론을 정리하여 나타낸 표이다.

배열 이름은 포인터 변수와 같은 특성들을 지니고 있지만 상수라는 차이점이 있다. 그래서 배열 이름을 포인터 상수라고도 표현한다. 포인터 변수와 배열 이름은 변수이냐, 상수이냐 의 차이점 밖에 없기 때문에 그 차이를 제외하면 포인터 변수와 배열 이름은 완전히 같다. 그렇기 때문에 다음의 예제가 가능하다.

10번 라인에서 배열 이름(포인터 상수)의 값을 포인터 변수에 대입시키고 있다. 이것이 가능한 이유는

1.     포인터 변수에 포인터 상수를 대입 -> 전혀 문제될 것이 없다.

2.     대입 연산자를 기준으로 l-valuer-value의 형(type)이 같아야 한다.
-> l-value : int * / r-value : int * == int
(시작) 주소 로 문제될 것이 없다.

배열 이름의 포인터 형은 배열의 이름이 가리키는 대상을 기준으로 결정되기 때문에 r-value 배열 이름의 자료형은 정수형 변수 공간의 시작 주소이므로 int * 가 맞다.

포인터 변수에 포인터 상수를 대입하게 되면 포인터 변수를 이용하여 배열 이름처럼 인덱스로써 접근하는 방법으로 각 요소에 접근을 할 수 있다. , 포인터 변수를 배열의 이름처럼 사용할 수 있다는 것이 결론.

그렇다면 그 반대로 배열 이름(포인터 상수)을 가지고 포인터 연산을 할 수 있을까? 다음은 이에 관한 예제이다.

11번 라인에서 배열 이름과 ‘ * ‘, 애스터리스크 연산자를 이용한 포인터 연산이 진행되고, 12번 라인에서는 배열 이름과 인덱스를 이용한 배열의 0번째 요소에 접근하여 출력을 한다. 11번 라인에서는 배열 이름이 가지고 있는 값 즉, 배열의 시작 주소에 접근하여 해당 값을 출력하기 때문에 0번째 요소를 출력한다.

  • 포인터 연산

포인터를 이용하여 참조 뿐만 아니라 증감 연산이 가능하다. 다만 일반 수를 이용한 연산에 비해서 연산이 많이 제한 된다.  -> 가능한 연산자 수의 제한

포인터 연산에서 중요한 포인트

1.     덧셈, 뺄셈, 참조 연산만 가능하다.
->
곱셈, 나눗셈 등의 연산을 불가능하다.

2.     덧셈, 뺄셈 연산은 일반 수를 통한 덧셈, 뺄셈 연산과는 다르다.
->
포인터 연산에서의 +1과 수에 대한 +1에서 1은 각각 의미가 다르다.

다음은 포인터에 + 연산자를 이용하여 1씩 더하는 예제이다.

int, char, double 자료형 별로 배열을 선언하고, 각각에 대한 포인터 변수를 선언하여 각 배열의 첫 시작 주소(배열 이름)를 대입시킨 상황에서 각각 포인터 연산을 진행하는 예제이다.

먼저, 자료형에 상관없이 배열 이름과 인덱스를 이용하여 요소별 주소 값을 출력한 결과와 포인터 연산에 대한 결과를 출력한 결과가 모두 동일함을 알 수 있다. 그렇다면 도대체 + 연산자를 이용하여 포인터 변수를 포인터 연산한 것이 어떤 의미가 있길래 각 요소의 주소를 출력한 결과와 같은 결과인가?

 

1.     A의 결과에서 pArrpArr+1 값의 차이는 4이다.

2.     B의 결과에서 pCrrpCrr+1 값의 차이는 1이다.

3.     C의 결과에서 pDrrpDrr+1 값의 차이는 8이다.

 

단순히 포인터 변수에 +1 연산을 했을 뿐인데 그 증가의 차이는 자료형 별로 다른 결과가 나타났다. 포인터 연산은 피연산자로 오는 그 대상이 메모리 주소인 만큼 메모리 주소에 대한 연산이 이루어지는데 이 메모리는 무작정 작은 단위인 1바이트 별로 보는 것은 의미가 없다. int 형의 경우에는 4바이트 전부 참조해야 비로소 값이 의미가 있듯이 자료형 별로 최소 참조 바이트가 존재한다. 그렇기 때문에 주소에 대한 연산인 포인터 연산은 자료형 별 최소 참조 바이트를 반영하여 증감을 진행한다. 각 자료형 별로 포인터 연산에 대해서 1의 의미가 모두 다른 이유가 바로 이 때문이다. 1의 증감은 최소 참조 바이트 한 블록을 증감한다는 의미가 되기 때문이다.

포인터 연산에서 1의 의미는 sizeof(해당 자료형)*1 이 된다.
포인터 연산에서 2의 의미는 sizeof(해당 자료형)*2 이 된다.

포인터 연산에서 n의 의미는 sizeof(해당 자료형)*n 이 된다.

그렇다면 다음의 예는 무엇을 의미하는가?

*(p + 1)
p는 임의의 변수를 가리키고 있는 포인터 변수

이는 다음과 같이 해석할 수 있다.

P가 가리키고 있는 주소에 sizeof(해당 자료형) * 1만큼을 더한 주소를 참조(*)한다.

 다음은 그 예제이다.

위와 같은 포인터 연산에서의 성질과 예제를 통해서 알 수 있는 결론은 다음과 같다.

배열 이름[i] == *(배열 이름 + i)
혹은
포인터 변수[i] == *(포인터 변수 + i)

완전히 같다.

  • 문자열과 포인터

c언어에서 문자열을 표현할 때 두 가지 방법으로 표현한다.

1.     배열을 통한 문자열의 저장

2.     포인터 변수를 통한 문자열의 저장

먼저, 첫 번째 방법인 배열을 통한 문자열의 저장이다. 이 경우에는 str 배열의 길이가 자동 계산되어 각 인덱스 별로 문자를 하나씩 저장하여 해당 문자열의 끝에 ‘\0’을 저장함으로써 문자열의 저장이 비로소 완료되는 형태이다.

그러나 두 번째 방법인 포인터 변수를 통한 문자열의 저장의 경우이다.  c언어 프로그램 상의 모든 상수는 CPU를 이용한 연산의 대상이 되기 위해서는 해당 상수가 메모리 상에 적재되어야 한다. “hello, my name is yoo seung ho!” 문자열은 상수로서 메모리에 적재되고, 그 시작 주소인 ‘h’의 주소 값이 반환되어 pStr 포인터 변수에 대입이 된다.

대입 연산자를 기준으로 l-valuechar * 형이고, r-value는 문자열의 시작 주소 값 즉, char * 이기 때문에 대입 연산에는 아무런 문제가 없다.

각 두 경우는 위 그림의 왼쪽과 오른쪽 형태로 동작한다. 배열의 이름의 경우 포인터 상수이기 때문에 가리키는 타겟 문자열을 변경할 수는 없지만, 변수 형태, 배열에 값이 저장되어 있기 때문에 대상이 되는 문자열의 내용에 대해서는 변경이 가능하다. 포인터의 경우 포인터 변수이기 때문에 가리키는 타겟 문자열을 변경할 수 있다. 그러나 그 대상은 상수 형태의 문자열이기 때문에 대상이 되는 문자열의 내용에 대해서는 변경이 불가능하다.

사실은 아래와 같이 되는 것이다.

  • 포인터 배열

말 그대로 포인터 변수들로 배열을 이룬 형태를 말한다.

 

 

728x90
반응형

'컴퓨터 언어 정리 > C 언어' 카테고리의 다른 글

11 다차원 배열  (0) 2020.09.14
10 포인터와 함수  (0) 2020.09.13
08 포인터 기본  (0) 2020.09.11
07 1차원 배열  (0) 2020.09.11
06 함수와 변수의 생명주기  (0) 2020.09.10

+ Recent posts