반응형

어셈블리어를 이용하여 문자열 입출력을 구현해보자.

필자는 Visual Studio 2017과 Irvine 라이브러리를 활용하여 개발할 예정이다.


1. 문제

  • 환경 : Visual Studio 2017, Irvine 라이브러리, x86 assembly
  • 키보드에서 문자열을 입력받아 그 문자열을 출력하고, 길이와 메모리 덤프된 결과역시 출력하라.


2. 문제 해결 방법

  1. 안내문 문자열을 출력한다.
  2. 문자열을 입력받는다. > ReadString 사용(edx에 offset 지정 및 ecx로 글자수 지정)
  3. 문자열 길이 계산 > StrLength 사용(edx에 문자열 offset 지정)
  4. 문자열 길이 0과 비교하여 0이 아닐경우 1번으로 돌아감
  5. 0이면 종료
  6. 메모리 덤프 결과 출력 > DumpMem 사용(ecx에 출력할 길이, esi에 시작주소, ebx는 단위 크기)

3. 코딩

 

TITLE Program asm3inputoutput(asm3inputoutput.asm)
; 프로그램 설명문 :	문자열 입력받고 출력하는 프로그램

INCLUDE c:\Irvine\Irvine32.inc

.data
Ask			BYTE "INPUT : ", 0
Len			BYTE "Length : ", 0
Output		BYTE "OUTPUT : ", 0
Answer		BYTE 50 DUP(?)

.code
main			PROC

START:
	mov 		edx, 0
	mov 		edx, OFFSET Ask
	call		WriteString				; ASK 안내문 출력
	mov 		edx, OFFSET Answer
	mov 		esi, edx
	mov 		ecx, SIZEOF Answer - 1
	call		ReadString				; 문자열 읽기
	call		Crlf
	mov 		edx, 0
	mov 		edx, OFFSET Len
	call		WriteString				; Len 안내문 출력
	mov 		edx, 0
	mov 		edx, OFFSET Answer
	call		StrLength				; 문자열 길이 계산
	call		WriteDec				; 문자열 길이 eax 출력
	call		Crlf
	call		Crlf
	cmp 		eax, 0					; 문자열 길이 0 비교
	jz  		LOUT					; 길이 0일 경우 프로그램 종료
	mov 		edx, 0
	mov 		edx, OFFSET Output
	call		WriteString				; OUTPUT 안내문 출력
	mov 		edx, 0
	mov 		edx, OFFSET Answer
	call		WriteString				; 입력한 문자열 출력
	call		Crlf
	mov 		ecx, eax				; 메모리 덤프 출력 길이 지정
	call		DumpMem					; 메모리 덤프 출력
	call		Crlf
	call		START					; 반복

LOUT:		exit					; 빠져나가기

main			ENDP
END			main

 

실행 결과는 다음과 같다.

 


어셈블리를 통해 다양한 문제를 풀어볼 예정이다.

반응형

'Programming > Language' 카테고리의 다른 글

[C++] 클래스(Class) 공부  (0) 2022.06.06
[Assembly] 파일 읽고 출력해보기  (0) 2020.06.06
[Assembly] 1부터 10까지의 합 계산  (0) 2020.06.04
[C++] STL - multimap 공부  (0) 2020.05.24
[C++] STL - map 공부  (0) 2020.05.14
반응형

어셈블리어를 이용하여 1부터 10까지의 합을 구하는 프로그램을 개발해보자.

본 필자는 Visual Studio 2017과 Irvine 라이브러리를 활용하여 개발할 예정이다.


1. 문제

  • 환경 : Visual Studio 2017, Irvine 라이브러리, x86 assembly
  • 1부터 10까지 합산 값을 출력하고 레지스터를 덤프하는 프로그램을 개발하라.


2. 문제 해결 방법

  1. 문자열을 출력한다.
  2. eax를 xor로 초기화 시킨다.
  3. ecx에 10을 mov 한다.
  4. loop를 돌며 eax에 ecx 값을 더한다.(지정된 위치로 넘어가고 ecx가 1씩 감소하며, ecx가 0이되면 빠져나온다.)
  5. 결과 값을 출력한다.
  6. 덤프된 레지스터를 출력한다.


3. 코딩

TITLE Program asm2sum.asm(asm2sum.asm)
; 프로그램 설명문 : 1부터 10까지의 합을 구해 출력하는 프로그램

INCLUDE c:\Irvine\Irvine32.inc

.data
str_data db "1 + 2 + 3 ... + 10 = ", 0	; 문자열 str_data db(1바이트)크기 선언

.code
main	PROC

mov 	ecx, 10					; ecx에 10을 mov
xor 	eax, eax				; eax는 0

LP:
add 	eax, ecx				; eax에 ecx를 더해 eax에 결과값 저장
loop	LP						; ecx가 0보다 크면 LP로 다시 돌아감, ecx 1 감소

mov 	edx, offset str_data	; str_data offset 주소 edx에 mov
call	writeString				; edx에 있는 값 문자열 출력

call	WriteDec				; eax에 있는 값 10진수 출력
call	Crlf					; 줄 바꿈
call	DumpRegs				; 덤프된 레지스터 출력

exit							; 종료
main	ENDP
END main

 

실행 결과는 다음과 같다.


어셈블리를 통해 다양한 문제를 풀어볼 예정이다.

반응형

'Programming > Language' 카테고리의 다른 글

[Assembly] 파일 읽고 출력해보기  (0) 2020.06.06
[Assembly] 문자열 입력과 출력  (0) 2020.06.05
[C++] STL - multimap 공부  (0) 2020.05.24
[C++] STL - map 공부  (0) 2020.05.14
[C++] STL - set 공부  (0) 2020.05.14
반응형

