『Lena's by ☆볶기!!』: Restart !!

Rorolena
[C/C++] 5. 상수와 기본 자료형
이번 포스팅에선 상수와 기본 자료형에대한
간략적 개념을 살펴보도록 하겠습니다.


[C언어가 제공하는 기본 자료형의 이해]

1. 자료형은 데이터를 표현하는 방법입니다.

변수는 데이터의 저장을 위해서 할당된 메모리 공간에
붙여진 이름 이라고 앞서 설명하였습니다.

그런데 이 메모리 공간의 할당에 앞서 다음의 내용이 결정되어야 합니다.
"어떤 데이터를 저장 할것입니까"

이는 변수의 타입마다 저장을 위한 범위와 저장방식이 틀리기 때문입니다.
그래서 우리는 변수선언에 앞서 이것지 실수인지 정수인지 문자인지
그리고 얼만큼의 수를 쓸지를 미리 탐색하고 알맞은 타입을 선언해주어야 합니다.

2. 기본 자료형의 종류와 데이터의 표현범위
C언어는 여러가지의 자료형을 정의하고 있습니다.

자료형

크기

값의 표현범위

정수형

char 1바이트 -128이상 +127이하
short 2바이트 -32,768이상 +32.767이하
int 4바이트 -2,147,483,648이상 +2,147,483,647이하
long 4바이트 -2,147,483,648이상 +2,147,483,647이하
long long 8바이트 -9,223,372,036,854,775,808이상
+9,223,372,036,854,775,807이하

실수형

float 4바이트 ±3.4 × 10^-37이상 ±3.4 × 10^38이하
double 8바이트 ±1.7 × 10^-307이상 ±3.4 × 10^308이하
long double 8바이트 이상 double 이상의 표현범위

상기 제공되는 자료형을 가르켜 기본 자료형이라 합니다.

여기서 우리는 외이렇게 많은 자료형을 제공하는지
생각해보아야 하는대 이이유는 다음과같습니다.

- 데이터의 표현방식이 다르므로, 최소 둘이상의 자료형이 필요합니다.
- 메모리 공간의 적절한 사용을 위해서 다양한 크기의 자료형이 필요합니다.

3. 연산자 sizeof를 이용하면 자료형의 크기를 확인할 수 있습니다.
메모리 공간에서 소모하는 메모리의 크기를 바이트 단위로
계산해서 반환하는 sizeof라는 연산자가 존재 합니다.

이 연산자는 피연산자로는 변수와 상수뿐만 아니라, 자료형의 이름도
올 수 있기 때문에 이를 이용해서 자신이 사용하는 컴파일러의
자료형 별 바이트 크기도 확인할 수 있습니다.

sizeof 연산자의 사용방법은 다음과 같습니다.

int main(void)
{
int num = 10;
int sz1 = sizeof(num);
int sz2 = sizeof(int);
return 0;
}

위의 예제에서는 sizeof 연산자의 피연산자를 모두 소괄호로 감싸줬는데,
이소괄호는 int와같은 자료형은 필수지만 나머지 피연산자에 대해서는 선택사항 입니다.
하지만 소괄호를 사용하는 것이 가독성이 높기에 쓰도록합니다.

4. 정수를 표현 및 처리하기 위한 일반적인 자료형의 선택
지금까지 자료형 이야기를 하였는데 그렇다면
이런 다양한 자료형 중에서 무엇을 선택하는 것이 적합하겠는가?
이를 정할 어느정도 지침이 있다면 선택이 한결 수월 해질것 입니다.
그럼 먼저 정수 데이터 처리의 경우를 살펴봅시다.

가령 short형 범위 만으로 저장할수 있는 정수가 있다면 이를
int형 변수를 대신해서 short형 변수를 선언하는 것이 더 효율적인가?
이 질문의 답은 상황에 따라 다릅니다.

이는 아래의 코드를 통해 확인 해보도록 합시다.

int main(void)
{
char num1=1, num2=2, result1=0;
short num3=300, num4=400, result2=0;

printf("size of num1 & num2: %d, %d \n", sizeof(num1), sizeof(num2));
printf("size of num3 & num4: %d, %d \n", sizeof(num3), sizeof(num4));
printf("size of char add: %d \n", sizeof(num1+num2));
printf("size of short add: %d \n", sizeof(num3+num4));

result1=num1+num2;
result1=num3+num4;
printf("size of result1 & result2: %d, %d \n", sizeof(result1), sizeof(result2));
return 0;
}

