#include <stdio.h>
모든 내용은 메모리에 0과 1의 이진 값으로 저장된다.
[단위]
비트(bit) : 이진 값의 각 자리
바이트(byte) : 8개 비트
워드(word) : 메모리에서 읽혀지는 단위
-비트열을 다울 때 , 16진수를 많이 사용
-int, short, long, long long, char 등과 같은 정수형 수식에 사용이 되며 시스템 종속적이다.
[~연산자]
1의 보수 연산자, 비트 단위 보수 연산자
피 연산자의 각 비트를 0은 1로, 1은 0으로 한다.
예)
int a = 0x10F07; // 0x10F07 = 69383
//a의 이진수 표현
00000000 00000001 00001111 00000111
//~a의 이진수 표현
11111111 11111110 11110000 11111000
// = -69384(0xFFFEF0F8)
//cf) !a = 0
[예제 코드]
#include <stdio.h>
#include<stdio.h>
int main(void) {
int a = 33333;
int b = -77777;
int c,d,e = 0;
c = a & b;
d = a | b;
e = a ^ b;
printf("a: %x, b: %x\n", a, b);
printf("a & b: %x\n", c);
printf("a | b: %x\n", d);
printf("a ^ b: %x\n", e);
return 0;
}
[마스킹 연산]
& 연산자를 사용하여 주어진 비트열의 특정 비트를 0으로 만드는 것
마스크 : 마스킹 연산을 위해 사용되는 상수나 변수
int i , mask = 1;
//mask : 00000000000000000000000000000001
for(i=0; i<10 ; ++i)
printf("%d", i &mask);
//i가 홀수면 1, 짝수면 0을 출력
//1과 and 연산
//맨 끝자리만 확인
(v & 0x4) ? 1: 0
//0x4 : 00000000 00000000 00000000 00000100
//v의 3번 째 비트가 1일 때 1
v & 255
//00000000 00000000 00000000 11111111
//v의 하위 1바이트 값
[이동연산자]
-지정된 값의 비트열을 이동시키는 연산자
-이동 연산자의 두 피연산자는 정수 수식이어야 함
왼쪽 이동 연산자(<<)
: 수식1 << 수식2
수식1의 비트 표현을 수식 2가 지정하는 수만큼 왼쪽으로 이동
왼쪽 이동으로 발생하는 빈 공간은 0으로 채워짐
2의 거듭제곱 효과를 볼 수 있음
ex) 1 * 2 * 2 * 2 보다 1<<3이 속도가 더 빠르다.
int c = 7;
// c = 00000000 00000000 00000000 00000111
//c<<1 00000000 00000000 00000000 00001110 //값:14
//c<<8 00000000 00000000 00000111 00000000 //값:1792
//c<<30 11000000 00000000 00000000 00000000 //값:-1073741924
오른쪽 이동 연산자(>>)
-수식1의 비트 표현을 수식2가 지정하는 수만큼 오른쪽으로 이동
-수식1이 unsigned 형이면 상위비트로 0이 들어옴
:2의 거듭제곱으로 나누기 연산 효과
-수식1이 signed 형이면 시스템 종속적이다.
: 상위 비트로 0이 들어올 수도 있고, 1이 들어올 수도 있다.
unsigned int c = 0xf0000000
// c = 4026531840
// c = 111100000 00000000 00000000 00000000
//c >> 1 001111000 00000000 00000000 00000000 //2013265920
//c >> 8 000000000 11110000 00000000 00000000 //15728640
//c >>30 000000000 00000000 00000000 00000011 //3
c >> 8 11111111 11110000 00000000 00000000 //-1048576
c >> 8 00000000 11110000 00000000 00000000 //15728640
[복합 배정 연산자]
-&=, ^=, |= ,<<=, >>=
예
a &= 255;
a = a&255;
[예제 프로그램: 이진수 출력하는 프로그램]
#include <stdio.h>
void bit_print(int a) {
int i;
int n = sizeof(int) * 8;
int mask = 1 << (n-1);
for (i=1; i <= n; ++i) {
putchar(((mask & a) == 0) ? '0' : '1');
a <<= 1;
if (i % 8 == 0 && i < n)
putchar(' ');
}
}
int main(void) {
int i;
printf("정수를 입력하세요 : ");
scanf("%d", &i);
printf("%d 비트열 : ", i);
bit_print(i);
printf("\n");
return 0;
}
[패킹]
여러 정보를 비트 단위 연산자를 사용하여 적은 바이트로 압축하는 것
: 메모리 절약, 전송시간 줄임
EX) 직원 관리 프로그램에서 다음과 같은 정보를 다루어야한다고 가정
//직원 ID: 6자리(10진수) 000000 ~ 999999 => 20비트
//작업 형태 : 200가지 000 ~ 199 => 8비트
//성별 0 ~ 1 => 1비트
#include <stdio.h>
unsigned pack_employee_data(unsigned id_no, unsigned job_type, char gender) {
unsigned employee = 0;
employee |= id_no;
employee |= job_type << 20;
employee |= ((gender == "m") || (gender == "M") ? 0 : 1) << 28;
return employee;
}
int main(void) {
printf("%x",pack_employee_data(20192952, 100, 'f'));
return 0;
}
[언패킹]
패킹된 정보는 사용하기 전에 언패킹 해야한다.
적절한 마스크가 필요하다.
EX) 직원 관리 프로그램
- 직원 ID를 위한 마스크 : 20개의 1
- 작업 형태를 위한 마스크 : 8개의 1
-성별을 위한 마스크 : 1개의 1
#include <stdio.h>
void print_employee_data(unsigned employee) {
unsigned id_no, job_type;
char gender;
id_no = employee & 0xFFFFF;
job_type = (employee >>20) & 0xFF;
gender = (employee >> 28) & 1;
printf("ID : %u", id_no);
printf("작업 형태 : %u", job_type);
printf("성별 : %s\n", gender ? "여" : "남");
}
비트 필드
구조체나 공용체에서 int 형이나 unsigned 형의 멤버에 비트 수 (폭)를 지정하는 것
-폭은 콜론 다음에 음수가 아닌 정수형 상수 수식으로 지정되고, 최대 값은 멤버 변수의 비트 수와 같음
-컴파일러는 멤버들을 최소의 공간으로 패킹한다.
EX)
struct employee {
unsigned id_no: 20;
unsigned job_type : 8;
unsigned gender : 1;
}
// struct employee는 총 4바이트에 저장이 된다.
// 크기를 측정할 때에는 sizeof 연산자를 사용한다.
비트 필드를 사용하는 구조체에 일반 멤버도 있을 수 있다.
struct employee_with_name {
char name[30];
unsigned id_no : 20;
unsigned job_type : 8;
unsigned gender :1;
}
컴파일러는 비트 필드를 워드 경계에 걸치지 않게 메모리에 할당
struct abc {
int a:1. b:16, c:16;
}x;
//32비트 워드 가정
//-a,b : 첫 번째 워드에 할당
//-c: 두 번째 워드에 할당
//c를 워드 사이에 걸쳐서 저장하면 , c를 읽기 위해서 1,2번째 워드 모두 읽어야하는데,
//아예 1번째 워드 나머지 메모리를 버리고 , 2번째 워드에 저장을 하면 2번째 워드만 읽으면 된다.
주의사항
- int 형 비트 필드는 시스템에 따라 unsigned int 비트 필드로 다루어딘다.
: unsigned 비트 필드만을 사용하는 것이 좋다.
- 비트 필드 배열은 허용하지 않는다.
-비트 필드에 주소연산자 &를 적용할 수 없다.
: byte 별로 주소는 있으나, bit별 주소는 없다.
-포인터가 직접 비트 필드를 포인트 할 수 없다.
패딩과 정렬을 위해 이름 없는 비트 필드나 폭이 0인 비트 필드를 사용할 수 있다.
#include <stdio.h>
struct small_integers {
unsigned i1 : 7, i2 : 7, i3 : 7,
: 11,
i4 : 7, i5 : 7, i6 : 7;
};
struct abc {
unsigned a : 1, : 0, b : 1, : 0, c : 1;
};
#include <stdio.h>
typedef struct {
unsigned b0 : 8, b1 : 8, b2 : 8, b3 : 8;
}word_bytes;
typedef struct {
unsigned
b0 : 1, b1 : 1, b2 : 1, b3 : 1, b4 : 1, b5 : 1,
b6 : 1, b7 : 1, b8 : 1, b9 : 1, b10 : 1, b11 : 1,
b12 : 1, b13 : 1, b14 : 1, b15 : 1, b16 : 1, b17 : 1,
b18 : 1, b19 : 1, b20 : 1, b21 : 1, b22 : 1, b23 : 1,
b24 : 1, b25 : 1, b26 : 1, b27 : 1, b28 : 1, b29 : 1,
b30 : 1, b31 : 1;
} word_bits;
typedef union {
int i;
word_bytes byte;
word_bits bit;
}word;
void bit_print(int);
int main(void)
{
word w = {0};
w.bit.b8 = 1;
w.byte.b0 = 'a';
printf("w.i = %d\n", w.i);
bit_print(w.i);
return 0;
}
void bit_print(int a) {
int i;
int n = sizeof(int) * 8;
int mask = 1 << (n-1);
for (i=1; i <= n; ++i) {
putchar(((mask & a) == 0) ? '0' : '1');
a <<= 1;
if (i % 8 == 0 && i < n)
putchar(' ');
}
}
[출력 결과]
'c언어' 카테고리의 다른 글
[C언어] 사용자 정의형 - 구조체 (0) | 2022.10.24 |
---|---|
[C언어] 전처리기 (0) | 2022.10.23 |
[C언어] 공용체(union) / 열거형 (0) | 2022.10.12 |
[C언어] 동적 메모리 할당 (0) | 2022.09.07 |
[C언어] 포인터와 배열 (0) | 2022.09.07 |