IT 과학/C언어

C언어 | 배열

곰뚱 2019. 12. 22.

 

 

 

1. 1차원 배열

배열은 다량의 데이터를 다루기 위한 데이터형이다. 본 내용은 C의 배열 선언과 조작 방법에 관해서 설명한다. 또 C에서는 배열에 데이터를 초기화하는 것이 가능하므로 그 방법에 관해서도 설명한다.

 

(1) 배열의 정의

배열을 사용할 경우에는 얼마만큼 크기의 배열을 사용할 것인가를 선언해야 한다. 가령 100개의 동일한 성격의 데이터가 있을 때, 이것을 배열로 선언하지 않고 a0, a1, a2… 등의 단순 변수를 100개 씩이나 사용해야 한다는 것은 대단히 복잡한 작업이다. 예를 들어, 관련된 100개의 정수형 변수가 프로그램에서 필요할 경우 이제까지 공부한 방식으로는 어떻게 처리해야 할까? 먼저 100개의 정수형 변수의 이름을 정한 후에 각각 선언해야 할 것이다. 변수들이 서로 관련되므로 이름은 유사하게 a0, a1, ..., a99라고 정한 다음에 각각을 선언해야 할 것이다.

 

int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19,a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95, a96, a97, a98, a99;

 

생각만해도 끔찍하지 않은가? 현실적으로 관련된 동일한 형(타입)의 변수들을 사용할 경우가 매우 많다. 그럴 때마다 위와 같은 방식으로 변수들을 선언하고, 프로그램에서 사용한다는 것은 정말 짜증나는 일이다. 우리가 프로그램을 작성하는 의미조차 없는 상황이 될 것이다.

 

그래서 동일한 성격의 여러 개의 데이터를 전체적으로 다루기 위하여 배열이라는 것을 생각할 수 있다. 배열은 데이터를 대표하는 배열명과 몇 번째 데이터인가를 지정하는 첨자(색인)로서 사용된다. 첨자는 [ ]로 싸여져 있다. 첨자가 하나인 경우를 1차원 배열이라고 한다.

 

C에 있어서 정수형인 100개의 데이터를 배열로 선언하기 위한 방법은

 

int a[100];

 

라고 표시한다. 앞에서 배열을 이용하지 않고 각각을 별도로 선언한 경우와 비교해 보자. 배열의 편리함이 자명해질 것이다. 그렇다면 100개의 변수 각각은 어떻게 지정하고, 참조할 수 있을까? 즉, 프로그램에서 각각의 변수를 어떤 방식으로 지정하여 이용할 수 있을까? 배열 변수를 이것에 대해 a[0], a[1], …a[99]라는 100개의 배열 요소들이 선언된다.

 

(2) 배열의 선언

C에 있어서 배열의 선언의 예는 다음과 같이 된다.

 

                int temp[365];

                main()
 

               {

                        float rain[365];

                        static char code[12];

                        extern temp[];

                        ......

                }

 

구체적인 형식은 다음과 같다.

 

형 배열명[요소의 수] {[요소의 수]…… };

 

형은 int형 이외에도 char형, double형 등의 모든 형(void형과 함수형을 제외함)을 지정할 수 있다. 변수 이름 앞에 형(타입)을 지정하는 것은 C 언어의 변수 선언의 기본적인 패턴임을 상기하기 바란다. [ ]가 하나이면 1차원 배열, [ ]가 두 개이면 2차원 배열이라고 한다. [요소의 수]는 배열 요소의 개수이지만 위에서 설명한 것처럼 첨자는 0에서부터 시작하여 마지막 요소를 가리키는 첨자는 배열 요소의 개수를 나타내는 숫자에 1을 뺀 값이다.

 

배열명을 붙이는 방법은 앞에서 설명한 변수 이름을 정하는 규칙에 따른다. 배열을 선언할 때에 지정하는 [ ]는 값이 정해져 있는 정수 또는 식 이외에는 사용되지 않지만, 배열을 참조할 때는 [ ]안에 변수를 사용해도 된다. 이러한 경우는 배열의 첨자를 프로그램 상에서 조작할 수 있다는 것이다. 단순 변수 a0, a1, a2… 등의 0이나 1, 2는 변수명의 일부분이므로 프로그램 상에서 조작할 수 있는 것은 아니다.

     

배열 a[ ]의 첫 번째 요소는 a[0]으로 시작하기 때문에 i+1번째 요소를 a[i]라고 표시한다. 이 때에 a를 배열명, i를 첨자, a[i]를 배열 요소라고 한다. 그러면 int형 배열 a[ ]의 데이터를 모두 0으로 초기화하기 위해서는 다음과 같이 한다.

 

