배열을 사용하는 이유는 여러가지가 있다. 우선 변수선언에 관한 문제이다. 예를 들어, 배열이 없다고 가정하자. 그리고 나라 카테고리의 변수를 선언한다고 생각해보자. 몇 백개의 변수를 선언해야 한다. 그걸 외울 수 있을까? 사람이라서 불가능하다. 이럴때 사용하는 것이 배열이다.
#include <iostream>
using namespace std;
int main()
{
int one_countries;
int countries[5];
cout << sizeof(one_countries) << endl;
cout << sizeof(countries) << endl;
return 0;
}
output : 4
20
하나의 inteager 변수와 inteager 배열 5개를 선언한 것과 메모리의 크기 비교 프로그램이다. 배열이라는 것이 어떤 느낌인지 알아가는게 중요!!!
#include <iostream>
using namespace std;
int main()
{
int one_countries;
int countries[5];
one_countries = 100;
countries[0] = 10;
countries[1] = 20;
countries[2] = 30;
countries[3] = 40;
countries[4] = 50;
return 0;
}
선언한 배열에 접근하는 방법은 다음과 같다. 원래 1st element라 하면 1번부터 시작해야 수학적으로 맞지만, 거의 모든 프로그램 언어는 0번 인덱스부터 시작한다. 이건 익숙해져야 하므로 여러번 반복하여 익숙해지도록 하자!.
#include <iostream>
using namespace std;
int main()
{
int one_countries;
int countries[5];
one_countries = 100;
countries[0] = 10;
countries[1] = 20;
countries[2] = 30;
countries[3] = 40;
countries[4] = 50;
countries[5] = 50;
cout << countries[5] << endl;
return 0;
}
output : 50
이때 가장 무시무시한 일이 벌어진다. Runtime error가 발생하는데, 실제 console창에서는 또 잘 출력된다. "그럼 뭐가 문제인 것이냐?" 라고 생각할 수 있는데, 이는 os가 화는 나지만 user을 '겁나게' 배려하여 출력해주는 것이다. 하지만, 프로그램이 매우 큰 상황에서 이런 오류가 나면 os가 날라가거나 프로그램이 꺼지는 현상이 발생하니 최대한 기피하도록 하자.
#include <iostream>
using namespace std;
struct Rectangle
{
int length;
int width;
};
int main()
{
cout << sizeof(Rectangle) << endl;
Rectangle rect_arr[10];
cout << sizeof(rect_arr) << endl;
return 0;
}
output : 8
80
구조체를 사용하여 메모리 크기를 확인하는 프로그램이다. 다들 예상했던 대로 나오는 것을 볼 수 있다. Rectangle 하나가 8byte므로 총 10byte가 출력되는 것을 볼 수 있다.
#include <iostream>
using namespace std;
int main()
{
int my_array[5] = { 1, 2, 3, 4, 5 };
//int my_array[] = { 1, 2, 3, 4, 5 };
//int my_array[]{ 1, 2, 3, 4, 5 };
cout << my_array[0] << endl;
cout << my_array[1] << endl;
cout << my_array[2] << endl;
cout << my_array[3] << endl;
cout << my_array[4] << endl;
return 0;
}
output : 1
2
3
4
5
각 배열을 초기화하는 것은 다음과 같다. int my_array[]{ 1, 2, 3, 4, 5 };
과 같이 uniform initialization을 사용하는 것과 비슷한데 이도 허용된다. 한번씩 해보자!
#include <iostream>
using namespace std;
enum StudentName
{
JACKJACK,
DASH,
VIOLET,
};
int main()
{
int Students_scores[VIOLET];
Students_scores[JACKJACK] = 0;
return 0;
}
이것이 왜 되는지 모른다면 enum
에 대한 설명을 다시 보고 와야할 것이다. 이 enum
같은 경우에는 안에 선언되었던 변수들이 전역변수 역할을 하여 scope가 끝나도 유지되는 상황을 볼 수 있다. 이를 방지하기 위해 enum class StudentName
을 사용하는 것을 저번에 설명했었다.
#include <iostream>
using namespace std;
int main()
{
const int num_students = 20;
int students_scores[num_students];
cout << (int)students_scores << endl;
cout << (int)&students_scores << endl;
cout << (int)&students_scores[0] << endl;
cout << (int)&students_scores[1] << endl;
cout << (int)&students_scores[2] << endl;
cout << (int)&students_scores[3] << endl;
return 0;
}
output : 12253148
12253148
12253148
12253152
12253156
12253160
주소가 출력되는데, 각 크기가 4byte씩 차이가 나는 것을 볼 수 있다. 여기서 눈여겨 봐야할 것은 students_scores
과 students_scores[0]
의 주소가 같다는 것을 봐야한다.
또 하나 매우 중요한 것이 있다. 배열을 그 이름 자체가 주소이기 때문에, &
연산자를 사용하지 않아도 주소가 출력되는 것을 볼 수 있다. 이러한 이유는 배열 자체가 매우 크기 때문에, 배열안에 있는 모든 원소를 복사하는 것보다 주소 하나만 옮기며 사용하는 것이 효율적이기 때문에 이처럼 사용한다.
또 아직 포인터의 개념에 대해 배우진 않았지만, 어려운 개념에 대해 잠시나마 짚고 넘어갈려 한다.
#include <iostream>
using namespace std;
void doSomething(int students_scores[20])
{
cout << (int)&students_scores << endl;
/*cout << students_scores[0] << endl;
cout << students_scores[1] << endl;
cout << students_scores[2] << endl;*/
}
int main()
{
const int num_students = 20;
int students_scores[num_students];
cout << (int)&students_scores << endl;
/*cout << (int)&students_scores[0] << endl;
cout << (int)&students_scores[1] << endl;
cout << (int)&students_scores[2] << endl;
cout << (int)&students_scores[3] << endl;*/
doSomething(students_scores);
return 0;
}
output : 10484864
10484652
output은 항상 달라지므로 나와 다르다고 해서 문제될 것이 없다. 하지만 여기서 왜 주소가 다른지에 대해 궁금해야 한다. main()
의 students_scores
는 배열이 맞는데, doSomething()
의 students_scores
는 배열일까? 같은 배열이라면 주소가 같아야 하는데 말이다. 뭐가 문제일까?
그것은 doSomething()
의 students_scores
는 포인터이기 때문이다. 즉,
void doSomething(int students_scores[20])
{
cout << (int)&students_scores << endl;
/*cout << students_scores[0] << endl;
cout << students_scores[1] << endl;
cout << students_scores[2] << endl;*/
}
여기서 cout << (int)&students_scores << endl;
의 students_scores
는 배열이 아니고 void doSomething(int students_scores[20])
의 students_scores
의 포인터 변수인 것이다. 그래서 주소 값이 다른 것이다.
결론적으로 함수안에 배열을 선언하면 그것은 포인터로 작동하고 그 배열의 이름을 출력하면 배열을 담은 포인터 변수의 주소가 출력되는 것이다. 어렵겠지만 제대로 이해하고 넘어가면 좋겠다.