2020 카카오 하계 인턴 코딩테스트에서 처참한 결과를 보며 느낀점이 많다.

아직 기초를 한참 더 쌓아야 가능할 것같다고 생각하였다.

이번 시간엔 파이썬과 스택 구조를 활용하여

수식의 후위 표기, 계산에 대해 공부해보도록 하자.


1. 수식의 후위 표기법

후위 표기법을 이야기하기 전에 수식의 여러가지 표기법에 대해 짚고 넘어가야한다.

수식엔 다양한 표기법이 존재한다.

 

  • 전위(prefix) 표기법 : 연산자 먼저 표시, 피연산자를 나중에 표시
    • * + A B - C D
  • 중위(infix) 표기법 : 피연산자 사이에 연산자를 표기(일반적으로 사용)
    • (A + B) * (C - D)
  • 후위(postfix) 표기법 : 피연산자를 먼저 표시, 연산자를 나중에 표시
    • A B + C D - *

3개 예제는 모두 같은 공식이다. 중위 표기법을 일반적으로 우리가 사용하지만

프로그래밍으로 괄호가 포함된 중위 표기 연산 수식을 계산하긴 힘들다.

 

컴퓨터 프로그래밍시 괄호표기가 필요없는 후위 표기법을 이용하여

다양한 수식을 계산하는 것이 속도가 빠르다.

 

*계산기나 컴퓨터 내부에선 원래 스택을 활용해 중위표기법을 후위표기법으로 변환하여 연산한다.



2. 중위 표기법 -> 후위 표기법 변환

후위 표기법을 연산하기 위해선 일반적인 중위 표기법을 후위 표기법으로 변환할 필요가 있다.

변환 방법은 다음과 같다.

 

  1. 수식의 각 연산자에 대해 우선순위에 따라 괄호를 사용하여 다시 표현한다.
  2. 각 연산자를 그에 대응하는 오른쪽 괄호의 뒤로 이동시킨다.
  3. 괄호를 제거한다.

예제를 통해 살펴보자. 위 공식을 이용하여

  • A * C + B(중위 표기법)를 후위 표기법으로 변환해보자.
  1. 우선순위에 따라 괄호를 다시 표현하면 ((A * B) + C)
  2. 각 연산자를 오른쪽 괄호 뒤로 이동시키면 ((A B) * C) +
  3. 괄호제거하면 마무리, A B * C +


3. 후위 표기법 계산

이제는 후위 표기법을 계산해보자.

이제부터 스택 개념을 적용할 것이며 계산 방법은 다음과 같다.

*스택 : https://saynot.tistory.com/28

  1. 처음부터 차례대로 읽으며 피연산자는 스택에 쌓는다.
  2. 연산자를 만나면 스택에서 피연산자 2개를 꺼내 연산을 수행하고 다시 스택에 쌓는다.
  3. 모두 읽을 때 까지 위 두 공식을 반복한다.

역시 예제를 통해 알아보자.

위 공식을 이용하여 13 5 + 3 -(후위 표현식) 수식을 계산해보자.

  1. 13은 피연산자, 스택에 PUSH
  2. 5는 피연산자, 스택에 PUSH
  3. +는 연산자이므로 5 POP, 13 POP하여 연산 수행
  4. 13 + 5 = 18, 결과값을 스택에 PUSH
  5. 3은 피연산자, 스택에 PUSH
  6. -는 연산자이므로 3 POP, 18 POP하여 연산 수행
  7. 18 - 3 = 15, 계산 끝
  8. 13 5 + 3 - = 15


4. 알고리즘 설계

중위 표현식 > 후위 표현식 변환 알고리즘

  1. 연산자 우선순위 지정(이걸 바꾸는 코딩테스트 문제 존재)
  2. 후위 표현식으로 만들어져 반환될 리스트 생성
  3. 중위 표현식을 왼쪽부터 한 글짜씩 읽음
  4. 피연산자면 리스트에 append
  5. '('이면 스택에 PUSH, ')'이면 '('가 나올때까지 스택에서 POP, 리스트에 append
  6. 연산자면 스택에서 이보다 높은 우선순위들 POP, 리스트에 append
  7. 그리고 이 연산자를 스택에 PUSH
  8. 스택에 남아있는 연산자는 모두 POP, 리스트에 append

후위 표현식 계산 알고리즘

  1. 후위 표현식을 왼쪽부터 한 글짜씩 읽음
  2. 피연산자면 스택에 PUSH
  3. 연산자면 스택에서 POP해서 op1, 다시 POP해서 op2 지정
  4. op2와 op1을 연산자로 계산하여, 결과값 스택에 PUSH
  5. 수식의 끝이면 스택에서 POP하여 계산 결과 도출


4. 코딩 - Python

위 두가지를 이용하여 중위 표현식을 입력받으면 후위 표현식으로 변환하여

결과값을 도출하는 알고리즘을 구현해보았다.

*splitTokens함수와 Stack 클래스는 해당 주제에서 제외되는 부분이라 생략하였다.

def infixTopostfix(tokenList): #중위 표현식을 후위 표현식으로 변환
    prec = {
        '*': 3,
        '/': 3,
        '+': 2,
        '-': 2,
        '(': 1,
    }

    opStack = ArrayStack()
    postfixList = []

    for token in tokenList:
        if type(token) is int:
            postfixList.append(token)         
        elif token == ')':
            if token == ')':
                while opStack.peek() != '(':
                #print(opStack.peek())
                    postfixList.append(opStack.pop())
                opStack.pop()
        else:
            if opStack.isEmpty() == False:  
                if prec[opStack.peek()] >= prec[token] and token != '(':
                    postfixList.append(opStack.pop())
                    opStack.push(token)
                elif prec[opStack.peek()] >= prec[token] and token == '(':
                    opStack.push(token)
                else:
                    opStack.push(token)
            elif opStack.isEmpty() == True:
                opStack.push(token)

    while not opStack.isEmpty():
        postfixList.append(opStack.pop())

    return postfixList


