정신과 시간의 방
카테고리
작성일
2022. 9. 19. 23:44
작성자
risehyun

0. C언어 소개

  • C언어의 특징
    1. 간결한 언어이다.
      - C언어는 꼭 필요한 기능만 들어 있고 모든 표기법이 아주 간결하게 되어 있다.

    2. 효율적인 언어이다.
      - C로 작성된 프로그램은 크기가 작으며 실행 속도가 빠르고 메모리를 효과적으로 사용한다.

      - 거의 어셈블리 언어 수준의 효율성을 가지고 있으며 이로 인해 상업용 프로그램을 작성할 때 큰 장점이 있다.

    3. 저수준의 프로그래밍도 가능하고 고수준의 프로그래밍도 가능하다.
      - C언어는 운영체제를 만들 수 있을 만큼 어셈블리어처럼 구체적인 하드웨어 제어가 가능하다. 대부분의 임베디드(내장) 프로그램이 C언어로 개발된다.

      - C언어는 모듈 단위의 프로그램 작성을 지원하며 분할 컴파일도 가능하기 때문에 고수준에서의 프로그램 작성도 가능하다. 하향식 설계, 구조화 프로그래밍, 모듈화 설계 등 소프트웨어 공학의 다양한 기법과 더불어 포인터, 비트 단위의 조작으로 여러 하드웨어를 섬세하게 제어할 수 있어 프로그램의 유지 관리가 쉽다.

    4. 이식성이 뛰어나다.
      - 이식성(Portability)이란 한번 작성된 프로그램을 다른 CPU를 가지는 하드웨어로 쉽게 이식할 수 있다는 뜻이다. 많은 종류의 CPU에 대하여 C 컴파일러가 개발되어 있으므로 C 프로그램은 상대적으로 이식성이 좋다. 어셈블리 언어가 사용하는 CPU에 따라 프로그램을 변경시켜 줘야 하는 치명적인 단점과 대조되는 점이다

C언어는 포인터뿐만 아니라 메모리를 직접 할당하고 해제하는 기능까지 갖췄기 때문에 섬세한 제어가 가능하지만 반대로 그렇기 때문에 사용 시 주의가 필요하며 프로그래머의 높은 언어 이해가 요구된다.

 

이번에는 아주 간단한 예제를 통해 프로그램의 기본 뼈대에 대해 알아보자.

#include <stdio.h>

int main(void)
{
	printf("Hello World!");
	return 0;
}

 

1. #include 전처리기

 

 - '#' 기호 : 전처리기()를 나타내는 기호이다.


 - include : 뒤에 오는 파일을 소스에 포함하라는 C언어의 예약어이다.
   간단히 말해서 그 파일을 사용하겠다고 선언하는 것과 같은데,

   include 하기로 지정한 헤더 파일 안의 코드가 그대로 현재 파일에 복사붙여넣기 된다.

 - <stdio.h> : 'Standard Input/Output' 을 의미하는 표준입출력 헤더 파일(.h)이다.

즉, #include <stdio.h>는 소스코드를 기계어로 번역하기 전에 뒤에 오는
stdio.h 파일을 소스 코드의 일부로 포함(복사->붙여넣기)하여 컴파일하라는 의미이다.

printf() 함수에 대한 선언이 stdio.h 파일에 담겨있기 때문에 이와 같이 파일을 포함해주어야 한다.

 

※ 전처리기란?
- '전'이란 컴파일 전을 의미한다. 즉 C언어로 작성된 소스코드를 기계어로 번역하기 전을 의미한다.
- 본격적으로 컴파일하기 전에 사전 정지 작업을 하는 컴파일러의 일부이다.
- 전처리기가 실행되는 동안 주석이 삭제된다. 따라서 주석은 프로그램의 크기나 성능에 영향을 미치지 않는다.

※ 헤더파일이란?
확장자가 .h인 파일을 헤더 파일이라고 한다.
컴파일러가 필요로 하는 정보를 가지고 있는 파일로,  각각의 기능을 수행하는 함수들이 선언되어 있다.

