티스토리 뷰
이번 문제는 그리 어려운 문제는 아닙니다. 다만 쉽고 단순한 알고리즘으로 풀 수 있는 문제라 하더라도 함수명, 변수명을 가독성(Readability)이 좋도록 명명하고 들여쓰기(Indentation)에 유의하면서 최적의 알고리즘을 적용하려는 노력을 기울여야 할 것입니다. 간단한 프로그램이지만 효율적인 프로그램 수행을 위한 다양한 기법을 익힐 수 있습니다.
■ 문제
# 지정 옵션에 따라 다양한 형태의 ASCII 테이블을 출력하는 콘솔 프로그램을(asciitbl) 작성하세요.
https://en.wikipedia.org/wiki/ASCII 를 참고합니다.
화면 출력은 고정 화면폭(80자)을 기준으로 합니다.
옵션을 지정하지 않으면 제어문자를 제외한(printable) 문자들만 출력합니다.
/a 옵션은 모든 문자를 출력합니다. 제어 문자는 위키 문서를 참고해서 약자(Symbol)로 표시합니다.
각 문자는 "십진 코드값 문자 또는 제어문자의 약자"의 형태로 표시합니다.
/b 옵션은 2진 코드값을 부가합니다.
/x 옵션은 16진 코드값을 부가합니다.
/o 옵션은 8진 코드값을 부가합니다.
사용예 : asciitbl /a /x
제어 문자를 포함한 모든 문자를 표시하면서 "십진 코드값 문자 또는 제어문자의 약자 16진 코드값"의 형태로 출력합니다.
■ 코드와 해설
프로그램은 크게 아규먼트를 식별해서 플래그에 저장하는 입력 검사부와 플래그 값에 따라 아스키 테이블을 출력하는 테이블 출력부로 나뉩니다. 여러 옵션을 나타내는 플래그는 정수 변수를 사용하여 각 비트의 On/Off에 따라 옵션이 주어졌는지를 확인하는 방법을 사용했습니다. 출력은 한줄에 두개의 문자를 표시하는 단순한 방법을 사용했으나 주어진 옵션 개수에 따라 한 줄에 표시하는 문자의 개수를 조정할 수도 있을 것입니다.
#include<stdio.h> #include<string.h> char *cntlchar[32] = { "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" }; enum flag_enum { OPT_ALL = 1, OPT_BIN = 2, OPT_HEX = 4, OPT_OCT = 8}; char *options[4] = { "/a", "/b", "/x", "/o" }; int option_bits[4] = { OPT_ALL, OPT_BIN, OPT_HEX, OPT_OCT }; // val : 출력할 값, byte_size : 출력 바이트 수(1~4) // return : 0-fail, 1-OK int print_binary(int val, int byte_size ) { int i; if (byte_size < 1 || byte_size > 4) return 0; for (i = 1 << ((byte_size * 8)-1); i > 0; i /= 2) { if (val & i) printf("1"); else printf("0"); } return 1; } //플래그에 따라 아스키 테이블 출력 void tableprint(int inflag) { int ch; for (ch=0; ch < 128; ch ++) { if (ch < 32) { // /a 옵션이 없으면 제어문자 출력 안함 if ((inflag & OPT_ALL) == 0) continue; printf("%3d = %-3s ", ch, cntlchar[ch]); } else { printf("%3d = %-3c ", ch, ch); } if (inflag & OPT_BIN) { printf(" "); print_binary(ch, 1); } if (inflag & OPT_HEX) { printf(" %02x", ch); } if (inflag & OPT_OCT) { printf(" %03o", ch); } if (ch % 2 == 1) printf("\n"); else printf(" | "); } } void UsageExit() { printf("asciitbl [/a][/b][/x][/o]\n"); printf("\t옵션이 없으면 제어문자를 제외한 아스키코드 출력\n"); printf("\t/a : 제어문자 포함\n"); printf("\t/b : 2진수 출력\n"); printf("\t/o : 8진수 출력\n"); printf("\t/x : 16진수 출력\n"); exit(0); } int main(int argc, char **argv) { int i, j, flag = 0; for (i = 1; i < argc; i++) { for (j = 0; j < 4; j++) { if (strcmp(strlwr(argv[i]), options[j]) == 0) { flag |= option_bits[j]; break; } } if (j >= 4) { printf("\nInvalid option : %s",argv[i]); UsageExit(); } } tableprint(flag); }
플래그와 비트(Bitwise) 조작
main()함수에서 프로그램 아규먼트로 건네진 각 옵션들을 분석한 결과는 flag라는 정수 변수에 담기게 됩니다. 여러 옵션을 문자열 배열(options[])에 준비했다가 개별 아규먼트와 비교해서(소문자 변환 비교를 위해서 사전에 strlwr() 함수 호출) 지원하는 옵션이 있으면 해당 옵션의 비트 위치값을 가지고 있는 배열을(option_bits[]) 참조해서 플래그의 해당 비트를 On합니다. 비트를 켤때는 Bitwise OR 연산(|)을 사용합니다. 테이블 출력 로직을 보면 플래그의 특정 비트가 켜있는지 확인하기 위해서 Bitwise AND 연산(&)을 사용하고 있는데 이처럼 특정 비트나 비트 그룹을 걸러내는 과정을 마스킹(Masking)이라 합니다.열거형 변수 정의하기
C언어에서 상수를 정의하는 방법으로 #define 구문을 사용하거나 "const int ...." 처럼 전역 변수를 정의하면서 const를 붙여 상수화하는 방법도 있지만 enum을 통해서 간편하게 열거형 변수를 정의할 수 있습니다. 정의 및 사용 방법은 구조체(union, struct 등)와 유사합니다. 문법은 아래와 같습니다.
enum enum태그 { enum목록, ......}
enum태그는 생략할 수도 있지만 enum 타입의 변수를 여러개 선언하려면 enum태그를 지정하는 것이 좋습니다. const변수나 #define 정의 처럼 사용하는 부분은 enum목록 부분으로 값은 0부터 시작하지만 "변수명=값"의 형태로 값을 직접 지정하면 그 다음 목록 부터는 이전 값의 +1 값으로 정의됩니다. enum enum태그 변수로 열거형 값을 가지는 변수를 정의할 수 있고 의미적으로 해당 변수는 enum목록에 기술된 값중에 하나를 갖지만, 실제로는 정수형 변수처럼 사용할 수 있습니다.이진수 출력과 시프트(Shift) 연산
print_binary(int val, int byte_size) 함수는 정수값과 출력 바이트수를 인수로 해서 이진수를 출력하는 함수입니다. 함수의 구조를 보면 지정한 바이트 크기에 해당하는 비트 스트림의 최상위 비트(MSB, Most Significant Bit)부터 최하위 비트(LSB, Least Significant Bit) 쪽 우측 방향으로 차례대로 비트 검사를 해서 '1' 또는 '0'을 출력하는 방식입니다. 주목할 부분은 MSB의 값을 갖도록 for 구문의 시작점에 <<(Left Shift) 연산자를 사용한 것과 루프의 단계 마다 우측으로 검사할 비트를 지정하도록 2로 값을 나눈 것입니다. 1바이트의 MSB는 1 << 7, 2바이트의 MSB는 1<<15로 비트를 시프트하는 것으로 얻을 수 있습니다. 좌시프트는 *2와 같은 효과를 나타내면 /2는 우시프트(>>)와 동일한 결과를 얻을 수 있습니다.printf()의 좌우맞춤(Align)과 공백 채우기(Padding), 자르기
printf()함수는 sprintf()나 fprintf() 등으로 확장되는등 매우 유용한 라이브러리 함수입니다. 숫자나 문자열을 출력하는 과정에서 "%d"나 "%s"같은 단순 값 출력을 넘어서 좌우 맞춤 및 공백 채우기를 적용하는 방법입니다. "%3d"나 "%3s"처럼 출력할 값의 자리수를 지정하면 우측으로 맞추어지고 값이 지정한 자리수 보다 작으면 좌측을 공백문자로 채웁니다. "%-3d"나 "%-3s"처럼 자리수를 지정하면서 바로 앞에 "-"문자를 붙이면 좌측으로 맞추어지고 값이 지정한 자리수 보다 작으면 우측을 공백문자로 채웁니다.
'프로그래밍' 카테고리의 다른 글
C# 닷넷으로 구글/네이버 메일(SMTP) 보내기 (0) | 2017.01.19 |
---|---|
PInvokeStackImbalance 오류 해결 방법 - C#에서 외부 DLL 사용하기 (0) | 2017.01.12 |
실습으로 배우는 C언어 2 - 달력 출력하기 (0) | 2017.01.02 |
배포 배키지로 Web API 응용을 IIS에 게시하기 (0) | 2016.12.28 |
LINQ 집합 연산 다루기 (0) | 2016.12.20 |