def postfixEval(tokenList): #후위 표현식 계산
    opStack = ArrayStack()
    for token in tokenList:
        if type(token) is int:
            opStack.push(token)
        elif token == '*':
            tmp1 = opStack.pop()
            tmp2 = opStack.pop()
            opStack.push(tmp2*tmp1)
        elif token == '/':
            tmp1 = opStack.pop()
            tmp2 = opStack.pop()
            opStack.push(tmp2/tmp1)
        elif token == '+':
            tmp1 = opStack.pop()
            tmp2 = opStack.pop()
            opStack.push(tmp2+tmp1)
        elif token == '-':
            tmp1 = opStack.pop()
            tmp2 = opStack.pop()
            opStack.push(tmp2-tmp1)
    return opStack.pop()



def solution(expr):
    tokens = splitTokens(expr) #문자열을 토큰화 시키는 함수
    print("tokens : ", tokens)
    postfix = infixTopostfix(tokens) #중위 표현식 > 후위 표현식
    print("postfix : ", postfix)
    res = postfixEval(postfix) #후위 표현식 계산
    return res

def main():
    quest = '( 13 + 15 ) * ( 16 + 23 )'
    ans = solution(quest)
    print(quest, " = ",ans)

if __name__ == '__main__':
    main()


결과는 다음과 같다.


일반적으로 수식 순서는 * /가 + -에 비해 먼저이다. 하지만 이를 바꿔서 계산해야하는

코딩 테스트 문제가 존재한다. 이럴때 위 코드에서 사전을 수정하여 우선순위를 조절해 코드를 바꾸면 될것이다.

반응형
반응형

저번 시간은 map 컨테이너에 대해 알아보았다.

map은 key와 value를 함께 관리할 수 있는 연관 컨테이너였다.

단, key가 중복이 허용되지 않았다.

그럼 key를 중복으로 사용하고 싶다면 어떻게 해야할까?

그 해답인 multimap에 대해 알아보자.


1. multimap이란?

multimap은 map과 매우 유사한 연관 컨테이너이다.

균형 이진트리(중위순회)이며, key와 value의 쌍(pair)인 원소들의 집합으로 이루어져 있다.

multimap의 특징은 map과 달리 key값이 중복이 된다는 것이다.

key를 중복하여 사용하고자 할때 해당 컨테이너를 이용하면 프로그램을 유연하게 구현가능하므로 기억해두자.

 

map과 같이 원소 삽입시(insert) 자동 정렬되며, default 기준 오름차순정렬이다.

 

multimap을 사용하기 위해선 <map> 헤더파일을 include 해주어야하며(multimap이 아니다!!),

std::multimap<[key 데이터타입], [value 데이터타입]> [변수이름]; 형식으로 선언해 사용할 수 있다.

(using namespace std;로 std 생략하여 편하게 작성가능)

 

2. multimap - 생성자

multimap 생성자는 다음과 같이 사용 가능하다. 아래부터 key는 int, value는 char형이라 가정하고 작성하겠다.

  • multimap<int, char> mm;
    • 빈 multimap 컨테이너 mm을 생성
  • multimap<int, char, greater<int>> mm;
    • key를 내림차순으로 정렬하는 빈 multimap 컨테이너 mm을 생성
  • multimap<int, char> mm2(mm);
    • mm를 복사한 mm2 컨테이너 생성


3. multimap - 멤버 함수 및 접근 방식

다음은 multimap 멤버 함수와 접근 방식이다. (multimap<int, char> m 선언 기준)

 

- 멤버 함수

  • mm.begin();
    • mm의 맨 처음 원소를 가리키는 반복자 리턴
    • iterator = mm.begin();
  • mm.end();
    • mm의 마지막 원소를 가리키는 반복자 리턴
    • iterator = mm.end();
  • mm.clear();
    • mm의 모든 원소를 제거
  • mm.size(), mm.max_size();
    • mm의 원소 갯수를 반환
    • mm의 최대 사이즈 반환
  • mm.empty();
    • mm이 비어있는지 확인
  • mm.count(k);
    • mm에서 k(key) 원소의 갯수 반환
  • mm.insert(p), m.emplace(p);
    • mm에 p(pair)라는 객체 추가
    • insert 성공시 true 반환, key 이미 존재하여 실패한 경우는 false 반환
    • emplace로도 추가 가능
  • mm.insert(pair<int, char>(k, v)), m.emplace(pair<int, char>(k, v));
    • mm에 k(key), v(value) 값 삽입
    • insert 성공시 true 반환, key 이미 존재하여 실패한 경우는 false 반환
    • emplace로도 추가 가능
  • mm.erase(k);
    • mm에서 k(key) 값 해당 요소를 찾아 제거
  • mm.erase(start, end), mm.erase(iterator);
    • mm에서 start부터 end 범위 원소 모두 제거
    • iterator가 가리키는 key 제거
  • mm.find(k), mm.find(k)->second;
    • mm에서 k(key) 값을 찾아 반복자 형태로 반환
    • mm에서 k(key) 값을 찾아 value 출력
    • 못찾은경우 반복자 end 리턴
  • mm2.swap(mm);
    • mm과 mm2의 데이터를 바꿈
  • mm.upper_bound(k1), mm.lower_bound(k2);
    • k1(key) 값 해당되는 맨 마지막 원소의 다음을 가리키는 반복자 반환(폐구간 ")"로 사용)
    • k2(key) 값 해당하는 맨 첫번째 원소를 가리키는 반복자 반환(개구간 "["로 사용)
  • mm.equal_range(k);
    • k(key)이란 원소가 시작하는 구간과 끝나는 구간 반복자 pair 객체 반환
    • pair의 first는 lower_bound 반환값, second는 upper_bound 반환값
    • [ first, second )
  • mm.key_comp(), m.value_comp();
    • 정렬 기준 조건자 반환(key기준, value 기준 선택)