실행결과는 다음과 같습니다.

여기서 우리가 주목 해야할점은 이부분입니다.
printf("size of char add: %d \n", sizeof(num1+num2));
printf("size of short add: %d \n", sizeof(num3+num4));
출력되는 값이 4바이트로 나오고 있습니다.
하지만 아래에 합한 값은 정상적으로 1, 2바이트로 표기 되고있습니다.

사실 이는 그럴만한 이유가 있는대
일반적으로 CPU가 처리하기에 가장 적합한 크기의 정수 자료형은 int로 정의됩니다.
따라서 int형 연산의 속도가 다른 자료형의 연산속도에 비해서 동일하거나 더 빠릅니다.
이렇듯 int형 연산이 CPU가 성능을 내기 가장 좋은 연산이다 보니
int보다 작은 데이터는 잠시 int형으로 바꿔 연산이 진행됩니다.

그럼 char와 short은 불필요 한것이 아니냐 라는 말이 나올수 있는대
이는 용량이 민감한 데이터 즉 영상,음성,그림 등등의 데이터 들을 예로 들수 있습니다.

5. 실수를 표현 및 처리하기 위한 일반적인 자료형의 선택
앞서 자료형 테이블에서 실수형의 범위를 보아서 알듯이
실수형 데이터가 표혈할 수 있는 값의 표현범위는 매우 넓습니다.
사실 값의 표현범위는 중요요소가 되지않습니다.

그럼 실수를 표현함에 가장 중요한것은 무엇인가 라는 물음에
실수 자료형의 가장 중요한 요소는 정밀도 입니다.
라고 대답할수 있습니다.

실수 자료형

소수점 이하 정밀도

바이트 수

float 6자리 4
double 15자리 8
long double 18자리 12

위의 표를 보면 double형의 경우, 소수점 15자리까지는
오차를 보장하니 이는 정밀도가 높은 편이라 할 수 있습니다.
이런 double형이 실수에선 int와 같은 역활을 합니다.

6. unsigned를 붙여서 0과 양의 정수만 표현하게 할 수 있습니다.
정수 자료형의 이름에 한해서 unsigned 선언을 추가하면, 0 이상의 값만 표현하는
자료형이 되어서, 표현할 수 있는 양의 정수범위가 두배로 넓어집니다.

[문자의 표현방식과 문자를 위한 자료형]
대부분의 프로그램에서는 사용자와의 상호작용이 필요하다 보니,
문자의 표현은 중요할 수밖에 없습니다.

따라서 문자를 저장하고 표현하는 방법을 알아보도록 하겠습니다.

1. 문자의 표현을 위한 약속! 아스키(ASCII)코드!
컴퓨터는 문자를 숫자로 저장 합니다.
그럼 이를 이용해 문자를 표현하도록 하려면
숫자를 문자에 견결 시키는 것이 유일한 방법 입니다.
즉, 미리 숫자와 문자를 약속해두는 겁니다.

이런 문자와 숫자의 약속은 종류가 다양한대,
그 중 가장 널리 사용되는 것이 ASCII코드 이며 아래와 같이 짜여있습니다.


2. 문자는 이렇게 표현되는 거구나!
그럼 문자를 숫자로 표현한다는 사실과 아스키코드의 존재를 알게 되었으니
이를 코드로 표현해보면 아래과 같이 사용할수 있습니다.

int main(void)
{
char ch1='A', ch2=65;
int ch3='z', ch4=90;

printf("%c %d \n", ch1, ch1);
printf("%c %d \n", ch2, ch2);
printf("%c %d \n", ch3, ch3);
printf("%c %d \n", ch4, ch4);
return 0;
}

우선 위 예제에 처음 보인 서식 문자 %c는 다음의 의미를 지닙니다.
"문자에 형태로 데이터를 출력9또는 입력)하라."

그럼 위 예제의 내용을 살펴보면 다음과 같이 요약할수 있습니다.
- 문자는 입력 방법에 따라서 문자와 숫자 어느것으로도 입력이 가능하다.
- 정수는 출력의 방법에 따라서 문자와 숫자로 출력이 가능하다.

