ASCII to Hex

간단히 말해서 입력이 String인데, 이것을 16진수 값으로 변환하는 코드를 구현해보도록 하겠습니다.

간단히 "CAFECAFE0102"라는 이런 문자열을 입력을 받았는데 16진수로 변환하고 싶은 것입니다.  즉, 0xCA, 0xFE, 0xCA, 0xFE, 0x01, 0x02의 숫자 배열로 입력을 변환하는 것이죠.

가장 간단하게 구현하기 위해서 매우 정상적인 입력값만 들어온다고 가정하겠습니다. 그러니까 16진수의 범위에 있지 않은 글자('G'를 넘어가는 알파벳)는 들어오지 않는다고 가정하겠습니다.

설명은 아래의 주석으로 대체하도록 하겠습니다.

 

 

#include <stdio.h>
#include <stdint.h>
#include <string.h>


//입력의 str은 모두 대문자인 16진수 변환가능한 문자열이라고 가정
//size는 str의 크기
//hex는 변환된 16진수 배열
unsigned int ascii_to_hex(const char* str, size_t size, uint8_t* hex)
{
    unsigned int i, h, high, low;
    for (h = 0, i = 0; i < size; i += 2, ++h) {
        //9보다 큰 경우 : 알파벳 문자 'A' 이상인 문자로, 'A'를 빼고 10을 더함.
        //9이하인 경우 : 숫자 입력으로 '0'을 빼면 실제 값이 구해짐.
        high = (str[i] > '9') ? str[i] - 'A' + 10 : str[i] - '0';
        low = (str[i + 1] > '9') ? str[i + 1] - 'A' + 10 : str[i + 1] - '0';
        //high 4비트, low 4비트이므로, 1바이트를 만들어주기 위해 high를 왼쪽으로 4비트 shift
        //이후 OR(|)연산으로 합
        hex[h] = (high << 4) | low;
    }
    return h;
}

int main() {
    char str[128] = "CAFEcafe0102";
    uint8_t hex[128] = { 0, };
    size_t size = strlen(str);
    int i;

    //소문자 cafe를 대문자로 만들기 위해 strupr함수 사용
    strupr(str);
    //hex에 실제 16진수의 값이 들어감
    ascii_to_hex(str, size, hex);
    
    //size는 반으로 줄어듦, ex) hex[0]=0xCA;
    for (i = 0; i < (size/2); i++)
        printf("0x%02X\n", hex[i]);
}

 

1바이트의 unsigned int 자료형인 uint8_t를 사용하기 위해서는 stdint.h를 include해야합니다.

결과

 

 

사실 ascii에 대한 개념과 char 자료형이 정수처럼 계산할 수 있다는 사실, 그리고 비트 연산(SHIFT, OR) 연산만 이해하고 알고 있다면 그렇게 어려운 구현은 아니라고 생각이 됩니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

비트연산자


컴퓨터가 사용하는 모든 데이터들은 전부 1과 0으로 이루어진 비트열이라는 것을 다들 잘 알겁니다.


C언어에서도 역시 그렇습니다. 우리는 정수형 변수 a에 16이라는 데이터를 집어넣는 것은 사실 코딩할때만 그렇습니다. 하지만 실행이 될때는 이진수로 메모리에 저장이 되어있죠.


만약

int a=16

이라고 정수를 메모리에 넣어준다면 이렇게 메모리에 잡히게 됩니다.

int는 4바이트의 메모리를 갖고 있으므로 

0000 0000 0000 0000 0000 0000 0001 0000

이렇게 저장이 되지요.


우리는 이러한 비트로 연산을 수행할 수 있습니다. 오늘은 이런 비트 연산에 대해서 알아보도록 하겠습니다.


NOT ( ~ )

NOT연산자는 비트열을 반전시키는 연산자입니다. 직관적으로 이해하기도 쉽습니다.

만약 비트가 1이면 0으로 반전하고, 0이면 1로 바꾸기만 하면 되니까요. 연산자 기호는 ~를 씁니다.


그래서 만약

~1000 0110 는 0111 1001로 바뀌게 됩니다.


 

OR( | )

OR 연산자는 | 입니다. 엔터 위쪽 \가 보이시나요? 이것을 쉬프트기로 눌러 입력한게 바로 OR연산자 | 입니다. A OR B는 A 또는 B가 1이라면 답은 1이 되는 겁니다.


0 | 0 = 0

0 | 1 = 1

1 | 0 = 1

1 | 1 = 1


두 비트가 모두 0일때 0이라는 것을 알 수 있네요.


다음의 계산 예를 봅시다. 


      0111 1001

 OR 1000 1010

---------------------

      1111 1011



AND( & )

AND 연산자는 두 비트열 모두 1일때 1이 됩니다. 


0 & 0 = 0

0 & 1 = 0

1 & 0 = 0

1 & 1 = 1


둘 다 1일때만 1인것을 알 수 있습니다.


다음의 예를 보고 AND연산에 대해서 보도록 합시다.


        0001 1001

AND  0111 1000

----------------------

        0001 1000


OR연산과 AND 연산은 정말 쉽습니다.


XOR( ^ )

XOR 연산자는 두 비트열 중 1이 하나만 있을때 답이 1이 됩니다. 또는 1이 홀수개 일때 답이 1이 된다고 기억하면 됩니다.


0 ^ 0 = 0

0 ^ 1 = 1

