Recursive-Descent parsing으로 계산기 짠건데.. 질문이 있습니다.
재귀를 이용하여 간단한 계산기 짜는 프로그램인데요.. (계산기를 짠다기보다 인터프리터를 짠다는게 맞는거 같긴하지만..)
예를들어 12+3을 입력하면 15가 나오는 프로그램입니다.
다 생략하고 궁금한 부분만 올리겠습니다.
함수를 돌고 돌다 마지막 값을 구하는 함수(number함수)에서
n = n*10 + c-'0';
이런게 있는데... c-'0' 이걸 하는 이유가 뭔가요?
n*10은 이해가 되는데.. 뒤에 c에 '0'을 빼주는 이유가 뭔가요?ㅠㅠ
#include
#include
#include
int c;
int expression(void);
void error(char *msg) {
puts(msg);
exit(1);
}
void next(void) {
c = getchar();
if (c==EOF) error("char expected");
}
int number(void) {
int n;
if (!isdigit(c)) error("digit expected");
n = 0;
do {
n = n*10 + c-'0';
next();
} while (isdigit(c));
return n;
}
int factor(void) {
int n;
if (c=='(') {
next();
n = expression();
if (c!=')') error(") expected");
next();
} else
n = number();
return n;
}
int term(void) {
int op, n, m;
n = factor();
while ((op=c)=='*' || op=='/') {
next();
m = factor();
n = op=='*' ? n*m : n/m;
}
return n;
}
int expression(void) {
int sign, op, n, m;
if ((sign = c=='-') || c=='+') next();
n = term();
if (sign) n = -n;
while ((op=c)=='+' || op=='-') {
next();
m = term();
n = op=='+' ? n+m : n-m;
}
return n;
}
int main(void) {
next();
printf("%d\n", expression());
return 0;
}
1. getchar로 반환한 값은 '문자'입니다.
1. getchar로 반환한 값은 '문자'입니다. 숫자가 아니에요.
십진 숫자 문자 하나, 예컨대 '5'를 입력받았다고 할 때, c에 저장될 값은 '5'라는 문자에 대응되는 코드 값이지, 숫자 5가 아닙니다.
2. 그러므로, 문자 '5'를 숫자 5로 바꿀 수 있는 방법이 필요합니다.
ASCII에서는 '5'에 대응되는 코드 값이 16진수 35입니다. '6'은 16진수 36, 등등. '0'은 16진수 30이지요.
다시 말해, 문자 '0', '1', ... '9'가 ASCII 코드표 상에 연속적으로 16진수 30~39에 위치하고 있는 겁니다.
그래서 c - '0'을 하면 딱 c가 나타내는 십진 숫자 문자에 대응하는 숫자를 얻을 수 있는 것입니다.
3. 근데 문자셋이 ASCII가 아니라면요?
요즘에야 이런 걱정을 할 필요가 있는 경우를 상상하기가 거의 어렵습니다만, 초중고 컴퓨터 교과서에서나 가끔 들어보셨을 법한 EBCDIC이라는 문자셋 혹시 기억하시는지?
예컨대 이 문자셋을 사용중일 때는 위 테크닉을 사용할 수 없지 않습니다.
농담이고요. EBCDIC에서도 '0'~'9'는 연속적으로 16진수 F0~F9에 위치하고 있기 때문에 위 테크닉을 그대로 쓸 수 있습니다. 'a'~'z'가 연속하여 위치하지 않은 독특한 문자셋인데, 다행이군요.
흠, 하지만 정말로 예외가 없을까요?
4. 드높은 천상의 C언어 표준은 '0'~'9'를 불연속적으로, 혹은 엉뚱한 순서로 문자셋에 배치하는 것을 엄격히 금합니다. 그게 소스 코드를 나타내는 데 쓰는 문자셋이든, 컴파일된 결과물이 실행될 때 쓰이는 문자셋이든 말이죠.
그래서, "C언어"를 사용할 수 있는 모든 환경에서 (c - '0')은 항상 c가 나타내는 십진 숫자 문자에 대응되는 숫자를 얻게 됩니다.
이 점을 볼 때, 웬만하면 (c - 0x30)보다는 (c - '0')을 쓰는 게 더 좋다는 근거가 되기도 하지요. 프로그래머의 의도가 좀 더 분명해 보일 뿐만 아니라, 아주 미약하게나마 이식성도 더 좋아집니다.
감사합니다.
와.. 감사합니다.
아직 초보 프로그래머라.. 많이 배워갑니다!
감사해요~!
댓글 달기