(.h는 일종의 선언이다. -> 'printf()'는 함수입니다. 
반면 .c, .cpp 등은 정의이다. -> printf()라는 함수를 이용하여, Hello, World! 라는 문자열을 출력해 주세요.

실제로 컴파일될 최종 소스 코드는 아래처럼 둘을 합친 것과 같다.

<'printf()'는 함수입니다. 
printf()라는 함수를 이용하여, Hello, World!라는 문자열을 출력해 주세요.>

 

2. 스코프와 구문


스코프는 여러 구문을 한 덩어리로 묶을 때 사용한다.
이 스코프 안에 원하는 작업을 수행하는 문장을 적어주면 된다.

{ <- 스코프가 시작된다
.....;
 .....;
} <- 스코프가 끝난다

 

구문이란 문장을 올바르게 구성하기 위한 규칙으로 연산식이나 함수 호출, 제어 문 등으로 이루어진다.

C언어를 포함한 여러 언어들은 구문의 끝을 세미콜론(;)으로 나타낸다.

만약 이를 생략하면 구문이 끝나지 않은 것으로 간주되어 문법 오류가 발생하므로 반드시 작성해야 한다.

 

이러한 구문이 모여 연속된 흐름으로 한 블록을 이룬 것을 프로그램이라고 한다.

이를 작성한다는 의미의 프로그래밍은 문법이나 논리적으로 문제가 없는 구문들을 나열하여 의미 있는 절차상의 흐름을 만들어 내는 과정이다.


3. 함수 

C언어는 대표적인 절차지향 프로그래밍 언어이다.

절차지향 프로그래밍이란 프로그램의 순서와 흐름을 먼저 세우고 필요한 자료구조와 함수들을 설계하는 방식을 의미한다.

이처럼 C언어는 함수로 시작하여 함수로 끝나는 언어라고 할 수 있다.

그렇다면 무엇을 함수라고 할까?

 

f(x) = y


함수란 구문의 절차적 흐름을 한 블록으로 묶은 코드 단위로 매개 변수(x), 함수 몸체(f()), 반환값(y)으로 이뤄져 있다.

 

달리 말하자면 프로그램에서 입력이 들어오면 출력을 만들어내는 작은 기계와 같은데 마치 믹서기와 같다.

변수를 오렌지, 함수 몸체를 믹서기 본체, 출력물을 완성된 오렌지 주스라고 생각하면 된다.

함수에서는 입력되는 변수(과일의 종류)에 따라 출력값(과일 주스)이 결정된다.

하나의 프로그램은 이와 같은 여러 개의 함수들로 구성된다.

 

※ 매개 변수는 인수, 인자라는 어휘와 혼용하여 사용한다.
혹은 파라미터, 아규먼트라고 영문을 직접 읽어 표시하기도 한다. 개념상 이 단어들은 같은 말이다.
 
하지만 세부적으로 구별하면 호출자 함수에 기술하는 것은 '실인수'라고 하며,
실인수를 줄여 인수 혹은 아규먼트라고 한다.

피호출자 함수에서는 형식인자라고 하며, 이를 파라미터라고 부른다.

보통은 매개 변수라고 하는 경우가 많다.

 

또한 함수의 사용과 관련해서 다음과 같은 용어가 자주 쓰이니 알아두자.

  • 함수와 관련해 사용되는 용어
    - 함수의 정의 : 만들어진 함수, 실행이 가능한 함수를 일컬음
    - 함수의 호출 : 함수의 실행을 명령하는 행위
    - 인자의 전달 : 함수의 실행을 명령할 때 전달하는 입력 값

함수 중에는 어느 프로그램에서나 반드시 사용되는 특별한 함수가 있는데, 바로 main() 함수이다.

 

  • main()  함수
    C 프로그램에서 가장 처음으로 실행되는 부분으로, 모든 프로그램이 운영체제의 컴파일러가 main() 함수를 호출하는 것에서 시작한다. 따라서 C프로그램은 main()을 제외한 함수들이 직/간접적으로 main()으로 부터 호출되는 방식으로 작동한다.
    그렇기 때문에 C 프로그램엔 반드시 main()이 존재하여야 하며, 이때 프로그램당 main() 함수는 오직 하나만 존재해야 한다.
    여러 개를 만들면 링크 오류가 발생하기 때문이다.

위에서 살펴본 문자열 출력 예제에서 main()은 자신의 스코프 안에서 print() 함수를 사용하였다.

이때 main() 함수를 호출자 함수라고 하며 main()에 의해 호출된 printf() 함수는 피호출자가 된다.

호출자 함수와 피호출자 함수
- 호출자 함수는 피호출자 함수를 호출함으로써 사용(=연결) 된다.

- 호출자 함수는 반드시 피호출자 함수 매개 변수의 초깃값(실인수)을 확정해야 한다.
EX. 위의 예제에서는 printf() 함수의 괄호 안에 "Hello World\n"를 넣어주었다. 이것이 바로 매개변수의 초깃값이다.

- 호출자 함수와 반대로 피호출자 함수는 반드시 호출자에게 적절한 정보를 반환해야 하는 의무가 있다.
  정보를 반환하는 예약어는 return이다. 피호출자 함수는 이 return 예약어 뒤에 반환해야 할 값을 명시해야 한다.
EX. 시스템의 어떤 함수가 main() 함수를 호출한다면 main 함수는 호출자 함수에게 정보를 반환해야 한다.
       위의 예제에선 return 0으로 호출자 함수에게 자신의 정보를 반환했다. (※)

 

※ C언어에서 main() 함수의 호출은 곧 프로그램의 시작이며 main() 함수가 반환되는 순간 프로그램이 끝이 난다.

그러므로 main() 함수는 반환값이 의미가 있다기 보단 연산 과정 그 자체가 더 의미가 있다고 볼 수 있다.

이처럼 함수는 결론이 목적일 수도 있고(호출한 영역으로 값을 전달 또는 반환하는 경우),
연산 과정 그 자체가 목적일 수도 있다(이 때는 연산 이후 현재 실행 중인 함수가 종료된다. 주로 void 타입의 함수가 이러한 목적을 가진다).

 

4. 빌드 - 컴파일과 링크

소스 파일을 완전한 실행 파일로 변환하는 과정을 빌드(build)라고 한다.
빌드 과정은 컴파일과 링크로 나눌 수 있는데, 

소스 파일의 작성이 완료되면 소스 파일을 컴파일(compile)하고 링크(link) 하는 순서로 작동한다.

 

먼저 컴파일에 대해 알아보자.
컴파일에 쓰이는 컴파일러는 일종의 번역기와 같다. 소스 파일 안의 문장을 분석하여 문법에 맞도록 작성되었는지를 체크한다. 도중에 잘못된 문법을 사용하거나 철자를 잘못 기입했다면 오류 메시지를 출력한다. 만약 오류가 없다면 컴파일러는 각 문장들을 컴퓨터에서 실행이 가능하도록 기계어로 변환한다. 

 

이 기계어로 되어 있는 파일을 오브젝트 파일(.obj)이라고 부른다.

우리가 소스코드를 작성한 .c 확장자 파일이 성공적으로 컴파일되면 같은 내용이 기계어로 바뀐 .obj 파일이 생성된다.

 

컴파일이 성공할 경우 다음 단계인 링크로 넘어간다.
링크는 오브젝트 파일을 라이브러리와 연결하여 실행 프로그램을 만든다.

 

라이브러리란 프로그래머들이 많이 사용하는 기능을 미리 작성해 놓은 것으로 컴파일러에 내장되어 있다.
링크를 수행하는 프로그램을 링커라고 한다.
.obj 파일에 라이브러리를 붙여서 실행 가능하게 만든 파일이 바로 .exe 파일이다.

 

  • 컴파일과 링크로 단계를 나눠 실행 파일을 만드는 이유가 뭘까?

    하나의 프로그램은 일반적으로 여러 개의 소스 파일로 구성된다. 이 경우 각각의 파일을 따로 컴파일하고 링커를 통해 결합한다.

    만약 하나의 소스 파일만 변경한 경우에도 모든 소스 파일을 다시 컴파일해야 한다면 상당한 시간이 낭비된다.

    링크 단계가 있을 땐 변경된 소스 파일만 다시 컴파일하면 되므로, 결론적으로 두 단계를 나눠 실행 파일을 만드는 것은 시간을 절약하기 위해서이다.

 

5. 주석

주석(Comment)은 프로그램의 가독성을 높이기 위해 프로그램 내에 삽입된 설명용 메모를 뜻한다.

 

/*

 넓은 범위(블록 단위)의 주석을 원한다면 이렇게 작성한다

*/

// 이것처럼 행 단위의 짧은 주석도 있다.

 

위를 응용하여 스타일에 따라 이렇게 주석의 왼쪽 모서리를 정렬시키는 사람도 있다.

 

/ **************************************
* 이 주석도 주석의 스타일 중 하나입니다.
* 왼쪽 모서리를 정렬시키는 스타일로
* 어디가 주석인지 쉽게 알 수 있습니다.
***************************************/


컴파일 단계에서 주석은 컴파일되는 대상에서 제외되어 모두 제거된다.

때문에 주석 유무는 프로그램 파일과 실행 결과에 영향을 미치지 않는다.

주석의 양과 오브젝트 코드의 크기 역시 전혀 상관이 없다.
이렇듯 주석은 순수하게 프로그램을 읽는 사람들을 위한 것이다.

/*   /*   주석 사용의 잘못된 예   */   */

 

  • 주석 작성 시 주의사항
    주의할 점은 위와 같이 주석 안에 주석을 넣으면 안 된다는 것이다. 이는 C언어의 주석이 PL-1이란 언어에서 그 형식을 빌려왔기 때문이다. 주석이 들어간 코드의 전체를 주석 처리 하고 싶다면 #if 0과 #endif라는 전처리 지시자를 원하는 범위의 앞 뒤로 명시해주어야 한다.

 

  • 주석의 중요성

아무리 천재 프로그래머여도 프로그램을 작성한 후 오랜 시간이 흘러 다시 코드를 수정하려고 하면

자신이 어떤 의도로 프로그램을 작성했는지 정확히 알지 못하기 때문에 재검토에만 오랜 시간이 걸린다.

특히 프로그램이 커지고 복잡해질수록 이런 현상은 심화된다.

 

따라서 반드시 주석을 사용해 프로그램의 구조와 동작을 모두 문서화하는 습관을 기르는 것이 좋다.

이때 코드를 그대로 설명하려고 하는 주석은 좋은 주석이 아니다.

 

좋은 주석이란 코드를 반복하거나 코드를 설명하지 않는 것이다.

코드를 그대로 설명하기 보다는 코드를 작성한 의도를 명확히 나타내는 편이 좋다.

즉, 주석은 프로그래머가 무엇을 하려고 하는지를 보다 높은 수준에서 설명해야 한다.

'C,C++' 카테고리의 다른 글

구조체 포인터  (0) 2023.01.20
문자, 문자열  (0) 2023.01.19
[C] 함수 응용  (0) 2022.10.01
[C/C++] 기본 제어문  (0) 2022.09.25
[C] 입출력  (0) 2022.09.23