[CS] TIL 7. 코드의 디자인, 배열(1)


  1. 코드의 정확성과 디자인을 관리하는 방법을 설명할 수 있다.
  2. C로 문자열 형식을 가진 변수를 선언하고 출력하는 프로그램을 만들 수 있다.


boostcourse모두를 위한 컴퓨터 과학 (CS50 2019) 강의를 듣고 정리한 필기입니다. 😀


1. 코드의 디자인

1.1 check50

check50은 코드의 정확도를 분석해주는 도구이다. 이 프로그램은 cs50 강의를 위해 작성되었지만, 실제로 많은 사람이 함께 작업하는 환경에서 이와 같은 자동 검사 프로그램은 많이 도움이 된다. check50은 사용 방법은 다음과 같다.

#include <stdio.h>

int main(void)
{
    printf("hello, world\n");
}

c언어로 작성될 새로운 파일을 만들고, stdio.h를 추가 후 int main (void)를 입력한다.

check50 절대경로

그리고 check50으로 코드의 정확도를 확인하려면 고유 식별자(check50)을 입력한 뒤 파일의 절대경로를 입력하면 해당 파일에서 실행하고자 하는 시험용 데이터 셋이 된다. 이후에 깃허브의 아이디와 페이스워드를 입력하면 cs50 서버로부터 코드의 정확도에 대한 피드백을 받을 수 있을 것이다. 위 코드는 정확하지 않다는 결과 뜰 것이다. 우리는 hello, world만 출력하는 것이 아니라, hello 뒤에 입력한 사람의 이름까지 출력해야 했기 때문이다.

이러한 check50은 cs50에서만 제공하는 프로그램으로 조교들과 교수가 학생들의 코드를 자동으로 채점하기 위한 것이지만, 이는 우리가 현실 세계에서 테스트라고 부르는 것과 동일하다. 만약, 우리가 회사에서 일하거나 소프트웨어를 작성할 때 코드 작성만큼이나 중요한 것이 바로 코드가 정확한지를 확인하는 테스트를 만드는 것이다. 특히나 우리나 타인이 우리의 프로그램에 새로운 기능을 추가할 때 프로그램이 고장 나지 않도록 작동 여부를 계속해서 확인해줘야 한다. 즉, cs50에서는 check50을 문제 채점을 위해 사용하지만, 실제로는 check50 같은 프로그램으로 작성 코드를 계속해서 점검하는 과정을 나타낸다.

1.2 style50

check50이 코드의 정확도에 대한 프로그램이라면 style50은 그 이름에서 확인할 수 있듯이 코드의 심미적인 부분을 점검하는 프로그램이다. C와 같은 언어로 프로그래밍을 할 때는 코드 작성을 조금 게으르게 할 수도 있다. 이게 무슨 말이냐 하면,

#include <stdio.h>

int main(void)
{
printf("hello, world\n");
}

이렇게도 쓸 수 있고,

#include <stdio.h>

int main(void)
{printf("hello, world\n");
}

이렇게도 쓸 수 있고,

#include <stdio.h>

int main(void){printf("hello, world\n");}

이렇게도 쓸 수 있다는 말이다. C언어는 괄호와 세미콜론을 모두 구분하여 이해할 만큼 충분히 똑똑하기 때문에 위 세 코드 모두 “hello, word”라는 문자열을 출력한다. 하지만 이러한 것이 문제가 되는 건, 사람이 읽기엔 가독성이 매우 떨어지기 때문이다. 이게 모두 읽기 좋다는 생각이 든다면, 그 생각을 버려야 한다. 모든 프로그래밍 언어에는 해당 언어를 사용하는 사람들끼리의 암묵적인 약속이 있다. 즉, 코드 작성 시에는 나 자신과 타인이 읽기 쉽게 되어야 하며, 무엇보다도 유지 보수하기 쉽게 해야 한다.

예를 들어, 프로그램에 문제가 생겨 수정해야 하는데 자신이 무슨 코드를 짰는지조차 이해할 수 없는 상황을 맞닥뜨리면 안 되지 않는가.

style50은 우리가 더 좋은 코드를 쓸 수 있도록 훈련시켜주는 도구이다. 이 프로그램은 코드의 정확성과는 아무 상관이 없다. “더 좋은 코드”의 기준은 사람마다 다르므로 style50은 마찬가지로 기업은 보기 좋은 C 코드를 표준화하여 해당 코드가 얼마나 보기 좋은지를 객관적으로 평가할 수 있는 프로그램을 만들었다.