그럼 여기서 나올수 있는 이야기로 앞서 int형 선언이 빠르다는 이야기를 했습니다.
그럼 문자도 int형 변수로 저장해야 하는것이 아닌가 하는 의문이 들수 있는데
이는 한가지 알아야 할점이 있습니다.

int형 선언이 빠른것은 연산을 실행 할때뿐이며
통상 문자를 가산할 일이 없다라는 점 입니다.

즉 문자간의 연산은 수행은 가능 하지만 문자간에
연산을 할일이 없기때문에 용량에 맞춰 사용 하는것 입니다.

[상수에 대한 이해]

상수는 크게 이름이 있는 상수와 이름이 없는 상수로 나뉘며,
상수 역시 앞서 설명한 int, double과 같은 자료형을 근거로 표현됩니다.

1. 이름을 지니지 않는 리터럴(Literal) 상수
상수란, 그 이름 그대로 변경이 불가능한 데이터를 뜻합니다.
상수와 관련해서 다음 코드를 보면

int main(void)
{
int num = 30 + 40;
}
위의 코드에서는 30과 40의 합을 결과를 변수 num에 저장하는 문장이 존재합니다.
그렇다면 대입연산과 덧셈연산중 어느쪽이 우선이겠는가?
당연히 덧셈연산이 우선입니다.

즉, 덧셈이 우선 진행되고, 그 결과값으로 변수 num을 초기화하게 됩니다.
그럼 CPU가 30과 40의 연산을 한다는 뜻인데,
메모리에 존재하지않는 값을 연산할수 없으니
그러기 위해서는 두수 모두 메모리상에 존재해야 합니다.
이런 이름 없는 상수를 가리켜 리터럴 상수 또는 리터럴 이라 합니다.

2. 리터럴 상수의 자료형

자료형은 변수뿐 아니라 상수를 위해서도 존재합니다.
메모라상에 저장되는 모든 데이터는 자료형이 결정되어야
저장하는 방법이 정해지기 때문입니다.
아래 예제를 통해 확인해보도록 합시다.

int main(void)
{
printf("lireral int size: %d \n", sizeof(7));
printf("lireral double size: %d \n", sizeof(7.14));
printf("lireral char size: %d \n", sizeof('A'));
return 0;
}

아마 소스를 보면서 리터럴 상수가 지정해준 변수 타입으로
타입이 잡힐꺼라 생각 했을지도 모르겠지만
리터럴 상수는 값에 상응하는 타입으로 알아서 잡히게 됩니다.

3. 접미사를 이용한 다양한 상수의 표현
정수형에는 int와 실수형 double만 있는 게 아닙니다.
그럼 이 이외의 자료형을 기반으로 상수를 표현하려면
해당 자료형을 의미하는 접미사를 붙여주면 됩니다.

예로 다음과 같이 코딩하면
float num1 = 5.789;

컴파일 에러는 나지 않지만 애러가 발생합니다.
이는 더블형 실수를 플롯형에 그냥 대입 되는 형태이기 때문입니다.

이를 방지하려면 다음과 같이 코딩하면됩니다.
float num1 = 5.789f;

이렇듯 C언어는 다양한 자료형의 상수를 표현할 수 있도록
다음과 같이 접미사를 정의하고 있습니다.

[정수형 상수표현]

접미사

자료형

사용의 예

U unsigned int unsigned int n = 1025U
L long long n = 2467L
UL unsigned long unsinged long n = 3456UL
LL long long long long n = 5768LL
ULL unsgined long long unsigned long long n = 8979ULL

[실수형 상수표현]

접미사

자료형

사용의 예

F float float f = 3.15F
L long double long double f = 5.789L

참고 사항으로 위의 접미사들은 대소문자 구분이 없으니
상황에 알맞게 눈에 잘 띄도록 사용하면 됩니다.

4. 이름을 지니는 심볼릭(Symbolic) 상수 : const 상수
심볼릭 상수는 변수와 마찬가지로 이름을 지니는 상수입니다.
이러한 심볼릭 상수를 표현하는 방법에는 두가지가 있는데,
그 중 하나는 'const 키워드'를 사용하는 방법이고, 또 하나는
매크로를 이용 하는 바업인대 매크로는 Chapter 26에서 언급하므로
여기서는 const 키워드를 사용하는 방법만 알아보겠습니다.