1 ^ 0 = 1

1 ^ 1 = 0


1이 홀수개일때만 1이 됩니다.



       1001 1010

XOR 0110 1111

---------------------

       1111 0101



SHIFT( <<, >>)

쉬프트 연산은 비트를 옮기는 연산을 수행합니다. 옮기는 방향에 따라 두 종류가 있습니다. 바로 left shift와 right shift가 그것이죠.

비트를 왼쪽으로 옮기려면 << 연산을 사용하고, 오른쪽으로 옮기려면 >>연산을 사용합니다.




옮길 기준이 되는 비트열은 항상 연산자의 왼쪽, 얼만큼 옮길 건지를 결정하는 건 연산자의 오른쪽에 위치합니다.


만약 Left Shift 연산으로 왼쪽으로 비트열을 옮긴다면 가장 오른쪽에서부터 옮긴 비트열 길이까지 0으로 채워집니다.


하지만 Right Shift 연산으로 비트열을 오른쪽으로 옮기면 가장 상위비트(MSB:Most Significant Bit 라고 합니다.)를 왼쪽에서부터 채웁니다.



아래 예를 보면서 이해합시다.


0011 0110 << 3 = 1011 0000


왼쪽으로 3비트를 이동시키니 001은 삭제되었습니다. 그리고 10110이라는 비트열이 왼쪽으로 3비트 이동이 되지요. 그렇다면 자리가 3개가 남겠군요. 그건 0으로 채웁니다. 이것을 패딩(padding)이라고 하지요.


0011 1100 >> 3 = 0000 0111


이제 오른쪽 비트이동입니다. 우선 이 비트를 오른쪽으로 3비트를 옮기니 100이 삭제가 됩니다. 그후 나머지 4비트는 오른쪽으로 3비트를 이동하면 왼쪽에 3비트를 추가로 채워야하지요. 옮기기전 가장 상위비트(MSB)는 빨간색으로 표시된 0입니다. 그러니 오른쪽으로 이동할때는 이 MSB를 가지고 왼쪽비트를 채우니 000으로 채워지는 겁니다.


1001 1011 >> 4 = 1111 1001


자, 이제 MSB는 1입니다. 이 비트를 오른쪽으로 4비트를 이동하니 1001은 없어지겠죠? 그후 나머지 4비트를 오른쪽으로 이동하고 비워진 4비트는 MSB로 채우니 1로 채워지는 것입니다.


아! 여기서 2의 보수법을 모르시는 분을 위해서 잠시 간략하게 설명하겠습니다.


자료형에는 음수를 나타낼 수 있는 signed 타입와 unsigned 타입이 있습니다. 우리가 int, char와 같이 그냥 사용하게 된다면 default로 signed 자료형입니다. 하지만 음수를 표현할 필요가 없다면 unsigned라는 키워드를 붙여 unsigned int와 같이 표현합니다.

이제 음수를 나타낼 수 있는 signed 자료형에 대해서 음수를 표현하는 방법을 설명하겠습니다.


signed자료형에서 컴퓨터는 MSB에 따라 음수인지 양수인지 구별하는데요.

0이면 양수, 1이면 음수를 나타냅니다.

만약 위의 예에서 0011 1100은 MSB가 0이므로 양수입니다. 

그러니 정수로 표현하면 MSB를 제외하고 32+16+8+4로 70이 됩니다.


하지만 위의 예처럼 1111 1001은 MSB가 1이므로 음수입니다. 이때 2의 보수를 사용하는데요. 방법은 이렇습니다.

1) MSB를 제외하고 비트를 반전시킨다(이것이 1의 보수법입니다.)

2) 1을 더해준다. (1을 더해주는 것이 2의 보수법입니다.)


1)+2)의 방법에 따라 1111 1001의 음수값을 구해보면

   1000 0110 (1111 1001의 반전)

+ 0000 0001 (1을 더해주는 2의 보수)

--------------------

   1000 0111


이렇게 7이 나오게 되지요. 이때 MSB가 여전히 1이므로 -7이 되는 것이죠.





다음의 코드는 비트 연산에 대한 해답을 제공하는 프로그램입니다. 


#include <stdio.h>
#define SIZE 8

void printBitArray(char bits) {
	
	int i;
	for (i = SIZE-1; i >= 0;i--) {
		char bit = ((1 << i)&bits) > 0 ? '1' : '0';
		printf("%c ", bit);
	}
	printf("\n");
}
int main() {

	char bits1=0b10110110;
	char bits2=0b01101101;
	printf("bits1:"); printBitArray(bits1);
	printf("bits2:"); printBitArray(bits2);
	printf("\n\n");

	printf("NOT bits1:");  printBitArray(~bits1);
	printf("bits1 | bits2 :");  printBitArray(bits1 | bits2);
	printf("bits1 & bits2 :");  printBitArray(bits1 & bits2);
	printf("bits1 ^ bits2 :");  printBitArray(bits1 ^ bits2);
	
	printf("bits1 << 4 :");  printBitArray(bits1 << 4);
	printf("bits2 >> 2 :");  printBitArray(bits2 >> 2);
	printf("bits1 >> 3 :"); printBitArray(bits1 >> 3);
	
}

bits1과 bits2를 바꿔서 실행해보고 printBitArray의 매개변수로 비트연산을 해서 답을 확인해 보세요.


여기까지 비트연산자에 대해서 알아보았습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,