- 접근 방식

  • iterator를 통한 접근
    • multimap<int, char>::iterator mmit;
    • mmit->first(key), mmit->second(value) 형식으로 접근가능


4. multimap - 연산자

multimap의 연산자이다.

  • ==, != 
    • == : 두 multimap 컨테이너의 모든 원소가 같으면 참, 아니면 거짓
    • != : 두 multimap 컨테이너에서 하나라도 원소가 다르면 참, 아니면 거짓
  • <, >, <=, >=
    • multimap 컨테이너간 크기 비교


5. multimap 구조 및 실습

위에서 설명한 multimap의 구조는 균형 이진트리로 다음 그림과 같다.

계속해서 말하지만 key의 중복이 허용된다.



multimap 컨테이너를 이용하여 실습을 진행해보았다.

#include <iostream>
#include <map>
using namespace std;

int main(void) {
	multimap<int, char> mm;
	multimap<int, char>::iterator mmit;
	mm.insert(pair<int, char>(1, 'a')); //insert key 1, value 'a'
	mm.insert(pair<int, char>(2, 'b')); //insert key 2, value 'b'
	mm.insert(pair<int, char>(3, 'c')); //insert key 3, value 'c'
	mm.insert(pair<int, char>(4, 'd')); //insert key 4, value 'd'
	mm.insert(pair<int, char>(3, 'e')); //insert key 3, value 'e' > Allow!

	for (mmit = mm.begin(); mmit != mm.end(); mmit++) {
		cout << "key값 : " << mmit->first; //iterator->first는 key
		cout << ", value값 : "<< mmit->second << endl; //iterator->second는 value
	}

	cout << "mm.erase(1) 수행" << endl;
	mm.erase(1); //key가 1인값 제거

	for (mmit = mm.begin(); mmit != mm.end(); mmit++) {
		cout << "key값 : " << mmit->first; //iterator->first는 key
		cout << ", value값 : " << mmit->second << endl; //iterator->second는 value
	}

	cout << "mm.size : " << mm.size() << endl; //크기
	cout << "mm.max_size : " << mm.max_size() << endl; //최대 사이즈
	cout << "mm.count(3) : " << mm.count(3) << "개" << endl; //key가 3인 원소 갯수
	cout << "mm.find(2)->second : " << mm.find(2)->second << endl; //key가 2인 원소 value
	cout << "mm.lower_bound(3) : "<< mm.lower_bound(3)->first << ", " << mm.lower_bound(3)->second << endl;
	cout << "mm.upper_bound(3) : " << mm.upper_bound(3)->first << ", " << mm.upper_bound(3)->second << endl;

	return 0;
}

실행결과는 다음과 같다.

 


여기까지 multimap과 동시에 stl 파트를 마친다. 다음시간엔 클래스에 대해 알아볼 예정이다.

반응형

'Programming > Language' 카테고리의 다른 글

[Assembly] 문자열 입력과 출력  (0) 2020.06.05
[Assembly] 1부터 10까지의 합 계산  (0) 2020.06.04
[C++] STL - map 공부  (0) 2020.05.14
[C++] STL - set 공부  (0) 2020.05.14
[C++] STL - stack 공부  (0) 2020.05.11
반응형

저번 시간은 set 컨테이너에 대해 알아보았다.

set은 key만을 관리하는데 key와 value를 함께 관리할 수 있는 연관 컨테이너가 있다.

바로 map 컨테이너이다. 오늘은 map 컨테이너에 대해 알아보자.


1. map이란?

map은 set과 같은 연관 컨테이너로 노드 기반 컨테이너이다.

map 역시 균형 이진트리(중위순회)로 이루어져 있으며, key와 value의 쌍(pair)인 원소들의 집합으로 이루어져 있다.

여기서 key는 중복이 허용되지 않으며 중복을 허용하기 위해선 multimap을 사용하여야 한다.

 

원소 삽입시(insert) 자동 정렬되며, default 기준 오름차순정렬이다.

 

map을 사용하기 위해선 <map> 헤더파일을 include 해주어야하며,

std::map<[key 데이터타입], [value 데이터타입], [정렬 기준 조건자]> [변수이름]; 형식으로 선언해 사용할 수 있다.

정렬 기준 조건자는 greater<key 데이터타입> 형태로 선언하여 내림차순으로 변경가능하며, 생략 역시 가능하다.

(using namespace std;로 std 생략하여 편하게 작성가능)

 

 

2. map - 생성자

map 생성자는 다음과 같이 사용 가능하다. 아래부터 key는 int, value는 char형이라 가정하고 작성하겠다.

  • map<int, char> m;
    • 빈 map 컨테이너 m을 생성
  • map<int, char, greater<int>> m;
    • key를 내림차순으로 정렬하는 빈 map 컨테이너 m을 생성
  • map<int, char> m2(m);
    • m를 복사한 m2 컨테이너 생성


3. map - 멤버 함수 및 접근 방식

다음은 map 멤버 함수와 접근 방식이다. (map<int, char> m 선언 기준)

 

