[CS50] 배열

14525 단어 CSCS

컴파일 단계

컴파일 단계는 전처리, 컴파일링, 어셈블링, 링킹의 네 단계를 거쳐 이루어진다.

전처리(Precompile)

컴파일의 전체 과정 중 첫번째 단계로, C에서 #으로 시작하는 코드는 전처리기에게 실질적인 컴파일이 이루어지기 전에 어떠한 것을 실행하라고 지시하는 역할을 한다. 예를 들어 #include <stdio.h> 라는 소스 코드가 전처리기에게 다른 파일의 내용을 포함시키라고 알려주면, 전처리기는 stdio.h 파일의 내용이 담긴 C 소스 코드 형태의 파일을 새로 생성해 #include 부분에 포함시키게 된다.

컴파일(Compile)

전처리기가 소스 코드를 생성하면 그 다음엔 컴파일러가 소스 코드를 어셈블리 언어로 컴파일한다.

어셈블(Assemble)

어셈블리 언어로 변환된 코드를 오브젝트 코드로 변환시키는 단계이다. CPU가 이해할 수 있도록 0과 1로 이루어진 명령어로 바꾸는 작업을 말한다. 이러한 작업은 어셈블러라는 프로그램이 수행한다. 오브젝트 코드로 변환되어야 할 파일이 한 개밖에 없다면 여기서 컴파일 단계가 끝이 나고, 그 이상인 경우에는 링크 단계로 넘어간다.

링크(Link)

프로그램이 여러 개의 파일로 이루어져 있을 때는 어셈블 단계 후 하나의 오브젝트 파일로 통합하기 위한 링크 단계가 필요하다. 링커는 여러 개의 오브젝트 코드 파일을 실행 가능한 하나의 오브젝트 코드 파일로 합쳐준다.

디버깅

디버깅 모드(CS50 IDE에서는 debug50)를 사용하면 소스 코드에서 문제를 확인하고 싶은 지점을 골라 브레이크 포인트를 설정해서 컴파일하여 "debug50 파일명"으로 실행하면, 변수의 값을 확인하거나 한 줄씩 코드를 실행해 볼 수 있다. 디버깅을 종료하려면 Ctrl+C를 누르면 된다.

메모리

C에는 여러 데이터 타입이 존재하는데, 각각의 데이터 타입마다 메모리를 차지하는 크기가 다르다.

  • bool : 1바이트
  • char : 1바이트
  • int : 4바이트
  • float : 4바이트
  • long : 8바이트
  • double : 8바이트
  • string : ?바이트 (문자열의 길이에 따라 달라짐)

배열

배열은 같은 데이터 타입의 값들을 메모리 상에 연이어 저장하고 이를 하나의 변수로 쳐서 관리하기 위해 사용되는 개념이다.

#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);
}

int score[3];은 int 데이터 타입의 크기를 3개 가질 수 있는 배열 하나를 'scores'라는 이름으로 생성하겠다는 의미이다. 배열의 인덱스는 0부터 시작한다.

배열의 동적 선언 및 저장

배열의 크기(length)를 사용자에게 직접 입력 받고, 배열의 크기만큼 루프를 돌면서 각 인덱스에 해당하는 값(array[])을 사용자에게 동적으로 입력받아 저장한다. 그리고 average 라는 함수를 따로 선언하여 평균을 구한다.

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

float average(int length, int array[]);

int main(void)
{
    // 사용자로부터 점수의 갯수 입력
    int n = get_int("Scores:  ");

    // 점수 배열 선언 및 사용자로부터 값 입력
    int scores[n];
    for (int i = 0; i < n; i++)
    {
        scores[i] = get_int("Score %i: ", i + 1);
    }

    // 평균 출력
    printf("Average: %.1f\n", average(n, scores));
}

//평균을 계산하는 함수
float average(int length, int array[])
{
    int sum = 0;
    for (int i = 0; i < length; i++)
    {
        sum += array[i];
    }
    return (float) sum / (float) length;
}

문자열과 배열

string 타입의 데이터는 char 타입의 데이터들의 배열이다. 따라서string s = "HI!"; 와 같이 string 타입의 s를 생성할 경우, 메모리 상에 그림과 같이 저장된다.

문자열이 끝나는 곳에는 널 종단 문자가 붙는다. 널 종단 문자가 필요한 이유는 string 타입은 메모리에 저장할 바이트의 크기가 정해져 있지 않아 문자열의 길이에 따라 그 크기도 달라지기 때문이다. 따라서 문자열이 끝난 위치에 널 종단 문자로 표시해 컴퓨터에 값을 저장할 데이터의 크기를 알리는 역할을 한다.

string 타입의 배열을 생성해 여러 문자열들을 저장할 경우, 메모리 상에 다음과 같이 저장된다.

전역 변수

변수를 선언할 때 할당할 값이 고정된 값(상수)이라면, 그 값을 선언할 때 const를 앞에 붙여서 전역 변수, 즉 코드 전반에 거쳐 바뀌지 않는 값임을 지정해 줄 수 있다. 이때 전역 변수에 붙일 이름은 모두 대문자로 표기한다.

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

const int N = 3;

int main(void)
{
    // 점수 배열 선언 및 값 저장
    int scores[N];
    scores[0] = 72;
    scores[1] = 73;
    scores[2] = 33;

    // 평균 점수 출력
    printf("Average: %i\n", (scores[0] + scores[1] + scores[2]) / N);
}

명령행 인자

❓ main 함수에 void 대신 인자값 argc, argv[0]를 정의할 경우

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

int main(int argc, string argv[])
{
    if (argc == 2)
    {
        printf("hello, %s\n", argv[1]);
    }
    else
    {
        printf("hello, world\n");
    }
}

argc는 main 함수가 받게 될 입력의 개수, argv[]는 그 입력이 포함되어 있는 배열이다.
argv[0]는 기본적으로 프로그램의 이름으로 저장된다. 따라서 위 프로그램을 "arg.c"로 저장하여 컴파일한 후 ./argc로 실행해보면 argc의 인자값을 1로 받아 hello, world가 출력된다.
반면 ./argc David로 실행할 경우 프로그램 이름 + David 값이 추가되어 argc가 2가 된다. 따라서 hello, David가 출력된다.

좋은 웹페이지 즐겨찾기