C

KNK Chap.2 (2.1~2.3)

Daryl 2022. 12. 2. 17:13

Chap.2 C Fundamentals

 

2.1 Writing a Simple Program

간단한 프로그램을 통해 preprocessing directive 전처리 지시자, function 함수,

variable 변수, statement 구문을 컴파일하고 링크하는 법을 배운다.

Printing a Pun

말장난 출력하기


To C, or not to C: that is the question. 출력하는 프로그램을 만들 것이다
(To be, or not to be: that is the question을 바꾼 말장난)
파일명은 pun.c 


코드 ↓

#include <stdio.h>

int main(void)
{
    printf("To C, or not to C: that is the question.\n");
    return 0;
}

코드의 의미 ↓


#include

C의 표준 입출력 라이브러리에 대한 정보를 넣기include 위해서 입력하는 코드.

그래서 실행 코드는 아님.
main

대표main 프로그램이라는 뜻. 사실상 실행되는 코드.
printf

표준 입출력 라이브러리에 내장된 함수. 데이터를 출력함.
\n

다음 줄로 넘긴다는 의미의 코드.
return 0;

프로그램이 종료될 때 운영체제에 0 값을 넘긴다는 뜻.
혹은 반환return한다는 뜻.


Compiling and Linking


위 코드를 이제 실행 가능한 형태로 만들려면 3개의 과정을 거쳐야함.
사실 컴파일러를 실행하면 자동으로 되는거긴 함.
일단 첫번째 과정인 전처리 과정도 보통 컴파일러에 추가되어 있음. 
그래도 정확히 아는게 좋으니까.

1. 전처리 preprocessing
전처리기preprocesor는 #으로 시작하는 지시어directive 코드의 지시를 먼저 따른다.
전처리기는 프로그램에 무언가를 추가하거나 수정할 수 있어 마치 에디터 같다고 함.

 

무슨 소리인지 모르겠어서 찾아봄. 참고글: https://www.it-note.kr/301

 

32. 전처리기(Preprocessor) - 기초

C언어에서 전처리 구문은 C 컴파일러가 컴파일을 하기전에 전처리기가 선행으로 처리되는 부분을 말합니다. C언어 작성된 프로그램(*.c)을 컴파일 할때에 전처리기는 전처리 구문을 해석하여 주

www.it-note.kr

전처리기는 전처리 구문을 해석해서 주석부를 없애고, 전처리 구문을 처리한 *.i 파일을 임시로 생성함.

이후 컴파일러가 이 *.i 를 컴파일하여 assembler 소스 코드를 생성함.

어셈블러는 assembler 소스를 처리해서 기계어 코드를 생성함.

이것을 다른 기계어 코드와 link하여 실행 파일을 생성함.

 

위 참고글에서 가져 온 그림을 보면 좀 더 이해가 쉽다.

이 과정을 하나씩 자세히 설명하는게 책 내용이다. ↓


2. 컴파일 compiling
전처리기에서 수정된 프로그램은 컴파일러로 보내짐. 
여기서 코드가 기계어 명령문 코드(목적 코드object code)로 번역됨.

3. 링킹 linking
그 목적 코드를 링커linker가 추가적으로 필요한 다른 코드*와 합침.
*: 프로그램에 사용되는 라이브러리 내부의 명령문.
위의 코드로 예시를 들자면 printf 같은 것이다.

이 과정을 거치기 위해선 명령을 입력해야 함. 
그런데 컴파일과 링크를 하기 위한 명령은 컴파일러와 운영체제에 따라 다름.
그래서 UNIX 체제라면 터미널이나 cmd에 아래의 명령문을 입력해야 함.

% cc pun.c


이건 컴파일 명령이고, 더불어 링킹은 자동으로 됨.
여기서 %은 UNIX에서 프롬프트를 의미함. 사용자가 입력할 필요는 없음.
그리고 cc는 컴파일러 이름임. UNIX에서는 C 컴파일러를 cc라고 부름.

 

The GCC Compiler


그러나 가장 유명한 컴파일러는 GCC 컴파일러임. linux에 기본 제공됨.
그러나 windows 등 다른 운영체제에서도 쓸 수 있음. 나도 윈도 사용자다.

 

컴파일과 링킹이 끝나면 gcc는 파일을 토대로 실행 가능 프로그램을 생성함.
이름은 보통 a.out.인데, gcc의 옵션 중 -o라는 옵션은
실행 가능 프로그램이 포함된 파일의 이름을 바꿀 수 있게 해준다.
위의 pun 코드로 예를 들면 