- 멤버 함수

  • m.begin();
    • m의 맨 처음 원소를 가리키는 반복자 리턴
    • iterator = m.begin();
  • m.end();
    • m의 마지막 원소를 가리키는 반복자 리턴
    • iterator = m.end();
  • m.clear();
    • m의 모든 원소를 제거
  • m.size(), m.max_size();
    • m의 원소 갯수를 반환
    • m의 최대 사이즈 반환
  • m.empty();
    • m이 비어있는지 확인
  • m.count(k);
    • m에서 k(key) 원소의 갯수 반환(중복안되므로 0 or 1)
  • m[k] = v;
    • m에 k(key)에 v(value)값 삽입
    • key 값 존재하는 경우 value 값 갱신, key 없는 경우엔 추가
  • m.insert(p), m.emplace(p);
    • m에 p(pair)라는 객체 추가
    • insert 성공시 true 반환, key 이미 존재하여 실패한 경우는 false 반환
    • emplace로도 추가 가능
  • m.insert(pair(k, v)), m.emplace(pair(k, v));
    • m에 k(key), v(value) 값 삽입
    • insert 성공시 true 반환, key 이미 존재하여 실패한 경우는 false 반환
    • emplace로도 추가 가능
  • m.insert(iterator, k);
    • m에서 iterator가 가리키는 위치부터 k(key)를 삽입할 위치 탐색하여 삽입(균형 이진트리)
  • m.erase(k);
    • m에서 k(key) 값 해당 요소를 찾아 제거
  • m.erase(start, end);
    • m에서 start부터 end 범위 원소 모두 제거
  • m.find(k);
    • m에서 k(key) 값을 찾아 반복자 형태로 반환
    • 못찾은경우 반복자 end 리턴
  • m2.swap(m);
    • m과 m2의 데이터를 바꿈
  • m.upper_bound(k1), m.lower_bound(k2);
    • k1(key)란 원소가 끝나는 구간 반복자
    • k2(key)란 원소가 시작하는 구간 반복자
  • m.equal_range(k);
    • k(key)이란 원소가 시작하는 구간과 끝나느 구간 반복자 pair 객체 반환
  • m.key_comp(), m.value_comp();
    • 정렬 기준 조건자 반환(key기준, value 기준 선택)

- 접근 방식

  • iterator를 통한 접근
    • map<int, char>::iterator mit;
    • mit->first(key), mit->second(value) 형식으로 접근가능
  • index 요소를 통한 접근
    • m[k](value) 형식 접근가능


4. map - 연산자

map의 연산자이다.

  • ==, != 
    • == : 두 map 컨테이너의 모든 원소가 같으면 참, 아니면 거짓
    • != : 두 map 컨테이너에서 하나라도 원소가 다르면 참, 아니면 거짓
  • <, >, <=, >=
    • map 컨테이너간 크기 비교


5. map 구조 및 실습

위에서 설명한 map의 구조는 균형 이진트리로 다음 그림과 같다.



map 컨테이너를 이용하여 실습을 진행해보았다.

#include <iostream>
#include <map>
using namespace std;

int main(void) {
	map<int, char> m;
	map<int, char>::iterator mit;
	m.insert(make_pair(3,'k')); //m에 3이란 key와, 'k'란 value 삽입
	m.insert(make_pair(1,'c')); //m에 1이란 key와, 'c'란 value 삽입
	m.insert(make_pair(2,'y')); //m에 2이란 key와, 'y'란 value 삽입

	cout << m[1] << endl; //첫번째 접근 방법

	mit = m.begin();
	cout << mit->first << ":" << mit->second << "\n" << endl; //두번째 접근 방법

	for (mit = m.begin(); mit != m.end(); mit++) {
		cout << mit->first << ":" << mit->second << endl; //모든 원소 key, value 출력
	}

	mit = m.find(3); //3이란 key 탐색
	if (mit != m.end()) { //m.end()가 아니면 출력 (find해서 없으면 m.end()반환하므로)
		cout << "\n 3이란 key를 찾았습니다. value 값은 " << mit->second << "입니다." << endl;
	}
	else {
		cout << "\n 3이란 key를 찾지 못했습니다." << endl;
	}
	return 0;
}

실행결과는 다음과 같다.


여기까지 map 공부를 마친다. 계속해서 stl에 대해 알아가볼 예정이다.

반응형

'Programming > Language' 카테고리의 다른 글

[Assembly] 1부터 10까지의 합 계산  (0) 2020.06.04
[C++] STL - multimap 공부  (0) 2020.05.24
[C++] STL - set 공부  (0) 2020.05.14
[C++] STL - stack 공부  (0) 2020.05.11
[C++] STL - list 공부  (0) 2020.05.07
반응형

이번 시간부터는 연관 컨테이너에 대해 알아보겠다.

연관 컨테이너는 일정 규칙에 따라 조직화하여 자료를 정렬하고 처리하는 컨테이너이다.

여기서 오늘 set 컨테이너에 대해 다뤄보려고 한다.


1. set이란?

set은 연관 컨테이너로 노드 기반 컨테이너이다.

균형 이진트리(중위순회)로 이루어져 있으며, key라고 하는 원소들의 집합으로 이루어져 있다.

여기서 key는 중복이 허용되지 않으며 중복이되는 key는 multiset으로 추후 설명할 예정이다.

 

원소 삽입시(insert) 자동 정렬되며, default 기준 오름차순정렬이다.

 

set을 사용하기 위해선 <set> 헤더파일을 include 해주어야하며,

std::set<[데이터타입]> [변수이름]; 형식으로 선언해 사용할 수 있다.

