포인터는 주소값이다. 이제 여기까지 왔다면 어느정도 이해될 것이다.
그럼 뜬금없이 배열이란 친구는 뭔데 여기붙어있는가??
그걸 말하려면 일단 메모리의 저장방식부터 알아보아야 한다.
뭔 소리야?? 하겠지만 어쩔 수 없다 일에는 순서가 있는법이다.
먼저 컴파일러를 켜고
int iTemp = 10;
int iSrc = 20;
int iDeft = 30;
cout << &iTemp << endl;
cout << &iSrc << endl;
cout << &iDeft << endl;
이라고 세가지 int를 순서대로 선언한 뒤, 각각의 주소값을 순서대로 출력해보자.
이 코드를 복붙해도 좋다.
결과값에 어떠한 규칙이 보이는가??
라고 생각한다면 다시한번 봐라. 비록 16진수라 계산이 까다롭지만 어쩔 수 없다. 계산이 영 힘들다면 맨 뒤 자리가 세개 모두 숫자가 나올때까지 다시 출력해봐라. 그러고 계산해봐라.
규칙이 있을리 없는데!!! 나도 그렇게 생각했는데 아니더라 있다. 무조건 있다.
나는 32비트 컴퓨터인지라 각각 20,14,08이 출력되었다. 물론 앞에 6글자는 모두 동일했다.
계산하면 각각 -6의 간격을 두고 선언되어있다.
의외지 않나?? 나는 이걸보고 굉장히 놀랐다.
당연히 선언할때 무작위적 공간에 저장되는줄 알았는데 나름의 규칙이 있더라!!
맞다 하지만 int는 4바이트이다. 4바이트를 넣고도 2바이트의 여유공간을 두었다. 이유는 모른다 묻지마라
이해 못했다면 다시 읽지말고 마저 읽어라 사실 이건 없어도 되는 이야기다.
내가 보여주고싶은것은 이것에 규칙이 있다!! 가 아니다. 단지 좀 신기할 뿐 그것에는 특별한 의미가 없다.
무엇보다 그렇게 접근할 일은 없을것이다.
하지만 배열은 다르다. 배열이란 같은 데이터타입의 객체를 두개 이상 한번에 선언하여 하나의 배열명으로 묶어부르겠다는 것이다.
학교에 한 반에 여러명의 학생이 있고 그 모두를 몇반 으로 묶어 부르는것처럼
선언 방식은 다음과 같다.
자료형 배열명[배열의 크기] = {배열의 요소,요소,요소들~~~}
자료형에 대한 설명은 이제 그만하겠다. 시간만 잡아먹고 무엇보다 내가 귀찮탈까......
배열명은 그냥 변수를 선언할때 변수명과 같다. 그 뒤에 대괄호를 열어 배열의 크기에 해당하는 상수를 집어넣는다.
그러고 나서 대입연산자 = 를 적고 중괄호를 열어 각각의 요소를 쉼표로 구분하여 집어넣게 된다.
여기서 주의할 점은 중괄호 안의 요소의 갯수가 대괄호 안에 적은 요소의 갯수보다 클경우 오류가 발생한다.
직접 보여주겠다.
int iArr[5] ={0,1,2,3,4,5};
이걸 컴파일러로 복붙해 보아라. 아마 5의 위치에 빨간 밑줄이 그이며 컴파일러가 잠시 면담을 요청한다.
내 컴퓨터는 "이니셜라이저 값이 너무 많습니다." 라고 하고있다. 단순히 5개짜리 배열을 만들어놓고 그 안에 6개를 넣었으니 "어 이거 안들어가겠는데요??" 라고 한것이다. 그럼 마지막 요소 5를 지우거나 대괄호 안의 숫자를 6으로 바꾸면 된다.
그렇다면 반대로 요소의 수가 더 적을때는 어떻게될까??
int iArr[5] ={0,1};
이번엔 5개의 배열을 만들고 0과 1 두개만 넣었다. 크게 문제되지 않는다. 박스안에 공간을 넉넉하게 넣는건 문제가 안된다. 이럴 경우 빈공간 세개가 남는데 이는 0값으로 초기화된다(굉장히 중요하고 굉장히 좋은 기능이다.) 자세한건 좀이따가 설명한다.
그렇다면 요소를 넣을 개수를 모른다면 어떻게 될까??
int iArr[] ={0,1,2,3,4,5};
이렇게 대괄호 안에 아무것도 넣지 않는다면 컴파일러가 알아서 맞춤 포장하여 6개 크기의 배열을 만들것이다.
좋다 이제 배열을 선언했으니 이제 배열의 값에 접근하는 법을 알아보자.
int iArr[5] ={0,10,20,30,40};
이 코드를 기준으로 설명하겠다. 이코드는 배열이 끝날때까지 지우지 말고 가자.
iArr[2]하면 배열의 값에 접근할 수 있다.
혹시 배열내 데이터를 바꾸고 싶다면 iArr[2]=200이라고 평소 변수에 값을 넣는것처럼 하면 된다.
다만 주의해야할 점이 있다.
배열의 요소는 각각 번호표를 받게되는데 첫 요소가 0의 값부터 시작한다. 컴퓨터는 0부터 숫자를 세기 때문이다. 그래서 내가 요소를 적을때 0부터 시작한것이다. 헷깔리지말라고.
그러니 배열을 선언할때 int iArr[5] 이라고 선언했다고 값에 접근할때 iArr[5]하면 원하는 값을 얻지 못할것이다.
아 그래서 이게 포인터랑 뭔상관인데
배열을 선언하면 배열의 요소들은 모두 밀착해서 여유공간 없이 정렬된다.
예를들어 iArr[0]이 10번지라면 iArr[1]은 14번지라는 소리다.
왜 11이 아니고 14지?? 라고 생각한다면 int는 4바이트이니 0이 가진 숫자가 끝난 그 다음 주소를 받아야하기 때문이다.
혹시 이전 포인터연산에서 4평짜리 우리집을 소개했던것이 떠올랐다면 좋다. 잘따라오고 있다.
그렇다면 배열의 포인터를 알아봐야 한다. 사실 배열의 포인터는 이미 나왔다.
iArr가 배열명이자, 배열의 첫 주소 즉 포인터이다.
무슨말인지 모르겠다면 cout << iArr <<endl;을 입력하여 배열을 출력해보자.
어디서 많이 보았던 형식이다. 맞다 포인터다.
그렇다면 포인터를 알았으니 그 값을 읽어내려면?? 앞에 *을 붙여 그 값을 추출해보자.
정상적이라면 0이 나왔을 것이다.
그 다음값을 알고싶다면 어떻게 해야할까?? 간단하다 +1 해주면 된다.
그렇다고 *iArr+1를 출력했다면 두번째 값인 11이 아닌 그냥 1이 나왔을것이다. 이는 연산 우선순위의 문제이다. 이렇게 될 경우 iArr첫번째 주소의 값 + 1 해서 0+1인 1이 출력된 것이다.
두번째 값에 접근하기 위해서는
*(iArr+1)로 써서 다음 주소로 이동한 뒤 그 값에 접근해 줘야한다.
이제 배열과 포인터의 관계를 이해할 수 있을것이다.
누군가는 그냥 iArr[1]로 접근하면 되잖아 라고 하는데. 맞다 현명하다.
근데 그 대괄호 연산자 자체가 이 포인터의 n칸 다음의 값을 추출하세요 라는 연산을 진행하는 도구이기 때문에 원리를 알고 쓰는것이 나을것이다.
배열의 크기를 어떻게 알 수 있을까?? 즉 iArr가 5개짜리 배열이라는것을 계산할 수 있을까??
우리는 sizeof연산자가 있다.
호기롭게
sizeof(iArr)
를 출력해보자.
뜬금없이 20이라는 숫자가 나올것이다.
눈치챘길 바란다. sizeof연산자는 그 내용물의 바이트 수를 반환하게 되어있다. iArr는 4바이트 int 타입 5개가 여백없이 있기 때문에 4*5 해서 20이라는 숫자가 나온것이다.
그렇다면 우리는 이런 연산또한 가능해진다.
sizeof(iArr) / sizeof(int)
iArr의 총 바이트 수 나누기 int타입의 바이트 수 = 5가 되어 우리는 iArr의 배열갯수를 얻어낼 수 있게 되었다.
sizeof(배열명) / sizeof( 배열 요소의 자료형 ) = 배열의 갯수
따로 정리한 이유는 매우 중요하고 자주 쓰이기 때문이다. 외워라.
아까 배열의 값을 호출할때 iArr[5]를 하면 안된다고 했다. 배열 내 요소는 0부터 순서대로 번호를 부여받기 때문이라고까지 설명했다. 하지만 그럼에도 불구하고 혹시 출력해본 사람 있다면 적잖게 당황했을것이다.
아마 이상한 값이 불려나왔을것이다.
설명하겠다.
배열이 5개짜리라고 6번째 칸을 호출하는것에는 아무런 문제가 없다. 포인터로 접근하였기 때문이다. 포인터의 접근에는 그 누구도 태클을 걸지 않는다. 주소를 아는데 그 집에 찾아가서 집주인의 얼굴을 보는것은 이상한것이 아니다. 그게 이상한거면 택배는 어떻게 오며 치킨배달은 어떻게 하는가??
그러니 6번째 칸에 가서 그 값을 읽어오는것은 문제가 되지 않는다. 다만 그 값은 우리가 아는 누군가가 아닐 가능성이 크다. 그걸보고 보통 "쓰레기 값" 이라고 부른다.
원리는 이전에 사용되던 데이터가 지워지지 않은 상태로 남아있었고, 그것을 우연히 읽어들이게 된 것 뿐이다.
그래서 아까 말했던 배열의 크기보다 적어준 요소의 값의 수가 적다면 남은 공간을 쓰레기값이 아닌 0으로 초기화하는 것이다.
미래의 입주자를 위해 집청소를 하는것과 같다.
만약
iArr[5]=100;
같이 6번째 칸의 데이터에 내가 원하는 값을 넣는것은 가능할까?
끔찍한 일이다. 만일 배달기사가 우리집의 주소를 알고 있으니 찾아와서 나의 얼굴을 보는것으로도 모자라 나를 내쫓고 자신이 들어앉아 살겠다고 하는것과 같다. 이건 얼굴을 보는것과는 달리 심각한 범죄행위이다.
그래서 그런 할당되지 않은 공간의 데이터를 조작하는 범죄행위는 컴파일러가 철저히 감시하고 막아내고 있다.
이상 포인터와 배열을 마친다.
이 뒤는 이중배열과 포인터배열 등등이 있다. (뭔 소리지 싶을꺼다. 나도 무섭다.)
그리고 넘어가기 전에 혹시 이전에 언급했던
cin >> 변수;
int iArr[변수]=...
처럼 cin을 이용하여 내가 입력하는 값만큼의 크기의 배열을 선언하고 싶다면 아주 좋은생각이다. 하지만 안된다.
첫째로 위에서 말했듯 배열의 크기는 꼭 상수여야된다. 그래서
int iTest=5;
int iArr[iTest]
같은 행위도 iTest는 변수이지 상수가 아니기 때문에 불가능하다.
나도 그걸 몇번 시도해 보았으나 어떤 방법으로도 안된다. 그런 방법은 추후에 "동적 할당" 이라고 따로 포스팅을 할것이다.
혹시 이 블로그에 검색해 보았으나 그런 제목 밑 내용이 없는것 같다. 라고 한다면 미안하다 너무 일찍오셨다. 와주셔서 감사하고 다른곳에서 원하시는 정보를 찾으시길 바란다.
'c언어' 카테고리의 다른 글
이중 포인터 (0) | 2023.02.05 |
---|---|
이중배열과 포인터배열 (1) | 2023.02.05 |
포인터2 그리고 포인터 연산 (0) | 2023.02.03 |
형 변환(캐스팅) (0) | 2023.02.03 |
포.인.터 기초 (0) | 2023.02.03 |