일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Tips강좌
- c++
- Programming
- doit코틀린프로그래밍
- 백준
- Visual Studio
- 포인터
- 이지스퍼블리싱
- Tips프로그래밍강좌
- 알고리즘
- VS ERROR
- 함수
- 지식나눔강좌
- 김성엽
- Javascript
- c
- c#
- Desktop
- 문법
- 리뷰
- CS
- 티스토리
- tipssoft
- Kotlin
- 연산자
- 프로그래밍
- 배열
- Windows
- Win32
- Direct2D
- Yesterday
- Today
- Total
F.R.I.D.A.Y.
포인터와 배열 본문
위 글에서는 배열의 첫 요소의 주소는 해당 배열의 이름임을 간단히 설명하고 있습니다. 어째서 이런 결론이 나오게 되는지, 어떤 배경이 있었는지 알아보겠습니다.
# 여기에서 (정적)배열은 소스코드에 직접 배열 문법으로 선언된 자료형을 말합니다.
태초에 포인터가 있었다
신께서 세계를 만들 때 법칙을 몇 가지 만들었으니, 그중 하나가 指 법칙이라.
이 법칙은 널리 사용되어야 했으며 충분히 많은 곳에서 이로울 법칙 이리다.
또한, 법칙은 이해하기 어려워 소수만이 법칙의 진리를 깨닫고 제 힘으로 사용할 수 있음이다.
이 모습을 가여워 한 신께서 대리자를 내려 보내나니, 세상이 이로워지더라.
우스갯소리로 지어본 말입니다. 그래도 포인터와 배열의 상관관계를 설명하고자 했던 취지는 잘 반영되어있다 생각합니다.
말 그대로, 포인터는 어린 개발자들에게는 어려운 문법입니다. 그래서 데니스 리치는 생각했습니다.
"조금 더 쉽게 포인터의 기능을 사용하게 할 수는 없을까?"
고심 끝에 나온 문법이 바로 배열입니다.
배열은 포인터처럼 유용하게 사용할 수 있습니다. 그러나 포인터 문법을 보다 쉽게 사용하기 위해 나온 문법이다 보니 기능의 제약이 들어가고 그만큼 자유도가 떨어지는 문법입니다.
배열의 사용
배열이 포인터의 기능을 약화시켜 나온 문법이니만큼, 포인터의 일부 표현을 사용할 수 있게 됩니다. 포인터가 가리키는 대상의 값을 가져오려고 하면 우리는 아래와 같이 코드를 작성해야 한다고 했습니다.
Problem. M(2,4)[# M=메모리\n(x, y)]의 위치에 존재하는 값 가져오기
// pointer style
*(*(p + 4) + 2);
모르는 사람이 보면 어떤 걸 의미하는지 잘 모를 것입니다. 그러나 배열의 표현 방식을 보면 얘기가 달라집니다.
// array style
arr[4][2];
프로그래밍을 모르는 사람이 보더라도 배열 스타일은 어느 정도 유추해 이해할 수 있을 것입니다.
이 과정에서 가장 중요한 것은 연산자의 문제였습니다. 연산자 자체가 어렵고, 우선순위에서도 뒤쪽으로 밀려있기에 연산 과정을 알기가 어려웠습니다. 그래서 대괄호 연산자를 개발하게 되었습니다.
대괄호 연산자는 아래와 같이 사용할 수 있습니다. 이미 알고 계시겠지만요.
arr[4][2];
대괄호 연산자 앞에 변수의 이름을 작성하고 대괄호 안에는 접근하고자 하는 인덱스를 작성해줍니다. 그러면 위 포인터 스타일과 같이 동작하도록 합니다.
1차원 배열을 기준으로 하면 아래와 같겠죠.
Problem. 1차원 배열의 세 번째 요소에 접근하기.
// pointer style
*(arr + 2);
// array style
arr[2];
언어 개발자들은 사람들이 기존에 통용해 사용하던 문법을 지키려고 합니다. 그래야 자신이 개발한 언어가 부흥하고 생태계에서 살아남을 수 있을 테니까요.
포인터 스타일과 배열 스타일 번갈아 쓰기
배열 자체가 포인터에서 나온 문법이다 보니 포인터 스타일을 배열에 적용할 수 있습니다. 또한, 배열 스타일은 편리하기에 포인터에서도 배열 스타일을 이용할 수 있습니다.
그렇다면 본론으로 돌아와서, 배열의 첫 번째 요소 주소가 어째서 배열 변수의 이름인지를 알아보겠습니다.
주소 연산자, 간접 참조 연산자
일반 변수의 주소가 궁금할 때는 변수 이름 앞에 주소 연산자 &를 붙여 알아봅니다. 또한, 특정 주소를 가진 메모리의 값을 보고 싶다면 간접 참조 연산자 *를 붙여 알아봅니다.
그렇다면 생각해보겠습니다. 위와 같이 변수 이름 var[# 편의를 위해 int타입으로 생각하겠습니다.]로 명명된 공간이 존재합니다. 이 공간의 주소는 0x100입니다. 이 공간의 값을 가져오려면 어떻게 해야 할까요?
printf("%d", var);
이렇게 생각할 수 있습니다. 그러나 위에서 말한 것처럼 주소를 알고 있다면 간접 참조 연산자를 이용해도 됩니다.
printf("%d", *0x100);
우리는 이 둘을 섞어볼 수도 있습니다.
printf("%d", *(&var));
var에 주소 연산자를 붙여 var로 명명된 공간의 주소를 가져왔고, 해당 주소에 간접 참조 연산자를 사용해 값을 가져왔습니다.
그리고 0을 더해봅시다. 0은 더해도 빼도, 원래 값이 변하지 않지만요.
printf("%d", *(&var + 0));
어디서 많이 본모습 아닌가요? 바로 위에서 설명한 포인터 스타일입니다.
// pointer style
*(arr + 2);
var 앞에 붙는 주소 연산자는 var가 일반 변수이기 때문입니다.
각 변수는 대응되는 하나의 공간을 가리켜야 합니다. 그러나 배열의 경우 하나의 이름으로 여러 공간에 접근할 수 있습니다. 어느 요소를 선택 해야하는지 모호할 수 있습니다. arr 자체는 요소의 시작을 가리키고 뒤에 대괄호 연산자를 통해 offset을 지정해주는 것입니다.
무엇보다 배열 변수의 이름이 첫 요소의 주소를 반환하는 것은 언어가 그렇게 지정했기 때문입니다. 앞서 설명한 내용들은 조금이라도 더 쉽게 이해할 수 있기를 바라며 작성했습니다.
이해하지 못해도 괜찮아요
C에서 배열 변수의 경우에는 그 변수의 이름이 첫 요소의 주소를 반환한다는 것만 기억하기 바랍니다.
더 알아보기
# index
'DEV > C C++' 카테고리의 다른 글
포인터 배열? 배열 포인터? (0) | 2020.01.26 |
---|---|
다차원 배열의 요소 값 교환하기 (0) | 2020.01.25 |
Designated Initializer (0) | 2020.01.21 |
배열(array) part2. 다차원 배열 (0) | 2020.01.20 |
배열(array) part1. default (1) | 2020.01.19 |