[24.08.07] C언어의 기초3
Chapter 3
1. 이식 가능한 데이터형 : stdint.h 와 inttypes.h
- int_least8_t : 8비트 부호 있는 정수값을 가질 수 있는 최소폭 데이터를 말함
- 어떤 시스템에서 가장 작은 데이터형이 16비트라면, 그 시스템에서는 int8_t형이 정의되지 않는다. 하지만 int_least8_t형은 사용이 가능하고 16비트 정수로 구현된다.
- c99와 c11은 가장 빠른 계산을 허용하는 데이터형들의 집합을 정희한다.
- 예를 들어 사용하는 시스템의 정수형에 대한 별명으로 int_fast8_t형이 정의되어 있을것이다. 이것은 8비트 부호있는 값들을 가장 빠르게 계산할 수 있다.
* intmax_t형 : 부호있는 최대폭 정수형
* uintmax_t형 :부호없는 최대폭 정수형
* longlong 과 unsigned long보다 더 클 수도 있다.
#include <stdio.h>
#include <inttypes.h>
// inttypes.h (헤더파일) : PRId32를, 32비트 부호 있는 값의 적당한 포맷 지정자 (예를들어, d또는 l)를 나타내는 문자열로 정의할 것이다.
// 이식 가능한 데이터형과, 연관된 포맷 지정자의 사용법을 설명하는 예제
int main(void)
{
int32_t me32;
// 현재 우리나라는 64비트 컴퓨터지만 전세계적으로 봤을때는 32비트형 컴퓨터도 보편화 되어있다.
// int32_t : 32비트 부호있는 정수형을 나타낸다.
// 사용하는 시스템이 고정폭 데이터형을 지원하지 않는다면 어쩔것인가? C99와 C11은 필요한 대용 이름들에 대한 제2의 이름 집합을 제공한다.
me32 = 45933945;
printf("먼저, int32_t를 int형이라고 가정한다: ");
printf("me32 = %d\n",me32);
printf("이제, 어떠한 가정도 하지말자.\n");
printf("그 대신에, inttypes.h에 있는 \"macro\"를 사용한다: ");
printf("me32 = %" PRId32 "\n\n",me32);
return 0;
}
Chapter 4
2. 문자열 Strlen()함수
" 문자열의 크기를 문자 수로 알아낸다. "
#include <string.h>
- 즉, 문자열 복사, 문자열 검색 등의 함수들과 같은 계열에 속해 있고, string.h헤더파일을 사용한다.
char name[40]
- name의 범위가 [40]이지만 셀값은 문자열 + "Null" 포함한 개수값을 출력한다.
#include<stdio.h>
#include <string.h>
// String.h 헤더파일 선언 하면 strlen()을 포함한, 여러 문자열 관련 함수들의 함수 프로토타입이 들어있다.
#define PRAISE "You are an extraordinary being."
int main()
{
// 사용하는 시스템에 따라 %zd를 인식하지 못한다면
// %u 또는 %lu를 사용한다.
char name[40];
printf("실례하지만 성함이 어떻게 되시는지?\n");
scanf("%s",name);
// name 은 문자열이기 때문에 &(앰퍼샌드)가 없어도주소를 찾아줌
printf("반갑습니다,%s 씨. %s\n",name,PRAISE);
printf("이름은 %zd글자인데 메모리 셀 %zd개를 차지합니다.\n", strlen(name),sizeof name);\
printf("감탄문은 %zd글자인데 ",strlen(PRAISE));
printf("메모리 셀 %zd개를 차지합니다.\n\n", sizeof PRAISE);
return 0;
}
*** PRAISE : 띄어쓰기 포함 셀 개수 출력함
3. #define 지시자 / Const 변경자
float taxrate;
taxrate = 0.015;
- 변수 자료형 선언 => 변수에 상수값 대입
*** #define 사용
#define TAXRATE 0.015
상수이름 "TAXRATE" 나오고, 그 다음 상수의 값 0.015을 적어준다.
* = (등호) 를 사용하지 않는다.
#define NAME value
1. NAME에 자리에 의미있는 상수이름을 지정해준다.
2. VALUE에 상수값 또는 문자열을 지정하면 된다.
* #define은 문법이 아니고 전처리기가 처리하는 대체 메커니즘이기 때문에 세미콜론(;)을 사용하지 않는다.
* 전처리기 란 : main함수 돌기 전 처리 된다는 뜻
C/C++에서 #이 붙은 요소는 전처리기(preprocessor)라고 불리며, 컴파일을 하기 전에 미리 처리되는 역할을 한다.
#define 은 전처리기 중 하나로 만약 선언된 문자가 있다면 해당 문자는 컴파일 시 지정한 문자로 변경됩니다.
3. 변수를 대문자로 표현하는 이유
- C의 전통이다.
- 프로그램에서 대문자로 표기된 어떤이름을 보게 되었을때, 그것은 변수가 아니고 상수라는 것을 즉시 알게 해준다.
#include <stdio.h>
#define PI 3.14159
int main()
{
float area, circum, radius;
printf("피자의 반지름이 얼마냐?\n");
scanf("%f", &radius);
area = PI * radius * radius;
circum = 2.0 * PI * radius;
printf("피자의 기본 매개변수는 다음과 같다: \n");
printf("circumference = %1.2f, area = %1.2f\n\n", circum,area);
return 0;
}
* 13번째 줄
printf("circumference = %1.2f, area = %1.2f\n\n", circum,area);
- %1.2f = %.2f 랑 같은 표현이다. (소수점 아래 2자리까지 출력하라고 지시한다.)
- #define 지시자는 문자 상수와 문자열 상수에도 사용할 수 있다.
#define BEEP '\a'
#define TEE 'T'
#define ESC '\033'
#define OOPS "야, 드디어 해냈다!"
*** 잘못된 예
#define TDES = 20
digits = fingers + TDES;
digits = fingers += 20;
- 이렇게 정의하면, TDES가 20이 아니라 = 20으로 대체된다.
- 3번째 줄 : digits = fingers + TDES ;
- 4번째 줄 : digits = fingers += 20; 으로 변환된다.
Const 변경자
- const 키워드를 사용하여 변수 선언을 상수선언으로 변환하는것
const int MONTHS = 12; // MONTHS는 12를 나타내는 기호상
- MONTHS를 읽기 전용 값으로 만든다. (MONTHS의 값은 변경할 수 없다.)
- MONTHS를 표시할 수 있고, 계산에도 사용할 수 있다.
#define / Const 차이점
- 가장큰 차이는 const는 메모리를 할당받고, #define은 메모리를 차지하지 않는다.
(ex. const int 하면 read-only memory에 그 값이 올라가는 것이고 define은 pre-compiling에서 치환된다.
즉, 메모리를 차지하지 않는다.)
ex) Const int a = 125면 Text section 즉 Code memory (Rom 혹은 flash memory)에 들어가게된다.
* Rom / flash memory = 비휘발성 메모리
* Ram (Data Memory) = 휘발성 메모리 (읽고 쓰기 편집가능)
위 메모리 구조에서 확인되듯이 const로 선언된 변수는 ROM 영역에 할당되기에 const가 늘어나면 명령어코드가 늘어나며, 자주 호출하게 되면 성능이 저하될 수 있습니다. 말도 안 되게 많이 호출되지 않는 한 성능 저하를 체감할 순 없겠지만 const로 선언된 변수가 자주 호출되고 있다면 const 제거 혹은 다른 대체 방법을 찾아보는 것도 좋을 것 같다.
* #define 단점
const를 사용하면 디버깅 심벌이 생성되기 때문에 디버깅 시 watch window를 이용해서 상수의 값을 확인하기 편하지만 Define을 사용하면 치환이 되기에 디버깅 하기 불편한 부분이 있다.
*** #define 사용할때
- 매크로 함수로 많이 사용된다.
* 매크로 함수 : C언어에서는 #define 선행처리 지시문에 인수로 함수의 정의를 전달함으로써, 함수처럼 동작하는 매크로를 만들 수 있습니다. 이러한 매크로를 함수 같은 매크로(function-like macro) 또는 매크로 함수라고 합니다.
#include <stdio.h>
#define SUB(X,Y) X-Y
#define PRT(X) printf("계산 결과는 %d입니다.\n", X)
int main(void)
{
int result;
int num_01 = 15, num_02 = 7;
result = SUB(num_01, num_02);
PRT(result);
return 0;
}
참고 : https://www.tcpschool.com/c/c_prepro_macroFunc
4. 명단 상수의 사용
Limits.h (헤더파일) / float.h (헤더파일)
- 정수형 과 부동소수점형의 크기 제한에 관련된 자세한 정보를 각각 제공한다.
* Limits.h
#define INT_MAX +32767
#define INT_MIN -32768
- 이 상수들은 int형이 가질 수 있는 최대값과 최소값을 나타낸다.
- 32비트 int형을 사용한다면, 이 기호 상수(#define INT_MAX)들이 다른 값으로 정의 되어 있을것이다.
- 사용자 시스템이 4바이트 int형을 사용한다면, Limist.h파일에는 4바이트 int형에 적합한 INT_MAX와 INT_MIN이 정의 되어 있을것이다.
- printf("이 시스템에서 int형의 최대값 = %d\n", INT_MAX); 코드를 사용하여 출력할 수 있다.
* float.h
- float형과 double형이 지원하는 유효숫자의 자릿수를 나타내는 FLT_DIG, DBL_DIG와 같은 상수를 정의 하고 있다.
- double형과 long double형의 경우에, 이름에서 FLT가 DBL과 LDBL로 각각 대체되어 있다.
***C99 표준을 완전하게 지원하지 않기 때문에 LLONG_MIN 식별자를 인식 못할 수도 있다.
// defines.c : limits.h와 float.h에 정의되어 있는 기호 상수들을 사용한다.
#include <stdio.h>
#include <limits.h>
#include <float.h>
int main()
{
printf("이 시스템이 표현하는 수의 한계: \n");
printf("int형 최대값: %d\n", INT_MAX);
printf("long long형 최소값 : %lld\n",LLONG_MIN);
printf("이 시스템에서 1바이트는 %d이다.\n",CHAR_BIT);
printf("double형 최대값 : %e\n",DBL_MAX);
printf("float형 최소값 : %e\n",FLT_MIN);
printf("float형 정밀도는 소수점 아래 %d자리 까지 \n",FLT_DIG);
printf("float형 epsilon = %e\n\n", FLT_EPSILON);
return 0;
}
** Epsilon :다른 말로는 1보다 큰 가장 작은 숫자를 머신 입실론이라 한다.
FLT_EPSILON은 float.h 헤더 파일에 정의되어 있으며 이 값을 머신 엡실론(machine epsilon)이라 부릅니다. 어떤 실수를 가장 가까운 부동소수점 실수로 반올림하였을 때 상대 오차는 항상 머신 엡실론 이하입니다. 즉, 머신 엡실론은 반올림 오차의 상한값이며 연산한 값과 비교할 값의 차이가 머신 엡실론보다 작거나 같다면 두 실수는 같은 값이라 할 수 있습니다. 만약 double, long double을 사용한다면 머신 엡실론은 DBL_EPSILON, LDBL_EPSILON을 사용합니다.
** 실행결과
숫자 범위에 E라고 쓴부분 : 이 표현방법은 지수를 표현하기 위한 방법
E이후 나오는 숫자는 10의 거듭제곱을 나타내는 숫자이다.
123 = 1.23 E2
0.12 = 1.2 E-1
즉 정수 부분을 항상 한자리로 만들고 나머지 소수부분은 10의 거듭제곱으로 표현하는 거죠.
참고로 printf 함수의 %E 는 이런 지수 표현 방법으로 화면에 출력한다는 의미입니다.
또 두 변수의 차이는 소수의 정밀도 차이가 있습니다.
여기서 정밀도라는 것은 소수점 이하 몇자리까지 표시하는가를 나타냅니다.
각 변수의 정밀도는 다음과 같습니다.
float - 7자리
double - 14자리