너는 내 타입 I
C언어는 기본 자료형과 배열, 포인터, 구조체, 공용체, 열거형 등 확장 형과 사용자 정의 자료형 등을 제공하고 있으며 자료형과 변수는 매우 밀접한 관계를 갖습니다. 프로그램이 실행되는 동안 필요한 정보를 담아 두는 것이 변수이며 모든 변수는 자료형 즉 다시 말해 타입을 갖습니다. 이번 글에서는 기본 자료형에 대해서 알아 보도록 하겠습니다. 확장 자료형들은 앞으로 쭈욱 공부해 나갈 것입니다.
어느 날 비도 오고 날씨가 꿀꿀해서 혼자 사는 제가 감자를 깎아서 강판에 갈고 부추를 썰어 넣고 감자 부추 전을 만들었습니다. 그런데 옆집에 예쁜 아가씨도 혼자 살고 있으니 몇 개 갖다 드려야겠습니다. 곱게 부친 부추감자전을 그냥 손으로 집어 갖다 드립니까? 감자전 한장 하실래예? 부침개는 접시에 담아야 합니다. 그뿐 만이 아닙니다. 커피는 머그잔에 따라 마십니다. 소주를 와인 잔에 마신다고 생각해 보면 정말 웃기지 않습니까? 맥주를 소주 잔에 따라 마시면 얼마나 감질나겠습니까? 그릇마다 용도가 있는 법 입니다. 어느 누가 국 끓여서 접시에 담아 먹겠습니까? 물론 자취경력 3년이 넘어가게 되면 모든 것을 더께가 잔뜩 앉은 머그잔 하나로 해결합니다. 설거지가 귀찮기 때문이죠. 맥주도 그냥 머그잔에 따라 마십니다. 소주도 머그잔에 따라 마시면서 기분은 소주 잔으로 마시는 기분을 냅니다. 심지어 라면을 끓여도 머그잔으로 덜어 먹습니다. 저 놈이 지금 무슨 소리하는 거야? 하겠지만 프로그래밍의 고수가 되면 더 이상 타입은 의미가 없어 진다는 말입니다. 그저 모든 것이 메모리에 존재하게 되며 그것은 바이트의 나열일 뿐입니다. 산은 산이고 물은 물이로다 하신 어느 노승의 말씀이 생각납니다. 아마도 득도의 경지는 비슷한가 봅니다. 머그잔에 따랐다고 소주가 아닙니까? 머그잔에 덜어먹으면 라면이 아닙니까? 하지만 아무리 그릇이 없어도 국 끓여서 접시에 따라 먹는 일은 없길 바랍니다. 그것 말고도 충분히 힘든 일이 넘쳐 나는 세상입니다. 왜 그렇게 세상을 힘들게 삽니까? 부디 고수의 반열에 오르기 전 까지는 라면은 라면그릇에, 와인은 와인 잔에 따라 마셔야 합니다.
이제부터 변수는 데이터를 담는 그릇이며 모든 그릇에는 담을 수 있는 음식이 정해져 있는데 그것을 타입, 자료형이라 생각하면 되는 것입니다.
여기서 반드시 기억하셔야 할 것은 컴퓨터는 이진수만 사용한다는 것입니다. 지금부터 설명하게 될 모든 자료형들도 컴퓨터 내부에서는 모두 이진수에 불과합니다. 우리가 출력해서 보고 있는 3.141592가 실제 컴퓨터 내부에서는 3.141592가 아닐 수 도 있다는 겁니다. 안타깝지만 저는 아직 까지 내부에서도 3.141592로 표현되는 걸 본적이 없습니다. 혹시 3.141592가 실제로 3.141592로 표현되는 것을 본 적이 있거나 그것에 대해 알고 있는 분은 부디 댓글로 알려주길 바랍니다. 아마 없을 겁니다. IEEE에서 이미 표준으로 정했기 때문입니다.
정수형: 소수부가 없는 데이터로 모든 비트가 그대로 가중치를 갖게 되며 음수를 표현하기 위해 보수(Complement)를 사용합니다.
int: 16bit 또는 32bit 정수형으로 OS 즉, 운영체제에 따라서 -32,768 ~ 32,767 또는 -2,147,483,648 ~ 2,147,483,647까지 표현합니다.
short: 16bit 정수형으로 -32,768 ~ 32,767까지 표현합니다. == short int
long: 32bit 정수형으로 -2,147,483,648~2,147,483,647까지 표현합니다. == long int
실수형: (C언어를 비롯한 대부분의 컴퓨터 상에서) 지수부와 가수부를 갖는 데이터로 정수형보다 아주 큰 값이나 아주 미세한 값을 표현할 수 있습니다. 대부분의 일반 유한 실수 값일지라도 컴퓨터가 사용하는 이진수로 변환하게 되면 때론 무한히 반복됩니다. 그렇기 때문에 오차가 발생하게 되므로 정확한 계산을 목적으로 한다면 단정도 float형 대신 배정도 double형을 사용하길 바랍니다.
예를 들면 일반적인 산수에서 0.1을 100번 더하면 10이 되는 것은 당연합니다. 하지만 float 0.1을 100번 더하면 10보다 작은 값이나 10 보다 큰 값이 나오기도 합니다. 물론 double을 사용하면 오류를 줄일 수 있습니다. 하지만 double을 사용하는 것 보다 더 좋은 해결 방법이 있기도 합니다. 한번 생각해 보세요.
#include <stdio.h>
/*
* copyleft (l) 2006 - 2017 programmed by Sinclair
*/
int main() {
float f = 0.0f ;
double d = 0.0 ;
int i = 0 ;
int index ;
for ( index = 100 ; index > 0 ; --index ) {
f += 0.1f ;
} // end for
for ( index = 100 ; index > 0 ; index-- ) {
d += 0.1 ;
} // end for
for ( index = 100 ; index > 0 ; --index ) {
i += 1 ;
} // end for
printf("%f %f %f\n" , f , d , i / 10.0 ) ;
printf("%.20f %.20f %.20f\n" , f , d , i / 10.0 ) ;
return 0 ;
} // end main()
float: 4바이트 32bit 단정밀도 부동 소수점 실수형, 대부분의 경우 단정밀도 32bit IEEE 754 값을 나타냅니다.
double: 8바이트 64bit 배정밀도 부동 소수점 실수형, 대부분의 경우 배정밀도 64bit IEEE 754 값을 나타냅니다.
문자형: C언어에서 문자형은 가중치를 부여한 코드를 사용합니다. 대부분 ASCII코드를 사용하기는 하지만 다른 코드를 사용하는 경우도 있습니다. 때문에 프로그램을 좀 짜봤다 하는 분들은 ASCII코드에 익숙한 나머지 'A'대신에 숫자 65를 쓰기도 하는데 이것은 좋은 방법이 아닙니다. 또한 ASCII코드를 외우려는 분들이 있는데 그럴 바에야 차라리 연산자 우선순위와 예약어를 외우길 바랍니다. ASCII코드는 사용하다 보면 저절로 외워집니다. 항상 다니는 지하철 노선을 외우게 되는 것처럼 말입니다. 억지로 외우려고 하지 않아도 10년을 하루 같이 다녔더니 서울 지하철 2호선을 외우게 되더랍니다. C 언어에서는 문자를 표현할 때 이렇게 가중치가 있는 코드를 사용하기 때문에 8bit 정수형이라고 생각하셔도 됩니다. 실제로 거의 모든 연산이 가능합니다. 그래서 저는 어떤 경우에도 100을 넘지 않는 숫자에 대해서는 버릇처럼 char를 사용하기도 합니다.
char: 8bit 코드 값으로 -128 ~ 127까지 표현
void: 타입 또는 값이 없다는 의미로 리턴 값으로 사용하면 리턴 값이 없다는 뜻으로 인자로 사용하면 인자가 없다는 뜻으로 사용됩니다. 포인터 타입으로 선언하면 타입과 상관없이 주소 값을 저장할 수 있는 범용 포인터가 됩니다. 하지만 void 단독으로 어떤 변수를 선언할 수는 없습니다.
수치형 데이터를 다룰 때 주의할 것은 0으로 나누거나 0을 나누는 일입니다. 그렇게 되면 inf(무한), NaN(부정)의 값을 갖게 되고 프로그램은 비정상적으로 멈추게 됩니다. 또한 간혹 표현할 수 있는 최대 값을 넘어서게 되면 가장 작은 값인 음수로 변하기도 합니다. 일명 수의 loop 라고 합니다. 특히 정수형 큰 수끼리의 연산은 원하지 않는 값이 나올 수도 있다는 것을 기억하길 바랍니다.
또한 실수형을 사용할 때에는 실제 눈으로 확인된(화면에 나오는) 값이 실제 값이 아닐 수도 있습니다. 특히 실수형 자료에 비교 연산자를 사용할 때 화면에 출력되어 나온 값은 같은 값일 수 있지만 실제로 아주 미세한 값이 차이 날 수도 있습니다. 하루는 주말 오전 늦잠을 자고 있는데 전화가 왔습니다. 다급하게 묻습니다. 분명히 값이 0이고 찍어 봤더니 0이 확실하답니다. 그런데 0 이냐고 비교 해보면 자꾸 아니라고 한답니다. 대체 이게 왜 이러냐고 밤새도록 보고 있는데 죽겠답니다. 그래서 한마디 했습니다. 타입이 뭔데요? float랍니다. 그런데 아마 double 이어도 마찬가지였을 겁니다. ㅎㅎㅎ~
그래서 항상 강조합니다. 눈에 보이는 것이 전부는 아니라고 말입니다. 어쩌면 진실은 저 너머에 있는지도 모르겠습니다.
그 외에 긴 배정도를 위해서 long double 타입이 존재합니다. 하지만 long double의 경우 운영체제마다 또는 컴파일러마다 10바이트 또는 16바이트 하는 식으로 사이즈가 약간씩 다릅니다. 또한 64bit 프로세서를 위해 16바이트 정수형을 표현할 long long (== long long int) 타입이 지원되고 있습니다.
지금까지 우리가 배운 이러한 자료형들은 그릇의 종류이며 실제로 데이터를 담기 위해서는 변수를 선언해야 합니다. 변수 선언은 다음과 같습니다.
type 변수명(, 다른 변수명(, 또다른변수명) ) ;
변수의 이름은 잘 지어야 합니다. 우리들의 이름을 짓기 위해 많은 고민을 하셨을 부모님을 생각한다면 함부로 a, b, c 등 이런 이름을 사용하지는 않을 것 입니다. 변수 이름을 지을 때 주의할 것이 있습니다. 이름을 지을 때는 영어 대소문자와 숫자 _를 사용합니다. 안타깝지만 아름다운 우리글 한글 변수 이름은 지원하지 않습니다. 영문자나 _로 시작할 수 있고 보통 32글자까지 인식합니다. 하지만 컴파일러에 따라서 더 많은 문자를 인식하기도 합니다. 자세한 것은 컴파일러 레퍼런스를 꼭 확인하셔야 합니다. C언어는 대소문자를 구별하기 때문에 대소문자를 잘 구별해서 사용해야 합니다. 그리고 이미 배운 예약어들은 변수 이름으로 사용할 수 없습니다. const라는 변수는 사용할 수 없지만 Const나 CONST는 사용 가능합니다. 사용 가능하다는 것과 사용하기 적절하다는 것은 분명 다릅니다. 오해의 소지가 있으니 사용하지 않는 것이 좋습니다. 쓸데없이 너무 긴 이름이나 _ioi_, __________i__ 이런 식의 이름은 좋지 않습니다. 일반적으로 대문자를 단어 단위로 사용하는 헝가리언 노테이션을 따르는 것이 관례입니다. 앨런 튜링의 스승이자, 프로그래밍의 아버지라 부르며 존경하는 폰 노이만(Von Neumann, John; 1903-1957)이 헝가리 태생이기 때문에 붙여진 방법이라고 알려져 있습니다.
#Sinclair #씽클레어 #싱클레어 #씽클레어도씨 #씨언어 #씨프로그래밍 #C언어 #Cprogramming #C_Programming #C #Programming #Clanguage #C_Language