const 키워드를 이용해서 상수를 만드는 방법은 무척이나 쉽습니다.
변수 선언 시 const 선언만 추가하면 되기 때문입니다.
단, 이는 상수이므로 선언과 동시에 초기화 해야 하며
선언후에는 값을 변경시킬 수 없습니다.

컨스트 상수는 다음과 같이 사용하며
const int MAX = 100;
const double PI = 3.1415;

다음과 같이 코딩할수 없습니다.
const int MAX;
MAX = 100;

이경우는 MAX를 쓰레기 값으로 초기화를 시킨후
다시 100으로 대입하려는 경우이기 때문에 안됩니다.

참고로 상수와 관련해서 다음사실도 기억하시기 바랍니다.

상수의 이름은 모두 대문자로 표시하고, 둘 이상의 단어로 연결할 때에는
MY_AGE와 같이 언더바를 이용해서 두 단어를 구분하는 것이 관례입니다.

[자료형의 변환]

char형으로 표현되어 있는 데이터의 표현방식을 int형으로 바꾸거나,
int형으로 표현되어 있는 데이터의 표현방식을 double형으로 바꾸는 것이 바로 자료형의 변환입니다.

즉, 자료형의 변환이라는 것은 데이터의 표현방식을 바꾸는 것입니다.
이러한 자료형의 변환은 다음과 같이 크게 두 종류로 나뉩니다.

- 자동 형 변환(묵시적 형 변환)
- 강게 형 변환(명시적 형 변환)

1. 대입연산의 전달과정에서 발생하는 자동 형 변환
대입 연산자의 왼편과 오른편에 존재하는 두 피연산자의 자료형에 일지하지 않으면,
왼편에 있는 피연산자를 대상으로 형 변환이 자동으로 일어납니다.

이는 저장소의 자료형에 맞춰서 형 변환이 일어나야 값의 저장이
가능 하기 때문 임으로 이와 관련해서 다음 문잔을 보면 다음과 같습니다.

double num1 = 245;

이 경우 대입 연산자의 왼편에 있는 num1은 double형이지만 245는 int형입니다.
따라서 245는 245.0으로 변환되어 저장되게 됩니다.
이어서 다음 문장을 보면

int num2 = 3.1415;

이경우는 double형 상수가 int형 에 저장되기 위해서
정수이외의 수 즉 소수점 .1415를 버리고 3으로 변환되어 저장되게 됩니다.
그렇다면 다음의 경우에는 어떻게 형 변환이 진행되겠는가?

int num3 = 129;
char ch = num3;

참고 사항으로 int형 129의 비트 열은 다음과 같습니다.

00000000 00000000 00000000 10000001

그런데 이른 char형으로 바꾸면 1바이트가 되므로
상위 바이트의 손실이 발생합니다.
따라서 이는 정수로 -127이 됩니다.

이렇듯 상위 바이트의 손실로 인해서 부호가 바뀌는 경우도 있으니 주의해야 합니다.
다음은 위의 사항을 정리한것입니다.

▶ 정수를 실수로 형 변환하는 경우
정수에 해당하는 값을 실수로 표현하게 되는데,
이는 손실이 발생하지 않으나 실수의 표현이기 때문에
오차가 존재하게 됩니다.

▶ 실수를 정수로 형 변환하는 경우
정수는 소수점 이하의 값을 표현하지 못하기 때문에,
소수점 이하의 값은 버려집니다.

▶ 바이트 크기가 큰 정수를 바이트 크기가 작은 정수로 형 변환하는 경우
변환하고자 하는 정수의 바이트 크기에 맞춰서 상위 바이트를 단순히 소멸시킵니다.
그리고 이로 인해서 부호가 바뀔 수도 있으니, 주의가 필요합니다.

2. '정수의 승격(Integral Promotion)'에 의한 자동 형 변환
앞선 자동 형 변환 외에도 다음과같은 방식도 가능합니다.

short num1=15, num2=25;
short num3=num1+num2;

이러한 형태의 형 변환을 가리켜 정수의 승격이라 합니다.
그런데 위의 코드에서는 정수의 승격만 일어난 게 아닙니다.
num1+num2의 계살결과는 4바이트 크기의 int형 정수입니다.
따라서 이 값을 변수 num3에 저장하기 위해서, 앞서 언급한
대입연산 과정에서의 형 변환이 다시 일어나게 됩니다.

