2 정보의 표현과 조작(Representing and Manipulating Information)

2.0 개요

2.0.1 이진 수

2.0.2 세 가지 가장 중요한 숫자 표현

2.0.3 overflow

2.0.4 정수 연산과 실수 연산

2.0.5 정보 표현을 배우는 목적

Java built-in types are of a specified size and range defined by the language specification. In C++, a minimal range of values is defined for built-in types, but the exact representation (number of bits) can be mapped to whatever native types are preferred on a given platform.

2.1 정보 저장

2.1.0 개요

2.1.0.1 용어

2.1.0.1 저장

C에서 포인터의 역할? 배열을 포함한 데이터 구조의 요소(elements)를 참조하기 위한 메커니즘을 제공. 변수와 마찬가지로, 포인터는 valuetype이라는 두 측면을 갖는다.

포인터를 제대로 이해하려면 포인터가 기계 레벨에서 어떻게 표현되고 구현되는지 확인할 필요가 있다.

2.1.1 16진수 표기법

F2.2

hex     1    7    3    A    4    C
bin  0001 0111 0011 1010 0100 1100

연습 문제 2.1

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>

int get_char_length(char arr[]){
    int length;
    for (length = 0; arr[length] != '\0'; ++length);

    return length;
}

void print_buf(char *buf, int length) {
    for(int k = length - 1; k >= 0; k--) {
        printf("%d", buf[k]);
        if(k % 4 == 0) {
            printf(" ");
        }
    }
    printf("\n");
}

// https://stackoverflow.com/a/26615844
int hexadecimal_to_binary(char hex[], char *buf) {
    int i;
    int decimal = 0;
    int length = get_char_length(hex);

    // 16진수를 10진수로 변환
    for (i = 0; hex[i] != '\0'; ++i, --length) {
        if(hex[i] >= '0' && hex[i] <= '9') {
            decimal += (hex[i] - '0') * pow(16, length - 1);
        }
        if(hex[i] >= 'A' && hex[i] <= 'F') {
            // printf("%c(%d): %d - %d\n",hex[i], hex[i], hex[i] - 'A', 'A');
            // 'A' = 65, 하지만 16진수에서 A는 10이어야 하므로 55를 뺀다
            decimal += (hex[i] - ('A' - 10)) * pow(16, length - 1);
        }
        if(hex[i] >= 'a' && hex[i] <= 'f') {
            decimal += (hex[i] - ('a' - 10)) * pow(16, length - 1);
        }
    }

    int j = 0;
    // 10진수를 2진수로 변환
    while (decimal != 0) {
        // 0번 인덱스부터 채워넣는다
        buf[j++] = decimal % 2;
        decimal /= 2;
    }
    
    print_buf(buf, j);

    return j;
}

// https://stackoverflow.com/a/5307692
int binary_to_hexadecimal(char bin[], char *buf) {
    int num = 0;
    // bin의 메모리 위치
    char * tmp = bin;
    // bin의 메모리 위치
    char * a =  tmp;
    do {
        int b = *a == '1' ? 1 : 0;
        // printf("tmp: %d, a: %d, (%d << 1) | %d: %d\n", tmp, a, num, b, (num << 1) | b);
        // 1비트씩 앞으로 밀어내고, 그 다음 0 또는 1인 b 비트를 합쳐 나간다. 이렇게 간단해지네...
        num = (num << 1) | b;
        a++;
    } while (*a);
    // https://stackoverflow.com/a/14733875 hex 출력
    printf("0x%08X\n", num);    

    return 0;
}

int main() {
    char bin[100];
    char buf[100];
    char hex[100];
    int result;

    strcpy(hex, "0x25B9D2");
    result = hexadecimal_to_binary(hex, buf); // 10 0101 1011 1001 1101 0010
    strcpy(bin, "1010111001001001");
    result = binary_to_hexadecimal(bin, buf); // 0x0000AE49
    strcpy(hex, "0xA8B3D");
    result = hexadecimal_to_binary(hex, buf); // 1010 1000 1011 0011 1101
    strcpy(bin, "1100100010110110010110");
    result = binary_to_hexadecimal(bin, buf); // 0x00322D96

    return 0;
}

정수 to 이진수

\[16 = 2^4 = 1 \enspace 0000\]

2의 거듭제곱 $\to$ 16진수

\[2^{i} + \text{j개의 0}: \enspace 2,048 = 2^{11}, n = 11, = 3 + (4 \times 2) = 0x800\]

연습 문제 2.2:

