base
)를 2(base-2, 이진수)로 실수(real numbers)를 나타내는 과학적 표기법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.
- For instance, Java characters are 16-bit Unicode characters, and strings are composed of a sequence of such characters. C++ offers both narrow and wide characters, but the actual size of each is platform dependent, as is the character set used. Strings can be formed from either type.
- This also implies that C++ compilers can automatically select the most efficient representation for the target platform (i.e., 64-bit integers for a 64-bit platform), while the representation is fixed in Java, meaning the values can either be stored in the less-efficient size, or must pad the remaining bits and add code to emulate the reduced-width behavior.
C에서 포인터의 역할? 배열을 포함한 데이터 구조의 요소(elements)를 참조하기 위한 메커니즘을 제공. 변수와 마찬가지로, 포인터는 value와 type이라는 두 측면을 갖는다.
- value: 어떤 오브젝트의 위치를 가리킨다
- type: 그 위치에 어떤 타입(예를 들어 정수 또는 실수)의 데이터가 저장되었는지 가리킨다
포인터를 제대로 이해하려면 포인터가 기계 레벨에서 어떻게 표현되고 구현되는지 확인할 필요가 있다.
hex 1 7 3 A 4 C
bin 0001 0111 0011 1010 0100 1100
연습 문제 2.1
- A. 0x25B9D2를 이진수로 변환
- B. 1010111001001001를 16진수로 변환
- C. 0xA8B3D를 이진수로 변환
- D. 1100100010110110010110를 16진수로 변환
#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;
}
연습 문제 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$ |
\(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\)
\(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진수로 변환하지 않고 다음 산술 연산 시도. 힌트: 정수 덧셈과 뺄셈에 사용하는 방법만 수정. 어떻게??
word
사이즈word size
: 포인터 데이터의 nominal size(명목상의 크기)라는데, 명목상의 크기가 뭐지?
32bit 프로그램
:
gcc -m32 prog.c
64bit 프로그램
gcc -m64 prog.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 |
signed
: 음수, 0, 양수 표현 가능unsigned
: 음이 아닌 수 표현 가능char
: 1 byteshort
, int
, long
: 어떤 범위의 크기를 제공*
: 포인터
T *p;
에 대해, p는 포인터 변수이며, 타입이 T인 오브젝트를 가리킨다
char *p;
에 대해, p는 포인터 변수이며, 타입이 char인 오브젝트를 가리킨다
little-edian
big-edian
big-edian
이지만, 실제로는 특정 OS가 결정되면 바이트 정렬 방식은 고정된다little edian
# int 타입 변수 x는 0x100 주소에 16진수 0x01234567 값 가질 경우
0x100 0x101 0x102 0x103
...| 67 | 45 | 23 | 01 | ...
little-edian
모드로만 작동big edian
# int 타입 변수 x는 0x100 주소에 16진수 0x01234567 값 가질 경우
0x100 0x101 0x102 0x103
...| 01 | 23 | 45 | 67 | ...
big-edian
모드로 작동4004d3: 01 05 43 0b 20 00 add %eax,0x200b43(%rip)
01 05 43 0b 20 00
:
43 0b 20 00
에 대하여
00 20 0b 43
이 되고20 0b 43
이 되어 우측 쓰여 있는 0x200b43
이란 값을 얻을 수 있다cast
나 union
은 오브젝트 생성 시 데이터 타입과 다른 데이터 타입으로 참조될 수 있도록 한다#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;
}
x
에 대한 포인터 &x
를 unsigned cahr *
타입으로 캐스팅((byte_pointer)
)하여 전달unsigned char
로 봐야 한다는 것을 알려준다sizeof(T)
? 타입 T를 저장하기 위해 필요한 bytes의 수를 반환
sizeof
사용? 서로 다른 머신 타입에 이식 가능한 코드 작성 위한 첫 단계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이다.
0x0027C8F8
:
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
null
로 종료되는 문자들의 배열로 인코딩 된다show_bytes("12345", 6)
을 실행해 보면, 31 32 33 34 35 00
가 출력된다
연습 문제 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
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
.s
, .o
, .i
파일을 얻을 수 있다$ 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
로 16진수 byte로 출력을 해보면 다음과 같다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 |