[ C 언어 스터디 ]

자료출처 : 핵심 길잡이 C 프로그래밍 언어
도서출판 : 성안당


9. 배열 ( Arrays )


★ 1. 배열의 개념 ★

배열은 데이터 구조의 한 형식으로서 동일한 데이터형의 원소들이 공통의 성격 ( 동일
한 크기 )을 가지고 어떤 규칙에 따라서 정렬되어 있는 데이터들의 집합이다.

지금까지의 프로그램에서 사용한 변수는 하나의 변수에 하나의 값만 기억시킬 수 있
는 변수였다. 이러한 변수를 단순 변수 ( simple variable ), 또는 첨자가 없는 변수라고
한다. 그런데 프로그램 내에서 기억시켜야 할 데이터가 많을 경우에 그것을 일일이 단
순 변수에 기억시칸다면 변수명을 각각 다르게 부여해야 하므로 불편한 경우가 많다.
예를 들어, 10과목의 시혐 점수를 각각 다른 변수에 기억시켜 이들의 합계를 구한다면,

tot = a+b+c+d+e+f+g+h+i+j ;

와 같이 길게 하나의 연산문으로 작성하여야 한다.

그러나 X라는 공통의 이름으로 10개의 기억 장소를 만들어 놓고, 그 곳에 값을 기억
시켜 X1 X2 ....... X10과 같이 첨자를 사용하면 편리할 것이다. 예를 들어 수학에서처럼

  10
tot = ∑ x i = x1 + x2 .... + x10
  i = 1



과 같이 첨자를 변수로 사용한 표현이 가능해진다.
C 언어에서도 변수명을 첨자로 표현하는 것이 가능하며, 첨자를 변수로 나타내는 것
도 역시 가능하다. 즉,

x[ 0 ], x[ 1 ], x[ 2 ], ...... x[ 9 ]

따라서 10과목에 대한 시험 점수의 합계를 다음과 같이 표현할 수 있다.

for ( i=0 , tot=0 , i<=9; i++ )
tot += x[ i ] ;

위의 예에서 x와 같이 배열에 부여된 이름을 배열명 ( array name )이라 하고, x[ 0 ],
x[ 1 ], x[ 2 ], ....... x[ 9 ]와 같이 배열을 구성하는 요소들을 배열 요소 ( array element ) 라
한다. 또한 [ ] 속의 숫자를 첨자 ( subscripted )라고 한다 특히 x[ i ]와 같이 [ ] 속에
있는 변수를 첨자 변수 ( subscripted variable )라고 한다. 그리고 C 언어에서 배열의
첨자는 0 ( zero )를 포함하여 양의 값을, 갖는 정수형 상수, 기호 상수, 변수, 산술식을
사용하여 나타낸다. 그리고 첨자의 범위는 0 ( zero )에서부터 시작하여 ( 배열 요소의
개수 - 1 ) 까지의 값을 가진다.

배열의 각 요소는 기억 장소에 연속적으로 위치하고 있으며, 이들은 단순 변수와 같
이 한 번에 하나의 값만을 기억할 수 있다. 그러므로 배열 요소는 일반 변수와 같이 취
급된다.

 

★ 2. 1차원 배열 ★

< 1 차원 배열의 선언 >

1차원 배열을 선언하는 일반 형식은 다음과 같다.

형 식
기억클래스 데이터형 배열명 [ 첨자 ] ;

이와 같이 1차원 배열은 선언할 때 배열명 다음에 대괄호 ( [ ] )가 하나만 나오
는 형태이다. 기억 클래스 auto인 경우에는 생략이 가능하며, 배열명을 정하는
은 단순 변수명을 정하는 규칙과 같다. 예를 들어,

int a[ 10 ] ;

과 같이 선언하면 정수형의 a[ 0 ], a[ 1 ], a[ 2 ], ..... , a[ 8 ], a[ 9 ]라는 이름으로 10개의
배열 요소가 선언된다. 각 원소는 2바이트씩 차지하여 기억 장소에 연속적으로 확보
된다.

  2Byte 2Byte 2Byte 2Byte
