210108 개발일지(32일차) - c언어 알아보기(2) : 컴파일, 문자열 등

고재개발·2021년 1월 8일
0

C Language

목록 보기
2/13

컴파일(compile)

소스코드를 머신코드(기계어)로 바꿔주는 일련의 과정으로 총 4단계이다.
컴퓨터 시스템 정리할 때도 봤던 내용인데, 강의에서 너무 친절하게 설명해줘서 좋았다. 나도 친절히 써봐야지.

  1. 전처리 과정(Precompile)
    - #으로 시작되는 C 소스 코드는 전처리기에게 실질적인 컴파일이 이루어지기 전에 무언가를 실행하라고 알려준다.
    예를 들어, #include는 전처리기에게 다른 파일의 내용을 포함시키라고 알려준다. 프로그램의 소스 코드에 #include 와 같은 줄을 포함하면, 전처리기는 새로운 파일을 생성하는데 이 파일은 여전히 C 소스 코드 형태이며 stdio.h 파일의 내용이 #include 부분에 포함된다.
  1. 컴파일(Compile)
    - 전처리한 C코드를 어셈블리 언어로 컴파일한다.
    어셈블리는 C보다 연산의 종류가 훨씬 적지만, 여러 연산들이 함께 사용되면 C에서 할 수 있는 모든 것들을 수행할 수 있다. C 코드를 어셈블리 코드로 변환시켜줌으로써 컴파일러는 컴퓨터가 이해할 수 있는 언어와 최대한 가까운 프로그램으로 만들어 준다. 컴파일이라는 용어는 소스 코드에서 오브젝트 코드로 변환하는 전체 과정을 통틀어 일컫기도 하지만, 구체적으로 전처리한 소스 코드를 어셈블리 코드로 변환시키는 단계를 말하기도 한다.
  1. 어셈블(Assemble)
    - 어셈블리 언어를 머신코드(기계어, 오브젝트코드)로 변환시켜준다. 여기서 컴파일 될 파일이 한 개라면, 컴파일 작업은 여기서 끝난다. 파일이 여러 개라면 링크 단계를 거친다.
  2. 링크(Link)
    - 만약 프로그램이(math.h나 stdio.h같은 라이브러리를 포함해) 여러 개의 파일로 이루어져 있어 하나의 오브젝트 파일로 합쳐져야 한다면 링크라는 컴파일의 마지막 단계가 필요하다. 링커는 여러 개의 다른 머신코드(기계어, 오브젝트코드) 파일을 실행 가능한 하나의 머신코드(기계어, 오브젝트코드) 파일로 합쳐준다. 링크를 통해 다른 라이브러리에 저장돼있던 함수들이 활용될 수 있다.

간단한 사진으로 정리하면 아래와 같다.

배열 및 함수 만들기

파이썬에서 list를 생각하면 되는데, C에서는 애초에 배열을 선언할 때 배열 안에 들어갈 자료형과 크기를 선언해줘야 한다. 예를 들면 아래와 같다.

  • 배열 만들기
int scores[3];		#'scores라는 변수에 int자료형을 가지는, 크기 3의 배열을 만들겠다'는 뜻이다.
  • 함수 만들기
//평균을 계산하는 함수
float average(int length, int array[])		#여기 float은 리턴할 값에 대한 자료형을 입력한 것이다.
{
    int sum = 0;
    for (int i = 0; i < length; i++)
    {
        sum += array[i];
    }
    return (float) sum / (float) length;
}

문자열(String)

문자열(string)은 문자(char) 자료형의 배열이다. 파이썬을 공부하며 알고 있었는데, 이 강의에서는 메모리와 연관지어 설명해줘서 너무 좋다.
문자열의 끝을 구분하는 방법은 아래와 같이 마지막 문자열 바이트 뒤에 \0, 즉 0을 8개 집어넣어서 null data로 문자열 종료를 알린다.

이번엔, 문자열이 여러개 들어간 배열에 대해서 메모리와 함께 공부해보자.
아래와 같이 코드와 출력값을 보고 정말 신기했다....

#include <stdio.h>
int main(void)
{
    char* names[4];

    names[0] = "EMMA";
    names[1] = "RODRIGO";
    names[2] = "BRIAN";
    names[3] = "DAVID";

    printf("%s\n", names[0]);
    printf("%c%c%c%c%i\n", names[0][0], names[0][1], names[0][2], names[0][3], names[0][4]);
    printf("%c%c%c%c%i%c\n", names[0][0], names[0][1], names[0][2], names[0][3], names[0][4], names[0][5]);
    printf("%c%c%c%c%i%i%i\n", names[0][0], names[0][1], names[0][2], names[0][3], names[0][4], names[0][5], names[0][6]);
}

출력값 :
EMMA
EMMA0
EMMA0R
EMMA0RO

이 것이 C의 재미인가보다.. 캬

명령어(?) : int main(void)의 비밀

(아직 완벽히 이해하지 못했다.)

int main(void){~}

이 부분을 void 대신 아래와 같이 적어도 똑같은 결과를 얻을 수 있다.

int main(int argc, char* argv[])	#argc는 argument character의 약자로 인자 갯수, argv는 argument vector의 약자로 배열을 의미
{
    if (argc == 2)
    {
        printf("hello, %s\n", argv[0]);
        printf("hello, %s\n", argv[1]);
    }
    else
    {
        printf("hello, world\n");
    }
}

여기서 첫번째 변수 argc는 main 함수가 받게 될 입력의 개수다. 그리고 argv[]는 그 입력이 포함되어 있는 배열이다. 프로그램을 명령행에서 실행하므로, 입력은 문자열로 주어진다. 따라서 argv[]는 string 배열이 된다.

argv[0]는 기본적으로 프로그램의 이름으로 저장되며, 만약 하나의 입력이 더 주어진다면 argv[1]에 저장된다. 따라서 위의 코드를 실행해보면 아래와 같은 출력값이 나온다.

입력 : ./a.out gojae

출력값 :
hello, ./a.out
hello, gojae

그 외의 알고 있으면 좋은 것들!

  • &은 주소를 가져오고, *은 그 주소로 간다!
  • C에서는 자료형을 알려주는 함수는 없다.
profile
고재개발

1개의 댓글

comment-user-thumbnail
2021년 1월 11일

오오 친절 감사해용😍🧡

답글 달기