for(i=0; i〈 100; i++)

 a[i]=0;

 

C에 있어서 배열 첨자는 0부터 시작되며 int a[100]; 라고 선언되면 a[0]∼a[99]까지 100개의 요소가 있다는 것을 의미하며 a[100]이라는 배열 요소는 확보되지 않는다. 그러나 C에서는 프로그래머가 실수로 준비되어 있지도 않은 배열 요소 a[100]에 데이터를 대입하여도 에러 메시지를 발생시키지 않는다.

 

만약에 그곳에 별도의 변수가 할당되어 있다면 그 데이터는 없어지고 만다. 이처럼 범위 바깥의 첨자를 사용하여 생기는 문제는 전혀 예측할 수 없고, 심각한 상황이 발생할 수 있는데도 불구하고 C 언어의 컴파일러는 경계값 검사를 하지 않으므로 프로그래머의 각별한 주의가 필요하다. 이것은 C 언어의 대표적인 문제점 중에 하나이다.

 

다음은 char형의 배열에 문자를 대입하는 예이다.

 

char str[10];

str[0]='T'; str[1]='u'; str[2]='r';

str[3]='b'; str[4]='o'; str[5]=\'o';

 

이렇게 한 후

 

 printf("%s", str);

 

라고 하면 Turbo 라고 출력된다. %s의 의미는 문자열(스트링) 형식으로 출력할 것을 지정하는 것이다.

 

예제  1. 숫자 문자들을 입력하여 '0'∼'9'의 숫자의 출현 횟수를 카운트하여라. 데이터의 종료 표시 키는 엔터 키이다.

● 프로그램

#include <stdio.h>
main( )
{
   char c, a[10];
   int i;
   for (i=0; i<10; i++)     
        a[i]=0;                 
        while ((c=getchar( )) !='\n')
                if ('0'<=c && c<='9')
                   a[c-'0']++;
        for (i=0; i<10; i++)
             printf("%2d %2d\n",i, a[i]);
}   


● 결과

156255123898590
0 1
1 2
2 2  
3 1
4 0  
5 4
6 1
7 0
8 2
9 2  

 

참고: 첨자 자리에 c-'0' 이 온 이유

C 언어 프로그래밍에 있어서 반드시 알고 넘어가야 하고, 자주 이용하는 표현입니다. 우선 배열 a의 첨자 범위가 어떻게 되는가 생각해 보세요. 0부터 9까지, 즉 a[0], a[1], a[2], ..., a[8], a[9] 이렇게 되지요? 만일 키보드에서 0을 눌러 c가 '0'이라고 생각합시다. 그럼 c - '0'이 뭐지요? '0' - '0'이네요. 그런데 '0' (문자 0)은 예제 4-9에 있는 것 처럼 아스키 코드로 16진수로는 30이지요. 10진수는 환산해 보면 48이 되겠네요. 따라서 '0' - '0'은 48 - 48이므로 0이 되지요. 결국 a[0]을 가리키는데 이용되는 첨자값이 계산되는 군요. 다음으로 c가 '1'이라면 '1' -'0'은 49 - 48 이 되겠지요? 왜 그런가요? '1'의 아스키 코드가 16진수로 31 (다시 예제 4-9를 보세요)이므로 10진수로는 49이지요? 따라서 이를 이용해 a[1]의 첨자인 1이 계산되는군요. 이렇게 계속해서 입력된 키를 그대로 이용해서 배열의 첨자를 구하는 효율적인 방법입니다. 자주 쓰이는 방법이니 잘 익히세요.

 

확인문제 1

/*  1주일의 평균 온도를 계산하는 프로그램 */
void main(void)
{
      int day;
      float temper[7], sum;

      for(day = 0; day < 7; day++)
      {
          printf("Enter temperature for day %d: ", day);
          scanf("%f", &temper[day]);
      }
      sum = 0.0;
      for(day = 0; day < 7; day++)
          sum += temper[day];
      printf("\nAverage is %4.2f\n", sum / 7);
}

 

확인문제 2

