본문 바로가기
Programming/C

[따배씨] 섹션 3. 데이터와 C언어

by DONGKU 2020. 7. 16.

3.1 데이터와 자료형

본질적으로는 정수 자료형과 실수 자료형 두가지로 나뉨
문자도 정수자료형이 들어가있다 -> 문자도 정수에 포함

3.2 변수와 상수

literal constant: 문자그대로의 의미를(literal) 갖고 값이 바뀔 수 없는상수

Symbolic constant: const에 의해서 값을 바꿀 수 없는 상수가 된 변수

3.3 scanf 함수의 기본적인 사용법

scanf_s 사용하라고 오류가 뜰 경우(VS와 마이크로소프트)
방법1. #define _CRT_SECURE_NO_WARNINGS
방법2. 프로젝트 -> property -> Configuration Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions에 _CRT_SECURE_NO_WARNINGS 추가

왜 & 를 붙여야 하는가?
call by reference 개념으로, 요즈음의 프로그래밍 언어와 달리
C 는 함수가 한 번에 하나의 출력 밖에 못하기 때문에,
scanf() 가 여러 개의 입력을 받고 출력에 넘겨주는 상황 모두에 대응하기 위해
변수의 메모리에 직접 접근해서 바꿔주는 방식을 취했다.

3.4 간단한 입출력 프로그램 만들기

int i = 0, j = 0;  // scanf함수를 쓰더라도 변수는 언제든 초기화 해놓는 것을 권장

printf 형식 지정자

3.5 정수와 실수

부동 소수점: 한자어 뜰 '부'

부호있는 비트는 한 비트가 줄어듬

부동소수점의 단정밀도(32bit)와 배정밀도(64bit)
실수 표현에선 unsigned가 없고 맨앞엔 무조건 sign

왜 8이고 23이고 / 11이고 52인지는 전문가들이 정해놓은 정의같은거. 처리하는 범위가 이정도면 문제 없겠다 싶어서 정해진 것. 역사적인거니 넘어가자

float은 뒤에 f를 붙여줌
double은 float의 2배라서 double이라는 이름으로 명명

부동소수점 연산은 정수형대비 구조 복잡. 당연히 조금 느림. 싱글보다 더블이 느림.
최근 배정밀도도 하드웨어 발전으로 인해 빨라지고 있다.
but 복잡한 프로그램 만들 때 double을 남용하는건 위험하다.

3.6 정수의 오버플로우

#include <stdio.h>
#include <limits.h>  // 각각의 자료형이 가질 수 있는 가장 큰 값과 가장 작은 값을 알려주는 헤더 파일
#include <stdlib.h>

int main()
{
    unsigned int i = 0b11111111111111111111111111111111; // 0b는 2진수 직접 입력('b'inary), int이므로 32자리수 입력
                             // 4,294,967,295 // 2^32 = 4,294,967,296 인데 0부터 시작하므로 출력값은 -1되어 출력
                                               // 0 ~ 4,294,967,295(2^32 - 1)
    unsigned int u_max = UINT_MAX;  // limits.h 헤더파일에 의해 매크로 사용
    unsigned int u_min = 0;
    signed int i_max = INT_MAX;
    signed int i_min = INT_MIN;

    sizeof(unsigned int);  // 마우스만 올려 놓아도 자료형크기 표시(컴파일러가 아닌 VS에서 표시해주는 것)

    printf("%u\n", sizeof(unsigned int));  // 출력하려는 데이터가 unsigned면 %u
    printf("%u", sizeof(i));

    printf("%u\n", i);  // %d로 출력시 오버플로우되어 '-1' 출력

    printf("max of uint = %u\n", u_max);  // 4294967295
    printf("min if uint = %d\n", u_min);  // 0
    printf("max of int = %u\n", i_max);  //  2147483647
    printf("min if int = %d\n", i_min);  // -2147483648
}