n $2^n$ (정수) $2^n$(16진수)
5 32 0x20
23 8,388,608 $23 = 3 + (4 \times 5) \to$ 0x800000
15 32,768 $15 = 3 + (4 \times 3) \to$ 0x8000
13 8,192 0x2000 = $1 + (4 \times 3) = 13 = n$
12 4,096 $12 = 0 + (4 \times 3) \to$ 0x1000
6 64 $6 = 2 + (4 \times 1) to$ 0x40
8 256 0x100 = $0 + (4 \times 2) = 8$

정수 $\to$ 16진수

\(x = (q \cdot 16) + r\) \(314,156 = 19,634\cdot 16 + 12 = C\) \(119,634 = 1,227\cdot 16 + 2 = 2\) \(1,227 = 76\cdot 16 + 11 = B\) \(76 = 4\cdot 16 + 12 = C\) \(4 = 0\cdot 16 + 4 = 4\) \(0x4CB2C\)

16진수 $\to$ 정수

\(7\cdot 16^2 + 10\cdot 16^1 + 15\) \(7\cdot 256 + 10\cdot 16^1 + 15\) \(1,792 + 160 + 15\) \(1,967\)

연습 문제 2.3: 단일 바이트는 2 개의 16진수로 표현될 수 있다(8비트니까)

decimal binary hexadecimal
0 0000 0000 0x00
158 $2^7 + 2^4 + 2^3 + 2^2 + 2 =$ 1001 1110 $16 \cdot 9 + 14$: 0x9E
76 $2^6 + 2^3 + 2^2 =$ 0100 1100 $16 \cdot 4 + 12$: 0x4C
145 $2^7 + 2^4 + 2^0 =$ 1001 0001 $16 \cdot 9 + 1$: 0x91
174 1010 1110 = $2^7 + 2^5 + 2^3 + 2^2 + 2^1$ $16 \cdot 10 + 14$: 0xAE
60 0011 1100 = $2^5 + 2^4 + 2^3 + 2^2$ $16 \cdot 3 + 12$: 0x3C
241 1111 0001 = $2^7 + 2^6 + 2^5 + 2^4 + 2^0$ $16 \cdot 15 + 1$: 0xF1
Decimal Binary Hexadeciaml
$7 \times 16 + 5 \times 16^0 = 117$ $2^6 + 2^5 + 2^4 + 2^2 + 2^0 =$0111 0101 0x75
$\text{B(=11)} \times 16^1 + \text{D(=13)} \times 16^0 = 189$ $2^7 + 2^5 + 2^4 + 2^3 + 2^2 + 2^0=$1011 1101 0xBD
$\text{F(=15)} \times 16^1 + 5 \times 16^0 = 245$ $2^7 + 2^6 + 2^5 + 2^4 + 2^2 + 2^0=$1111 0101 0xF5

연습 문제 2.4: 숫자를 10진수 또는 2진수로 변환하지 않고 다음 산술 연산 시도. 힌트: 정수 덧셈과 뺄셈에 사용하는 방법만 수정. 어떻게??

2.1.2 데이터 사이즈

word 사이즈

gcc -m32 prog.c
gcc -m64 prog.c

기본 C 데이터 타입의 일반적인 크기

Signed Unsigned 32-bit bytes 64-bit bytes
[signed] char unsigned char 1 1
short unsigned short 2 2
int unsigned 4 4
long unsigned long 4 8
int32_t uint32_t 4 4
int64_t uint64_t 8 8
char *   4 8
float   4 4
double   8 8

T *p;에 대해, p는 포인터 변수이며, 타입이 T인 오브젝트를 가리킨다
char *p;에 대해, p는 포인터 변수이며, 타입이 char인 오브젝트를 가리킨다

2.1.3 Addressing and Byte Ordering

메모리에 byte를 정렬은 어떻게?

\[[x_{w-1}, x_{w-2}, \dotsb, x_1, x_0]\]
little edian
# int 타입 변수 x는 0x100 주소에 16진수 0x01234567 값 가질 경우
     0x100  0x101  0x102  0x103
...|  67  |  45  |  23  |  01  | ...
big edian
# int 타입 변수 x는 0x100 주소에 16진수 0x01234567 값 가질 경우
     0x100  0x101  0x102  0x103
...|  01  |  23  |  45  |  67  | ...
바이트 정렬은 언제 중요할까?
네트워크로 서로 다른 머신 간 이진 데이터 통신 시
정수 데이터를 나타내는 바이트 시퀀스
4004d3: 01 05 43 0b 20 00   add   %eax,0x200b43(%rip)
일반 시스템을 우회하는 프로그램
#include <stdio.h>

