백준 알고리즘 1330번
문제
두 정수 A와 B가 주어졌을 때, A와 B를 비교하는 프로그램을 작성하시오.
입력
첫째 줄에 A와 B가 주어진다. A와 B는 공백 한 칸으로 구분되어 있다.
출력
첫째 줄에 다음 세 가지 중 하나를 출력한다.
- A가 B보다 큰 경우에는 '>'를 출력한다.
- A가 B보다 작은 경우에는 '<'를 출력한다.
- A와 B가 같은 경우에는 '=='를 출력한다.
출처 : https://www.acmicpc.net/problem/1330
사소한 오해로 인해 커진 문제
[문제]에서 두 정수 A와 B가 주어진다고 했다.
[입력]에서 첫째 줄에 A와 B가 주어지며 공백 한 칸으로 구분되어진다고 했으므로
ex) 1 3 과 같이 공백이 포함된 문자열로 받아들인다고 오해하였다. 사실 cin을 이용하여 각각 받아야 하는 문제였다.
C++을 시작한지 얼마 되지 않은 상황에서 계속 마주하는 오류는 이제 서서히 익숙해진다.
하지만 그 오류가 발생하면 "왜 이 오류가 발생했지?"라는 생각을 하며 파고든다.
답답하리만큼 시간이 오래걸리긴 하지만 그만큼 한 단계 성장한다고 생각하자 : /
소스코드와 정답 비교
- 백준 1330번 정답코드
#include <iostream>
using namespace std;
int main(){
int a, b;
cin>>a>>b;
if (a>b){
cout<<">";
}else if (a<b){
cout<<"<";
}else
cout<<"==";
return 0;
}
- 로보봇이 작성한 백준 1330번 정답 코드
#include <iostream>
#include <string>
using namespace std;
int main(){
string num;
getline(cin,num);
char c = num[0];
char d = num[2];
int a = c-'0';
int b = d-'0';
if (a > b){
cout<<">"<<endl;
}else if (a < b){
cout<<"<"<<endl;
}else{
cout<<"=="<<endl;
}
return 0;
}
어떤 차이점이 있을까?
정답 소스코드는 std::cin 함수를 이용하여 변수를 받아들이기 때문에 간단한 알고리즘으로 문제를 해결할 수 있다.
내가 작성한 소스코드는 문자열을 받아들여야 하기 때문에 복잡한 과정이 들어갔다.
내가 설계한 알고리즘을 간단하게 설명하자면 다음과 같다.
알고리즘 START |
사용자로부터 문자열을 받는다 |
문자 배열에서 숫자를 추출한다 |
If문을 통해 결과를 도출한다. |
간단해 보이지만 과정이 복잡하다.
- 공백을 포함한 문자열을 받아야 한다. string 객체를 도입했다.
- string 객체에 공백을 포함한 문자열 값을 받기 위해 getline() 함수를 이용한다.
- string 객체에 저장된 문자 배열에서 비교할 값들을 추출한다. 이 값들은 char 객체에 문자 형태로 저장한다.
- char 객체에 저장된 각 값들은 int 객체에 저장되면 ASCII코드의 숫자로 출력된다.
- ASCII 코드가 1byte이기 때문에 0부터 9까지는 [48 ~ 57] 까지의 정수로 나타난다.
- 소스코드상에서 문자를 서로 빼는 것처럼 보이지만 사실은 '0'의 ASCII코드 넘버인 48을 기준으로 상대 측정하는 것이다.
- 정수로 출력된 각 A와 B의 값을 if문을 통해 비교하고 값을 도출한다.
간결해야 하는 코딩에서 이와 같은 오류는 치명적이기 때문에 생각을 잘 정리하고 문제 해석 능력을 길러야 함을 깨달았다.
배운 것들을 살펴보자
1. char와 string
- char
- char는 1byte의 문자 변수 지정자이다.
- 1byte를 사용하는 ASCII 코드로 해석한다. 함수에 정수를 집어넣게 되면 해당되는 ASCII 코드 값을 출력한다.
- char [...] 문자열을 배열로 저장할 수 있다. 수정이 가능하다.
- char* 포인터를 이용하여 문자열을 저장할 수 있다. 할당된 주소 값을 이용하기 때문에 수정이 불가능하다.
- string
- string은 C++에 객체지향이 도입되며 생긴 함수이다.
- string을 사용하기 위해서 <string>이라는 헤더 파일을 정의해야 한다.
- 다양하고 편리한 멤버 함수들이 존재하므로 사용하기 편리하다.
string함수를 처음 쓸 때는 굉장히 복잡하고 이해하기 힘들었다. 하지만 이해하고 나니 활용하기 너무 좋을 것 같았다.
멤버 함수는 그 종류가 굉장히 많다. 내가 이 코드에서 사용했으면 좋았을법한 멤버함수 몇 가지를 적어본다.
[string의 멤버 함수]
1. string str.front()
여기서 str은 변수이고 이 뒤에. front()가 붙는 형태이다. 저장된 변수의 맨 앞 인자를 반환하는 멤버 함수이다.
나의 소스코드에서 문자 배열을 따로 지정하고 불러올 필요 없이 이 함수 하나면 변수를 호출할 수 있었다.
2. string str.back()
저장된 변수의 가장 뒤쪽의 인자를 반환하는 멤버함수이다.
3. string str.at(n)
저장된 변수에서 인덱싱하여 인자를 반환하는 멤버 함수이다. index가 string의 범위를 벗어나면 [예외]를 반환한다.
4. string str.operator[n]
저장된 변수에서 인덱싱하여 인자를 반환하는 멤버 함수이다. index의 범위를 검사하지 않으며 string의 범위를 초과해도 [예외] 반환은 없다.
2. cin.getline() 과 getline()
- cin.getline(char* str, streamsize n, char dlim);
- istream라이브러리에 있는 함수이다.
- 쉬운 표현 : cin.getline(변수 주소, 받을 문자수, 입력을 멈출 문자);
- char dlim(입력을 멈출 문자)는 생략 가능하다. 생략시 엔터적용됨.
- getline(istream& is, string str, char dlim);
- string라이브러리에 있는 함수이다.
- 쉬운 표현 : getline(입력스트림 오브젝트, 변수, 입력을 멈출 문자);
- 입력스트림 오브젝트는 보통 cin을 사용한다.
- char dlim(입력을 멈출 문자)는 생략 가능하다. 생략시 엔터적용됨.
3. cin.ignore()
cin.ignore() 는 getline 함수들을 보조해주는 역할을 한다.
이런 코드를 살펴보자.
string str1;
int num1;
cin>>num1;
getline(cin,str1);
string객체에 str1 변수를 지정하였고, int객체에 num1이라는 변수를 지정하였다.
컴파일하고 실행하게 되면 cin으로 num1을 받는데는 문제 없지만 getline함수가 무시되고 종료된다.
왜 이런일이 발생할까? 이유는 버퍼때문이다.
우리가 cin을 이용하여 정수를 입력하고 엔터를 치게된다. '\n' 말이다.
이 버퍼가 남아서 그다음 getline 함수에 영향을 끼친다.
따라서 getline함수 앞전에 cin.ignore를 선언해주면서 버퍼를 비워주면 위와같은 문제를 해결할 수 있다.
string str1;
int num1;
cin>>num1;
cin.ignore();
getline(cin,str1);
이러한 이유때문에 getline함수를 사용함에있어 cin.ignore의 역할은 중요하다고 볼 수 있다.
마무리 하며
이 한문제로 오늘 하루를 고민하고 찾아보며 골머리를 앓았다.
C++을 시작한지 얼마 되지 않은 시점에서 가장 중요한건 스스로 고민하면서 문제를 해결해 나가는 것이라고 생각했다.
오류가 난 부분을 찾아가다보면 밑도 끝도 없는 구렁텅이에 빠진 기분이였고, 이 한문제로 하루를 고민했다는 점도 스스로를 힘들게 했다.
이 방법으로 공부하는게 맞을까? 하면서도 또 다른 방법들이 눈에 들어온다.
각 공부법의 장단점이 명확하다는 점에서 갈팡질팡하며 딜레마에 빠진다.
하지만 알고리즘은 효율적이여야 하며 간단명료하게 표현되어야 한다고 생각하게 됐다.
스스로 시도는 해보되 효율적인 알고리즘을 도입하여 능력을 발전시키는 방향이 맞겠다는 생각을 했다.
'코딩 > C++' 카테고리의 다른 글
[C++] Mutex (1) | 2024.01.21 |
---|---|
[C++]Data structure alignment(데이터 구조 정렬) (0) | 2022.01.13 |