오버플로우

    unsigned int u_max = UINT_MAX + 1;  // 가장 큰 값에서 +1
    unsigned int u_min = 0 - 1;        // 가장 작은 값에서 -1
    // i to binary representation, 2진수 형태 출력
    char buffer[33];
    _itoa(u_max, buffer, 2);        // stdlib에 포함. 정수 -> 문자. 10진수를 2진수로 바꿀 수 있음
                        // char * itoa(int val, char * buf, int radix) // buf의 주소를 반환
                                        // buf: 변환 받을 문자열 저장할 문자열 변수
                                        // radix: 변환 할 진수 10 or 2

    // print decimal and binary
    printf("decimal: %u\n", u_max);  // 0  // 가장 큰 값에서 +1헸더니 가장 작은 값이 됨
    printf("binary: %s\n", buffer);  // 0

    _itoa(u_min, buffer, 2);
    printf("decimal: %u\n", u_min);  // 4294967295    // 가장 작은 값에서 -1했더니 가장 큰 값이 됨
    printf("binary: %s\n", buffer);  // 11111111111111111111111111111111

3.7 다양한 정수형들

흐리게 표시된 것들은 생략이 가능

#include <stdio.h>
#include <limits.h>
#include <stdlib.h>

int main()

{
    char c = 65;
    short s = 200;
    unsigned int ui = 3000000000U;  // unsigned int의 리터럴을 표현해주는 기호 U
    long l = 65537L;
    long long ll = 12345678908642LL;

    printf("char = %hhd, %d, %c\n", c, c, c);    // char = 65, 65, A
    printf("short = %hhd, %hd, %d\n", s, s, s);  // short = -56(형식지정자에 맞지 않아 오버플로우), 200, 200  
    printf("unsigned int = %u, %d \n", ui, ui);  // unsigned int = 3000000000, -1294967296 // unsinged int는 무조건 %u
    printf("long = %ld, %hd\n", l, l);             // long = 65537, 1(형식지정자에 맞지 않아 다른 값 출력)
    printf("long long = %lld, %ld\n", ll, ll);   // long long = 12345678908642, 1942899938(형식지정자에 맞지 않아 다른 값 출력)

    return 0;

}

3.8 8진수와 16진수

RGB 컬러값은 16진수로 표현

※괄호 안은 모두 10진수

배수마다 해당 배수로 앞자리 표기

10진수 10(10x1) 20(10x2) ....

2진수 10(2x1) 100(2x2) 110(2x3) ...

8진수 10(8x1) 20(8x2) ...

16진수 10(16x1) 20(16x2) ...

제곱수마다 자리수가 늘어남

10진수 10 (10^1) 100(10^2) 1000(10^3) ...

2진수 10(2^1) 100(2^2) 1000(2^3) ...

8진수 10(8^1) 100(8^2) 1000(8^3) ...

16진수 10(16^1) 100(16^2) 1000(16^3) ...

√16진수를 10진수로 변환
0x34 -> 16^1 x 3 + 16^0 x 4 = 52


int main()
{
    // 모두 최대값
    unsigned int decimal = 4294967295;
    unsigned int binary = 0b11111111111111111111111111111111;  // 0b는 2진수 표현
    unsigned int oct = 037777777777;  // 0은 8진수 표현
    unsigned int hex = 0xffffffff;  // 0x는 16진수 표현

    // 모두 출력값 4294967295 동일
    printf("%u\n", decimal);
    printf("%u\n", binary);
    printf("%u\n", oct);
    printf("%u\n", hex);

    printf("%o %x %#o %#x %#X", decimal, decimal, decimal, decimal, decimal);  // 37777777777 ffffffff 037777777777 0xffffffff 0XFFFFFFFF
    // %o는 8진수, %f는 16진수, %#을 하면 각 진수 표시 까지 출력
    return 0;
}

3.9 고정 너비 정수

서로 다른 시스템에서 자료형의 사이즈가 다를 경우를 대비해 이식성이 높은 고정 너비 정수를 사용
나중에 멀티 플랫폼 관련 실무에서 쓰일일이 있음

#include <stdio.h>
#include <stdint.h>    // also included in inttypes.h
#include <inttypes.h>  // 고정 너비 정수 자료형은 printf 사용시 정확한 형식 지정자를 알 수가 없으므로, 그것을 미리 정리해둔 헤더파일