// typedef: 데이터 타입에 이름 부여 > 가독성 향상
typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, size_t len) {
    int i;
    for (i = 0; i < len; i++)
        printf(" %.2x", start[i]);
    printf("\n");
}

void show_int(int x){
    show_bytes((byte_pointer) &x, sizeof(int));
}

void show_float(float x){
    show_bytes((byte_pointer) &x, sizeof(float));
}

void show_pointer(void *x){
    show_bytes((byte_pointer) &x, sizeof(void *));
}


int main(){
    show_int(1); // 01 00 00 00
    show_float(2.0); // 00 00 00 40

    return 0;
}
Machine Value Type Bytes(hex) edian
Linux32 12,345 int 39 30 00 00 little
Wndows 12,345 int 39 30 00 00 little
Sun 12,345 int 00 00 30 39 big
Linux64 12,345 int 39 30 00 00 little
         
         
Linux32 12,345.0 float 00 e4 40 46 little
Wndows 12,345.0 float 00 e4 40 46 little
Sun 12,345.0 float 46 40 e4 00 big
Linux64 12,345.0 float 00 e4 40 46 little
         
         
Linux32 &ival int* e4 f9 ff bf little
Wndows &ival int* b4 cc 22 00 little
Sun &ival int* ef ff fa 0c big
Linux64 &ival int* b8 11 e5 ff ff 7f 00 00 little
   0    0    0    0    3    0    3    9 
0000 0000 0000 0000 0011 0000 0011 1001
                       | |||| |||| ||||
            0100 0110 01 0000 0011 1001 00 0000 0000
               4    6     4    0    E    4    0    0

연습 문제 2.5
다음과 같이 show_bytes를 세 번 호출한다고 했을 때, Little-edian과 Big-edian에서 출력되는 값은?

int a = 0x12345678;
byte_pointer ap = (byte_pointer) &a;
show_bytes(ap, 1);  /* A. */
show_bytes(ap, 2);  /* B. */
show_bytes(ap, 3);  /* C. */
  size_t Little Big
A 1 78  
B 2 78 56  
C 3 78 56 34  

연습 문제 2.6
정수 2,607,352는 16진수로 0x0027C8F8이며, 부동소수점 3,510,593.0은 16진수로 0x4A1F23E0이다.

          2    7    C    8    F    8
         1001 1111 0010 0011 1110 00
          ||| |||| |||| |||| |||| ||
100 1010 0001 1111 0010 0011 1110 0000
  4    A    1    F    2    3    E    0

오브젝트 주소는 어디에?

2.1.4 문자열 표현

연습 문제 2.7
다음 show_bytes 호출하면 뭐가 출력되는가? a는 0x61, z는 0x7A다

const char *m = "mnopqr";
show_bytes((byte_pointer) m, strlen(m)); // 6d 6e 6f 70 71 72

2.1.5 코드 표현

int sum(int x, int y) {
  return x + y;
}
gcc -c sum.c -lm

-rw-rw-r--. 1 aimpugn aimpugn    42  4월  2 22:56 sum.c
-rw-rw-r--. 1 aimpugn aimpugn  1240  4월  2 22:59 sum.o
$ gcc -c -save-temps sum.c
$ ll
-rw-rw-r--. 1 aimpugn aimpugn    42  4월  2 22:56 sum.c
-rw-rw-r--. 1 aimpugn aimpugn   190  4월  2 23:03 sum.i
-rw-rw-r--. 1 aimpugn aimpugn  1240  4월  2 23:03 sum.o
-rw-rw-r--. 1 aimpugn aimpugn   450  4월  2 23:03 sum.s

$ cat sum.s 
        .file   "sum.c"
        .text
        .globl  sum
        .type   sum, @function
sum:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
        movl    -4(%rbp), %edx
        movl    -8(%rbp), %eax
        addl    %edx, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   sum, .-sum
        .ident  "GCC: (GNU) 8.3.1 20191121 (Red Hat 8.3.1-5)"
        .section        .note.GNU-stack,"",@progbits
od -x -t c sum.s 

0000000    2e09    6966    656c    2209    7573    2e6d    2263    090a
         \t   .   f   i   l   e  \t   "   s   u   m   .   c   "  \n  \t
0000020    742e    7865    0a74    2e09    6c67    626f    096c    7573
          .   t   e   x   t  \n  \t   .   g   l   o   b   l  \t   s   u
0000040    0a6d    2e09    7974    6570    7309    6d75    202c    6640
          m  \n  \t   .   t   y   p   e  \t   s   u   m   ,       @   f