/* 50개 이하의 임의의 수를 입력 받아 그 평균 온도를 계산하는 프로그램 */
#define LIMIT 50
#include <stdio.h>
void main(void)
{
      float temper[LIMIT], sum = 0.0;
      int count, day = 0;

      do
      {
          printf("Enter temperature for day %d: ", day);
          scanf("%f", &temper[day]);
      } while(temper[day] >= -50 && temper[day] <= 55 && ++day < LIMIT);

      if(day > 0)
      {
           for(count = 0; count < day; count++)
               sum += temper[count];
           printf("Average is %.1f\n", sum / count);
      }
      else printf("Input error !\n");
}

728x90

 

 

2. 2차원 배열

다음과 같은 성적표가 있다고 가정하자.

 

표 1 성적표  

번호

이름

국어

영어

수학

음악

체육

1

이효리

100

100

100

100

100

2

옥주현

93

85

70

99

84

3

성유리

92

93

79

83

60

4

이진

88

76

93

80

77

5

김이지

60

76

78

80

70

6

심은진

56

98

34

78

68

7

윤은혜

65

98

32

56

77

8

이희진

76

87

57

76

98

9

간미연

56

56

87

89

99

10

아유미

55

67

87

86

78

 

표 1은 학생과 과목이라는 두 개의 요소로 이루어져 있으며 이것을 배열로 선언하는 데는 첨자가 두 개로 이루어진 2차원 배열을 써서 다음과 같이 선언한다.

 

int a[10][5]

 

C의 배열은 0에서부터 시작하므로 1번 학생 이효리의 데이터는 0번 행의 요소에 대입되는 것에 주의해야 한다.

 

표 1로 선언된 배열에 과목별로 읽어들이면 다음과 같다. 쉽게 이해가 되지 않는 경우는 표를 그려 손으로 일일이 수행 과정을 확인하기 바란다.

 

for(k=0; k<5; ++)

for(j=0; j<10; j++)

scanf("%d",&a[j][k]);

 

이 예에서는 j가 학생, k가 과목을 지정하고 있다. 배열 요소에 scanf( )로 데이터를 입력하려면 &a[j][k]라고 표시한다.

 

더욱이 2차원 배열은 논리적으로 메모리에는 1차원 연속으로 저장되어져 있다. 즉 a[0][0]을 선두에, 차례 차례 a[0][1],…a[0][4], a[1][0]…의 순서로 기억된다.

 

예제 2.  4명의 국어, 영어, 수학의 점수를 입력하여 성적표를 작성하여라.

● 프로그램

#include <stdio.h>
main()  
{
   char course[3];
   int i, j, p[4][3];
   course[0] = 'K';
   course[1] = 'E';
   course[2] = 'M';
   for (j=0; j<3; j++) {
         printf("%c\n",course[j]);
         for (i=0; i<4; i++) {
                printf("번호%d ? ", i+1);
                scanf("%d",&p[i][j]);
         }
   }

   printf("       국어 영어 수학\n");
   for (i=0; i<4 ; i++) {
           printf("번호%d", i+1);
           for(j=0; j<3; j++)
                printf("%5d", p[i][j]);
           printf("\n");
    }
}

 

● 결과

  K
  번호1  ?  55
  번호2  ?  56
  번호3  ?  76
  번호4  ?  77
  E
  번호1  ?  56
  번호2  ?  76
  번호3  ?  68
  번호4  ?  87
  M
  번호1  ?  90
  번호2  ?  56
  번호3  ?  78
  번호4  ?  77
            국어 영어 수학 
  번호1   55     56   90
  번호2   56     76   56
  번호3   76     68   78
  번호4   77     87   77

 

 

3. 배열의 초기화

배열의 각 요소에 a[0]=0; a[1]=1;, …등의 대입문을 사용하여 데이터를 초기화하는 것은 불편하고 많은 시간이 걸린다. 그래서 C 에서는 배열 선언시에 간단한 방법으로 초기화할 수 있다.

 

int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

int *days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };/*ERROR*/

 

char s[] = "abcde";

char s[] = { 'a', 'b', 'c', 'd', 'e', '\0' };

char *s = "abcde";

char *s = { 'a', 'b', 'c', 'd', 'e', '\0' };  /* ERROR */

     

다음은 2차원 배열의 선언과 초기화의 방법을 표시한 것이다.

 

int p[4] [3] =  {{60,    56,    65},

{70,    80,    68},

{77,    70,    64},

{81,    75,    67}};

 

초기화 데이터는 행단위를 { }로 싸고, 콤마(,)로 구분한다. 초기화 데이터가 있는 경우는 배열의 첫 번째 요소 지정을 하지 않아도 컴파일러가 자동적으로 처리한다. 그러나 두 번째 요소 이후는 반드시 지정을 해야 한다.

 