% gcc -o pun pun.c


라고 입력하면 pun이라는 이름의 프로그램이 생성됨.


나는 이걸 어떤 블로그에서

gcc <파일명>.c -o <파일명>.exe 입력


이렇게 배웠었다. 그래서 vscode에서 터미널을 열어 책의 방식도 입력해봤다.
그랬더니 폴더에 pun 이라는 프로그램이 생성되어 있었다.

 

그래서 명령 프롬프트를 열고 해당 프로그램이 있는 폴더 위치를 입력한 다음
pun.exe를 입력해봤다. 그랬더니 정말 pun이 나왔다.

 

물론 vscode에서 F5 누르고 실행시켜도 된다.


이 단원의 Q&A에서 GCC를 좀 더 구체적으로 다루고 있다.

 

Integrated Development Environments

 

컴파일러 대신 쓸 수 있는 것은 통합 개발 환경(IDE)이다.

개발 환경을 유지한 상태로 수정, 컴파일, 링킹, 실행, 디버깅까지 해주는 SW 패키지다.

쓰면 편하고 종류도 다양하니 본인 플랫폼에 어떤 IDE가 적합한지 검색 추천.

나는 vscode를 쓰고 있다. 코드 편집에 최적화된 IDE로 알고 있다.

 

 

 

2.2 The General Form of a Simple Program

 

이번에는 프로그램을 일반화하는 방법을 알아보자.
pun.c를 예시로 일반화해보면 이렇게 표기할 수 있음.

*directives*

int main(void)
{
 *statements*
}

directives 지시자

statement 구문

 

여기서 **안의 글자들은 프로그래머가 직접 작성해야 할 코드다.
main 함수의 중괄호{}가 시작되고 끝나는 방식은 타 언어들의 begin과 end 방식과 같다.
즉, C는 축약과 특수 기호를 자주 사용함.

C 프로그램은 무조건 세 가지에 의존함.
지시자directives, 함수functions, 구문statements.

 


Directives

 

코드는 컴파일되기 전에 전처리기preprocessor에 수정됨.

아까 말했다시피 지시자는 전처기들을 위한 명령문이고, 언제나 #으로 시작함.

그리고 말단에는 ; 등의 특수 기호가 붙지 않음. (ex. #include)

 

실제로 pun.c의 첫 코드는 

#include <stdio.h>

이다.

pun.c 컴파일 전에 <stdio.h>에 존재하는 정보들을 pun.c에 포함include시킨다는 뜻. 
<stdio.h>는 C의 표준 입출력 라이브러리에 대한 정보를 담은 헤더header이다.
헤더는 표준 라이브러리의 여러 부분을 포함하고 있다.
C는 읽거나 쓸 수 있는 기본 내장 함수가 없기 때문에 헤더가 필요함.

 

Functions


함수란 집합화된 여러 개의 구문을 의미함. 수학에서 유래한 명칭이다.
프로그램이 건물이라면 함수는 벽돌이고, C는 벽돌들의 집합이라고 할 수 있다.

 

함수의 종류는 두 가지.

1) 프로그래머가 직접 제작한 함수

2) C에서 제공 가능한 함수

 

후자는 컴파일러가 제공하는 라이브러리에 속하는 함수이다. 
그래서 라이브러리 함수library function이라고 부름. 

함수 중 값을 연산하는 것도 있고 연산하지 않는 것도 있음.

차이는 return 구문 유무.
전자의 경우, return 구문을 이용해 그 함수가 어떤 값을 반환return하는지 명시한다.
예를 들어, 입력값에 1을 더하는 함수는 이런 식의 구문을 짜야 함.

return x + 1;


만약 함수가 두 개의 입력값을 받아 각각의 제곱의 차를 구하는 함수라면?

return y * y - z * z;


어떤 함수를 사용하던 main 함수는 필수다. 이 함수는 프로그램 실행시 자동 실행됨.
너무 중요한 함수이고, Chap.9 전까지는 오로지 main 함수로만 프로그래밍할 것이다.

 

 [!!!] 이름을 꼭 main으로 써야함. 매우 중요. begin, Main, MAIN, 등등... 절대 안됨.


그럼 main 함수는 어디에 속할까? 값을 연산하는 함수이다.
main 함수는 프로그램이 종료될 때 상태 코드status code를 반환함. 

 