a[ 0 ] a[ 1 ] a[ 2 ] ..... a[ 9 ]

여러 개의 변수를 사용하는 것보다 하나의 배열로 묶어서 처리하는 것이 그룹 전체
의 구성을 파악하기가 쉬우므로 데이터 표현이나 계산 등에 편리하다. 1차원 배열은
리스트 ( list ) 또는 벡터 ( vector ) 등으로도 표현된다.

다음 프로그램의 실행 결과를 나타내시오

▶ 프로그램
# include < stdio.h >
void main ( void )
{

int a[ 10 ] ;
int i, tot=0 ;

for ( i=0 ; i<10 ; i++ )
a[ i ] = i+1 ;

for ( i=0 ; i<10 ; i++ )
tot += a[ i ] ;

for ( i=0 ; i<10 ; i++ )
printf ( " a[ %d ] = %5d \n" , a[ i ] ) ;

printf ( " tot = %5d \n " , tot ) ;

}
▶ 실행 결과

a[ 0 ] = 1
a[ 0 ] = 2
a[ 0 ] = 3
a[ 0 ] = 4
a[ 0 ] = 5
a[ 0 ] = 6
a[ 0 ] = 7
a[ 0 ] = 8
a[ 0 ] = 9
a[ 0 ] = 10
tot = 55

 


< 1 차원 배열의 초기화 >

1차원 배열의 초기화는 선언할 때 배열의 정의 뒷부분에 중괄호 { }를 사용한
다. { }안에 초기화할 데이터를 나열하면 기억 장소에 초기값을 자동으로 설정해 줄
수 있다. 그리고 자동 ( auto ), 정적 ( static ), 외부 ( extern ) 형태로 선언된 배열도 초기화
가 가능하다. 그러나 레지스터 ( register )로 선언된 배열은 초기화할 수 없다.
배열을 초기화하는 일반 형식은 다음과 같다.

형 식
기억클래스 데이터형 배열명 [ 첨자 ] = { 초기값1, 초기값2, .... 초기값n } ;
또는
기억클래스 데이터형 배열명 [ ] = { 초기값1, 초기값2, .... 초기값n } ;

배열은 기억 클래스에 따라 초기화할 수 있다. 정적 배열 변수는 변수를 선언할 때
외부 변수는 정의할 때 초기화할 수 있다. 초기값들을 중괄호로 묶어서 나타내
며, 각 원소들은 콤마 ( , )로 구분된다. 예를 들어,

static char ch[ 5 ] = { 'a', 'b', 'c', 'd', 'e' } ;
static int num[ ] = { 10, 20, 30 } ;

과 같이 초기화할 때 배열 요소의 개수는 생략할 수 있다. 이런 경우 자동으로 원소의
개수만큼 기억 장소가 확보된다. 위의 배열은 다음과 같이 기억된다.

● char형의 크기

1Byte 1Byte 1Byte 1Byte 1Byte
'a' 'b' 'c' 'd' 'e'
ch[ 0 ] ch[ 1 ] ch[ 2 ] ch[ 3 ] ch[ 4 ]


● int형의 크기

2Byte 2Byte 2Byte
10 20 30
num[ 0 ] num[ 1 ] num[ 2 ]

배열을 선언하여 초기화할 때 주의할 사항은 다음과 같다.

① 배열 요소의 개수는 생략할 수 있으며, 첨자가 생략된 경우에는 데이터의 수만큼
자동으로 초기화가 이루어진다.

int a[ 5 ] = { 10, 20, 30, 40, 50 } ;



int a[ ] = { 10, 20, 30, 40, 50 } ;

은 서로 동일하다.

② 배열 요소의 개수가 초기값의 개수보다 많은 경우 나머지 배열 요소에는 데이터
의 형에 따라 0 ( zero )이나 '\0'로 초기값이 부여된다.

int a[ 5 ] = { 10, 20, 30 } ;
char ch[ 3 ] = { 'a', 'b' } ;