문자 데이터의 초기화는 다음 예와 같다.

 

char a[3] = {'a', 'b', 'c'};
 

 

예제 3. 다섯 사람의 세 과목 점수를 course[][]에 초기화하고 각 학생의 점수의 합계를 구하라.

● 프로그램

#include <stdio.h>

#define NUMBER 3

#define STUDENT 5

main( )

{

    int course[ ][NUMBER]={{80, 65, 70},

                        {70, 55, 75},

                        {75, 80, 70},

                        {80, 60, 60},

                        {90, 80, 75}};

    int i, j, k;

    printf("      국어   영어   수학|     총점\n");

    printf("      ---------------------------\n");

    for (i = 0; i<STUDENT; i++) {

            k = 0;  

            for (j = 0; j<NUMBER; j++) {

                  printf("%8d", course[i][j]);

                  k = k + course[i][j];

            }

            printf("|%8d\n", k);

    }

}

 

● 결과

국어    영어  수학|      총점

--------------------------

80        65       70|       215

70        55       75|       200

75        80       70|       225

80        60       60|       200

90        80       75|       245

 

예제 4. 점수 데이터의 분포를 10점 간격의 등급으로 히스토그램으로 표시하여라.
 
● 연구

0∼100점까지를 0∼9, 10∼19, … 90∼99, 100의 10점 간격으로 11등분하여 각 구간에 들어가는 점수를 조사한다. 배열로서는 histo[0] ∼ histo[10]을 준비한다. 점수를 나타내는 dat가 어떤 구간에 들어갈 것인지 (dat/10)으로 구할 수 있다. 가령 dat가 55이면 55/10은 5가 되므로 histo[5]의 내용을 1씩 증가하게 된다.

 

● 프로그램

