C 프로그래밍 입문/데이터 배열: 두 판 사이의 차이

내용 삭제됨 내용 추가됨
Joshuajh (토론 | 기여)
추가 →‎배열
 
Joshuajh (토론 | 기여)
19번째 줄:
'''다시한번 말하지만, C 에서 인덱스는 1이 아니라 0에서 부터 시작된다.'''
 
<hr height='1px' width='20%' align='center'>
 
그럼 이제 내부에서는 어떻게 동작하는지를 설명 하겠다. 위의 코드에서 선언된 data 라는 이름이 달린 메모리 공간은 메모리의 <em>n</em> 번째 메모리 공간이라 가정해 보자. 다른 변수와는 달리 배열형 변수는 데이터 열(a sequence of data)이라는 개념이 사용 된다. ' '''<em>n</em> 번째 메모리 공간에 있는 float 형 데이터와 <em>n + 4</em> 번째 메모리 공간에 있는 float 형 데이터를 대상으로 덧셈을 한다.''' '라는 형태로 기술을 한다. 이때 float 형 데이터가 메모리 공간에 저장될 때 차지하는 메모리 공간은 4바이트 라는 사실은 알려져 있기 때문에 프로그램 코드를 작성하는 사람이 일일히 계산하지 않아도 컴파일러가 대신 처리해 줄 수 있을 것이다.
줄 25 ⟶ 24:
그래서 메모리 공간 <em>n</em>은 data라는 이름으로 사용되고, 메모리 공간 <em>n</em> 에서 시작되는 float 형 공간에 값을 넣을땐 ' '''data[0] = 3.1416''' '이 되는 것이다. 그리고, 앞에서 말했듯이 float 형 데이터는 메모리를 4바이트 차지한다는 것을 컴파일러도 알고 있으므로, 궂이 +4 라고 하지 않고, 'float 형 데이터의 넓이 * 1'이라는 개념을 사용할 수 있다. 그래서 '메모리 위치 data에서 4 바이트 떨어진 메모리 공간'을 ' '''data[1]''' '이라고 쓰는 것 이다.
 
===== 다차배열1차 배열의 초기화 =====
1차 배열을 선언과 함께 초기화 할 때에는 '데이터 리스트'라는 것을 사용한다. 데이터 리스트는 말 그대로 하나이상의 데이터를 중괄화({, }; brace)를 사용해서 묶어주는 것을 말한다. 아래 코드는 10개의 배열을 선언과 동시에 초기화 하는 동작을 수행한다.
<!-- 코드 블럭 태그
 
<div style='border: dashed 1px #0000ff; background: #f0f0ff; padding: 7px; margin: 10px 0px 10px 0px;'>
int iarr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
<source lang="c" line="GESHI_NORMAL_LINE_NUMBERS">
 
</source>
아래의 코드 역시 위와 동일한 동작을 수행한다.
 
int iarr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 
위의 코드에서는 배열의 길이를 명시적으로 지정하지 않았는데, 실제로 컴파일러가 배열에 들어갈 데이터의 갯수를 세어서 데이터를 저장할 공간을 확보하게 한다. 위와같은 코드는 데이터를 저장하기 위한 메모리를 궂이 명시적으로 확보하지 않아도 되는 경우에 유용하게 사용된다. '''주의할 점은 위와 같은 코드를 이용해서 메모리를 데이터 갯수를 기준으로 확보하는 경우 프로그램을 실행 할 때 그 크기가 변하지 않는 다는 점이다.''' 위 코드에 의해 할당되는 메모리의 크기는 컴파일 할 때 결정된다. 그러므로 아래와 같은 코드는 사용할 수 없다.
 
int iarr[] = { };
 
마지막으로 아래의 코드와 같이 작성한 경우, 할당된 메모리는 10개지만 실제로 초기화는 5개만 했으므로 처음 5개만 지정된 값으로 초기화 되고 나머지 다섯개의 메모리 공간은 0으로 초기화 된다.
 
int iarr[10] = { 1, 2, 3, 4, 5 };
 
<div style='border: dashed 1px #0000ffff0000; background: #f0f0fffff0f0; padding: 7px; margin: 10px 0px 10px 0px;'>'''참고''':
초기화 되지 않은 변수의 경우 그 변수가 저장되는 위치에 따라 초기화 되는 조건이 다르다.
 
스택 영역에 저장되는 지역 자동변수(local auto variable)의 경우 초기화 되지 않은 변수의 값은 어떤 값이 될지 예측할 수 없으며, 일반적으로 쓰레기값(garbage value) 라는 표현을 사용한다. 일반적으로 스택은 여러 함수에서 공유해서 사용하며, 함수가 호출되는 순서나 시점에 따라 직전에 호출 되었던 함수가 지역 자동변수를 저장하기 위해 사용했던 공간을 그대로 다시 사용하기 때문에 어떤 값이 저장되었을 지 예측이 불가능하다. 그렇기 때문에 초기화 되지 않은 자동변수의 값을 바로 읽는 코드를 작성 한다면 상황에 따라 다른 결과를 보이는 프로그램이 만들어지게 되며, 이러한 버그는 찾아내기가 아주 어렵기 때문에 가능하면 초기화 하는 습관을 들이는 것이 좋다.
 
스태틱 데이터 영역을 사용하는 전역변수, 스태틱 변수는 초기화 하지 않았다면 컴파일시에 컴파일러가 0으로 초기화해준다.</div>
 
<div style='border: dashed 1px #ff0000; background: #fff0f0; padding: 7px; margin: 10px 0px 10px 0px;'>'''참고''':
함수 내에서 선언되어 사용되는 배열의 경우 배열 자체가 스택 영역에 생성되고, 함수가 호출될 때 마다 해당 배열을 위한 메모리를 새로 생성하고 초기화 하는 작업을 하기 때문에 배열의 크기가 큰 경우에는 다른 방법을 사용해야 한다.
 
함수 내에서 크기가 큰 배열을 생성하는 경우 다음과 같은 문제점을 일으킬 수 있다:
:* 배열을 초기화 하는 경우 함수가 호출될 때 마다 값을 채워넣어야 하기 때문에 프로그램의 수행 속도가 느려질 수 있다. 특히나 자주 호출되는 함수인 경우에는 프로그램의 속도를 치명적으로 느리게 만들 수 도 있다.
:* 이론적으로 스택 -- 지역 자동변수가 저장되는 영역의 크기는 제한되지 않지만, 시스템에 따라 하드웨어적인 제한이나 스택 운영의 효율을 높이기 위해 스택의 크기를 제한하기도 한다. 그렇기 때문에 함수 내에서 선언되는 배열의 크기가 과도하게 큰 경우 스택 오버플로우 에러가 발생하고 프로그램이 강제 종료 될 수 도 있다.
:* 컴파일러의 종류나 옵션에 따라서는 컴파일된 프로그램의 크기가 커질 수 도 있다.
 
그렇기 때문에 배열의 크기가 조금이라도 크다는 느낌이 든다면<ref>개인적인 관점에서 볼때 100바이트가 넘는 크기라면 함수 내에서 배열을 사용하는 것은 대부분의 경우 그다지 좋은 선택은 아닙니다.</ref> 다른 메모리 영역을 사용하는 것을 고려해 볼 필요가 있다:
:* 스태틱 영역을 사용하는 변수 - 전역 변수나 스태틱 변수를 사용한다.
:* alloc() 계열의 함수를 이용하여 힙영역을 할당하여 사용한다.
</div>
 
-->
 
===== 다차배열 =====
앞에서 배열을 설명하기 위해 '전교생의 국어성적'이라는 대 단위 데이터에 대하여 언급 했었다. 그러나 앞서 언급된 배열만 가지고는 '전교생의 국어성적'을 저장하기엔 무언가 부족한 점이 있다. 실제로, 앞에서 설명한 배열만 가지고는 '한 반의 국어성적'을 처리할 수 있을 뿐 그 이상은 다루기가 어렵다. 물론 1반을 위한 배열, 2반을 위한 배열, 3반을 위한 배열... 이라는 식으로 반 갯수 만큼의 배열을 따로 만들어서 프로그램 코드를 만드는 방법도 있겠지만, 아무래도 각각의 경우를 따져서 코드를 작성해야 하므로 프로그램의 크기도 커지고 실수를 할 확률도 높아지게 될 것이다.
 
줄 56 ⟶ 87:
# 로컬 변수인 경우 스택의 크기
등이 있습니다.</ref> 그렇기 때문에 너무 큰 배열을 할당하는 것은 그다지 좋은 프로그래밍 습관이 아니다. 일정크기 이상의 배열은 로컬 오토 변수로 선언하지 말고 스태틱 변수나 전역 변수로 선언해서 사용하는 것이 '''stack overflow''' 에러를 발생시킬 확률을 제거하는 길이다. 사실 전역 변수나 스태틱 변수를 사용하여 배열을 만드는 것 보다 더 좋은 방법은 '''alloc()''' 계열의 함수를 이용해서 힙 영역을 이용하는 것이다.
 
 
===== 다차 배열의 초기화 =====
다차 배열을 초기화 하는 것은 다차배열의 개념과 앞서 설명한 일차 배열을 초기화 하는 것을 응용하면 간단히 해결이 된다. 일단 2차 배열은 '배열의 배열'이라는 개념이고, 1차 배열을 초기화 하기위해서는 중괄호({, })로 데이터 리스트를 묶어서 초기화를 했었다. 결국 2차 배열을 초기화 하기 위해서는 배열들을 중괄호로 묶어서 초기화 해주면 된다는 의미 이므로, 중괄호들을 중괄호로 묶어서 초기화 해주면 된다. 다음의 코드를 보자:
 
int i2arr[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
 
위와 같이 코드를 작성해 주면 3 x 3 배열이 초기화 될 수 있다. 추가적으로, 2차 배열이란 1차 배열의 배열이며, 배열은 같은 종류의 배열이 나열되어 있는 것 이므로 실제로 다음과 같이 초기화 할 수 도 있다.
 
int i2arr[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 
<div style='border: dashed 1px #ff0000; background: #fff0f0; padding: 7px; margin: 10px 0px 10px 0px;'>'''참고''':
조금만 생각해보면 2차 배열은 배열의 배열이고, 배열들이 나열되어 있는 것 이기 때문에 다음과 같은 코드를 작성 할 수 있을 것 이라고 생각하기 쉽다.
 
int i2arr[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
printf("%d\", i2arr[4]);
 
언뜻 생각하기엔 위와 같은 코드를 작성해서 컴파일, 실행시키면 5라는 출력값을 얻을 수 있을 것으로 생각하기 쉽지만 실제로는 이상한 값이 출력되거나 포인터 관련 에러와 함께 프로그램이 멈추는 경험을 하게 될 것이다. <i>이러한 문제가 생기는 이유는 n차 배열의 경우 배열 액세스 속도를 높이기 위해서 컴파일 시에 2차 이상의 배열의 시작주소를 따로 저장해 두었다가 쓰기 때문이다.</i> 다시 말해서 컴파일러가 컴파일 시에 i2arr[0][0], i2arr[1][0], 그리고 i2arr[2][0]의 주소를 별도로 저장해 두었다가 액세스 배열의 멤버를 액세스 할 때엔 저장된 주소를 사용해서 액세스 하기 때문에, 위의 코드는 i2arr[4][0]의 위치에 해당되는 데이터를 액세스 하겠다는 의미로 컴파일러가 이해 한다. 그러므로 위와 같은 의미를 갖는 동작을 하는 코드를 작성하고 싶다면 아래와 같이 해야 한다:
 
printf("%d\n", i2arr[0][4]);
 
위 코드로 바꾸어 프로그램을 실행시키면 5라는 값을 출력하는 것을 볼 수 가 있을 것 이다. 그러나 이런 코드는 컴파일러의 원리를 이해하는 용도로 한 번쯤 실험해 볼 가치는 있을 수 있으나 실제 프로그램을 작성할 때 이렇게 코드를 만드는 것은 프로그램 코드를 읽기 어렵게 만들 뿐 이므로 가능하면 사용하지 않는 것이 프로그램의 가독성을 향상시키는데 도움이 될 것 이다.</div>
아래 코드의 경우 i2arr[2][0], i2arr[2][1], 그리고 i2arr[2][2]가 명시적으로 초기화 되지 않았으므로, 세 배열 멤버는 모두 0으로 초기화 될 것이다.
 
int i2arr[3][3] = { {1, 2, 3}, {4, 5, 6} };
 
참고로 아래 코드는 배열멤버 모두를 0으로 초기화 하는 효과를 갖는다.
 
int i2arr[3][3] = { 0 };
 
 
===== 문자 배열 =====
'''문자배열'''이란, 당연한 이야기 이겠지만, ''''문자형 데이터(char)의 배열''' '이다. 기본적으로 char 타입은 부호있는 8비트 정수형 데이터로, 숫자와 영어 대소문자, 그리고 구두점 등의 기호들만 표현이 가능하다. 한글이 안되는 프로그램을 자주 볼 수 있는 이유가 바로 이 때문이다. 한글은 부호 없는 16비트의 데이터가 필요한데, 기본으로 제공되는 데이터가 부호 있는 8비트, 결국 7비트 데이터 이기 때문에 처리가 안되는 것 이다. 인터넷을 통해 메일을 보내거나 할 때도 한글로 보낸 이메일 제목이나 본문이 제대로 전달되지 않는 이유역시 이것과 관련이 깊다.<ref>주: 실상은 한글이 안되는 이유는 많은 가능성이 복합적으로 작용하기 때문입니다. 이렇게 단순하게 말하면 어떤 사실에 대해 고지식 하게 접근하는 사람들은 할 말이 아주 많겠지만, 이해를 돕는다는 허울을 뒤집어 쓰고 여러 이유중 한 가지만 기술 하였습니다.</ref>
줄 142 ⟶ 205:
</source></div>
프로그램이 조금 어이없어 보일지 모르겠지만, C프로그래밍을 하면서 프로그램의 수행속도 향상등의 이유로 위와 같은 코드를 작성하는 경우가 가끔 있다. 어찌 되었든 한 단위 이상의 메모리 공간에 값을 넣기 위해서는 위와 같이 반복작업을 수행해야 한다.
 
 
==== 주석 및 참고자료 ====