인 경우에는 a[ 0 ]=10, a[ 1 ]=20, a[ 2 ]=30, a[ 3 ]=0, a[ 4 ]=0 각각 할당되
고, ch[ 0 ]='a', ch[ 1 ]='b', ch[ 2 ]='\0'가 할당된다.

③ 배열 요소의 개수가 초기값의 개수보다 적은 경우 나머지 초기값은 무시된다. 예
를 들어,

int a[ 5 ] = { 10, 20, 30, 40, 50, 60, 70 } ;

의 경우에는 a[ 0 ]=10, a[ 1 ]=20, a[ 2 ]=30, a[ 3 ]=40, a[ 4 ]=50 이 할당되고
초기값 60과 70은 무시된다.

다음 프로그램의 실행 결과를 나타내시오

▶ 프로그램
# include < stdio.h >
void main ( void )
{

int num[ 5 ], kor[ 5 ], eng[ 5 ], mat[ 5 ], tot[ 5 ] ;
int i ;
float ave[ 5 ] ;

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

printf ( " 학번, 국어, 영어, 수학 점수를 입력하시오 : " ) ;
scanf ( " %d %d %d %d ", &num[ i ], &kor[ i ], &eng[ i ], &mat[ i ] ) ;
tot[ i ] = kor[ i ] + eng[ i ] + mat[ i ] ;
ave[ i ] = tot[ i ] / 3.0 ;

}

printf ( " \n\n 학번 국어 영어 수학 합계 평균 \n " ) ;

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

printf ( " %4d %5d %5d %5d %5d %5.2f \n " , num[ i ], kor[ i ], eng[ i ],
mat[ i ], tot[ i ], ave[ i ] ) ;

}

}
▶ 실행 결과

학번 국어 영어 수학 점수를 입력하시오 : 101 85 80 75
학번 국어 영어 수학 점수를 입력하시오 : 102 90 100 80
학번 국어 영어 수학 점수를 입력하시오 : 103 75 80 70
학번 국어 영어 수학 점수를 입력하시오 : 104 75 65 70
학번 국어 영어 수학 점수를 입력하시오 : 105 80 50 100

학번 국어 영어 수학 합계 평균
101 85 80 75 240 80.00
102 90 100 80 270 90.00
103 75 80 70 225 75.00
104 75 65 70 210 70.00
105 80 50 100 255 85.00

 

★ 다차원 배열 ★

< 다차원 배열의 선언 >

C 언어에서 배열의 차원 ( dimension )은 첨자의 수에 의해 결정된다. 배열의 크기를
나타내는 첨자가 2개이면 2차원 배열, 3개이면 3차원 배열이다. 다차원 배열은 1차원
배열을 하나의 객체로 놓고 다시 배열을 지정한 것과 같으므로 배열의 배열이라고도
한다. 다차원 배열의 일반 형식은 다음과 같다.

형 식
기억클래스 데이터형 배열명 [ 첨자 1 ], [ 첨자 2 ], ..... [ 첨자 n ]

예를 들면,

int a[ 10 ] ; // 1차원 배열
int a[ 2 ][ 3 ] ; // 2차원 배열
int a[ 2 ][ 3 ][ 4 ] ; // 3차원 배열

와 같다. 이론적으로는 무한 차원까지 가능하지만 일반적으로 시스템에 따라 7차원 이
내로 허용하고, 있으며, 2차원 이상의 배열을 다차원 배열이라 한다. 배열 요소들은 기
억 장소에 연속적으로 저장되며, 저장되는 순서는 행 우선( row wise ) 형식이다. 즉, 행
우선은 행( row )이 먼저 변경된 후 열( column )이 나중에 변경되는 것을 의미한다. 따
라서, 배열의 첨자 중 가장 오른쪽의 첨자부터 먼저 지정 범위만큼 변경된 후 왼쪽으로
순서를 옮겨 지정 범위만큼 변경된다.
[ 그림 9 - 1 ]은 다차원 배열의 선언과 기억 형태를 나타낸 것이다.

 
int a[ 2 ][ 3 ] ;

