* 해당 글은 학과 수업으로 배운 내용과 코드가 포함되어 있으며 개인적 공부 목적으로 업로드하였습니다.
C0/C1에서는 int는 32 byte이었는데 C에서의 int size는 오랜 시간 변해왔다.
현재 C에서는 int size는 compiler에 의해 결정된다.
이를 우리는 implementation-defined라고 부를 수 있다.
단, 이는 undefined behavior와는 다른 개념이다.
C에서는 다양한 integer type이 존재한다.
long : int보다 큰 타입의 integers
short: int보다 작은 타입의 integers
char: short보다 작은 타입의 integers (1 byte)
C는 각 integer별로 unsigned variants를 제공한다.
-같은 bits
unsigned long
unsigned int
unsigned short
unsigned char
이때에 size_t는 memory address의 size를 의미한다
이를 표로 정리해보자면
signed | unsigned | c99 constraints | Today's size |
signed char | unsigned char | exactly 1 byte | 8 bits |
short | unsigned short | range at least(-2^15, 2^15) | 16 bits |
int | unsigned int | range at least(-2^15, 2^15) | 32 bits |
long | unsigned long | range at least(-2^31, 2^31) | 64 bits |
Integer Casts
우리는 다양한 숫자들 사이에서 casting을 반복할 수 있다.
-Literal number는 항상 int type을 가진다. compiler가 필요할 때 implicit casts를 진행한다.
long x = (long) 3;
하지만 이는 간혹 예기치 못한 결과를 가져올 수도 있다.
ex) long x = 1 <<40는 undefined behavior가 된다.
long x = (long) (1 <<40);
-1은 32 bits라서 40 positions으로 shift할 수 없다.
-> long x = ((long)1) << 40;
Casting Rules
새로운 type이 이전 type의 value를 나타내는데에 문제 없다면 그 value는 지켜져야 한다.
Ex 1)
signed char x = 3; // x is 3 (0x03)
unsigned char y = (unsigned char)x; //y is 3 (0x03)
Ex 2)
signed char x = 3; //x is 3 (=0x03)
unsigned int y = (unsigned int)x; //y is 3 (0x00000003)
Ex 3)
signed char x = -3; //x is -3 (=0xFD)
int y = (int)x; //y is -3 (0xFFFFFFFD)
Ex 4)
unsigned char x = 253; //x is 253 (=0xFD)
unsigned int y = (unsigned int)x; // y is 253 (=0x000000FD)
Ex 5)
int x = -3; //x is -3 (0xFFFFFFFD)
signed char y = (signed char) x; //y is -3 (0xFD)
만약 새로운 타입이 이전 타입의 value를 똑같이 나타낼 수 없지만 새로운 타입이 unsigned 라면 두가지 경우로 나뉜다.
- 새로운 타입이 작거나 같으면 least significant bits만 남겨진다.
ex)
int x = INT_MAX; //(0x7FFFFFF)
unsigned char y = (unsigned char)x; //(0xFF)
signed char x = -3; //(=0xFD)
unsigned char y = (unsigned char)x; //(0xFD)
만약 새로운 탕비이 더 크다면 bits는 sign extended 된다.
signed char x = -3; //(0xFD)
unsigned int y = (unsigned int)x; //(0xFFFFFFFD)
마지막으로 새로운 타입이 value를 그대로 나타낼 수 없지만 signed의 형태로 cast된다면 결과는 implementation defined가 된다.
ex)
int x = INT_MAX;
signed char y = (signed char)x;
int x = -241;
signed char y = (signed char)x;
Casting을 요약해보자면