int main()
{
    int i;
    // #include <stdint.h> 해야 사용 가능 
    int32_t i32;            // 32 bit integer  // int32_t: 32숫자를 넣어 크기를 고정을 시켜버림 
    int_least8_t i8;        // smallest 8 bit 
    int_fast8_t f8;            // fastest minimum 
    intmax_t imax;            // biggest signed integers 
    uintmax_t uimax;        // biggest unsigned integers

    i32 = 1004;

    printf("me32 = %d\n", i32);
    printf("me32 = %" "d" "\n", i32);  // 매크로 #define PRId32 "d" // inttypes.h 헤더파일에 지정되있음
    printf("me32 = %" PRId32 "\n", i32);  // 매크로 PRId32: 'PRI'nt 'd'ecimal '32' bit 정수

    return 0;
}

3.10 문자형

컴퓨터는 2진수만 다루므로 문자도 모두 숫자로 바꾼다.
문자형도 정수형의 일부
ASCII Chart: 어떤 문자가 어떤 숫자에 대응되는지 정리 해놓은 표

#include <stdio.h>

int main()
{
    char c = 'A';
    char d = 65;    // d = 'A' (아스키코드로 'A' = 65)

    printf("%c %hhd\n", c, c); // A 65
    printf("%c %hhd\n", d, d); // A 65

    printf("%c \n", c + 1); // B (66)  // 문자와 정수를 혼용해서 사용

    char a = '\a';  // alarm (7)
    printf("%c", a);

    printf("\07"); // alarm 8진수 표현
    printf("\x7"); // alarm 16진수 표현

    float salary;
    printf("$______\b\b\b\b\b\b");
    scanf("%f", &salary);  // $_______  // \b(한칸 뒤로)와 scanf 혼용해서 사용하면 시각적으로 좋다

    printf("AB\tCDEF\n");    // AB        CDEF    // \t:tap으로 줄을 맞춰주는 용도
    printf("ABC\tDEF\n");    // ABC        DEF

    printf("\\ \'HA+\' \"Hello\" \?\n");
}

3.11 부동소수점형

컴퓨터는 실수를 부동소수점 자료형으로 저장을 할 때 Normalized significand를 사용

부동소수점수가 어떻게 메모리에 저장되는지.

fraction(분수) 소수점 아래부분.
지수를 저장할때도 음의 지수가 필요할때가 있음.
exponent에서도 signed, unsigned 나뉠 수 있음.
signed : 비트에 sign을 한칸 할당해주거나. unsigned로 해서 -127빼버리면 음수도 같이 표현됨
8비트 = 0~255 -127 해버리면 -127 ~ 128 로 바뀜 00000000 / 11111111은 다르게 사용한다고 함.
fraction(23bits)2^-1 / 2^-2 / 2^-3 ---- 순으로 진행.
부동 소수 표현방법이 메모리를 쪼개쓰기 때문에 의도치 못한 곳에서 우리가 문제가 생길수도 있다. 주의를 할 부분이 있다.

부동 소수점수는 공학/ 과학분야에서 꼭 필요.
메모리를 지수, significand를 표현하는 부분으로 쪼개쓰기 때문에 손해보는 부분이 있을수 밖에 없음. 정밀도가 낮다 (유효숫자 6개로 줄어듦. / 정수는 9 ~ 10자리)

#include <stdio.h>
#include <float.h>

int main()
{
    printf("%u\n", sizeof(float));            // 4
    printf("%u\n", sizeof(double));            // 8
    printf("%u\n", sizeof(long double));    // 8

    float f = 123.456f;    // float형 literal f
    double d = 123.456; // double형은 literal 없음

    float f2 = 123.456;        // literal이 없는 double형을 double보다 작은 float에 넣으려해 경고 메시지 출력
    double d2 = 123.456f;    // float형보다 double형이 더 크므로 경고 메시지 출력x 

    int i = 3;
    float f3 = 3.f;    // 3.0f  // 정수를 실수형에 넣을 때는 .을 꼭 찍어주자
    double d3 = 3.; // 3.0

    float f4 = 1.234e10f;  // 과학적 표기법 e(E)10 = 10^10

    float f5 = 0x2.1p1;        // 0x는 16진수, e대신 p사용
    double d5 = 1.0625e0;    // 0.625 = 1/16

    printf("%f %F %e %E\n", f, f, f, f);    // 123.456001 123.456001 1.234560e+02 1.234560E+02 // %e(E)는 과학적 표기법으로 출력
    printf("%f %F %e %E\n", d, d, d, d);    // 123.456000 123.456000 1.234560e+02 1.234560E+02
    printf("%a %A\n", f5, f5);                // 0x1.7400000000000p+4 0X1.7400000000000P+4 // %a(A)는 16진수 부동 소수점 형태로 출력
    printf("%a %A\n", d5, d5);                // 0x1.1000000000000p+0 0X1.1000000000000P+0 //  0.625 = 1/16 이므로 소수점 1에 대응

    return 0;
}