0행 a[ 0 ][ 0 ] a[ 0 ][ 1 ] a[ 0 ][ 2 ]
1행 a[ 1 ][ 0 ] a[ 1 ][ 1 ] a[ 1 ][ 2 ]
  0열 1열 2열

( a ) 2차원 배열

 
int a[ 2 ][ 3 ][ 4 ] ;

0 면 a[ 0 ][ 0 ][ 0 ] a[ 0 ][ 0 ][ 1 ] a[ 0 ][ 0 ][ 2 ] a[ 0 ][ 0 ][ 3 ] 1 행
a[ 0 ][ 1 ][ 0 ] a[ 0 ][ 1 ][ 1 ] a[ 0 ][ 1 ][ 2 ] a[ 0 ][ 1 ][ 3 ] 2 행
a[ 0 ][ 2 ][ 0 ] a[ 0 ][ 2 ][ 1 ] a[ 0 ][ 2 ][ 2 ] a[ 0 ][ 2 ][ 3 ] 3 행

1 면 a[ 1 ][ 0 ][ 0 ] a[ 1 ][ 0 ][ 1 ] a[ 1 ][ 0 ][ 2 ] a[ 1 ][ 0 ][ 3 ] 1 행
a[ 1 ][ 1 ][ 0 ] a[ 1 ][ 1 ][ 1 ] a[ 1 ][ 1 ][ 2 ] a[ 1 ][ 1 ][ 3 ] 2 행
a[ 1 ][ 2 ][ 0 ] a[ 1 ][ 2 ][ 1 ] a[ 1 ][ 2 ][ 2 ] a[ 1 ][ 2 ][ 3 ] 3 행
  0열 1열 2열 3열


배열에서 배열명은 배열 전체의 시작 주소를 나타내는 포인터 상수이다.
따라서 int a[ 2 ][ 3 ] ; 에서

a == &a[ 0 ][ 0 ]

이다. 또한 a[ 0 ]는 0행에 속하는 배열 요소들이 시작 주소를 가리키는 포인터 상수이
고 a[ 1 ]은 1행에 속하는 배열 요소들이 시작 주소를 가리키는 포인터 상수이다. 즉,

a[ 0 ] == &a[ 0 ][ 0 ] == a
a[ 1 ] == &a[ 1 ][ 0 ]

의 같은 관계가 성립된다.  

< 다차원 배열이 초기화 >

다차원 배열의 초기화도 1차원 배열의 초기화와 같다. 1차원 배열에서는 배열의 첨
자를 생략할 수 있었다. 2차원 배열에서는 초기화 데이터를 지정하는 경우 배열의 첫
번째 첨자는 생략할 수 있지만 2번째 첨바부터는 반드시 지정해 주어야 한다. 그렇지
않으면 컴파일러 에러가 발생한다.

다차원 배열을 초기화하는 일반 형식은 다음과 같다.

형 식 (2차원 배열의 경우)
기억클래스 데이터형 배열명[ 첨자 1 ][ 첨자 2 ] = { {초기값1 , 초기값2 , 초기값n} , { 초기값1 , 초기값2 , 초기값n } } ;

기억클래스 데이터형 배열명[ ][ 첨자 2 ] = { {초기값1 , 초기값2 , 초기값n} , { 초기값1 , 초기값2 , 초기값n } } ;

형 식 (3차원 배열의 경우)
기억클래스 데이터형 배열명[ ][ 첨자 2 ][ 첨자 3 ] ={ { {초기값1 , 초기값2 , 초기값n} ,
{ 초기값1 , 초기값2 , 초기값n } } ,
{ {초기값1 , 초기값2 , 초기값n} ,
{초기값1 , 초기값2 , 초기값n} } } ;

기억클래스 데이터형 배열명[ 첨자1 ][ 첨자 2 ][ 첨자 3 ] ={ { {초기값1 , 초기값2 , 초기값n} ,
{ 초기값1 , 초기값2 , 초기값n } } ,
{ {초기값1 , 초기값2 , 초기값n} ,
{초기값1 , 초기값2 , 초기값n} } } ;