(using namespace std;로 std 생략하여 편하게 작성가능)

 

2. set - 생성자

set 생성자는 다음과 같이 사용 가능하다. 아래부터 데이터타입이 int라고 가정하고 작성하겠다.

  • set<int> s;
    • 빈 set 컨테이너 s 생성
  • set<int> s(predicate);
    • predicate 정렬 기준으로 빈 s 컨테이너 생성
  • set<int> s2(s);
    • s를 복사한 s2 컨테이너 생성

3. set - 멤버 함수

다음은 set 멤버 함수이다. (set<int> s 선언 기준)

 

- 멤버 함수

  • s.begin();
    • s의 맨 처음 원소를 가리키는 반복자 리턴
    • iterator = s.begin();
  • s.end();
    • s의 마지막 원소를 가리키는 반복자 리턴
    • iterator = s.end();
  • s.clear();
    • s의 모든 원소를 제거
  • s.size(), s.max_size();
    • s의 원소 갯수를 반환
    • s의 최대 사이즈 반환
  • s.empty();
    • s가 비어있는지 확인
  • s.count(3);
    • s에서 3이란 원소의 갯수 반환(중복안되므로 0 or 1)
  • s.insert(6);
    • s에 6이란 원소 삽입(자동 정렬)
    • 삽입 성공시 리턴값 pair<iterator, bool>에 pair.second 값으로 true, 삽입 실패시 false가 나옴(pair.first는 삽입한 원소 가리키는 반복자)
  • s.insert(iterator, 5);
    • s에서 iterator가 가리키는 위치부터 5를 삽입할 위치 탐색하여 삽입(균형 이진트리)
  • s.erase(iterator);
    • s에서 iterator가 가리키는 원소를 제거하고 다음 원소 가리키는 반복자 리턴
  • s.erase(start, end);
    • s에서 start부터 end 범위 원소 모두 제거
  • s.find(3);
    • s에서 3이란 원소 찾아 가리키는 반복자 반환, 없으면 s.end() 반환
  • s2.swap(s);
    • s와 s2 서로 데이터를 바꾼다.
  • s.upper_bound(4), s.lower_bound(5);
    • 4란 원소가 끝나는 구간 반복자
    • 5란 원소가 시작하는 구간 반복자
  • s.equal_range(3);
    • 3이란 원소가 시작하는 구간과 끝나느 구간 반복자 pair 객체 반환
  • s.key_comp(), s.value_comp();
    • 정렬 기준 조건자 반환(set에선 둘이 같음)

4. set - 연산자

set의 연산자이다.

  • ==, != 
    • == : 두 set 컨테이너의 모든 원소가 같으면 참, 아니면 거짓
    • != : 두 set 컨테이너에서 하나라도 원소가 다르면 참, 아니면 거짓
  • <, >, <=, >=
    • set 컨테이너간 크기 비교


5. set 구조 및 실습

위에서 설명한 set의 구조는 균형 이진트리로 다음 그림과 같다.



set 컨테이너를 이용하여 실습을 진행해보았다.

#include <iostream>
#include <set>
using namespace std;

int main(void) {
	set<int> s;
	set<int>::iterator sit;
	s.insert(3); cout << "3 원소 추가!" << endl;
	s.insert(7); cout << "7 원소 추가!" << endl;
	s.insert(2); cout << "2 원소 추가!" << endl;
	
	sit = s.begin();
	s.insert(sit, 6);
	cout << "s.begin()부터 탐색하여 6 원소 추가! \n" << endl;
	
	cout << "==========원소출력==========" << endl;
	for (sit = s.begin(); sit != s.end(); sit++) {
		cout << *sit << endl;
	}
	cout << "============================\n" << endl;

	s.erase(7); cout << "7 원소 삭제!" << endl;
	//s.erase(s.begin(), s.end()); // 모든 원소 삭제

	sit = s.upper_bound(3); cout << "3 원소 끝나는 구간에 반복자 지정" << endl;
	cout << "반복자가 가리키는 원소 : " << *sit << "\n" << endl;

	sit = s.find(6); cout << "6이라는 원소 찾기" << endl;
	if (sit != s.end()) {
		cout << *sit << " 원소가 존재 합니다." << endl;
	}
	else {
		cout << "해당 원소를 찾지 못했습니다." << endl;
	}
	
	return 0;
}

실행결과는 다음과 같다.


여기까지 set 공부를 마친다. 다음 시간엔 map에 대하여 알아보는 시간을 갖겠다.

반응형

'Programming > Language' 카테고리의 다른 글

[C++] STL - multimap 공부  (0) 2020.05.24
[C++] STL - map 공부  (0) 2020.05.14
[C++] STL - stack 공부  (0) 2020.05.11
[C++] STL - list 공부  (0) 2020.05.07
[C++] STL - vector 공부  (0) 2020.05.06
반응형

지난 시간에 STL의 시퀸스 컨테이너중 하나인 list에 대해 알아보았다.

연관 컨테이너로 넘어가기 전에 어댑터 컨테이너에 있는 몇몇 컨테이너들을 짚고 넘어가려고 한다.

이번 시간은 stack 컨테이너를 공부해볼 예정이다.


1. stack이란?

stack은 어댑터 컨테이너 중 하나로 vector, deque, list 구조와 같은 시퀸스 컨테이너를 변형한 것이다.

LIFO(Last In First Out) 방식이며 default로 deque 컨테이너를 기반으로 작동한다.

 

이를 사용하기 위해서 C언어에선 PUSH, POP과 같은 함수를 직접 만들어줘야하지만 C++에선 STL이 해결해준다.

<stack> 헤더파일을 include 해주어야하며,

