공부했던 자료 정리하는 용도입니다.
재배포, 수정하지 마세요.
데이터 표현 방식
- 정수 자료형
* 정수 표현방식
* 음의 정수
* 정수 오버플로우
* 음수의 나머지 연산
* 정수 나눗셈 주의사항 - 실수 자료형
* 실수 표현 방식
* 실수 자료형의 최소값과 최대값
* 실수 자료형의 오버플로우와 언더플로우
* 실수 자료형과 증감 연산자
* 실수 자료형의 오차
* 실수 나눗셈 주의사항
* 실수의 나머지 연산 - 기본자료형의 종류와 데이터 표현 범위
* 정수 자료형의 최소값과 최대값
* 크기가 표시된 정수 자료형
* 실수 자료형의 정밀도 - boolean 자료형(논리 자료형)
- sizeof 연산자
- 아스키(ASCII) 코드 표
- 상수
- 리터럴(Literal) 상수
* 상수의 접미사 - 심볼릭(Symbolic) 상수 : const 상수
- 리터럴(Literal) 상수
bit : 컴퓨터가 표현하는 데이터의 최소 단위로서 2진수 값 하나를 저장할 수 있는 메모리의 크기
ex) 8bit = 1byte
정수 자료형
정수 표현 방식
``1byte``의 정수라면 가장 왼쪽의 bit가 부호 비트가 되고 나머지 ``7bit``가 정수의 크기를 나타내는 데 사용된다.
- MSB(Most Significant Bit) : 가장 왼쪽에 존재하는 bit로 부호를 표시하는 데 사용된다.
0 : 양수
1 : 음수
음의 정수
- 1의 보수법 : bit만 반대로 전환(`` 0 `` → `` 1 ``, `` 1 `` → `` 0 ``)
- 2의 보수법 : 1의 보수에서 +1
2의 보수가 양수와 더했을 때 @@0@@이 나오므로 컴퓨터는 2의 보수를 사용한다.
정수 오버플로우
c언어 표준에서는 부호 없는 정수와 부호 있는 정수의 오버플로우의 정의가 다르다.
- 부호 없는(unsigned) 정수 : 2의 거듭제곱 값으로 나머지 연산한 값(modulo power of 2)
ex) unsigned char에 257을 할당할 경우 → 257 % 2^8 = 1 - 부호 있는(signed) 정수 : 정의되지 않은 행동(undefined behavior). 컴파일러에 따라 오버플로우의 값이 다를 수 있다.
음수의 나머지 연산
C99 표준에서 나머지 연산자는 a == (a / b) * b + a % b 로 정의하고 있다. 따라서 a % b를 연산하면 a의 부호를 따른다.
ex1) 5 % (-3) → 2
→ 5 == (5 / (-3)) * (-3) + 5 % (-3)
→ 5 == (-1) * (-3) + 2
→ 5 == 3 + 2
ex2) (-5) % 3 → -2
→ (-5) == ((-5) / 3) * 3 + (-5) % 3
→ (-5) == (-1) * 3 + (-2)
→ (-5) == (-3) + (-2)
ex3) (-5) % (-3) → -2
→ (-5) == ((-5) / (-3)) * (-3) + (-5) % (-3)
→ (-5) == 1 * (-3) + (-2)
→ (-5) == (-3) + (-2)
정수 나눗셈 주의사항
정수를 0으로 나눌 수 없다. ``num1 = 1 / 0``과 같이 소스코드에서 정수를 0으로 직접 나누면 컴파일 에러가 발생한다. 변수에 0을 저장해서 나누면 컴파일 에러는 발생하지 않지만 실행하면 에러가 발생한다.
실수 자료형
실수 표현 방식
컴퓨터가 소수점 이하의 수를 표현하는 데 있어서 위와 같이 부동 소수점 방식을 사용하면 오차가 발생하게 된다. 이를 부동소수점 오차라고 한다.
실수 자료형의 최소값과 최대값
``float.h``에 정의되어 있다. ``%.40f``, ``%2f``처럼 서식지정자를 이용해서 출력하는 것도 가능하다. 소수점 자리가 긴 경우 ``%e``, ``%Le``를 사용하여 지수 표기법으로 출력하는 것도 가능하다.
- float형 : ``FLT_MAX``, ``FLT_MIN``
- double형 : ``DBL_MAX``, ``DBL_MIN``
- long double형 : ``LDBL_MAX``, ``LDBL_MAX``
실수 자료형의 오버플로우와 언더플로우
C언어에서는 실수 언더플로우를 0 또는 쓰레기 값으로 처리한다. 오버플로우인 경우에는 실수와 달리 최소값이 아닌 무한대(infinity)가 된다. (출력은 ``inf``로 출력된다. ``-``붙으면 ``-inf``로도 출력된다.)
실수 자료형과 증감 연산자
실수에서도 ``++``, ``--``연산자를 사용할 수 있으며, 1을 증가하거나 감소시킨다. 정수부분만 바뀌면 소수점 이하 자리에는 영향을 미치지 않는다.
실수 자료형의 오차
실수를 유한개의 bit로 표현하기 위해서는 근사값으로 표현해야 한다. 이 때문에 오차가 발생하는데 이런 문제를 부동소수점 반올림 오차(rounding error)라고 한다. 그래서 실수는 연산 값을 ==로 비교하면 안 되고 FLT_EPSILON을 이용해서 비교해야 한다.
- FLT_EPSILON
: ``float.h`` 헤더에 정의되어 있으며 이 값을 머신 엡실론(machine epsilon)이라고 한다. 어떤 실수를 가장 가까운 부동 소수점 실수로 반올림하면 상대 오차는 항상 머신 엡실론 이하이다. 즉, 머신 엡실론은 반올림 오차의 상한 값이며 연산한 값과 비교할 값의 차이가 머신 엡실론보다 작거나 같으면 두 실수는 같은 값이라 할 수 있다. double형인 경우엔 ``DBL_EPSILON``, long이면 ``LDBL_EPSILON``이다.
#include <stdio.h>
int main(void)
{
int i;
float num1 = 0.0f;
float num2 = 0.1f;
// 0.1을 10번 더한다.
for (i = 0 ; i < 10 ; i++)
{
num1 = num1 + num2;
}
printf("num1 : %.16lf\n", num1);
if (num1 == 1.0f)
printf("true\n");
else
printf("false\n");
return 0;
}
0.1을 10번 더해서 1.0과 비교하는 것인데 반올림 오차가 발생해 버렸기 때문에 등호로 비교하면 잘못된 결과가 나온다.
#include <stdio.h>
#include <float.h> // float의 머신 엡실론 값 FLT_EPSILON이 정의된 헤더
#include <math.h> // float의 절대값을 구하는 fabsf 함수를 위한 헤더
int main(void)
{
int i;
float num1 = 0.0f;
float num2 = 0.1f;
// 0.1을 10번 더한다.
for (i = 0 ; i < 10 ; i++)
{
num1 = num1 + num2;
}
printf("num1 : %.16lf\n", num1);
// '연산한 값 - 비교할 값'의 절대값이 FLT_EPSILON이하면 같은 값으로 본다.
if (fabsf(num1 - 1.0f) <= FLT_EPSILON)
printf("true\n");
else
printf("false\n");
return 0;
}
연산할 값과 비교할 값의 차이를 구한 뒤 ``FLT_EPSILON``이하인지 판단해야 정확한 결과가 나온다.
fabsf 함수를 사용해서 절대값으로 만들면 음수도 정상적으로 판단할 수 있다.
실수 나눗셈 주의사항
실수 값에 0.0을 나누면 정수와 달리 실행이 중단되지 않고 무한대(inf)가 나온다.
변수를 ``INFINITY``와 비교하거나 ``isinf``함수를 사용하면 값이 무한대인지 검사할 수 있다. 무한대는 아니지만 실수가 아닌 수(허수처럼 실수로는 표현할 수 없는 수)는 NaN(Not a Number)라고 하며 ``isnan``함수로 검사할 수 있다. (모두 math.h 헤더에 정의되어있다.)
참고 : dojang.io/mod/page/view.php?id=739
실수의 나머지 연산
나머지 연산은 정수에서만 사용할 수 있고 실수에서는 사용할 수 없다. (컴파일 에러 발생) 그래서 실수의 나머지 연산을 할 때는 ``math.h``헤더의 fmod, fmodf, fmodl 함수를 사용한다.
- fmod(나누어지는 수, 나누는 수) : double형 실수
``double fmod(double _X, double _Y);`` - fmodf(나누어지는 수, 나누는 수) : float형 실수
``float fmodf(float _X, float _Y);`` - fmodl(나누어지는 수, 나누는 수) : long double형 실수
``long double fmodl(long double _X, long double _Y);``
기본자료형의 종류와 데이터 표현 범위
- 정수의 경우 일반적으로 CPU가 처리하기에 가장 적합한 크기의 자료형은 ##int##기 때문에 ##int##형 연산의 속도가 다른 자료형의 연산속도에 비해서 동일하거나 더 빠르다. (##int##보다 작은 자료형은 ##int##로 변환된 후에 연산된다.) 그래서 연산의 횟수가 빈번한 경우에는 저장되는 값의 크기가 작더라도 ##int##형 변수를 선언하는 것이 좋다. (실수 자료형은 ##double##형을 사용하는 것이 좋다.)
- ##int##보다 작은 자료형들은 데이터 양이 많아 연산속도보다 데이터 크기를 줄이는 것이 더 중요한 경우에 주로 사용된다. ex) MP3와 같은 음성 데이터나 영상 데이터
- ##unsigned##를 붙이면 MSB도 값을 표현하는 데 사용하므로 표현할 수 있는 값의 범위가 2배가 된다.
자료형 | 크기 | 값의 표현범위 | |
정수형 | char | 1 byte | -128 이상 +127 이하 |
unsigned char | 0이상 (128 + 127) 이하 | ||
short | 2 byte | -32,768 이상 +32,767이하 | |
unsigned short | 0이상 (32,768 + 32,767)이하 | ||
int | 4 byte | -2,147,483,648 이상 +2,147,483,647 이하 | |
unsigned int | 0이상 (2,147,483,648 + 2,147,483,647)이하 | ||
long | 4 byte | -2,147,483,648 이상 +2,147,483,647 이하 | |
unsigned long | 0이상 (2,147,483,648 + 2,147,483,647)이하 | ||
long long | 8 byte | -9,223,372,036,854,775,808 이상 +9,223,372,036,854,775,807 이하 | |
unsigned long long | 0이상 (9,223,372,036,854,775,808 + 9,223,372,036,854,775,807)이하 | ||
실수형 | float | 4 byte | ±3.4 x 10^(-37)이상 ±3.4 x 10^38 이하 |
double | 8 byte | ±1.7 x 10^(-307) 이상 ± 1.7 x 10^308 이하 | |
long double | 8 byte 이상 |
double 이상의 표현범위 |
정수 자료형의 최소값과 최대값
``limits.h``에 정의되어있다. 정의된 최댓값을 넘어서면 오버플로우, 최소값보다 작아지면 언더플로우가 발생한다.
자료형 | 최소값 | 최대값 |
char | CHAR_MIN | CHAR_MAX |
short | SHRT_MIN | SHRT_MAX |
int | INT_MIN | INT_MAX |
long | LONG_MIN | LONG_MAX |
long long | LLONG_MIN | LLONG_MAX |
unsigned char | 0 | UCHAR_MAX |
unsigned short | 0 | USHRT_MAX |
unsigned int | 0 | UINT_MAX |
unsigned long | 0 | ULONG_MAX |
unsigned long long | 0 | ULLONG_MAX |
크기가 표시된 정수 자료형
자료형의 크기가 os마다 다르기 때문에 이식성을 위해서 사용하는 경우가 있다. ``stdint.h``에 포함되어 있다. (새로운 자료형이 아니라 내부적으로 typedef를 사용하여 운영체제에 맞게 자료형을 재정의 한 것이다.) 네트워크 프로그래밍이나 파일 압축 및 암호화 등 변수 크기에 민감한 프로그램을 만들 때 사용한다.
+ unsigned char는 파일이나 네트워크 패킷의 내용을 1byte 단위로 표현할 때 주로 사용한다. Window에서는 unsigned char를 BYTE 자료형으로 정의해서 사용한다.
- 부호 있는 정수(signed) 최대값 : ``INT8_MAX``, ``INT16_MAX``, ``INT32_MAX``, ``INT64_MAX``
- 부호 있는 정수(signed) 최소값 : ``INT8_MIN``, ``INT16_MIN``, ``INT32_MIN``, ``INT64_MIN``
- 부호 없는 정수(unsigned) 최대값 : ``UINT8_MAX``, ``UINT16_MAX``, ``UINT32_MAX``, ``UINT64_MAX``
- 부호 없는 정수(unsigned) 최소값 : 0
- int_least32_t, int_fast32_t : least가 붙은 자료형은 최소 몇 bit만 만족하면 된다는 의미
실수 자료형의 정밀도
- 정밀도 : 오차가 발생하지 않는 소수점 이하의 자리수
ex) 정밀도가 15자리면 소수점 이하 15자리 까지는 오차가 발생하지 않음을 보장한다.
실수 자료형 | 소수점 이하 정밀도 | byte 수 |
float | 6자리 | 4 byte |
double | 15자리 | 8 byte |
long double | 18자리 | 12 byte |
boolean 자료형(논리 자료형)
c에서는 0을 거짓, 0이 아닌 숫자를 참으로 사용하지만 ``stdbool.h``헤더를 사용하면 true를 참, false를 거짓으로 나타낼 수 있다. (출력할 때는 전용 서식지정자가 없기 때문에 ##%d(1, 0)##로 출력하거나, 문자열로 true, false를 직접 출력해야 한다.)
bool의 크기 : 1 byte. ``sizeof(bool)``로 확인할 수 있다.
■ stdbool.h를 쓰지 않고 bool 자료형을 사용하는 방법
1. 열거형으로 TRUE, FALSE 만들기
typedef enum _boolean {
FALSE,
TRUE
} boolean;
2. #define으로 1을 TRUE, 0을 FALSE로 정의
#define FALSE 0
#define TRUE 1
3. _Bool로 선언 (stdbool.h는 #define으로 _Bool을 bool로 재정의한것)
참고 : dojang.io/mod/page/view.php?id=745
sizeof 연산자
함수 모양을 하고 있지만 연산자이다. (함수는 실행 시점(run-time)에 호출되지만 sizeof는 컴파일 시점(compile-time)에 연산된다.) 메모리 공간에서 소모하는 메모리의 크기를 byte 단위로 계산해서 반환한다. 이 연산자의 피연산자로는 변수와 상수뿐만 아니라 자료형의 이름도 올 수 있기 때문에 이 연산자를 이용해서 자신이 사용하는 컴파일러의 자료형 별 바이트 크기도 확인할 수 있다.
sizeof 연산자의 형식
- sizeof표현식
- sizeof(자료형)
- sizeof(표현식)
#include <stdio.h>
int main(void)
{
char ch = 9;
int inum = 1052;
double dnum = 3.1415;
printf("변수 ch의 크기 : %d \n", sizeof(ch));
printf("변수 inum의 크기 : %d \n", sizeof(inum));
printf("변수 dnum의 크기 : %d \n", sizeof(dnum));
printf("\nchar의 크기 : %d \n", sizeof(char));
printf("int의 크기 : %d \n", sizeof(int));
printf("long의 크기 : %d \n", sizeof(long));
printf("long long의 크기 : %d \n", sizeof(long long));
printf("float의 크기 : %d \n", sizeof(float));
printf("double의 크기 : %d \n", sizeof(double));
return 0;
}
``sizeof`` 연산자를 이용해서 각 자료형 및 변수의 byte 크기를 확인해보는 예제이다.
아스키(ASCII) 코드 표
출처 : http://www.ministory.net/xe/3331
C언어는 미국 표준 협회(ANSI : American National standards Institute)에 의해서 제정된 아스키코드라는 표준을 선택해서 문자를 표현한다. 총 128개의 문자로 이루어져 있다.
#include <stdio.h>
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);
}
정수는 출력의 방법에 따라서 문자나 숫자 형식으로 출력이 가능하다.
상수
- 리터럴(Literal) 상수 : 이름이 없는 상수
- 심볼릭(Symbolic) 상수 / const 상수 : 이름을 지니는 상수
1. 리터럴(Literal) 상수
int num1 = 3 + 4;
int num2 = 7 + num1;
변수와 달리 이름이 없는 상수를 가리켜 리터럴(literal) 상수 또는 리터럴이라고 한다.
만약 위와 같은 코드가 있다고 하면 변수는 ``num1``, ``num2``로 총 2개이고 상수는 `` 3 ``, `` 4 ``, `` 7 ``으로 3개이다.
리터럴 : 값 그 자체를 의미
리터럴 접미사 : 리터럴의 크기와 부호 유무를 명시적으로 표기할 때 사용한다.
#include <stdio.h>
int main(void)
{
printf("literal int size : %d \n", sizeof(7));
printf("literal double size : %d \n", sizeof(7.14));
printf("literal char size : %d \n", sizeof('A'));
return 0;
}
리터럴 상수의 크기를 확인하는 예제이다.
■ 상수의 접미사
float num1 = 5.789; // double형 리터럴을 float에 넣으려고 해서 경고 발생
// 접미사를 붙여야 경고가 발생하지 않음
float num1 = 5.789f;
float num2 = 3.24F + 5.12F; //대문자 F도 사용가능
예를 들어 ``5.789`` 라는 실수 리터럴이 있을 때 이 값을 ``float``에 넣으면 값이 잘려 나갈 수 있다는 경고를 받는다. 선언한 자료형에 맞는 접미사를 붙여야 경고를 받지 않는다. 접미사들은 대소문자를 구분하지 않는다.
정수형 상수의 접미사
접미사 | 자료형 | 사용 예 |
생략 | int | |
u, U | unsigned int | unsigned int n = 1025U |
l, L | long | long n = 2467L |
ul, UL | unsigned long | unsigned long n = 3456UL |
ll, LL | long long | long long n = 5768LL |
ull, ULL | unsigned long long | unsigned long long n = 8979ULL |
실수형 상수의 접미사
접미사 | 자료형 | 사용 예 |
생략 | double | |
f, F | float | float f = 3.15F |
l, L | long double | long double f = 5.789L |
2. 심볼릭(Symbolic) 상수 : const 상수
const int MAX = 100;
// 초기화 하지 않으면 쓰레기값으로 초기화 되버림
const int MAX;
변수와 마찬가지로 이름을 지니는 상수이다. 심볼릭 상수를 표현하는 방법은 두 가지가 있는데 하나는 ``const`` 키워드를 사용하는 방법이고 또 하나는 매크로를 이용하는 방법이다. @@const@@를 사용할 경우 상수이므로 선언과 동시에 초기화해야 하며 초기화를 하고 나면 값을 변경할 수 없다. 보통 상수는 모두 대문자로 표시하고 둘 이상의 단어를 연결할 때는 언더바(## _ ##)를 이용한다.
'기타 > C' 카테고리의 다른 글
[C] 함수 (0) | 2020.04.27 |
---|---|
[C] 조건문, 반복문 (0) | 2020.04.16 |
[C] printf, scanf 함수 (0) | 2020.04.09 |
[C] 자료형 변환 (0) | 2020.04.08 |
[C] 함수, 변수, 연산자 (0) | 2020.04.03 |