다음 프로그램의 실행 결과를 나타내시오

▶ 프로그램
# include < stdio.h >
void main ( void )
{

int num[ 2 ][ 3 ]={ { 1, 2, 3} , { 4, 5, 6 } } ; /* 2차원 배열의 초기화 */
int i, j ;

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

for ( j=0 ; j<3 ; j++ )
printf ( " %5d " , num[ i ][ j ] ) ;
printf ( " \n " ) ;

}

}
▶ 실행 결과

1 2 3
4 5 6

 


다음 프로그램의 실행 결과를 나타내시오

▶프로그램
# include < stdio.h >
void main ( void )
{

// 3차원 배열의 초기화
int a[ 2 ][ 3 ][ 4 ]={ { { 1, 2, 3, 4 } , { 5, 6, 7, 8 } , { 9, 10, 11, 12 } } ,
{ { 13, 14, 15, 16 } , { 17, 18, 19, 20 } , { 21, 22, 23, 24 } } }

int i , j , k ;

for ( i=0 ; i<2 ; i++)
for ( j=0 ; j<3 ; j++)
{

for ( k=0 ; k<4 ; k++)
printf ( " a[ %d ][ %d ][ %d ] = %3d " , i , j , k , a[ i ][ j ][ k ] ) ;
printf ( " \n " ) ;

}

}
▶ 실행 결과

a[ 0 ][ 0 ][ 0 ] = 1 a[ 0 ][ 0 ][ 1 ] = 2 a[ 0 ][ 0 ][ 2 ] = 3 a[ 0 ][ 0 ][ 3 ] = 4
a[ 0 ][ 1 ][ 0 ] = 5 a[ 0 ][ 1 ][ 1 ] = 6 a[ 0 ][ 1 ][ 2 ] = 7 a[ 0 ][ 1 ][ 3 ] = 8
a[ 0 ][ 2 ][ 0 ] = 9 a[ 0 ][ 2 ][ 1 ] = 10 a[ 0 ][ 2 ][ 2 ] = 11 a[ 0 ][ 2 ][ 3 ] = 12
a[ 1 ][ 0 ][ 0 ] = 13 a[ 1 ][ 0 ][ 1 ] = 14 a[ 1 ][ 0 ][ 2 ] = 15 a[ 1 ][ 0 ][ 3 ] = 16
a[ 1 ][ 1 ][ 0 ] = 17 a[ 1 ][ 1 ][ 1 ] = 18 a[ 1 ][ 1 ][ 2 ] = 19 a[ 1 ][ 1 ][ 3 ] = 20
a[ 1 ][ 2 ][ 0 ] = 21 a[ 1 ][ 2 ][ 1 ] = 22 a[ 1 ][ 2 ][ 2 ] = 23 a[ 1 ][ 2 ][ 3 ] = 24

 


다음 프로그램의 실행 결과를 나타내시오

▶ 프로그램
# include < stdio.h >
void main ( void )
{

int score[ ][ 4 ]={ { 101, 85, 80, 75 } , { 102, 90, 100, 80 } , { 103, 75, 80, 70 } , { 105, 80, 50, 100 } } ;

int i , j , tot ;
float ave ;

printf ( " =================== \n " ) ;
printf ( " 학번 국어 영어 수학 합계 평균 \n " ) ;
printf ( " =================== \n " ) ;

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

tot = 0 ;
for ( j=0 ; j<4 ; j++) {

printf ( " %6d " , score[ i ][ j ] ) ;
tot += score[ i ][ j ] ;
}
ave = tot / 3.0 ;
printf ( " %6d %5.2f \n " , tot , ave ) ;

}

printf ( " =================== \n " ) ;

}
▶ 실행 결과

===================
학번 국어 영어 수학 합계 평균
===================
101 85 80 75 240 80.00
102 90 100 80 270 90.00
103 75 80 70 225 75.00
104 75 65 70 210 70.00
105 80 50 100 255 85.00
===================


★ 문자 배열 ★