0000060    6e75    7463    6f69    0a6e    7573    3a6d    2e0a    464c
          u   n   c   t   i   o   n  \n   s   u   m   :  \n   .   L   F
0000100    3042    0a3a    2e09    6663    5f69    7473    7261    7074
          B   0   :  \n  \t   .   c   f   i   _   s   t   a   r   t   p
0000120    6f72    0a63    7009    7375    7168    2509    6272    0a70
          r   o   c  \n  \t   p   u   s   h   q  \t   %   r   b   p  \n
0000140    2e09    6663    5f69    6564    5f66    6663    5f61    666f
         \t   .   c   f   i   _   d   e   f   _   c   f   a   _   o   f
0000160    7366    7465    3120    0a36    2e09    6663    5f69    666f
          f   s   e   t       1   6  \n  \t   .   c   f   i   _   o   f
0000200    7366    7465    3620    202c    312d    0a36    6d09    766f
          f   s   e   t       6   ,       -   1   6  \n  \t   m   o   v
0000220    0971    7225    7073    202c    7225    7062    090a    632e
          q  \t   %   r   s   p   ,       %   r   b   p  \n  \t   .   c
0000240    6966    645f    6665    635f    6166    725f    6765    7369
          f   i   _   d   e   f   _   c   f   a   _   r   e   g   i   s
0000260    6574    2072    0a36    6d09    766f    096c    6525    6964
          t   e   r       6  \n  \t   m   o   v   l  \t   %   e   d   i
0000300    202c    342d    2528    6272    2970    090a    6f6d    6c76
          ,       -   4   (   %   r   b   p   )  \n  \t   m   o   v   l
0000320    2509    7365    2c69    2d20    2838    7225    7062    0a29
         \t   %   e   s   i   ,       -   8   (   %   r   b   p   )  \n
0000340    6d09    766f    096c    342d    2528    6272    2970    202c
         \t   m   o   v   l  \t   -   4   (   %   r   b   p   )   ,    
0000360    6525    7864    090a    6f6d    6c76    2d09    2838    7225
          %   e   d   x  \n  \t   m   o   v   l  \t   -   8   (   %   r
0000400    7062    2c29    2520    6165    0a78    6109    6464    096c
          b   p   )   ,       %   e   a   x  \n  \t   a   d   d   l  \t
0000420    6525    7864    202c    6525    7861    090a    6f70    7170
          %   e   d   x   ,       %   e   a   x  \n  \t   p   o   p   q
0000440    2509    6272    0a70    2e09    6663    5f69    6564    5f66
         \t   %   r   b   p  \n  \t   .   c   f   i   _   d   e   f   _
0000460    6663    2061    2c37    3820    090a    6572    0a74    2e09
          c   f   a       7   ,       8  \n  \t   r   e   t  \n  \t   .
0000500    6663    5f69    6e65    7064    6f72    0a63    4c2e    4546
          c   f   i   _   e   n   d   p   r   o   c  \n   .   L   F   E
0000520    3a30    090a    732e    7a69    0965    7573    2c6d    2e20
          0   :  \n  \t   .   s   i   z   e  \t   s   u   m   ,       .
0000540    732d    6d75    090a    692e    6564    746e    2209    4347
          -   s   u   m  \n  \t   .   i   d   e   n   t  \t   "   G   C
0000560    3a43    2820    4e47    2955    3820    332e    312e    3220
          C   :       (   G   N   U   )       8   .   3   .   1       2
0000600    3130    3139    3231    2031    5228    6465    4820    7461
          0   1   9   1   1   2   1       (   R   e   d       H   a   t
0000620    3820    332e    312e    352d    2229    090a    732e    6365
              8   .   3   .   1   -   5   )   "  \n  \t   .   s   e   c
0000640    6974    6e6f    2e09    6f6e    6574    472e    554e    732d
          t   i   o   n  \t   .   n   o   t   e   .   G   N   U   -   s
0000660    6174    6b63    222c    2c22    7040    6f72    6267    7469
          t   a   c   k   ,   "   "   ,   @   p   r   o   g   b   i   t
0000700    0a73
          s  \n
0000702
OS  
Linux32 55 89 e5 8b 45 0c 03 45 08 c9 c3
Windows 55 89 e5 8b 45 0c 03 45 08 c9 c3
Sun 81 c3 e0 08 90 02 00 09
Linux62 55 48 89 e5 89 7d fc 89 75 f8 03 45 fc c9 c3

2.1.6 불 대수(bool algebra) 소개

2.2 정수 표현

2.3 정수 산술 연산(Integer Arithmetic)

2.4 부동소수점(Floating Point)