Fixed-size Integers
bit patterns 역시 C에서 마찬가지로 작동하게 할 필요가 있다.
<stdint.h> header file이 정해진 size의 integer type을 제공하고 있다.
Fixed size signed | signed equivalent | unsigned equivalent | fixed size unsigned |
int8_t | signed char | unsigned char | uint8_t |
int16_t | short | unsigned short | uint16_t |
int32_t | int | unsigned int | uint32_t |
int64_t | long | unsigned long | uint64_t |
Float
float은 floating point numbers를 나타낸다.
ex) float x = 0.1;
float y = 2.0235E-27;
float과 int는 같은 bit를 사용하지만 float이 더 넓은 range를 포함한다.
-단 어떠한 숫자는 decimal 표현이 어려울 수 있다.
또한 이는 rounding error를 일으킬 수 있다.
ex)
#include <math.h>
#define PI 3.14159265
float x = sin(PI); //sin(pi)는 수학에서는 0이지만 0.0과는 다르다
float y = (10E20/10E10) * 10E10; //(10^20/10^10) * 10^10
//이는 compiler에 따라 값이 달라질 수도 있다.
for(float res = 0.0; res!=50; res+=0.1)
printf("res=%n", res);
//우리는 이 반복문이 50번의 iteration을 거친 후 멈추길 예쌍하지만 계속해서 돈다
// 그 이유는 0.1 이 binary에서 periodic number이기 때문이다.
이에 따라 bit을 더 추가한다고 해서 해결되지는 않는다.
Union/Enum types
// 0 = winter, 1=spring, 2=summer, 3 =Fall
int today = 3;
if(today==0)
printf("snow/n");
elseif (today==3)
printf("leaves!\n");
else
printf("sun!\n");
4개의 int로 나눠서 생각하는 것은 memory 낭비 문제가 있어보인다.
이때 enum type은
-프로그래머가 직접 연상기호 값을 고를 수 있게 해준다
-compiler가 이를 어떻게 implement할지 결정하도록 한다.
enum season {WINTER, SPRING, SUMMER, FALL};
enum season today = FALL:
if (today==WINTER)
printf("snow\n");
else if (today==FALL)
printf("leaves!\n");
else
printf("sun!\n");
Switch statement
switch statement는 numerical value에서 if-else를 대체할 수 있는 alternative 중 하나이다.
enum season {WINTER, SPRING, SUMMER, FALL};
enum season today = FALL;
switch(today){
case WINTER:
printf("snow\n");
break;
case FALL:
printf("leaves!\n");
break;
default:
printf("sun!\n");
각각의 값들은 case로 다뤄지며 다음 break이 오기전까지 혹은 switch statement가 끝날때까지 반복된다.
-default는 남은 value들의 케이스들을 다룬다.
만약 break가 빠진다면 하나의 케이스에 해당해도 다음 케이스를 지속해서 compile한다.
leaves에 int type만 있는 binary tree type을 정의해보자
이 tree는 : 1) 두 children을 향하는ㄴ pointer의 inner node일 수 도 있고
2) int data를 가진 잎을 가질 수도 있고
3) empty tree일 수도 있다.
enum nodekind = {INNER, LEAF, EMPTY};
struct ltree{
enum nodekind kind;
int data;
leafytree* left;
leafytree* right;
};
typedef struct ltree leafytree;
이는 메모리를 낭비하고 있음을 볼 수 있다.
이때 생각해볼 것이 union type이다.
Union type
union type은 같은 space를 다른 방향으로 나눠 사용할 수 있도록 한다.
enum nodekind = {INNER, LEAF, EMPTY};
struct innernode{
leafytree* left;
leafytree* right;
}; //innernode는 두가지 pointer를 소유한다.
union nodecontent{
int data;
struct innernode node;
};
struct ltree{
enum nodekind kind;
union nodecontent content;
};
typedef struct ltree leafytree;
이를 활용해 tree를 만들어보자
leafytree* T = malloc(sizeof(leafytree));
T -> kind = INNER;
T -> content.node.left = malloc(sizeof(leafytree)); //pointer를 follow하는게 아니면 dot notation을 활용해야한다
T -> content.node.left->kind = EMPTY;
T -> content.node.right = malloc(sizeof(leafytree));
T -> content.node.right->kind = LEAF;
T -> content.node.right->content.data = 42;
그림으로 나타내면 아래와 같다.

이또한 switch를 활용해 작성할수있다.
int add_tree(leafytree* T){
int n = 0;
switch(T->kind){
case INNER:
n+= add_tree(T->content.node.left);
n+= add_tree(T->content.node.right);
break;
case LEAF:
n = T->content.data;
break;
default:
n=0;
}
return n;
}
'👩💻Developer > Language' 카테고리의 다른 글
[C] Search in Graph (0) | 2022.12.16 |
---|---|
[C] Graph In C (0) | 2022.12.15 |
[C]Bytecode In C (1) | 2022.12.15 |
[C/C++] C언어의 Memory model (1) | 2022.11.29 |
[C] C언어에 대해 알아보자 (0) | 2022.11.25 |
댓글