3. 피연산자의 자료형 불일치로 발생하는 자동 형 변환
사칙연산과 같은 기본적인 산술연산에는 두 개의 피연산자가 필요합니다.
그런데 이 둘읭 자료형은 일치해야 하며, 만약 일치하지 않을 시에는 자료형의
일치를 목적으로 자동 형 변환이 일어납니다.
그럼 아래 문장을 예로 이야기 해봅시다.

double num1 = 5.15 + 19;

이 문장에서는 실수형 데이터 5.15와 정수형 데이터 19의 합을 요구하고 있습니다.
그런데 정수와 실수는 표현방식이 다르므로 이를 CPU가 연산하기 위해선
어느 한쪽으로 형변환이 이루어져야 합니다.
그렇다면 어떤 쪽의 형변환이 좋은 선택이겠습니까?

만약 5.15를 정수로 반환하면 소수부의 손실이 발생하기 때문에
이럴때에는 다소 오차가 있어도 정수 19가 실수로 변환되어 계산됩니다.

이렇듯, 피연산자의 자료형이 일치하지 않아서 발생하는 자동 형 변환은
데이터의 손실을 최소화하는 방향으로 진행됩니다.
그리고 여기서 말하는 데이터 손실의 최소화 기분은 다음과 같습니다.

int -> long -> long long -> float -> double -> long double

여기서 참고사항이 두가지가 있는대 먼저

char와 short는 왜 생략됬는가 입니다.
char와 short형 정수의 경우는 정수의 승격에 의해
둘 다 int형 정수로 형 변환이 되기때문입니다.

그다음은 long long이 어째서 float보다 우선운위가 낮은가 입니다.
단순히 bit만 두고 보면 lonb lonb이 더 큰수를 표현 할수 있을것 같만
실은 범위로 보면 float형이 더큰수를 표현 할수있으며
또한 정수형 변수는 소수점을 저장하지 못하기 때문에
반드시 손실이 발생하기 떄문입니다.

4. 명시적 형 변환: 강제로 일어키는 형 변환
명시적 형 변환이란, 형 변환 연산자를 이용해서
강제로 형 변환을 명령하는 것을 의미합니다.
다음 예제를 통해 알아보도록 합시다.
{
int num1 = 3, num2 = 4;
double divResult;
divResult = num1 / num2;
printf("나누셈 결과: %f \n", divResult);
}


나눗셈의 결과로 0.75가 출력될 것을 기대한 분들에게는 안타깝지만
상기 문항은 연산결과의 자로형은 피연산자의 자료형과 일치하기 때문에
나눗셈 결과는 0이 되고 이값이 double형으로 자동 형 변환되어
변수 divResult에 저장됩니다.

그럼 상기 내용을 다음과 같이고치면
divResult = (double)num1 / num2;
우리가 원하던 0.75가 출력이 됩니다.

C언어에서의 소괄호는 연산의 순서를 지정하기 위해서도 사용되지만
형 변환을 명령할 때에도 사용이 됩니다.
이렇게 사용되는 소괄호를 가리켜 형 변환 연산자라 하며
연산의 결과로는 변환된 값이 반환됩니다.

즉 위의 변화 과정을 살펴보면 다음과 같습니다.

divResult = (double)num1 / num2;
       ↓
divResult = 3.0 / num2;
       ↓
divResult = 3.0 / 4.0;

이번 포스팅에선
수에대한 개념을 다루었는대요
이것은 굉장히 중요합니다.

프로그램은 죄다 수로 이루어져있기 때문에
정확한 수에대한 개념과 표현을 확실히 하지않으면
만들어진 프로그램들이 엉뚱한 작동을 할수도 있기때문이죠
그러니 이번 포스팅은 반드시 이해하고 넘어가주시면 좋습니다.

'보관소 ▦ ━━ > 작업 기록' 카테고리의 다른 글

2012/01/14 노려라 Tistory!! 포스팅 시작!  (2) 2012.01.14
[API] 리소스 활용  (4) 2012.01.13
[API] 기초적 입력  (0) 2012.01.12
[API] 기초적 출력  (0) 2012.01.09