C 언어는 문자열을 특수한 배열로 취급한다. 이러한 문자열은 ASCII 문자로 이루어
져 있고 문자열 끝에 null 문자 ( '\0' )가 있는 1차원 배열로 구성된다. 예를 들어, 문자열
상수 "hello"가 기억되는 형태는 다음과 같다.

1Byte 1Byte 1Byte 1Byte 1Byte 1Byte
'h' 'e' 'l' 'l' 'o' '\0'

이것은 다음과 같이 선언된 문자 배열의 기억 형태와 같다.

char ch[ 6 ]={ 'h', 'e', 'l', 'l', 'o', '\0' } ;

위의 문자 배열은 다음과 같이 선언할 수도 있다.

char ch[ ]="hello" ;

이때 , 문자 배열은 6개의 요소로 이루어지며 마지막 요소에 null 문자가 자동으로
들어간다. 그러나

char ch[ ]={ 'h', 'e', 'l', 'l', 'o' } ;

와 같이 선언된 문자 배열은 5개의 요소를 가지며, 배열의 마지막 요소가 null이 아니기
때문에 문자열이 될 수 없다. 또한 문자열 상수는 첫 번째 문자가 기억된 주소를 나타내
는 포인터 상수이다. 그러므로 문자열 상수는 문자형 포인터 변수에 기억될 수 있다.
즉,

char ch[ 6 ]={ 'h', 'e', 'l', 'l', 'o', '\0' } ;

또는

char ch[ ]= " hello " ;

와 같이 문자 배열에 기억시키거나

char *ptr ;
ptr = " hello " ;

또는

char *ptr = " hello " ;

와 같이 문자형 포인터 변수에 기억시킬 수 있다.
문자 배열을 다음과 같이 선언하면,

char ch[ 6 ]={ 'h', 'e', 'l', 'l', '\0' } ;

배열명 ch는 배열의 첫 번째 요소를 가리키는 포인터이므로 다음과 같은 관계가 성립
한다.

ch==&ch[ 0 ]
ch+1==&ch[ 1 ]
ch+2==&ch[ 2 ]
ch+3==&ch[ 3 ]
ch+4==&ch[ 4 ]
ch+5==&ch[ 5 ]

이것을 그림으로 나타내면 다음과 같다.

1Byte 1Byte 1Byte 1Byte 1Byte 1Byte
'h' 'e' 'l' 'l' 'o' '\0'
ch[ 0 ] ch[ 1 ] ch[ 2 ] ch[ 3 ] ch[ 4 ] ch[ 5 ]
ch ch + 1 ch + 2 ch + 3 ch + 4 ch + 5

또한 ch를 배열명이 아닌 다음과 같이 포인터 변수로 선언하면,

char *ch= " hello " ;

포인터 변수 ch는 문자열 " hello "의 첫 번째 시작 주소를 기억하고 있다. 이것을 나
타내면 다음과 같다.

*ch == ch[ 0 ] == 'h'
*( ch + 1 ) == ch[ 1 ] == 'e'
*( ch + 2 ) == ch[ 2 ] == 'l'
*( ch + 3 ) == ch[ 3 ] == 'l'
*( ch + 4 ) == ch[ 4 ] == 'o'
*( ch + 5 ) == ch[ 5 ] == '\0'

다음 프로그램의 실행 결과를 나타내시오

▶ 프로그램
# include < stdio.h >
void main ( void )
{

char ch[ ]={ 'H', 'e', 'l', 'l', 'o' } ;
char msg[ ]="world ! ! " ;
int i ;

for ( i=0 ; i<5 ; i++)
printf ( " %c " , ch[ i ] ) ;
printf ( " %s " , msg ) ;

}
▶ 실행 결과
Hello world ! !


다음 프로그램의 실행 결과를 나타내시오

▶ 프로그램
# include < stdio.h >
void main ( void )
{

char ch[ ]=" C programming language " ;
char *ptr ;
ptr = ch ;

for ( i=0 ; ptr[ i ] !='\0' ; i++)
printf ( " %c " , ptr[ i ] ) ;

printf ( " \n " ) ;

}
▶ 실행 결과
C programming language