std::stack<[데이터타입]> [변수이름]; 형식으로 선언해 사용할 수 있다.

(using namespace std;로 std 생략하여 편하게 작성가능)

 

*내부 컨테이너를 바꾸고 싶으면 std::stack<[데이터타입], [컨테이너타입]> [변수이름]; 형태로 선언하여 사용 가능하다.



2. stack - 생성자

stack 생성자는 다음과 같이 사용 가능하다. 아래부터 데이터타입이 int라고 가정하고 작성하겠다.

  • stack<int> stk;
    • 빈 stack 컨테이너 stk 생성(내부컨테이너 deque)
  • stack<int, vector<int>> stk;
    • 빈 stack 컨테이너 stk 생성(내부컨테이너 vector(int))


3. stack - 멤버 함수

다음은 stack 멤버 함수이다.

 

- 멤버 함수

  • stk.push(3);
    • stk에 3이란 데이터 삽입
  • stk.pop();
    • stk의 top이 가리키는 원소를 삭제
  • stk.top();
    • 맨위에 있는 데이터를 반환
  • stk.empty();
    • stk이 비어있는지 확인
  • stk.size();
    • stk의 사이즈 반환
  • stk.emplace(args);
    • C++11부터 지원
    • 특정 클래스의 stack이면 해당 인자를 해당 클래스 생성자로 넘겨 해당 클래스 객체 생성하여 Stack에 Push
  • stk.swap(stk2);
    • C++11부터 지원 
    • stk과 stk2의 데이터를 서로 바꿈


  •  

4. stack - 연산자

stack의 연산자이다.

  • ==, != 
    • == : 두 stack 컨테이너의 모든 원소가 같으면 참, 아니면 거짓
    • != : 두 stack 컨테이너에서 하나라도 원소가 다르면 참, 아니면 거짓
  • <, >, <=, >=
    • stack 컨테이너간 크기 비교


5. stack 구조 및 실습

위에서 설명한 stack의 구조는 다음 그림과 같다.



실습을 진행해보았다. stack 컨테이너를 이용해 원소를 추가, 삭제해보며 실습해보았다.

 

#include <iostream>
#include <stack>
using namespace std;

int main(void) {
	stack<int> stk;
	stk.push(10); cout << "stk 10 push" << endl;
	stk.push(20); cout << "stk 20 push" << endl;
	stk.push(30); cout << "stk 30 push" << endl;
	
	cout << "stk의 맨위 데이터 : " << stk.top() << endl;
	cout << "stk의 사이즈 : " << stk.size() << endl;
	cout << "stk이 비어있는가 : " << stk.empty() << endl;

	stk.pop(); cout << "stk pop > 30 out" << endl;
	stk.pop(); cout << "stk pop > 20 out" << endl;
	
	cout << "stk의 맨위 데이터 : " << stk.top() << endl;

	stk.pop(); cout << "stk pop > 10 out" << endl;

	cout << "stk의 사이즈 : " << stk.size() << endl;
	cout << "stk이 비어있는가 : " << stk.empty() << endl;
	return 0;
}

 

실행결과는 다음과 같다.

 


여기까지 stack 공부를 마친다. 슬슬 연관 컨테이너에 대해 공부해볼 계획이다.

반응형

'Programming > Language' 카테고리의 다른 글

[C++] STL - map 공부  (0) 2020.05.14
[C++] STL - set 공부  (0) 2020.05.14
[C++] STL - list 공부  (0) 2020.05.07
[C++] STL - vector 공부  (0) 2020.05.06
[C++] STL(Standard Template Library) 시작  (0) 2020.05.05
반응형

지난 시간에 STL의 vector에 대해 알아보았다.

이번시간은 STL 시퀸스 컨테이너 중 이중연결리스트구조인 list에 대해 공부해보려한다.


1. list란?

list는 시퀸스 컨테이너 중 하나로 노드 기반 컨테이너이다.

구조는 doubly linked list 구조로 말그대로 이중 연결 리스트이다.

 

list는 vector, deque와 달리 노드 기반으로 서로 연결이 되어있어서 원소를 삽입하거나 삭제하고자할 때 모든 원소를 밀어내지 않기때문에 상수 시간복잡도를 갖아 속도가 빠르다.

또, 삽입과 삭제시 반복자가 가리키고있는 원소 자체를 삭제해야 원소가 사라지며, 동작자체에서 반복자를 무효화시키지 않는다.

 

이외에도 sort(), splice(), merge() 함수 등 list 정렬, 결합 혹은 사용시 유용한 멤버 함수들을 많이 제공한다.

 

list를 사용하기 위해선 <list> 헤더파일을 include 해주어야하며,

std::list<[데이터타입]> [변수이름] 형식으로 선언해 사용할 수 있다.

(using namespace std;로 std 생략하여 편하게 작성가능)



2. list - 생성자

list 생성자는 다음과 같이 사용 가능하다. 아래부터 데이터타입이 int라고 가정하고 작성하겠다.

  • list<int>lst;
    • 빈 list 컨테이너 lst을 생성
  • list<int>lst(3);
    • 3개의 원소를 갖는 lst을 생성(기본값 초기화)
  • list<int> lst(3, 4);
    • 4로 초기화된 3개의 원소를 갖는 lst을 생성
  • list<int> lst2(lst);
    • lst 컨테이너를 복사한 lst2를 생성


3. list - 멤버 함수, 멤버 형식

다음은 lst 멤버 함수와 멤버 형식이다. 이번 역시 대표적인 몇몇개를 정리해보았다.

 

