Lecture 2 of Introduction to Computer science

Lecture 2


이번 강의에서는 컴파일에 대한 전반적인 내용과 sorting법을 알아보았다.




전 시간에 진행했던 내용 중 저 명령이 어떠한 의미를 지니는지 알게되었다. (뒤의 -lcs50는


이제껏 make라는 간단한 명령을 통해 compile을 진행하였었는데 이는 위와 같은 4단계로 나뉘게 된다.


우선 preprocessing에서는 header file같은 것들에 대한 전처리가 일어난다. 가령 #include <cs50.h>는


아래의 프로그램 중 get_string에 대한 정보를 가져오게 되고,


#include <stdio.h>의 경우,


아래 printf 함수에 대한 정보를 읽어들이게 된다.


다음은 compiling을 통한 전환과정인데 위의 내용은


이 같은 CPU 연산이 가능한 정보로 변환이 된다.


아직까지는 기계어로 보기는 어렵고 알아볼 수 있는 main, get_string, printf가 보인다.


그 이외의 다양한 명령들이 포함되어 있는 것을 알 수 있으며 이는 CPU가 이해할 수 있는 수준의 low level instruction이다.


그리고 assembling과정을 지나게 되면 이렇게 컴퓨터가 이해할 수 있는 기계어(2진수)로 바뀌고


프로그램에 포함되어 있던 모든 파일들을 assembling을 진행해야 하기 때문에


각각 이렇게 변환이 된다.


이들을 마지막으로 linking을 통해 연결을 시켜주게 되는 것이다. 이것이 실질적으로 컴퓨터가 읽어들이고 수행하는 정보가 된다.


위의 일련의 과정들을 지나는 중에 에러가 어떠한 방식으로 발생하는지 보여주기 위해 'buggy0.c'를 코딩하고 있다. 여기서는 printf를 찾을 수 없다는 내용인데 이는 당연히 <stdio.h>를 include하지 않았기 때문에 생기는 기초적인 내용이다.


위에서 발생한 에러를 해결하기 위해 어떤 방법을 취해야 하는지 도움을 청할 때 'help50'라는 명령어를 입력한 모습이다. 그러면 위와 같이 어떤 부분에서 에러가 발생했으며 어떻게 변경을 하면 좋을지 조언을 받을 수 있음을 알 수 있다. 노란색으로 강조된 곳에서 printf함수가 포함된 라이브러리를 선언하라고 나와있는 것을 볼 수 있다.


이번에는 10개의 #를 출력하는 프로그램인데 에러는 없지만 #이 11개가 출력이 되었을 때 어떤 식으로 접근하는지 방법론에 대해 얘기하였다. 0에서 시작하여 10개까지 포함하였기 때문에 11개가 출력이 된 것이었고 이런 문제가 생길 것을 예방하기 위해 에러가 없더라도 항상 결과에 대해 확인을 해야한다고 했다.


여기서는 coding style에 대한 얘기를 하였고 위와 같이 들여쓰기가 전혀없이 코딩하더라도 실행은 되지만 보기 좋음을 위해 들여쓰기 할 것을 권장하였다.


coding style에 대한 도움도 'style50'명령을 통해 받을 수 있음을 보여주는 장면이다.



다시 RAM으로 돌아와서 데이터가 공간을 어떻게 차지하며 이 중에 array를 다루는 방법에 대해 알아 보았다. RAM에 있는 위와 같은 칩 중 한개를 선택해보자.


요즘 RAM의 성능이 좋아 GB단위까지 올라가지만 예시를 위해 위와 같이 몇칸만 가지고 있는 걸로 가정을 하고 4byte만 할당을 해보겠다. 이 곳에 문자열을 넣게 된다면 한칸한칸 한글자씩 입력하면 되고 이는 즉 array가 됨을 의미한다.


Array는 빽빽히 연결되어있는 데이터이며 여기에는 int, float, char 등 여러 타입의 데이터가 저장이 될 수 있고 매우 빠르게 처리될 수 있다.


이번에는 array에 어떤식으로 정보가 저장이 되고 활용이 되는지 'score0.c'라는 예시를 작성하여 보여주고 있다. 점수 3개를 받아 점수만큼 비례하게 #을 출력하는 간단한 프로그램이다.


하지만 너무 반복성이 심하고 비효율적인 코드였기 때문에 'score2.c'로 더욱 간결한 코드로 바꾸었다. 단순히 점수를 입력받는 부분을 3번 써놓기보다는 for문을 통해 반속하게 하였고 #으로 차트를 그리는 부분도 모듈화 하여 활용도를 높인 것을 알 수 있다. 또한 array에도 순차적으로 데이터를 저장하게 되어 연산에 유리하다고 한다. 또 여기서 보이진 않지만 활용도를 높인 대신 3번을 반복하게 된다는 내용이 3곳에 들어가는데 이를 하나의 변수로 제어를 하게되어 더욱 유동성있는 코드로 변하였다.


이번에는 문자열을 저장하여 한글자씩 열거하는 프로그램을 'string0.c'로 짠 것을 볼 수 있다. 주목할 점은 strlen(s)를 통해 문자열의 길이를 계산하고 이것이 조건에 부합하는지 확인하는 조건식에 들어간 것을 알 수 있다.


하지만 이는 한번 입력된 문자열의 길이가 바뀌는 것이 아니기 때문에 for문이 돌 때마다 불필요하게 계속 문자열의 길이를 계산하게 되는 딜레마(?)에 빠지게 된다. 그렇기 때문에 이를 위와 같이 다른 변수(n)에 할당하고 그 수에만 비교할 수 있도록 바꿔주는과정을 거쳤다.


그리고 array에 문자열이 저장 될 때 어떤 상황이 벌어지는 설명하는 장면이다. 'Zamyla'라는 이름을 저장할 때 array의 첫 시작은 당연히 'Z'가 저장되어 시작인 줄 알 수 있지만 어디서 끝나는 지 컴퓨터로써는 알 수 있는 방법이 없기 때문에 '\0'를 마지막에 배치하여 'a'가 문자열의 마지막임을 알려준다고 한다. (\0 = 00000000)


그래서 이런 원리를 확인하기 위해 strlen을 통해 문자열의 길이를 계산하는 프로그램을 만들어 확인하였다.


위의 코드는 이전 strlen함수를 사용하지 않고 실질적으로 하나하나 계산해나가는 방법을 구현한 것이고 array의 한칸한칸을 읽어들이다가 '\0'을 만나면 연산을 마치고 계산값을 내놓는 프로그램이며 결과는 동일하였다.


여기서는 이름을 입력하면 각 문자를 하나씩 나열할 뿐 아니라 그에 대응하는 ASCII코드까지 출력해주는 프로그램을 짜고 결과를 보였다. 약간씩 변형을 주며 시험했지만 위의 형태가 가장 간결하게 완성된 모습이며 이는 각 문자열의 인덱스의 정보가 char로 호출이 되면 그대로 문자가 출력이 되고 int형으로 호출이 되면 해당 문자의 ASCII코드가 나오는 것을 알 수 있었다. 이를 implicit casting이라고 칭한다.


이번엔 'capitalize0.c'라는 입력받은 문자에 대해 모두 대문자화하는 프로그램이다. 재밌는 부분은 중간에 'a' - 'A'라는 연산을 통해 ASCII코드에서의 대문자와 소문자 간의 차이를 보상하는 것이었다. 각 문자들의 대문자와 소문자간의 차이는 32로 동일하며 이를 역이용한 것이다. ('a' = 97, 'A' = 65)


이번엔 main함수가 항상 parameter가 없는 void형이었지만 이제는 argc, argv를 parameter로 가지는 케이스에 대해 배웠다. argv[]라는 인자의 경우 string type인 것으로 보아 바로 문자열을 받을 수 있도록 하는 역할임을 예측할 수 있고 실제로 프로그램을 실행하고 문자를 입력했던것과 달리 실행함과 동시에 문자열을 전달할 수 있게한다. (ex. ./hello David or ./hello Zamyla)
*argc = argument count, argv = argument vector (argc는 인자가 몇개인지 argv는 문자열을 전달)


이를 'argv0.c'에 구현하여 테스트를 했고 터미널창에 보이는 것처럼 argc == 2의 조건에 따라 단어(인자)가 2개라면 hello와 2번째 단어(argv[1])를 연결하고 단어가 2개가 아니라면 hello, world만 출력하는 것을 알 수 있다.


여기서는 단순히 프로그램 실행 시 입력한 단어들의 갯수만큼(프로그램 명 제외) 단어들을 순서대로 나열하는 모습이다.


이제까지의 변환과정들은 예전에 사용되었던 암호학의 기초인데 이는 위와 같은 과정을 거친다. 일반적인 text를 입력하면 암호화 과정을 통해 암호로 변환이 되는데 밑의 예시를 보자.


독일에서 멕시코로 보내는 전신이라고 한다. 암호화 된 편지를 통해 복호화하는 규칙을 찾아내고 해석을 하게 되었다고 하는데


이처럼 해석을 하여 정보를 확보할 수 있었다고 한다. 예전에는 통신체계가 그렇게 발달하지 않았으니 정보 하나하나가 중요했을텐데 이로 인해 전세가 기울수도 있었을 것이다.


하지만 암호화하는데는 좀 더 중요한 요소가 필요한데 이를 key(secret value)라고 해보자. key에서는 어떠한 문자열을 일정한 숫자만큼 옮기는 역할을 하고 여기서는 +1이라는 예시로 진행한다.


여기 'I LOVE YOU'라는 문자열이 있을 때 이를 plaintext로 지정하고 key값은 +1이었다.


Plaintext를 단순히 ASCII코드로 변환했을 때는 위와 같은 숫자들의 모임이지만


Key값에 의해 +1이 되면 각각 1씩 더해서 다른 값들의 모임이 되고


다시 복호화 했을 때는 전혀 다른 문자열이 됨을 알 수 있다. 이를 해석할 때에는 확률적으로 해당 문자가 조합이 되었을 때 대체될 수 있는 단어들의 집합을 추측한다고 한다. J의 경우 하나만 있으니 A나 I나 한글자로 성립되는 것들, MPWF의 경우 4글자로 이루어진 단어들을 list up하여 비교대조하는 과정 등을 말한다.


마지막 예시는 'exit.c'로 어떤 식으로 에러메세지를 내보내고 어떠한 값을 리턴하는지 설명했다. 인자는 두개일 때만 정상적으로 작동하고 그 이외에는 인자가 잘못되었다는 에러메세지를 출력하게 된다.


좀 더 상세히 들어가서 정상작동할 때와 비정상작동할 때의 return값을 'echo $?'의 명령을 통해 알아보았는데 터미널에 나와있는 것처럼 정상일 때는 0, 비정상일 경우 1인 것을 확인할 수 있다. 에러를 처리할 때 0이 아닐 경우는 1이상의 값들을 통해 어떤 문제가 발생했는지 알 수 있는 좋은 실마리가 되는 것이다.


이번에 소개하는 개념은 array의 값들이 정렬하는 방법을 포함한다.


재밌게도 이번에도 자원자들을 통해 어떻게 array sorting이 진행되는지 눈으로 확인할 수 있었다. 옷에 적힌 값이 작은 값이 왼쪽에, 큰 값이 오른쪽에 앉도록 정렬하는 식이다.


첫번째 방법은 'Bubble sort'이며 근접한 값들끼리 비교하여  둘 중 작은 값이 왼쪽에 큰값이 오른쪽에 위치하도록 하며 더이상 정렬할 필요가 없을 때까지 반복하는 방법이다.


pseudocode로 서술했을 때 표현이다.


다음은 'Selection sort'이며 이는 가장 작은 값을 찾고 기억해뒀다가 맨 왼쪽부터 채우는 방법이다.


마찬가지 pseudocode로 표현했을 때 문장이다.


이 들이 최대한으로 교체를 실시했을 때 횟수를 계산하면 위와 같이 계산할 수 있으며 결국 n의 자승에 비례하여 스케일이 커진 만큼 교체 횟수가 늘어날 것이다.


위의 학생들은 8명이서 했기 때문에 n = 8이었고 비교적 금방 정렬이 됬지만 현실적으로 데이터를 정렬할 때는 더욱 큰 숫자를 다루게 될 것이다. 위는 n = 1 million일 때의 계산값이며 스케일이 커진 만큼 교환 횟수가 늘어남을 알 수 있다.


그렇다면 이렇게 비효과적인 알고리즘만 있을 것인가... 하고 포기할려는 찰나


이전에 전화번호부에서 Mike를 찾는 알고리즘 중 3번째의 효과적인 알고리즘같은 것이 있다고 한다. (좋은 알고리즘은 스케일이 커짐에 따라 해결시간이 선형적으로 비례하지 않고 log식으로 그래프를 그려야 한다-logarithm)


명칭은 'Merge sort'이며 방법은 다음과 같다. 위와 같이 숫자들이 배치되어 있다고 할 때 절반을 나누고 또 나누고 나눠서 요소가 한개가 될 때까지 나눈다.


요소가 한개일 때는 정렬할 수 없으니 두개일 때부터 정렬이 시작되며 위와 같이 초기에 '4 2'로 배치되어 있던 것을 '2 4'로 정렬한다.


다음 두개의 요소에 대해서도 '7 5'로 배치되어 있던 것을 '5 7'로 정렬하고


두개씩 왼쪽 절반이 완성되었으니 왼쪽 절반을 정렬한다. 정렬이 되어 있던 상태기 때문에 '2 4 5 7'로 배치가 유지되며 이번엔 오른쪽 절반씩 정렬을 시작한다.


같은 방식으로 오른쪽 절반마저 정렬이 끝나면 이제 마지막으로 전체에 대해 정렬을 시작한다.


그럼 3번의 단계만에 정렬이 끝난 것을 확인 할 수 있다.


실제로 3가지 방법을 한번에 비교하는 것을 가시적으로 보여주는 페이지였으며 확실히 Merge sort가 가장 빨랐고 그 다음 Bubble sort, Selection sort 순으로 정렬이 완료 되었다.

댓글