#include <stdio.h>
main( )
{
          int histo[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
          int i, j, dat;
          while (scanf("%d", &dat) != EOF) {
              if (0<=dat && dat<=100)
                 histo[dat/10]++;
          }
          printf("\n-------히스토그램--------\n");
          for (i=10; i>=0; i--) {
              printf("%3d- ", i*10);
              for (j=1; j<=histo[i]; j++)
                  printf("*");
              printf("\n");
          }  
}

 

● 해설

먼저 histo[ ]를 초기화하고 while문에서 dat에 데이터를 입력하여 histo [dat/10)의 요소를 1 증가시킨다. histo[dat/10]++; 는 histo[dat/10]=histo[dat/10]+1; 과 같은 표시이다. for 문에서 histo[i]에 i * 10으로 시작하는 구간 구분이 되어 있으므로 그 수만큼 *로 표시된다.

 

● 결과

   55
   65
   53
   58
   62
   45
   80
   66
   56
   43
   ^Z   

 

   -------히스토그램--------

  100-
   90-
   80-  *
   70-
   60-  * * *
   50-  * * * *
   40-  * *
   30-
   20-
   10-
    0-

 

확인문제 3

/* 2차원 배열의 초기화 */
#include <stdio.h>
void main(void)
{
      int array[4][3] = {{100, },{0, } ,{111, 222}, };
      int i, j;

      for(i = 0; i < 4; i++)
      {
          for(j = 0; j < 3; j++)
              printf("array[%d][%d] = %d\n", i, j, array[i][j]);
          putchar('\n');
      }
}

 

확인문제 4

/* 행의 크기가 지정되지 않은 배열의 행의 크기를 계산하는 예 */
#include <stdio.h>
void main(void)
{
      int A[ ][3] = {{100, 200, 0}, {0,}, {111, 0, 0}, {0, }};
      int i, j, size;

      size = sizeof(A) / sizeof(int) / 3;
      for(i = 0; i < size; i++)
      {
          for(j = 0; j < 3; j++) printf("A[%d][%d] = %d\n", i, j, A[i][j]);
          putchar('\n');
      }
}

 

참고:

확인문제 5-4의 size = sizeof(A) / sizeof(int) /3;이라는 식은 2차원 배열의 행의 크기를 계산하는 식입니다. 행의 크기를 계산하는 일반적인 식을 써 보지요.

행의 크기 = sizeof(배열명) / sizeof(배열형) / (열의 크기)

위에서 sizeof(배열형) 을 가지고 나누는 것이 이상한가요? 그럼 한 번 생각해 보세요. 정수형 배열과 실수형 배열, 문자형 배열에서 이 부분이 빠지면 어떻게 될까요? 사실 이 문제에서 더욱 중요한 것은 2차원 배열에서 행의 크기를 생략한 것입니다. 배열의 크기 값은 배열의 원소 수가 될 수도 있고 안될 수도 있습니다. 예를 들어 int array[5] = {1, 2, 3};을 봅시다. 배열의 크기는 5이지만 원소의 수는 3이지요. 물론 나머지 원소는 0으로 초기화 되기는 되지요.

 

배열 크기를 생략해 봅시다. int array[] = {10, 20, 30, 40, 50}; 이 배열의 초기화는 배열의 크기를 지정하는 대괄호 내부에 배열의 크기를 지정하는 정수가 없지요. 이와 같이 배열의 크기를 지정하지 않으면 컴파일러는 초기화되는 초기치의 개수를 확인하여 배열의 크기를 초기치의 개수로 정하여 배열의 크기와 초기치의 수가 불일치되는 문제점을 제거해 버립니다. 따라서 위의 배열 array의 크기는 초기치의 개수와 동일한 5가 됩니다. 그렇다면 초기화할 때 위와 같이 배열의 크기를 생략할 경우 프로그램에서 전체 배열을 어떻게 참조할 수 있을까요? 간단합니다. sizeof 연산자를 이용하여 전체 배열의 바이트 수를 구하여 배열 원소의 자료형에 대한 바이트 수로 나누면 배열의 크기를 알 수 있습니다. 즉, 다음과 같이 하면 되지요.

 

배열의 크기 = sizeof(배열명) / sizeof(배열 원소의 자료형)

 

예를 들면, 위의 int 형 배열 array의 크기는 다음과 같이 구할 수 있지요.

size = sizeof(array)/ sizeof(int)

sizeof 연산자는 무엇이지요? 피연산자의 크기를 바이트 수로 구하는 것입니다. 즉 sizeof(char)하면 1이 되겠지요. sizeof(int)이면? 2가 되는 컴파일러도 있고 4가 되는 컴파일러도 있어요. 2주차 강의 노트에 살짝 언급이 되었고 앞으로 자세히 공부할 테지만, 예습 삼아 미리 공부합시다. (사실은 일찍 해야 하는데.) 10주차 강의에 sizeof() 연산자가 나옵니다.

 

확인문제 5

/* 2차원 배열 array[3][5]의 각 행의 합과 전체 합을 계산하는 프로그램 */

#include <stdio.h>

#define ROW 3

#define COLUMN 5

void main(void)

{

      int array[ROW][COLUMN];

      int i, j;

      long temp, total = 0;

      for(i = 0; i < ROW; i++)

      {

          temp = 0;

          for(j = 0; j < COLUMN; j++)

          {

              printf("Input data in array[%d][%d] : ", i, j);

              scanf("%d", &array[i][j]);

              temp += array[i][j];

          }

          switch( i ) /* 각 행의 합을 출력 */

          {

              case 0: printf("\narray[%d] = %ld\n\n", i, temp);

                      break;

              case 1: printf("\narray[%d] = %ld\n\n", i, temp);

                      break;

              case 2: printf("\narray[%d] = %ld\n\n", i, temp);

          } /* end of switch */

          total += temp;

      } /* end of for statement */

      printf("The total of array is %ld \n", total);

}

 

예제 5. n 개의 정수를 읽어서 합, 평균, 가장 큰 값을 구하는 프로그램

        #include <stdio.h>

        main()  /* calculate the sum, average, highest data */
  

      {

                int n, data[100];

                long sum, highest;

                float average;

                int i;

                printf("Input number of data : ");

                scanf("%d", &n);

                for (i = 0; i < n; i++) /* input data */

                        scanf("%d", &data[i]);

 

                printf("Data = ");

                for (i = 0; i < n; i++) /* print-out data */

                        printf("%5d ", data[i]);

                putchar('\n');

                for (i = 0, sum = 0; i < n; i++)        /* sum & average */

                        sum += data[i];

                average = (float) sum / (float) n;

                printf("sum = %ld, average = %.2f\n", sum, average);

                highest = data[0];

                for (i = 0; i < n; i++)

                        if (data[i] > highest) highest = data[i];

                printf("highest data = %d\n", highest);

         }

 

<참고> data 입력 방법

1. data를 입력하기 전에 data의 수를 먼저 입력

2. data의 끝을 나타내는 값(정수의 경우 -1, 문자의 경우 '$' 등)을 정해서 그 값이 입력되면 그 때까지 입력된 data의 수를 count하는 방법

 

그리드형

댓글