3.12 부동소수점의 한계

#include <stdio.h>
#include <float.h>    // float과 double이 가질 수 있는 가장 크고 작은 숫자 매크로 포함
#include <math.h>    // 수학 수식 관련 함수

int main()
{
    // round-off errors (ex1)
    float a, b;
    a = 1.0E20f + 1.0f;
    b = a - 1.0E20f;    
    printf("%f\n", b);    // 0.000000 출력, 1.0E20f에 비해 1.0f가 상대적으로 너무 작아 사라져버리는 부동소수점의 내부구조 한계

    // round-off errors (ex2)
    float c = 0.0f;
    c = c + 0.01f;
    c = c + 0.01f;
    c = c + 0.01f;
    c = c + 0.01f;
    c = c + 0.01f;
    c = c + 0.01f;
    c = c + 0.01f;
    c = c + 0.01f;
    c = c + 0.01f;
    c = c + 0.01f;

    printf("%f\n", c);    // 0.100000    // c를 100번 더하면 0.999999 출력 -> 부동 소수점은 0.01을 깔끔하게 표현할 수 없어 오차가 누적이 되어 이런 결과가 나옴
                                    // 2진수로 분수 부분을 저장하는 fraction 부분이 1/2, 1/4, 1/8, 1/16 이런 숫자들의 조합으로 10진수를 만들어내므로 딱 떨어지는 경우가 많지 않음


    // oerflow
    float f_max = 3.402823466e+38F;    
    double d_max = 1.7976931348623158e+308;
    printf("%f \n\n%f\n", f_max, d_max);    // 340282346638528859811704183484516925440.000000    // float에서 10진수의 정밀도를 보장해주는건 앞에서 6자리
                                            // 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000
    f_max = f_max * 100.0f;
    d_max = d_max * 100.0;
    printf("%f %f\n", f_max, d_max);        // inf inf -> infinite(너무 큰 숫자)


    // underflow
    float f = 1.401298464e-45F;    // #define FLT_TRUE_MIN 
    printf("%e\n", f);    // 1.401298e-45
    f = f / 2.0f;        // subnormal: 부동 소수점 형의 정밀도로는 더 이상 표현할 수 없이 작은 숫자가 돼버리면 숫자가 사라져버림
    printf("%e\n", f);    // 0.000000e+00

    float g = asinf(1.0f);  // 아크사인함수
    printf("%f\n", g);

    g = asinf(2.0f);        // 1.570796
    printf("%f\n", g);        // -nan(ind) -> not a number 아크사인은 2.0 값을 갖지 못함

    return 0;

}

3.13 불리언형

논리연산시 많이 사용

#include <stdio.h>
#include <stdbool.h>    // 불리언 타입 사용 헤더파일

int main()
{
    printf("%u\n", sizeof(_Bool));    // 메모리에서 주소를 배정받을 수 있는 최소 단위는 1 byte 이므로 0과 1로 한비트만 쓰는 불리언도 '1 Byte'

    _Bool b1;
    b1 = 0;    // false
    b1 = 1;    // true;

    printf("%d\n", b1);    // 1

    bool b2, b3;        // stdbool.h 헤더파일에의해 사용 가능한 불리언 타입
    b2 = true;            // true, false 모두 문자열아닌 예약어로 사용
    b3 = false;

    printf("%d %d\n", b2, b3); // 1 0

    return 0;
}

3.14 복소수형

복소수는 따로 만들어 쓰는 경우가 더 많다.

#include <stdio.h>
#include <complex.h>    // 복소수형 사용 위한 헤더파일

int main()
{
    _Dcomplex z;
    z._Val[0] = 1.0;
    z._Val[1] = 1.0;

    return 0;
}

댓글