style50 파일이름

style50 또한 코드의 디자인을 확인하려면 고유 식별자(style50)를 입력한 뒤 파일의 이름를 입력하면된다.

title

터미널 창에 그 결과가 나오게 되는데, 우리가 작성한 코드는 흰색으로 출력되지만, 우리가 공백을 넣어야 하는 자리에는 초록색 칸으로 표시해주는 것을 알 수 있다.

title

만약 위 코드처럼 작성하면, 붉은 색과 녹색으로 수정해야하는 사항을 알려준다.

많은 회사들은 사내에서 코드를 작성할 때 특정한 스타일 가이드를 따르도록 한다. 여러 사람들이 코드를 작성하기 때문에 서로 불필요한 오해를 없애고, 코드를 이해하는 데 드는 비용을 최소화하기 때문이다.



2. 배열(1)

2.1 메모리

C에는 여러 종류의 자료형이 있고, 각각의 자료형은 서로 다른 크기의 메모리를 차지합니다.

  • bool: 불리언, 1바이트(8비트)
  • char: 문자, 1바이트(8비트)
  • int: 정수, 4바이트(32비트)
  • float: 실수, 4바이트(32비트)
  • long: (더 큰) 정수, 8바이트(64비트)
  • double: (더 큰) 실수, 8바이트(64비트)
  • string: 문자열, ?바이트

이것이 배열과 무슨 상관일까?

title

위 사진은 RAM이라고 하는 물리적 칩이다. 컴퓨터나 스마트폰의 하드 드라이브를 구성하고 있으며, 이 검은 칩들은 일정 크기의 바이트를 의미한다. RAM은 컴퓨터든 노트북이든 스마트폰이든 상관없이 소프트웨어 구동 시에 정보가 저장되는 곳이다. 즉, 메모리 역할을 한다는 것이다. 쉽게 생각하면 여러 개의 노란색 사각형이 메모리를 의미하고, 작은 사각형 하나가 1바이트를 의미한다고 볼 수 있다.

title

이것이 필요한 이유는 단순하다. 여러 종류의 자료형 중에 char는 1바이트였다. 즉, char라는 자료형 하나를 저장하기 위해서는 컴퓨터 메모리 안의 수많은 칸 중 하나를 요청해야 하는 것이다. int는 어떨까? int는 4바이트로 컴퓨터 메모리에서 4칸을 할당해야 한다.

그렇다면 각 칸에 뭐가 있을까? 각 칸에는 8bit가 있고, 즉, 8개의 작은 트랜지스터나 8개의 작은 전구가 있을 것이다. 무엇이 되었든 0과 1을 표현하고 있을 8bit가 존재한다. 이걸로 무얼 할 수 있나? 하는 질문에는 우리가 작성한 프로그램에서 컴퓨터는 어떻게 정보를 저장하는지 생각해보면 답을 알 수 있다.

2.2 배열

예를 들어 아래와 같이 세 개의 문자만 있는 프로그램이 있다고 치자.

#inclue <stdio.h>

int main(void)
{
    char c1 = 'H';
    char c2 = 'I';
    char c3 = '!';
    
    printf("%c%c%c\n", c1,c2,c3);
}

위 프로그램에서 흥미롭게 봐야 할 것이 있는데, 바로 char 자료형은 1바이트를 차지하는 단일 문자인데, 입력할 때는 ''(작은따옴표)가 필요하다는 것이다. 물론 앞서 사용했단. string이라는 자료형은 쌍따옴표를 사용했다. 이유는 그냥 그렇기 때문이다. C도 이 둘을 구분해야 하기 때문이다.

위 프로그램은 “HI!”를 출력할 것이다. H, I, ! 는 모두 char이다. char 안에 있는 ASCII 문자는 2진법으로 이루어져 있다. H, I, !를 각각 숫자(ASCII와 유니코드)로 변환한 것이다.

#inclue <stdio.h>

int main(void)
{
    char c1 = 'H';
    char c2 = 'I';
    char c3 = '!';
    
    printf("%i%i%i\n", (int) c1, (int) c2, (int) c3);
}

그렇다면 변수 c1, c2, c3의 숫자 값을 실제로 보고 싶다면 printf()를 이용할 때 (int)라는 명령어를 사용하여 정수처럼 여겨달라고 하면 된다. 이렇게 하나의 자료형을 다른 종류로 바꾸는 행위를 형변환이라고 한다. 이제 이 프로그램을 저장하고 컴파일을 하면 72, 73, 33을 출력할 것이다.

