F.R.I.D.A.Y.

배열(array) part1. default 본문

DEV/C C++

배열(array) part1. default

F.R.I.D.A.Y. 2020. 1. 19. 23:03
반응형

 이전 포스트[# 한참 지난 포스트이긴 하지만..\n 한참 시간이 지난 포스트다 보니 설명하는 어투가 다릅니다. 대표적으로 존댓말로 바뀌었다던지.. 아마도 조만간 개편을 진행해야겠군요.]에서 우리는 자료형과 변수에 대해 공부했습니다.

 이번 포스트에서는 배열에 대해 공부해봅니다.

 


변수 선언하기

 이전 시간에 공부했던 내용입니다. 변수 하나에 값 하나. 달리 이야기하면 여러 개의 값을 저장하고 싶다면 그 개수만큼의 변수가 필요하다는 말이 되겠죠.

 어떤 학급에 30명의 학생이 있다고 생각해봅시다. 이 학생들의 고유 번호를 저장할 변수가 필요합니다. 그렇다면 30명의 정보를 저장할 30개의 변수가 필요하겠군요.

int student1, student2, student3, student4, student5, student6, 
    student7, student8, student9, student10, student11, student12, 
    student13, student14, student15, student16, student17, student18, 
    student19, student20, student21, student22, student23, student24, 
    student25, student26, student27, student28, student29, student30;

 작성해봤는데 굉장히 비효율적이지 않을 수 없습니다. 물론 저는 다른 방법을 차용해 작성[# 귀찮아서 프로그램으로 저 문자열 뽑아내도록 코드 짜서 돌렸습니다. 그렇지만 이 정도 돌릴 정도라면 이미 배열은 알고 있을 테죠.]했지만, 이러나저러나 비효율적인 것은 매 한 가지입니다.

 저 방식의 몇 가지 큰 문제를 꼽으라면 아래처럼 말할 수 있을 것입니다.

  • 반복문 사용 불가
  • 코드 길이 증가
  • 유지 보수 능률 하락

 결국 코드에 악영향을 미치는 것들만 이야기하네요. 그래서 이 분께서는 생각했습니다. 어떻게 하면 더 편하게 코드를 작성할 수 있을까. 우리는 비슷한 성향을 가지는 것들을 한 데 묶어 그룹으로 관리합니다. 이 방식을 C언어에도 차용했습니다.

 그것은 바로

 

배열

 입니다. 위와 같이 작성해야 할 코드는 배열을 이용하면 아래와 같이 이용할 수 있습니다.

int student[30];

 수 십 단어로 선언해야 할 코드가 이렇게 간편해졌습니다. 위 선언에 대해 설명을 하면 우리가 변수를 선언할 때와 마찬가지입니다.

자료형 변수명[길이];

 배열의 자료형도 변수를 선언할 때와 마찬가지로 먼저 작성합니다. 그러고 나서 변수명을 작성합니다. 일반적인 변수와 다른 점이라 하면 대괄호 쌍을 사용한다는 점입니다. 배열은 비슷한 성격을 띠는 변수[#참고 배열에 한데 뭉쳐 놓은 변수들은 배열 안에서 변수라고 말하지 않습니다. 배열 자체가 하나의 변수이기 때문인데요, 그래서 우리는 배열 안의 하나하나 변수들을 '요소'라고 부릅니다. 앞으로의 설명에서도 이렇게 부를 테니 참고하기 바랍니다.]를 한 데 뭉쳐 그룹으로 관리하는 문법이라 했습니다. 그렇다면 그룹 안에 몇 개의 요소가 존재하는지 컴파일러에 알려주어야 합니다. 이 역할을 대괄호 쌍 안에 숫자로 작성해주는 것입니다.

더보기

# 배열의 길이를 사용자로부터 입력받을 수는 없나요?

 가능합니다. Variable Length Array(가변 길이 배열, 이하 VLA)라는 기능으로 C99에 포함된 문법 사항입니다. 표준을 잘 지키고 있는 컴파일러라면 이 기능을 사용해 아래처럼 선언할 수 있습니다.

#include <stdio.h>

int main(void){
    int num;
    scanf("%d", &num);
    int arr[num];
    
    for(int i = 0; i<num;++i){
        scanf("%d", arr + i);
    }
    
    for(int i = 0; i<num;++i){
        printf("%d ", arr[i]);
    }
}

 이 기능을 사용할 수 있는 대표적인 컴파일러 제품은 GCC 계열입니다.

 

 그러나 저를 포함해 많은 개발자들로부터 찬양받는 비주얼 스튜디오는 이 VLA 문법을 제공하지 않습니다. 그래서 이 기능을 적용한 코드를 비주얼 스튜디오에서 컴파일 진행하면 오류가 발생합니다.

https://namu.wiki/w/%EB%B9%84%EC%A3%BC%EC%96%BC%20%EC%8A%A4%ED%8A%9C%EB%94%94%EC%98%A4

 이 기능을 지원하지 않는 이유는 아래와 같은 추측이 있습니다.

 일단 메인이 C언어인 컴파일러가 아닌것도 있고, 현직 개발자의 말을 들어보면 VLA를 이용할 때 프로그램의 속도가 느려진다는 문제가 있다는 것입니다. VLA 문법의 경우 개발자가 조금만 신경 쓰면 굳이 VLA를 사용하지 않더라도 충분히 잘 만들 수도 있습니다.


초기화 방법

 변수를 초기화했을 때는 대입 연산자를 작성하고 원하는 값을 바로 넣었다면 되었습니다. 그러나 배열의 경우 초기화 방법이 약간 다릅니다.

 처음 다섯 학생의 고유 번호가 다음과 같다고 봅시다.

1 2 3 4 5

 배열을 초기화하는 코드는 아래와 같이 초기화할 값을 중괄호 {]로 묶어서 표현합니다.

int student[30] = {1,2,3,4,5};

 이상합니다. 분명 배열 내의 요소 수는 30개인데 초기화는 5개만 했습니다. 그러나 이 코드 또한 정상입니다. 코드를 위와 같이 작성했다면 처음에서부터 다섯 개의 요소에는 순서대로 1, 2, 3, 4, 5가 들어가게 됩니다.

 그렇다면 나머지 25개의 요소에는 어떤 값이 들어갈까요? 0이 들어가게 됩니다. 어떠한 경우에서라도 초기화를 하는데 값이 지정되지 않은 요소라면 0이 들어가게 됩니다.

 위에서 설명한 초기화 코드는 사실 아래와 같이 표현되는 것이죠. 단지 길게 작성할 필요를 느끼지 않아 컴파일러가 연장해 작성해주는 것이라고 보면 되겠습니다.

int student[30] = {1,2,3,4,5
                  ,0,0,0,0,0
                  ,0,0,0,0,0
                  ,0,0,0,0,0
                  ,0,0,0,0,0
                  ,0,0,0,0,0
                  };
더보기

# 배열은 최종 값을 따라서 초기화하지 않습니다.

 초기화를 설명하며 제시한 코드는 명시적으로 선언된 마지막 초기화 값은 5입니다. 약식 표기처럼 작성했다면 이런 의문이 들 수 있습니다.

int student[30] = {1,2,3,4,5}; // 약식 표기

"초기화 이후 요소들은 마지막 값을 따라서 초기화하지 않을까?"

 이 말은 즉, 5 번째 요소 이후 6 번째부터 30 번째까지의 요소들의 값이 5 번째 요소의 값인 5로 초기화되는 것이 아닌가 하는 생각을 할 수 있다는 말입니다.

 

 충분히 이렇게 생각할 수 있지만, 초기화 값이 명시적으로 표현되지 않은 요소의 값은 0으로 초기화가 진행됩니다.

 만일 0이 아닌 다른 값을 초기화 값으로 이용하고 싶다면 memset 함수[# 이 함수는 초기화 범위가 1Byte, 즉 배열의 자료형이 char 타입일 때 정상 동작합니다.], 혹은 반복문을 이용해야 합니다.

 만일 중간의 요소부터 초기화를 진행하고 싶다면[# 굳이 이럴 필요가 있나?] 선언과 동시에 초기화는 것은 불가능합니다. 선언과 동시에 초기화하는 작업은 무조건 앞선 요소부터 초기화를 진행하므로 4 번째 요소의 값을 4로 변경하는 코드를 선언과 동시에 작성하려면 아래처럼 앞선 요소 또한 특정 값으로 초기화를 명시해주어야 합니다.

int student[30] = {0,0,0,4};

 

 컴마 연산자[# ' , '. comma operator]로 요소를 구분한다고 해서 아래처럼 작성하면 안 됩니다.

// 초기화의 부적절한 예
int student[30] = {,,,4};

 

또한 배열의 초기화를 진행할 때는 유의할 점이 있습니다. 앞서 설명한 배열의 초기화 방법은 계속해서 선언과 동시에 진행했었습니다. 초기화 값을 중괄호로 묶는 방식의 초기화는 선언 때에만 사용할 수 있습니다.

 

 초기화의 또다른 문법이 존재하네요, Designated Initializer라고 불린다는데 자세한 내용은 아래 포스트를 참고하세요.

 

Designated Initializer

변수의 초기화 방법은 각 문법에서 설명했습니다. 다른 언어에서는 잘 사용하고 있었는데 C언어에서는 그 문법을 찾을 수가 없었습니다. 최근에 문법 지원이 이루어지고 있음을 알게 되어, 초기화 문법을 추가로..

pang2h.tistory.com


배열 사용하기

 배열을 선언하는 방법을 배웠으니 이제 실제 사용하는 방법입니다. 간단한 코드를 만들어 실습형으로 알아보겠습니다.

#include <stdio.h>

int main(void){
    int arr[10];
    
    for(int i = 0; i < 10; ++i){
        printf("%d번 째 값:", i+1);
        scanf("%d", &arr[i]);
    }
    for(int i = 0; i < 10; ++i){
        printf("%d번째 요소의 값: %d\n", i+1, arr[i]);
    }
    return 0;
}

 변수 사용할 때와 같이 배열의 변수명을 작성합니다. 그리고 배열의 몇 번째 요소에 접근할 것인지를 알리도록 대괄호 연산자를 이용합니다. 대괄호 연산자는 모든 연산자보다 먼저 평가가 이루어지기 때문에 우선순위 문제를 걱정하지 않아도 됩니다.

 

Point! 배열은 0번부터

 우리가 순서를 셀 때는 1부터 시작합니다. 건물의 층 수부터 학생의 번호, 대기표 등. 많은 부분이 1부터 시작합니다. 그러나 프로그래밍 언어, 특히 C언어에서는 순서가 0번부터입니다. 컴퓨터에서는 실제로 음수, 정수, 양수로 나누는 방식이 아닌 음수, 양수로 수를 구분 짓는데, 양수에 0이 포함됩니다. 그래서 양수의 가장 낮은 정수를 찾으라 하면 1이 아닌 0이 됩니다. 그 영향으로 배열의 첫 번째 요소는 1번 인덱스가 아닌 0번 인덱스입니다.

 달리 이야기하면 길이 30인 배열의 첫 번째 요소는 0번 인덱스, 마지막 요소는 29번 인덱스라는 말이 되겠지요.

순서 인덱스
첫 번째 0
마지막(30번 째 요소) 29

용어 정리

 배열은 여러 요소의 합으로 이루어져 있기 때문에 배열의 크기를 나타내는 단어가 한 가지 더 있습니다. 우리가 일반적으로 크기라 하면 해당 변수가 메모리를 차지하는 크기를 의미합니다. 그러나 배열은 요소의 수가 존재하기 때문에 길이라는 단어를 또 사용합니다. 배열의 크기와 길이라는 단위는 아래와 같이 사용됩니다.

  • 길이: 요소의 수. 대괄호 연산자로 접근할 수 있는 수
  • 크기: 전체 요소의 크기 합. 자료형이 int이고 길이가 30인 배열[# int arr(30); // 스크립트 한계로 대괄호가 아닌 소괄호로 표기.]의 크기는 4[# sizeof(int) = 4] * 30[# 길이] = 120Byte

알아보기

 

배열(array) part2. 다차원 배열

Prev. 배열(array) part1. default 이전 포스트[# 한참 지난 포스트이긴 하지만..\n 한참 시간이 지난 포스트다 보니 설명하는 어투가 다릅니다. 대표적으로 존댓말로 바뀌었다던지.. 아마도 조만간 개편

pang2h.tistory.com

 

배열 변수의 이름이 0번 인덱스의 시작 주소인 이유

이번 포스트는 제목 그대로 배열 변수의 이름이 어째서 해당 배열의 0번 인덱스의 주소가 되는지 알아봅니다. 간단해요! int arr[10]; &arr[0]; // 처음 배울 때 주로 사용하는 0번 인덱스의 주소를 가져오는 법..

pang2h.tistory.com

# index

728x90
반응형

'DEV > C C++' 카테고리의 다른 글

Designated Initializer  (0) 2020.01.21
배열(array) part2. 다차원 배열  (0) 2020.01.20
포인터(pointer) part2. 다차원 포인터  (0) 2020.01.17
CallByValue vs CallByReference  (0) 2020.01.09
함수 호출 구조  (0) 2020.01.07
Comments