Part 09 배열 안산1대학 디지털정보통신과 임 성 국
이 장의 내용 배열이란 배열 선언과 초기화 배열 인수 전달 2차원 배열 재미있는 몇 가지 이야기 배열 활용 연습
9.1 배열이란
이상한 요구사항 5개의 정수를 받아서 역순으로 출력하는 프로그램을 작성하라. 실행 예 점진적 세분화 입력: 19 10 8 29 36 출력: 36 29 8 10 19 점진적 세분화 5개의 정수를 읽어 x0, x1, x2, x3, x4에 저장한다. (scanf를 이용하여 바로 구현 가능) x4, x3, x2, x1, x0 순으로 출력한다. (printf를 이용하여 바로 구현 가능)
revPrint.c 마음에 듭니까? scanf와 printf 패턴이 중복되는 것이 마음에 불편하지요? "배열"을 이용하면 이 중복을 제거할 수 있습니다. scanf 중복 printf 중복 실행결과: 다섯 개의 정수를 입력해 주세요. 19 10 8 29 36 입력된 정수를 역순으로 출력하면 다음과 같습니다. 36 29 8 10 19
배열 소개 배열이란? 배열 원소 참조 배열 예 같은 자료형의 자료 여러 개를 하나로 묶은 것 또는 이런 자료를 나타내는 자료형 첨자 연산자(index operator) []를 이용하여 참조한다. C 배열의 첨자는 항상 0부터 시작한다. 배열 예 다섯 개의 원소로 이루어진 배열 x
revPrint2.c 크기 5인 int 배열 x 첨자연산자 []가 주소연산자 &보다 높은 우선순위임. 따라서 &(x[i])와 같은 뜻임 실행결과: 다섯 개의 정수를 입력해 주세요. 19 10 8 29 36 입력된 정수를 역순으로 출력하면 다음과 같습니다. 36 29 8 10 19
배열 원소 훑어보기 배열 원소를 차례로 훑어볼 경우에는 for 문이 제격이다. 표준 C(C99)와 예전 C와의 차이점 크기 N인 배열 a의 원소를 차례로 훑어보는 관용어구(programming idiom) for (int i = 0; i < N; ++i) { ... a[i] ... } 표준 C(C99)와 예전 C와의 차이점 표준 C에서는 for의 초기화 수식 부분에서 변수를 선언할 수 있음 이렇게 선언된 변수의 유효범위는 for 문 내부로 한정됨 표준 이전 C에서는 다음과 같이 별도의 블록으로 작성함 { int i = 0; for (i = 0; i < N; ++i) {
9.2 배열 선언과 초기화
배열 선언과 초기화 배열 선언 배열 초기화 첨자연산자 내부에 크기를 명시하여 선언 int a[5]; // 크기 5인 int형 배열 일반 변수 선언과 함께 선언할 수 있음 int x, y, a[5]; 배열 초기화 중괄호 {} 내부에 초기값을 나열 int a[5] = {1, 3, 5, 7, 9}; 초기화 목록이 있을 경우 배열 크기 생략 가능 int x[] = {1, 3, 5, 7, 9}; 초기값 개수와 배열 크기가 다르면… int x[5] = {1, 3}; // 나머지는 0 int x[3] = {1, 3, 5, 7, 9}; // 오류!
arrName.c 배열이름의 비밀 배열 이름은 배열이 할당된 메모리 공간의 시작주소(base address)임 실행결과: 배열 이름 값 x = 1245036 배열 전체 크기 sizeof(x) = 20 배열 원소 크기 sizeof(x[0]) = 4
9.3 배열 인수 전달
배열 인수 전달 배열을 인수로 전달할 때에는 '배열 이름'만 전달함 매개변수는 일반 배열처럼 선언하지만, 이 때 크기는 무시됨 배열 이름만 전달 매개변수 선언 시 배열 크기는 무시되므로 int sum(int a[]) 와 같은 뜻임 실행결과: 1 + 2 + ... + 10 = 55
배열 크기 전달 배열 매개변수 선언 시 크기는 무시되므로, 배열 크기는 별도의 인수로 전달함 배열 크기 계산 배열 전체 크기(sizeof x)를 원소 크기(sizeof x[0])로 나눔 배열 크기 전달 실행결과: 1 + 2 + ... + 10 = 55 2 + 4 + ... + 10 = 30
배열매개변수를 통한 배열원소 변경 배열매개변수 값 배열매개변수에 배열이름을 전달한 상황 배열매개변수에는 배열이름(배열 시작주소)가 전달되므로 배열매개변수를 통해 배열을 바꾸면 전달된 원본 배열이 변경됨 배열매개변수에 배열이름을 전달한 상황 함수 f에서 함수 g를 호출할 때, 배열 a를 매개변수 x[]에 전달했다면 x는 a의 시작주소(그림의 xxx)이므로 피호출자 g에서 x[1]의 값을 변경하면 a[1] 값이 변경된다.
sum2.c (1/2) sum main x main의 배열 x의 원소 값을 read가 채워주고 sum이 x의 원소 합을 구함
sum2.c (2/2) 함수 sum은 매개변수 a로 전달된 배열의 원소 합을 구하여 돌려준다. 프로시저 read는 배열의 각 원소 값을 표준 입력에서 읽는다. 실행결과: 5 개의 정수를 입력하세요: 1 3 5 7 9 입력한 숫자의 합은 25 입니다.
9.4 2차원 배열
다차원 배열 다차원 배열이란? 2차원 배열 선언 및 초기화 여러 개의 첨자를 갖춘 배열 선, 면, 입체, … 1차원, 2차원, 3차원, … 2차원 배열 선언 및 초기화 int x[2][3]; int y[2][3] = {{1,3,5}, {2,4,6}}; 2차원 배열을 초기화할 때에는 초기화 목록 내에 초기화 목록을 명시한다.
2차원 배열 활용 예 N×N 정방행렬을 입력받은 후, 각 행과 열의 합을 구하는 프로그램을 작성하라. N×N 행렬 x의 원소를 읽는다. 각 행과 각 열의 합을 구하여 rSum, cSum에 저장한다. 행렬 x와 rSum, cSum을 출력한다.
rcSum.c (1/3) 각 배열 원소를 0으로 초기화 정방행렬 x를 읽고 각 행과 열의 합의 구하고 x와 계산 결과를 출력함
rcSum.c (2/3) 정방행렬 a의 각 원소를 읽음 정방행렬 a의 각 행의 합 rSum과 각 열의 합 cSum을 구함
rcSum.c (3/3) 각 원소의 출력폭은 lseg의 길이보다 하나 작게 정함 각 원소 출력폭을 별도의 인수로 받음 실행결과: 3 x 3 정수 행렬을 입력하세요: 1 2 3 4 5 6 7 8 9 1 2 3 | 6 4 5 6 | 15 7 8 9 | 24 ---------------------+ 12 15 18
9.5 재미있는 몇 가지 이야기
실행 중 printf의 출력 폭 지정 printf의 출력 폭을 별도의 인수로 받을 수 있음 출력 폭을 *로 지정하고 인수로 출력 폭을 넘김 활용 예 width가 6이라면 다음 문장은 printf("%*d", width, a[i][j]); 다음 문장처럼 해석된다. printf("%6d", a[i][j]);
C에 2차원 배열은 없다?! 사실 2차원 배열은… 2차원 배열도 1차원 배열로 취급할 수 있음 1차원 배열의 1차원 배열 배열 원소가 1차원 배열인 배열 2차원 배열도 1차원 배열로 취급할 수 있음 int y[2][3] = {{1,3,5}, {2,4,6}}; y[0]는 {1,3,5}, y[1]은 {2,4,6} 따라서 int y[2][3] = {1,3,5,2,4,6}; 처럼 초기화할 수 있다.
9.6 배열 활용 연습
배열 활용 연습: 난수 발생 난수 발생 함수 난수 발생 범위 변경 초기 발생 난수 변경 int x = rand(); // 0~RAND_MAX 사이의 난수 발생 난수 발생 범위 변경 0이상 n이하 난수를 발생시키려면(범위 내 정수는 n+1개) rand() % (n+1) x이상 y이하의 난수를 발생시키려면(범위 내 정수는 y – x + 1개) rand() % (y – x + 1) + x 초기 발생 난수 변경 time이 반환한 calendar time을 srand의 인수로 전달 srand((unsigned int)time(NULL));
histogram.c (1/3) 발생시킬 난수 개수 난수 발생 범위(LB: 하한, UB: 상한) n개의 별표 *를 출력함 각 배열원소에 대해 첨자 값과 원소 값을 출력하고 원소 개수만큼 별표를 출력함
histogram.c (2/3) 초기 발생 난수 변경 lower 이상, upper 이하 범위의 난수를 발생시켜 되돌려 줌 LB이상 UB이하의 n개 난수를 발생시켜 배열 a의 각 원소에 저장
histogram.c (3/3) 초기 발생 난수를 변경하고 NUM개의 난수를 발생시켜 a에 저장한 후 실행결과: 0 [ 19]:******************* 1 [ 8]:******** 2 [ 20]:******************** 3 [ 3]:*** 4 [ 5]:***** 5 [ 12]:************ 6 [ 9]:********* 7 [ 12]:************ 8 [ 16]:**************** 9 [ 15]:***************
Key Point
Key Point 1 배열이란 같은 자료형의 변수 여러 개를 묶은 자료, 또는 이러한 자료형을 뜻한다. 배열 첨자 연산자는 대괄호 []를 이용하여 나타낸다. C 언어에서 배열 첨자는 항상 0부터 시작한다. 따라서 n번째 원소를 참조하려면 첨자 n-1을 사용해야 한다. 배열을 선언할 때는 배열 원소 자료형으로 선언하되 배열 이름 다음에 배열 크기를 첨자 연산자([])로 묶어서 선언한다. 배열 이름은 배열이 할당된 메모리 공간의 시작주소다. 배열을 함수의 인수로 전달할 때는 배열 이름을 전달한다. 일차원 배열 인수를 받는 형식 매개변수는 배열 크기가 생략된 형태로 선언한다. 배열을 인수로 전달할 때, 배열의 크기는 별도의 인수로 전달해야 한다.
Key Point 2 배열 인수를 전달받은 함수에서 배열 원소에 값을 저장하면 원본 배열의 원소 값이 바뀐다. 2차원 배열이란 배열의 첨자가 두 개인 배열이다. 2차원 배열을 초기화할 때에는 초기화 목록 내에 다시 초기화 목록을 기입한 형태를 사용한다.
Key Point(고급 주제) printf의 포맷 스트링에서 너비 지정 상수를 별도의 인수로 받도록 할 수 있는데 이 때 "%*d"와 같이 포맷 스트링의 너비를 *로 지정한다. C에는 2차원 배열이 없다. C에서 2차원 배열은 1차원 배열의 1차원 배열로 구현된다. C의 다차원 배열도 1차원 배열의 초기화 목록을 이용하여 초기화할 수 있다. C에서 난수를 이용해야 할 경우에는 난수 발생 함수 rand()를 이용할 수 있다. rand()가 생성하는 초기 난수를 변경하려면 srand((unsigned)time(NULL));와 같이 time()과 함께 srand()를 호출한 후에 rand()를 호출하면 된다.
요약(1/2) 배열 배열 선언 및 초기화 배열 인수 전달 배열 매개변수를 이용한 배열 변경 같은 자료형의 자료를 일렬로 묶은 자료 혹은 그러한 자료형 배열 원소는 첨자연산자 []를 통해 참조함 배열 선언 및 초기화 배열을 선언할 때에는 배열 크기를 첨자연산자 내에 명시함 배열을 초기화하기 위해서는 초기값 목록을 {, }로 묶어 명시함 배열 인수 전달 배열을 인수로 전달할 때에는 배열 이름만 전달함 배열 크기는 필요에 따라서 별도의 인수로 전달함 배열 매개변수를 이용한 배열 변경 배열 이름은 배열이 할당된 공간의 시작주소이므로 배열 매개변수를 통해 배열 원소를 변경하면 원본 배열도 변경됨
요약(2/2) 다차원 배열 난수 발생 함수 다차원 배열은 첨자를 덧붙임으로써 선언할 수 있음 다차원 배열은 배열의 배열로 간주함 2차원 배열은 1차원 배열을 배열 원소로 하는 1차원 배열임 난수 발생 함수 난수가 필요한 경우에는 rand()를 이용할 수 있음 난수 발생범위는 나머지 연산자 %를 이용하여 변경 가능 초기 발생 난수를 임의로 변경하려면 srand() 이용 srand((unsigned int)time(NULL));
프로그래밍 실습
▶ 프로그래밍 실습 1 중심극한 정리를 확인하는 프로그램 작성 크기 N인 모집단 a를 생성한 후, 이를 확률분포로 변경 double sum(double a[], int NUM); void to_pdf(double a[], int NUM, double sum); 모집단 a에서 크기 2인 샘플을 모두 구한 후 샘플 평균들을 배열 b에 기록함 void normalize(double a[], double b[], int NUM); 샘플들의 평균 분포 b도 확률분포로 변경(to_pdf 이용) 배열 값을 scale 만큼 확대하여 히스토그램을 그림 void histogram(double a[], int NUM, int scale); 모집단 a의 확률분포와 샘플 평균 분포 b의 확률분포를 히스토그램으로 출력함(b의 히스토그램은 정규분포와 유사해야 함)
▶ 프로그래밍 실습 1 (출력 예) 샘플 평균의 분포 b의 확률분포를 출력하면 다음과 같이 정규분포와 유사하게 출력되어야 한다 x = 0 [ 0.04]:*** x = 1 [ 0.09]:******** x = 2 [ 0.11]:*********** x = 3 [ 0.14]:************** x = 4 [ 0.19]:****************** x = 5 [ 0.16]:**************** x = 6 [ 0.11]:*********** x = 7 [ 0.09]:******** x = 8 [ 0.06]:***** x = 9 [ 0.01]:*
▶ 프로그래밍 실습 2 틱택토(Tick-Tac-Toe) 프로그램 말판의 좌표는 그림과 같이 정한다. 각 위치의 말판을 입력하면 빈 자리인가 확인하고 말판을 놓은 후, 변화된 말판을 출력한다. 각 선수는 O, X로 출력한다. 선수 O가 b3에 두었다면 그림과 같이 출력되어야 한다. 매번 돌을 놓을 때마다 승패를 판단하고, 특정 선수가 이겼을 경우 축하 메시지를 출력한다. 1 2 3 +---+---+---+ A | | | | B | | | O | C | | | |