1번 질문에 대한 답.
위 캡쳐는 실제 산업현장에서 사용되는 RAM의 일종인 NVRAM의 핀 어사인입니다. NVRAM은 램과 건전지가 결합되어 저장된 내용이 반 영구적으로 보존되게 만든 특수형 램인데, 기본적인 동작은 RAM과 동일합니다.
위에서 주목할 것은 DQ0~DQ7로 표현된 핀들인데, 실제 하드웨어의 롬과 램같은 IC들은 저 핀들이 나와있습니다. 핀 하나당 2진수 값 하나가 할당되는 것이죠. 즉, 만일 A라는 데이터를 0101 1000으로 설정하고 A값을 의의 램에 저장할 땐, DQ7 = 0, DQ6 = 1, DQ5 = 0...DQ3=1..DQ0= 0으로 신호가 입력되어 값이 저장됩니다. 즉, 컴퓨터 시스템에서 모든 자료들은 기본적으로 2진수로 저장됩니다.
문제는 아무리 공돌이에 엔지니어도 2진수 숫자를 보고 금방 그 값이 10진수로 어떤 값인지 환산하기는 어렵습니다. 그래서 그 2진수를 10진수나 16진수로 바꾸는 과정이 필요한데, 결론적으로 사람들이 읽기 좋은 방식으로 표현한다...가 그 표현의 의미로 생각하면 될 것 같습니다. 어떻든, 10진수든 16진수는 8진수든 모든 자료는 하드웨어레벨에서 2진수로 연산이 진행됨으로 최종적으로 C같은 컴파일러는 소스코딩 단계에서는 사람들이 편하도록 10진수 연산 같은 것으로 코드를 받아들이지만, 컴파일 단계에서는 사람들이 알아차리기 힘든 수준에서 2진수 연산을 진행하고 그 결과를 사람들이 원하는 표현방식(10진수 혹은 16진수)으로 표현한다는 것으로만 이해하시면 될 것 같습니다.
2. 비트연산은 다양한 용도를 가지고 있습니다.
우선, 이진수 연산에서 쉬프트연산이 사용되는 형태 하나를 설명하면,
0b00000001(0b는 c에서 2진수를 의미하는 것입니다)이라는 숫자에 <<1이란 쉬프트 연산을 하면, 이건 원래 수에 X2를 하는 것과 같은 의미가 됩니다.
0b00000001<<1 == 0b00000010;
즉, <<2하면 4를 곱해주는 것, <<3이면 8을 곱해주는 의미가 됩니다. 즉, <<n하면 2^n만큼 곱해주는 의미가 되고, 반대로 >>n하면 2^n만큼 나눠주는 의미가 됩니다.
또 아래는 2진수 연산에 관련된 코드인데, 빌드해서 확인해보시고 소스를 확인해보시면 비트연산자들이 어떤 형태로 사용되는지 어느정도 아시게 될 것 같습니다. 솔직히 비트연산은 너무 다양하게 많이 사용되기 때문에 여기서 모든 것을 대답하기는 힘들 것 같습니다.
#include <stdio.h>
#define UINT8 unsigned char // 중간에 캐스트 연산자를 자주 쓰는데
// 그때마다 unsigned char라고 쓰기 귀찮아서..
void binary_printf(unsigned char i);
// printf()문에는 이진수 출력형식이 없기때문에 별도 "사용자" 함수 등록
void main(void) { // void main(void)로 선언하는 것은 제 버릇입니다.
// int로 선언하고 마지막에 return 0; 해줘도 상관없습니다.
unsigned char x;
int a, b;
printf("Enter Two numbers, first is Value 2nd is bit number\n");
scanf_s("%d %d", &a, &b); // scanf_s는 scanf에 보안성강화형이고 기능은 동일.
x = (UINT8)a; // scanf_s로 입력된 a는 int형이므로 x에 넘길 때
// 캐스트 연산자로 자료형을 맞춰줘야 함. 안해도 되지만..
printf("입력된 값 %d는 2진수로",a);
binary_printf(x);
printf("이며\n");
x = (x & (~(1 << b)));
printf("%d의 bit %d를 0으로 변경하면 이진수 ", a, b);
binary_printf(x);
printf("가 된다\n");
x = (UINT8)a;
printf("입력된 값 %d는 2진수로", a);
binary_printf(x);
printf("이며\n");
x = (x | (1 << b));
printf("%d의 bit %d를 1로 변경하면 이진수 ", a, b);
binary_printf(x);
printf("가 된다\n");
x = (UINT8)a;
printf("그리고 %d의 bit %d를 반전시키면", a, b);
if ((x&(1 << b)) == 0) x = x | (1 << b);
else x = x & (~(1 << b));
binary_printf(x);
printf("가 된다\n");
}
void binary_printf(unsigned char i) {
char c, d;
printf("0b",i);
for (c = 0; c < 8; c++) {
printf("%d", ((i>>(7-c))&1));
}
}
3. C를 포함한 컴파일러들은 마이크로프로세서 혹은 CPU라는 물건을 다루기위해 사용되는 프로그램입니다. C는 1972년에 개발되어 약간의 발전이 있었지만, 거의 원래 형태를 유지하고 있습니다. 그동안 포트란, 코볼, 파스칼 같은 다른 컴파일러들도 많이 사용되었지만 C와 포트란을 제외한 다른 언어들은 역사속으로 사라졌죠. 최초의 마이크로프로세서는 4비트였고, 이후 8bit 마이크로프로세서들이 주류를 이루던 때가 있었습니다. C의 자료형은 이 8bit 마이크로 프로세서를 기준으로 작성됩니다.
8비트 마이크로프로세서는 데이터 버스(같은 기능을 하는 다수의 하드웨어 신호들을 버스라고 합니다)가 8비트입니다. 즉, 위의 램을 기준으로 Data Pin들이 8개만 나와있는 CPU들이었죠. 자료형은 여기서 출발합니다. 알파벳은 8비트 데이터만으로도 모두 표현이 가능하기 때문에, 모든 문자들은 8비트 변수로 표현되고 다뤄질 수 있습니다. 그래서 8bit 자료형을 char = character 형이라 표현을 합니다. 그런데, char형은 0 ~255의 숫자밖에 구분하지 못합니다. 실제로 char c = 256; 처럼 255보다 큰 수를 저장하면 윗부분은 잘리고 남은 부분만 char형 변수에 저장되게 됩니다(그리고 프로그램은 "버그"라는 증상에 걸리게되죠...프로그램의 실행결과가 프로그래머가 원한것과 전혀 다른 방향으로 동작하는...) 그래서 그보다 더 큰 수를 표현하기위해 int형(2byte), long(4byte)등의 자료형을 만들게 됩니다. 8비트 CPU에서는 메모리의 8bit가 메모리 어드레스의 1자리를 차지 합니다. 그리고 int형은 2개의 주소를 차지하고, long은 4바이트를 차지하는 형태로 변수를 저장하게 됩니다. 지금까지는 정수형 변수를 설명하였는데, 정수형 변수는 메모리에 저장된 2진수 정보가 어떠한 변동없이 10진수나 16진수로 저장됩니다. 즉, 0b10001000은 16진수 0x88, 10진수 136으로 표현될 수 있는데, 만일 프로그램에서 10진수 136을 저장하는 명령을 진행하면 이 값은 실제로 램이나 롬에 0b10001000의 형태로 저장되게 됩니다. 그러나, float 혹은 double같은 실수형은 어떤 약속을 통해 소수점의 정수부, 소수부, 지수부를 나누어서 이진수로 저장하고 이렇게 저장된 2진수를 처음 언급했던 약속된 방식으로 꺼내와서 지수부/소수부/지수부로 할당하여 실수를 표현하는 것입니다. 실수형 변수에 대해서 더 자세히 알고 싶다면 다음 링크를 참고하시기 바랍니다.
http://karmainearth.tistory.com/143
처음 저렇게 약속된 자료형들의 바이트할당에 문제가 생기기 시작합니다. 그 문제라는 것은 CPU들이 16비트, 32비트를 거쳐 현재는 64bit로 발전하면서 발생합니다. 처음에 C는 마이크로 프로세서를 다루기위한 프로그램이라고 말씀드렸습니다. 과거에는 C가 거의 8비트 마이크로 프로세서들만 다루었기 때문에 큰 문제는 없었지만, 특히 현대에 와서는 이 자료형의 표현에 문제가 발생하기 시작합니다. 예를 들어 8비트 마이크로프로세서의 어드레스 1자리는 램이나 롬의 8bit를 지정하는 주소지만, 32비트 마이크로프로세서에서는 이 어드레스 1자리가 32bit의 롬이나 램의 주소를 나타내게 됩니다. 즉, 8bit CPU에서 가장 작은 자료형은 8bit의 char형이지만, 32비트 마이크로프로세서에서는 최소단위가 32bit의 int형이 되어버립니다. 여기서 short형이 발생되게 됩니다. char형은 32비트를 4개를 나눠서 32비트에서 8비트씩을 할당하여 사용하도록, 그리고 short형은 32bit에서 2개의 16bit공간을 사용하도록 할당됩니다(그래서 16비트 이하의 마이크로프로세서에서는 short와 int는 차이가 없습니다).
결론적으로 현재 사용하는 C Compiler가 어떤 마이크로프로세서를 제어하기 위해 사용되는지에 따라 자료형은 약간씩 다르게 적용됩니다. 이 부분에 대한 자세한 설명은 아래 링크를 참고하시기 바랍니다.
http://www.angel25.com/ClanguageStudy1.html