다시 pun.c를 보자.

#include <stdio.h> 

int main(void) 
{              
 printf("To C, or not to C: that is the question.\n");

 return 0; 
}

int main(void)

여기서 int는 main 함수가 정숫값을 반환한다는 것이다.

void는 main 함수가 입력값을 요구하지 않을 때 사용한다.

return 0;

여기서 이 구문은 2가지 효과가 있다.

1. 프로그램 종료

2. 0 값 반환

 

[Q&A] return 구문이 없어도 프로그램은 종료된다.

하지만 main 함수가 정숫값을 반환받지 못했으므로

대부분의 컴파일러에서 에러 메시지를 줄 것이다.

 

 

Statements


구문이란, 프로그램 실행시 같이 실행되어야 하는 명령들이다. 
예를 들어 pun.c에는 2개의 구문이 있다. 

1. return 구문

2. 함수 호출function call 구문.
함수를 호출call한다는 건 함수가 임무를 수행하게 만든다는 것이다.

pun.c에서는 printf 함수를 호출해서 화면에 문자열을 출력시킨다.

printf("To C, or not to C: that is the question.\n");


C에서 각 구문은 무조건 세미콜론(;)으로 끝난다. (복합문compound statement 예외)
구문 자체는 여러 줄이 될 수 있기 때문에, 세미콜론은 구문의 끝을 알려주는 역할을 한다. 
세미콜론이 없으면 구문이 어디서 끝나는지 명확하지 않을 수 있다.
이와 달리 지시자directives는 한줄 짜리라 굳이 세미콜론이 필요 없어서 쓰지 않는다.

 

Printing Strings

 

printf 함수를 이용하여 문자열을 출력해보자.

printf 함수는 매우 강력한 함수이며, 큰따옴표 안에 들어 있는 정보만을 출력한다. 

또한 다음 줄로 넘어가라는 지시를 해줘야만 넘어가기에, \n*을 추가해줘야 한다.

*\n: new-line character 개행 문자, 줄 내림 문자

 

개행 문자 작동은 다음 예제로 확인해보자.

printf("To C, or not to C: that is the question.\n");

이 구문을 아래와 같이 두 개로 나누면

printf("To C, or not to C: ");
printf("that is the question. \n");

어차피 결과는 같다. 개행 문자가 하나만 있으니까.

 

개행 문자는 하나의 문자열 리터럴string literal에서 여러 번 등장할 수 있다.

예를 들어 아래의 메시지를 출력하고 싶다 치자.

Brevity is the soul of wit.
--Shakespeare

그럼 이렇게 코드를 작성해도 된다.

printf("Brevity is the soul of wit. \n --Shakespeare");

 

 

 

 

2.3 Comments

 

pun.c 프로그램에는 주석이 없다. 하지만 모든 프로그램은 신원 확인 정보를 담고 있다.
프로그램 이름, 생성 날짜, 프로그래머, 프로그램의 목적 등등을 다 주석으로 담는다.

주석은 프로그램의 거의 어디에나 있을 거다. 그냥 도배 수준이다.
/* : 주석 시작 기호
*/ : 주석 끝 기호

 

예를 들면

/* 이런게 주석이다 */

 

만약 pun.c의 시작 부분에 주석을 넣는다면 이렇게 넣을 수 있다. 
예제와는 다르게 내맘대로 수정했다.

Atoz는 내가 예전에 쓰던 닉네임이다


여러 줄로도 할 수 있다.

근데 이렇게 하면 끝이 어디인지 알기 어렵다.
그래서 박스를 치기도 한다.

아니면 간단하게 이런 식으로. 

코드와 같은 줄에 주석을 넣을 수도 있다. 

이런 주석은 날개 주석 winged comment 라고 부름.

 

[!!!] 주석을 깜빡하고 닫지 않으면, 컴파일러는 주석이 열린 부분부터 싹 다 무시한다.

예를 들면 이런 것이다.

printf("My "); /* forgot to close this comment...
printf("cat ");
printf("has ");/* so it ends here */
printf("fleas");

출력 결과는 이렇게 나올 것이다

My fleas


[C99]
C99는 주석 기호로 /* 이외에도 //을 제공한다. 끝마침 기호가 없어서 편하다.

또한, 끝마침 기호 깜빡하는 실수를 없애준다. 여러 줄을 쓰고 싶으면 계속 //을 쓰면 된다.

어디가 주석인지 딱 보이는게 장점이다.