- 멤버 함수

  • lst.assign(2, 3);
    • lst에서 3이란 값으로 2개의 원소를 할당
  • lst.front();
    • lst의 첫번째 원소를 참조
  • lst.back();
    • lst의 마지막 원소를 참조
  • lst.clear();
    • lst의 모든 원소를 제거하며 메모리는 그대로 내비둠
  • lst.push_front(3);
    • lst 앞에 3 원소를 push
  • lst.push_back(5);
    • lst 끝에 5란 원소를 push
  • lst.pop_front();
    • lst 첫번째 원소를 pop
  • lst.pop_back();
    • lst 마지막 원소를 pop
  • lst.insert(iterator, 4); lst.insert(iterator, 4, 2);
    • lst의 iterator이 가리키는 위치에 4란 값 삽입
    • lst의 iterator이 가리키는 위치에 4개의 2란 값 삽입
  • lst.erase(iterator)
    • iterator가 가리키고 있는 원소를 제거, 다음 원소 가리킴(반환)
  • lst.clear();
    • lst의 모든 원소를 제거
  • lst.begin();
    • lst의 첫번째 원소를 가리킴
    • iterator와 같이 사용
  • lst.end();
    • lst의 끝을 가리킴
    • iterator와 같이 사용
  • lst.size();
    • lst의 원소 갯수 참조
  • lst.empty();
    • lst의 size가 0이면 참, 0보다 크면 거짓
  • lst.swap(lst2);
    • lst1와 lst2의 원소와 할당된 메모리를 맞바꿈
  • lst.merge(lst2), lst.merge(lst2,predicate);
    • lst2를 lst로 합병하여 정렬
    • lst2를 조건자를 통해 lst로 합병하여 정렬
  • lst.remove(3), lst.remove_if(predicate);
    • 3 원소를 모두 제거
    • 단항 조건자가 참인 모든 원소를 제거
  • lst.sort(), lst.sort(predicate);
    • lst의 모든 원소를 오름차순 정렬
    • lst의 모든 원소를 조건자 기준으로 정렬
  • lst.splice(iterator, lst2);
    • iterator가 가르키는 곳에 lst2 모든 원소를 잘라 넣는다.
  • lst.unique(), lst.unique(pred);
    • 입접한 원소의 값이 같다면 유일한 원소 순차열로 만듦
    • 인접한 원소가 이항 조건자 기준에 맞으면 유일한 원소 순차열로 만듦

 

- 멤버 형식

  • allocator_type
    • 메모리 관리자
  • iterator
    • 반복자
  • pointer
    • value_type *
  • refernce
    • value_type &
  • reverse_iterator
    • 역 반복자
  • size_type
    • 인덱스, 원소 갯수 등
  • value_type
    • 원소 형식


4. list - 연산자

list의 연산자이다.

  • ==, != 
    • == : 두 list 컨테이너의 모든 원소가 같으면 참, 아니면 거짓
    • != : 두 list 컨테이너에서 하나라도 원소가 다르면 참, 아니면 거짓
  • <, >, <=, >=
    • list 컨테이너간 크기 비교


5. list 구조 및 실습

위에서 설명한 list의 구조는 다음 그림과 같다.



실습을 진행해보았다. list 컨테이너를 이용해 원소를 추가, 삭제해보고 다양한 멤버 함수를 사용해보았다.

 

#include <iostream>
#include <list>
using namespace std;

int main(void) {
	list<int> lst; //빈 list 컨테이너 lst 생성
	list<int>::iterator lstit; //연산자 선언
	lst.push_back(30); //lst 뒤에 30 push > 30
	lst.push_back(20); //lst 뒤에 20 push > 30, 20
	lst.push_back(40); //lst 뒤에 40 push > 30, 20, 40
	lst.push_front(10); //lst 앞에 10 push > 10, 30, 20, 40
	cout << "lst > push_front, push_back" << endl;
	for (lstit = lst.begin(); lstit != lst.end(); lstit++) { //lst begin부터 end까지 출력
		cout << *lstit << endl; 
	}
	cout << "lst > pop_front, pop_back" << endl;
	lst.pop_back(); //lst 맨뒤 원소 pop
	lst.pop_front(); //lst 첫번째 원소 pop
	for (lstit = lst.begin(); lstit != lst.end(); lstit++) {
		cout << *lstit << endl;
	}
	cout << "lst2 > lst복사하여 컨테이너 생성" << endl;
	list<int> lst2(lst); //lst복사하여 lst2 컨테이너 생성
	for (lstit = lst2.begin(); lstit != lst2.end(); lstit++) {
		cout << *lstit << endl;
	}
	cout << "lst2 > sort 정렬" << endl;
	lst2.sort(); //lst2 오름차순 정렬
	for (lstit = lst2.begin(); lstit != lst2.end(); lstit++) {
		cout << *lstit << endl;
	}
	cout << "lst2 > iterator가 가리키고있는 위치에 lst 값 잘라붙여넣기" << endl;
	lst2.splice(lstit, lst); //lterator 가리키는 위치에 lst 값 잘라붙여넣기
	for (lstit = lst2.begin(); lstit != lst2.end(); lstit++) {
		cout << *lstit << endl;
	}
	return 0;
}

실행결과는 다음과 같다.


여기까지 list 공부를 마친다. 이와같이 시퀸스 컨테이너 포스팅을 계속하여 진행하겠다.

반응형

'Programming > Language' 카테고리의 다른 글

[C++] STL - set 공부  (0) 2020.05.14
[C++] STL - stack 공부  (0) 2020.05.11
[C++] STL - vector 공부  (0) 2020.05.06
[C++] STL(Standard Template Library) 시작  (0) 2020.05.05
[C++] 템플릿(Template) 공부  (0) 2020.05.04

+ Recent posts