#inclue <stdio.h>

int main(void)
{
    char c1 = 'H';
    char c2 = 'I';
    char c3 = '!';
    
    printf("%i%i%i\n", c1, c2, c3);
}

clang은 너무 똑똑해서 우리가 char를 입력하고 int로 출력하도록 형식지정자만을 바꿔도 72, 73, 33을 출력한다.

title

이제 c1, c2, c3라는 변수를 가진 프로그램이 컴퓨터에 어떻게 저장되는지 살펴보자. 위와 같은 노란색 칸은 1GB는 10억 개가 존재할 것이다. (정확한 수치는 아니다.) 컴퓨터는 위 프로그램을 작성할 시 위 그림처럼 H를 하나의 칸에 넣고, I를 또 다른 칸에 넣고, !를 세 번째 칸에 넣는다. 그리고 각 칸에 해당하는 변수명을 저장한다.

title

이들은 char 형태로 저장된 것이니라 10진법인 72, 73, 33을 2진법으로 표현하여 저장되었을 것이다.

Q1. 변수 세 개를 정수로 선언하고 72, 73, 33을 저장한 뒤 %c로 출력할 수 있나요?

▶ 반대로도 가능하다. 물론 그렇게 하려면 ASCII 코드가 무엇인지 프로그램 작성자도 알아야 할 것이다. 대부분의 프로그래머는 각 알파벳을 10진수, 2진수로 하면 어떤 값이 출력되는지 암기하지 않는다. 단지 일종의 대응 관계가 있다는 사실 정도만 알고 있을 뿐이다.

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    // Scores
    int score1 = 72;
    int score2 = 73;
    int score3 = 33;

    // Print average
    printf("Average: %i\n", (score1 + score2 + score3) / 3);
}

위 코드처럼 숫자를 이용해 평균을 구할 수 있다. 이 프로그램에는 여러 문제가 있다. 첫 번째는 평균을 구해야 하는 점수의 개수가 더 많아질 때마다 이 프로그램을 수정해야 할 것이다. 두 번째는 평균이 소수점으로 떨어질 경우 부정확함의 문제도 있다. 세 번째로는 사실상 위 코드는 복사 붙여넣기 한 것에 가깝다는 것이다. 같은 일을 계속 반복하는 것은 디자인 개선의 여지가 있다는 뜻이기도 하다.

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    // Scores
    int scores[3];
    scores[0] = 72;
    scores[1] = 73;
    scores[2] = 33;

    // Print average
    printf("Average: %i\n", (scores[0] + scores[1] + scores[2]) / 3);
}

C에서 하나 이상의 값들이 있고 서로 연관되어 있다고 할 때… scores 같은 복수형의 변수 이름을 사용하는 것이 지적할 것이다. 이렇게 여러 개의 값을 가진 하나의 변수를 만들고자 할 때 사용하는 것이 배열이다. 대부분의 프로그래밍 언어에는 배열이라는 자료형의 데이터 타입이 존재한다. 배열은 값들의 리스트로 모두 같은 자료형의 값들이 같은 이름의 변수에 저장되어있다. 메모리상에서는 연이어 저장하고 이를 하나의 변수로 관리하기 위해 사용된다. (일부 언어에서는 비어있는 메모리에 끼우기도 한다는데… 그냥 해당 이론이 모든 언어에 통용되는 것은 아니라고 생각해주면 된다.)

배열을 사용하면 그냥 int scores라고 입력하면 된다. 여기서 점수의 갯수는 총 3이다. 그럴 때는 [](대괄호)를 사용해서 원하는 점수의 개수를 적고 세미콜론을 붙이면 된다. (int scores[3];)

score1, score2, score3이라는 변수 대신 배열의 인덱스에 값을 부여하면 된다. 배열의 인덱스는 0부터 시작하기 때문에, scores의 인덱스는 0, 1, 2가 있다. 이렇게 하면 아까와 같이 세 개의 변수가 생겼지만, 복사 붙여넣기로 생긴 지저분함을 없애고 모두 같은 변수에 저장할 수 있게 되었다. 그리고 무엇보다, 컴퓨터에게도 이 세 점수가 서로 관련이 있는 것이라고 알려준 것이 마찬가지이다.

이 모든 내용을 한 줄에 사용하는 법은 나중에, 평균의 값을 소수점까지 출력하는 것은 내일 배우도록 하겠다.




© 2020. by RIVER

Powered by RIVER