[C] TIL 1. C 언어란?


  1. 프로그래밍에서 말하는 이식성(portability)란 무엇인가?
  2. B언어와 C언어의 차이는 무엇인지 설명하라.
  3. 소스 코드 파일, 오브젝트 코드 파일, 실행 파일의 차이점을 설명하라.
  4. 컴파일러와 링커가 하는 일에 대해서 설명하라.
  5. 링커는 무슨 일을 하는가.


inflearn독하게 시작하는 C프로그래밍강의와 C기초 플로스 6판을 기초로하여 정리한 필기입니다. 😀


1. C언어란?

1.1 C언어의 역사

C언어는 Dennis Ritchie에 의해서 만들어졌으며, UINX 운영체제를 설계한 언어라고 알려져 있다. 하지만, 사실 UINX 운영체제는 1969년에, C언어는 1972년에 개발되었다.

UINX 운영체제는 원래 AT&T 벨 연구소의 Thompson의 B언어 토대로 만들어졌었으나 실패하고 만다. 후에 Ritchie가 프로젝트에 투입되었고, B언어를 토대로 C언어가 만들어지게 된다. B 언어 역시 또 다른 언어로부터 유래한 것이지만…

B언어는 BCPL을 바탕으로 만들어졌는데, 그래서인지 B언어에는 자료형이 없었다. 자료형은 낡은 컴퓨터에서는 쓸모 없었지만, 현태적인 컴퓨터가 지원하는 문자 자료형을 처리할 수 없기 때문에 자료형이 없다는 것은 문제가 되었다. 그래서 Ritchie는 자료형을 변수와 포인터(메모리를 통제하는 형식)까지 포괄하는 C언어를 만들게 된다. 자료형은 대단히 중요한데, 메모리에 저장된 정보 즉, 일정길이의 정보를 해석하는데에 사용하기 때문이다.

대다수의 프로그래밍 언어의 첫 예제가 Hello, world인 이유는 B언어를 만든 Thompson이 B언어의 메뉴얼을 만들었을 때, 첫 예제가 Hello, world였기 때문이다. 그래서 C언어를 포함, C언어 기반 언어는 모두 Hello, world로 시작한다.

1.2 C 언어의 장단점

C언어 전에도 좋은 언어들이 많았다. 1963년에 탄생한 Basic은 컴퓨터에 익숙지 않은 학생들이 쉽게 프로그래밍을 배울 수 있도록 영문법 체계와 비슷하게 개발되었으며, 1969년에는 좋은 프로그래밍 원리를 가르치기 위한 튼튼한 기초를 제공하는 것을 목표로 Pascal이 탄생했다. 그런데 왜 현재 사용하는 언어들은 대부분이 C언어를 기반으로 만들어졌을까? 영어 문법 체계, 교육용 모두 중요한 요건이지만, 가장 중요한 것은 실용적 유용성일 것이다. 그에반해 C언어는 현장에 있는 프로그래머들을 위해 개발되었다. 즉, 프로그래머의 요구에 적절하게 대응할 수 있어. 하드웨어를 제어할 수 있고, 메모리에 있는 개별 비트를 조작할 수 있다. 이외에도 top-down 설계구조화 프로그래밍, 모듈화를 쉽게 적용할 수 있는 환경을 제공하고, 효율성이식성, 유연성 등이 매우 좋다는 특징이 있다.

정점이 있다면 단점도 있을 것이다. C언어의 단점은 포인터의 사용으로 인해 추적하기 어려운 에러를 쉽게 만들 수 있다는 것이다.

1.3 컴퓨터의 기초

CS50강의를 들었을 때 단순히 언어에 대해서만 배우는 것보다 컴퓨터에 대해서 어느정도 이해를 하는 것이 C언어를 배움에 있어 도움이 될 것이라는 생각이 들었다. 컴퓨터의 구성요소에는 CPU, RAM, 하드디스크(요즘은 SSD) 그 외에 키보드, 마우스, 모니터 등이 있다.

  • CPU(중앙처리장치)

    기억, 해석, 연산, 제어를 관할하며 컴퓨터가 하는 일 대부분을 처리한다고 보면 된다. 보통 명령을 하나 가지고와 실행하는 것을 반복한다. CPU는 자신만의 작업공간을 가지고 있다. 이 작업공간은 여러개의 레지스터로 구성되어있며, 각 레지스터는 하나의 수를 저장할 수 있다. 그 중 한 레지스터는 다음 명령이 있는 메모리주소를 저장한다. 이 정보는 CPU가 다음 명령을 가지고 올때 사용된다. 이렇게 가지고온 명령을 또 다른 레지스터에 저장하면, 원래 메모리 주소를 저장한 레지스터에는 그 다음 명령이 있는 주소로 업데이트 된다.

    CPU는 명력 집합(insetruction set)이라고 부르는 한정된 명령 리스트를 가지고 있고, CPU는 이 명령만을 이해할 수 있다. (다소 구체적이라고 함)

  • RAM : 프로그램과 파일을 담는 작업공간 역할을 한다.

  • 하드디스크 혹은 SSD : 컴퓨터 전원이 꺼져있을 때도 프로그램과 파일을 기억한다.

  • 키보드, 마우스, 모니터 : 컴퓨터 사용자와 연결해주는 입출력장치.

이외에도 컴퓨터에는 두가지 특징이 있다. 첫번째는 컴퓨터에 저장되는 것들은 하나의 수로 저장된다. 숫자 뿐만 아니라, 알파벳 같은 문자들도 수로 저장된다. 각각의 문자는 하나의 수치(numeric)코드를 갖는다. 둘째는 컴퓨터 프로그램은 종래에는 기계어(machine language; 수치코드 명령으로 표현된 것)여야 한다.

1.4 C 언어의 표준

1.4.1 Classic C 표준 (1978)

처음 C언어은 표준은 Brian Kernighan과 Dennis Rithie가 저술한 “The C Programming Language” 초판이 표준으로 받아들여 졌고, K&R C 혹은, Classic C라고 부른다. 그리고 이 책의 부록인 “C Reference Manual”이 C 컴파일러들의 가이드 역할을 했지만, C언어만 정의하고, C라이브러를 정의 하지 않았다. C언어는 다른 언어보다 라이브러리에 더 의존하기 때문에 Unix와 함께 제공된 라이브러리가 사실상 표준이 되었다.

1.4.2 ANSI/ISO C 표준 (1989/1990)

컴퓨터의 보급과 함께 C언어 사용자들이 늘어나면서 좀 더 엄격한 표준이 필요했다. 그래서 미국표준협회(ANSI)는 1983년에 새로운 표준 제정을 위해 위원회(X3J11)를 설립고, 이 위원회가 제안한 표준이 1989년에 정식으로 채택되었다. 이 표준의 이름은 ANSI C라고 불렀다. 그리고 1990에는 국제표준화기구(ISO)도 C 표준인 ISO C를 채택했다. ANSI C와 ISO C는 본질적으로 동일한 표준이다. 그리고, ANSI C는 Classic C와는 달리 C언어 표준과 C 라이브러리를 함께 정의하고 있다. ANSI/ISO 표준 최종 버전을 C89 혹은 C90이라고 부른다. C89는 ISO/IEC 9899:1989, C90는 ISO/IEC 9899:1990의 비공식 이름이다. 그런데도 ANSI C라고 부르는 이유는 ANSI 버전이 먼저 나왔기 때문이다.

수정을 거치고, 새로운 표준을 만들어도 위원회는 “C의 정신을 계승하자”를 지키기 위해 몇 가지 원칙을 지켰다.

  • 프로그래머를 믿자.
  • 프로그래머가 하려고 하는 것을 막지 말자.
  • C언어를 작고 단순하게 유지하자.
  • 하나의 연산을 처리하는 데 오직 한 가지 방법만 제공하자.
  • 이식성이 훼손되더라도 빠르게 실행되기 하자.

위원회는 위 원칙에 따라 특정 연산을 추상적이고 획일적으로 정의하지 않고, 컴퓨터에서 가장 잘 동작하는 방식으로 정의하려고 애썼다.

1.4.3 C99 표준 (1978)

C9X 위원회(ANSI/ISO 공동위원회)에서는 1994년에 C 표준의 개정 작업을 시작해 C99표준을 만들었다. 하지만 이 위원회의 설립취지는 국제화, 미비점 보완, 계산 실용성의 개선과 같은 새로 대두화 된 목표들에 맞춰 보충하는 것이었다. 보충을 하면서도 C99 개정표준은 C의 본성을 훼손시키지 않았으며, C는 날씬하고 깔끔하고 효율적인 언어였다.

  • 국제화 : 국제 문자 집합을 처리하는 방법을 제공하여 국제적인 프로그래밍이 가능하도록 지원하는 것
  • 미비점 보완 : 미비점을 이미 파악하고 실무 관행을 성문화하는 것. C가 64비트 프로세스를 지원해야할 때, 실무적으로 이 문제를 고민했던 경험을 표준에 추가
  • 계산 실용성의 개선 : 과학과 공학 프로젝트가 요구하는 정밀한 수치 계산이 가능하도록 C의 계산 실용성을 개선하는 것.

C99을 배웠는지 아닌지의 차이는 long long int가 있는지. inline 함수에 대해서 아는 지에 대해서 물어보면 된다.

1.4.3 C11 표준 (1978)

2007년에 표준 위원회는 다음번 계정으로 C1X에 대해 언급했고, 그것이 C11로 실현되었다. 이 위원 해는 새로운 기본 원칙을 알렸다. 그중 하나가 프로그래밍 보안 및 안전에 대한 시대적 요청에 직면하여 “프로그래머를 믿자”는 원칙을 완화해야 한다는 것이다. 그리고 벤더(VENDOR)들이 C90만큼 C99를 지원하지 않아서 C99의 일부 기능이 C11에서는 선택사항이 되었다. 이는 소형 머신 시장을 공략하는 벤더들에게 고객이 사용하지 않은 기능을 지원하도록 가용해선 안 되기 때문이다. 그리고 표준이 개정된 이유는 새로운 기술을 따라잡을 필요가 있기 때문이라는 사실에도 주목했다. 컴퓨터에 멀티 프로세서가 사용되는 트렌드에 대응한 대응책으로 병행 프로그래밍(concurrent programming)에 대한 지원을 선택사항으로 추가한 것이 한 가지 예이다.

1.5 프로그래밍의 일곱 단계

프로그래밍의 일곱 단계는 아래와 같다.

  1. 프로그램 목적 정의
  2. 프로그램 설계
  3. 코드작성
  4. 컴파일
  5. 프로그램 실행
  6. 테스트 및 디버깅
  7. 유지보수

이렇게 나누어서 설명하는 이유는 프로그래밍의 전체적인 윤곽을 설명하기 위함이다. 이것은 이상화 된 것이며, 실전에서는 단계들이 앞 뒤로 왔다갔다 하게 된다.

1.6 컴파일러와 링커

title

프로그래밍언어는 기계어(low-level language)에서 고급어(high-level language)로 발전해 왔다. 기계어에 가까울수록 컴퓨터는 이해하기 쉽지만, 사람이 이해하기 어렵고, 하이레벨언어에 가까울수록 그 반대이다. 1.3 에서 설명했듯이 컴퓨터 프로그램은 종래에 1과 0으로 이루어진 기계어로 저장된다. 하지만 우리에게는 컴파일러라는 것이 있기 때문에 기계어를 신경 쓸 필요는 없다.

소스 코드 파일(source code file)

소스 코드 파일란, C언어와 같은 하이레벨 언어로 작성된 파일을 말한다. 그리고 이런 소스코드 파일의 확장자는 대부분 .c, .py, .cpp 으로 되어있다. .c가 확장자로 되어있으면 C언어로 작성된 것이며, .py는 파이썬, .cpp은 C++ 언어로 작성된 것이다.(예를 들면 HelloWorld.c) 확장자가 프고로그래밍 언어로 되어있는 파일명은 운영체제에 따라 조건이 다르다. 예를 들어 MS-DOS는 IBM PC와 그 호환 기종을 위한 운영체제인데, 영문 8자를 초과할 수 없다. 일부 Unix 스시템은 확장자를 포함해 영문 14자 내외로 파일명을 정해야한다. 또 어떤 Unix 시스템은 255자까지 긴 파일 이름을 허용한다. Linux, Windows, Macitosh OS도 긴 파일 이름을 허용한다.

소스 코드 파일을 기계어 코드가 들어가 있는 실행 파일로 변환시키는 과정에서 컴파일링(compiling)링킹(linking)이라는 두 단계로 나누어 변환한다. C언어는 이 두 단계로 분리된 접근을 사용함으로써 프로그램의 모듈화를 가능하게 한다.

컴파일러(compiler)

title

컴파일러(compiler)는 하이레벨 수준 언어로 작성된 프로그램(소스 코드 파일)을 컴퓨터가 이해할 수 있는 기계어로 번역하는 프로그램이다. 컴파일러는 단순히 기계어를 번역하는 것이 아니다. 인텔, 라이젠 등 컴퓨터의 부품인 CPU는 서로 다르게 설계되었으며, 자신만의 독특한 기계어를 갖는다. 인텔 CPU를 위해 작성된 기계어는 라이젠 CPU에서는 읽히지 않는다. 하지만 컴파일러는 CPU 회사들에 맞게 조절하여, 다양한 버전의 기계어 프로그램으로 변환 할 수 있다.

오브젝트 파일(object file)

컴파일러는 소스 코드를 기계어로 변환하지만, 바로 실행할 수 있는 것은 아니다. 중간파일을 만드는 방식에는 여러 가지가 있는데, 그 중 대표적인 것이 소스코드를 기계어 코드로 변환하여 그 결과를 오브젝트 코드 파일(object code file) 또는 오브젝트 파일(object file)에 저장하는 것이다. (둘은 완전히 같은 것이다.)오브젝트 파일은 그저 우리가 C언어로 작성한 것을 기계어로 번역한 것뿐, 아직 링커를 거치기 전이기 때문에 실행할 준비가 되어있지 않다. 이런 오브젝트 파일에 실행 파일로 만들기 위해서는 두 가지 요소가 필요하다.

첫 번째는 프로그램과 운영체제 사이의 인터페이스를 담당하는 시동 코드(start-up code)이다. 예를 들어 MC windows와 Linux는 동일한 하드웨어에서 운영된다. 그 때문에 하나의 오브젝트 코드가 두 운영체제에서 각각 동작하리라 생각하지만, 운영체제는 프로그램을 처리하는 방식이 다르기 때문에 그렇지 않다. 이때 동일한 오브젝트 코드로 각각의 운영체제에서 동작하게 하는 것이 시동 코드이다.

두 번째는 라이브러리(library) 루틴을 위한 코드이다. 라이브러리는 이미 링커를 설명할 때 언급되었다. 대부분은 C언어는 #include 지시문으로 라이브러리를 참조하고 있다.

#include <stdio.h>

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

당장 hello, world를 프린트하는 간단한 코드에서도 #include 지시문을 사용했다. 왜냐하면 오브젝트 코드에서는 printf() 에 해당하는 코드를 가지고 있지 않기 때문에다. 그러므로 printf함수의 코드를 실제로 가지고 있는 라이브러리 stdio.h#include라는 지시문으로 참조하는 것이다.

링커(linker)

링커는 부품을 완성품으로 조립하는 일을 한다. 무슨 말이냐면 링커의 역할은 중간 코드를 시동 코드와 라이브러리를 합쳐서 하나의 실행 파일을 만든다는 것이다. C언어로 프로그램은 대게 #include 지시문으로 시작하는 것이 많다. 이 지시문이 나타나는 지점에서 이미 컴파일된 코드(라이브러리)의 내용을 작성 프로그램에 포함하도록 전처리기에 지시하는 역할을 한다.


요약하면, 오브젝트와 실행 파일 모두 기계어 명령어들로 이루어져 있다. 그러나, 오브젝트 파일은 순수하게 개발자가 작성한 소스 파일을 번역한 것이고, 실행 파일은 링커라는 작업을 거쳐 오브젝트 파일 뿐 아니라, 라이브러리, 시동 코드까지 포함하고 있다.어떤 파일에선 컴파일러와 링커가 별도로 실행해야 하기도 하고, 다른 시스템에서는 컴파일러가 자동으로 링커를 호출한다. 그리고 이 모든 과정은 컴퓨터 환경(Unix인지, Linux인지 등등)에 따라 조금씩 다르다.



2. C 언어의 첫 걸음

무슨 프로그램을 작성하든 공통적으로 쓰이는 C의 몇가지 형식과 특징에 대해서 살펴본다. 아래는 C 프로그램의 기본 골격이다.

C 프로그래밍
├── #include        #전처리기 지시자
├── int main(void)  # main()은 가장 먼저 호출되는 함수이다.
│    └── 문장들      # 함수는 문자들로 구성된다.
├── 함수 a( )        # C는 함수들로 구성된다.
│    └── 문장들
├── 함수 b( )
│    └── 문장들
│         ├── 선언   # C 언어의 문장 형식 5가지  
│         ├── 대입   # 키워드, 식별자, 연산자, 데이터
│         ├── 함수
│         ├── 제어
│         └── 널(null)
│
↓

아래는 간단하게 만들어진 프로그램이다. 아래 프로그램으로 기본 바탕을 살펴본다.

#include <stdio.h>  

int main(void)
{
    int num1;
    num1 = 1;
    int num2 = 2;
    printf("River");
    
    return 0;
}

include 지시문

#include <stdio.h> 

include은 전처리 지시자(preprocessor directive)의 한 예이다. 일반적으로 C 컴파일러는 컴파일하기 전에 소스 코드를 대상으로 사전 작업을 실시하고 이것을 전처리(reprocessing)라고 한다. include 지시문을 사용하면, #include라인에 그 뒤 <> 안에 있는 파일을 가지고 온다. #include <stdio.h>를 보면 include 라인에 stdio.h 파일의 전체 내용을 프로그램에 그대로 복사한 것과 동일한 효과를 본다. stdio.h에는 printf()와 같은 입출력 함수에 대한 정보가 들어있으며, 이 정보를 컴파일러가 사용한다. stdio.h의 stdio는 standard input/output header를 나타낸다.

C언어를 사용하는 개발자들은 상단에 놓인 정보를 헤더(header)라고 부른다. 대부분의 C 컴파일러들이 여러 개의 헤더 파일을 제공하며, 컴파일러가 프로그램을 바르게 구성하도록 안내하는 역할을 한다. 이러한 헤터 파일에는 컴파일러가 최종 실행 프로그램을 만드는 데 사용하는 정보들이 들어가 있다. 상수를 정의하거나, 함수 이름과 그 함수들이 어떻게 사용되어야 하는지에 관한 내용이 말이다. 하지만, 조금만 더 자세히 들어가면 이러한 내용은 헤더 파일에 들어가 있는 것이 아니라, 컴파일된 코드로 구성된 라이브러리 파일에 들어가 있다. 그리고 이 라이브러리에서 필요한 코드를 찾는 것을 링커가 한다.

main() 함수

int main(void)

위 코드에서는 int는 변환 형식이고 main은 함수이름이다. 그리고 (void)는 매개변수에 해당한다. 이런식으로 변환형식 함수이름 (매개변수)로 된 형식을 함수 선언 및 정의라고 한다. 이건 우리가 중고등학교 때 배운 함수인 f(x)= y와 매우 흡사한 모양을 가지고 있다.

title

main은 단순히 평범한 이름이지만, 고려하지 않은 몇 가지 예외를 제외하고는 C 프로그램에서는 언제나 main이라는 이름의 함수로 시작한다. 다른 함수 이름이야, 우리 마음대로 지어도 되지만 main이라는 이름의 함수는 무조건 있어야 하며, 모든 것의 시작이다. 다른 선택이 없다. 그저 약속이고, C 언어의 바뀌지 않는 기본이다. (함수 이름 뒤에 붙어있는 () 또한 마찮가지이다.)

intmain()의 리턴형, 즉 변환 형식이다. 이런 형식이에는 여러가지 종류가 있는데, 그건 내일 배우기로 하고, 위 코드에서는 정수라는 종류로 운영체제에 리턴하라는 것이다. 더 자세한 것은 TIL6과 TIL7에서 배우게 될 것이다.

(void)는 매게변수이다. 이는 main이라는 함수에 전달될 정보를 말한다. 위 코드에서 viod는 없다는 뜻에 해당한다. 이것을 읽는 누군가는 “없다는 표시면 생략해도 되지 않는가?”라고 생각할 수 있다. 하지만 그저 표기할 필요가 없어 생략한 것과 없다는 것은 완전히 다른 의미이다.

main()
{
    ...
}

특히 오래전에 작성한 C 코드를 살펴보면 위 코드처럼 main()으로 시작하는 프로그램을 쉽게 마주할 수 있다. C90은 이 것을 허용했기 때문이다. 하지만 C99와 C11 표준에서는 그렇지 않다. 따라서 이런 형식을 지원하는 컴파일러를 사용하더라도 사용해서는 안된다.

void main()
{
    ...
}

위와 같은 형태도 종종 볼 수 있고, 몇몇 컴파일러도 지원하지만 이 형식 역시 사용해서는 안된다. 어떠한 표준도 위와 같은 코드를 승인하지 않았다.

이런식으로 비 승인된 형식을 사용하지 말라는 이유는 컴파일러를 옮길 때 쉽게 옮기기 위함이다.

주석(///* */)

// 이것은 주석입니다. 
/*
 얘도 주석이다.
*/

///* */는 컴파일을 해도 머신 언어로 바뀌지 않는다. 이것을 주석이라고 부른다. 주석이 필요한 이유는 무엇일까? 개발이라는 것은 혼자서도 가능하지만, 회사뿐만 아니라 대학에서도 팀을 짜서 활동한다. 그렇다면, 친구, 동료, 혹은 인수인계를 받아야 하는 후임 등이 우리가 짠 코드를 보게 된다. 코드 줄이 짧다면 상관없겠지만 만약 코드가 수백, 수천 줄이 된다면 어떤 부분이 어떤 역할을 하는지 해석하기 힘들 것이다. 당장 내가 짠 코드를 일 년, 한 달, 더 짧게는 일주일 후에 보게 되어도 새롭다. 그렇기 때문에 주석을 습관화하는 것은 매우 중요한 문제이다.

중괄호({})

int main(void)
{
    ...
}

한 프로그램에 main() 만을 넣을 이요해 코드를 작성할 수도 있지만, 대게 한 개 이상의 함수를 사용한다. 이때 중괄호가 함수 들끼리의 경계를 구분한다. 그렇기 때문에 절대로 빠트리면 안되는 요소이며 소괄호(())나 대괄호([])는 안된다. 오로지 중괄호({})만 사용이 가능하다. 이것을 블록이라고 부른다.

선언 및 정의

int num1;       # 선언
num1 = 1;       # 정의
int num2 = 2;   # 선언  정의

int num1;은 선언이고, num1 = 1;은 정의이다. 그 아래에 있는 int num2 = 2;는 선언 및 정의라고 한다. 또다른 말로는 선언은 선언문(declaration statement), 정의는 대입문(assignment statement)이라고도 한다.

num1num2는 식별자(identifier) 혹은 변수명이라고 불린다. C 언어에서 변수의 본질은 메모리이며, 이 메모리는 주소를 가지고 있다. 그리고 이 메모리는 그릇 같은 존재이기 때문에 정보, 숫자, 자료 같은 것을 담는다. num1num2앞에 있는 int는 해당 메모리에 어떤 종류의 데이터를 담을지 미리 정의를 해주는 것이다. TIL2에서 배우겠지만 int는 C의 기본 데이터형 중의 하나이며, 이것은 일종의 키워드이다. 키워든 그 언어를 표현하기 위해 사용되는 것이다. 그래서 함수 이름, 변수 이름과 같은 다른 목적으로는 사용될 수 없다.

여하튼, 선언은 하나의 특정 식별자를 컴퓨터 메모리 상의 특정 주소와 연결하고, 그 위치에 저장할 정보의 종류(데이터형)을 확정하는 행위이다.

int main()
{
    int num1;
    int num2;
    num1 = 1;
    num2 = 2;
}

C 의 전통적인 선언 방식은 위와 같다. 선언 앞에 다른 종류의 문장이 오는 것을 허락하지 않았기 때문에 변수를 블록 ({})의 시작 위치에 선언할 것을 요구했다. 설령 printf()를 사용하여 단순한 문장을 선언하는 것도 말이다. 하지만 C99과 C11부터는 C++의 관행을 받아들여, 블록 안의 어디서든 변수를 선언할 수 있다. 이렇게 선언되어지고 정의된 변수는 어디서든 사용이 가능하다.

printf() 함수(함수 안에서 함수를 호출)

#include <stdio.h>  

int main(void)
{
    
    ...
        
    printf("River");
    
    ...
    
    return 0;
}

printf("River\n");는 단순히 어구를 함수를 통해 출력한다. 이러한 정보를 함수의 전달인자(argument) 또는 함수의 식전달인자(actual argument)라고 한다. C에서는 함수에 전달하는 특정 값과 함수에서 그 값을 받아들이는 변수를 구별하기 위해 식전달인자(actual argument)와 형식 매개변수(formal parameter)라는 용어를 사용한다. 이에 대해선 뒤에서 좀 더 설명된다 . printf()는 전달받은 단순한 어구를 그대로 출력한다.

C에서 단순한 어구를 입력받은 printf()를 어떻게 호출(call)하고 실행(invoke)하는지에 대해서 알아보자. 프로그램이 위에서부터 순차적으로 실행되다가 printf("River\n");를 만나면 printf()로 제어가 넘어간다. 그리고그 호출된(called) 함수의 일이 끝나면 제어는 다시 원래 호출한(calling) 함수 즉, main()으로 넘어간다.

title

당장은 호출자(caller) 함수가 피호출자(Calle) 함수를 호출한다고 한다라고 이해하면 된다.. 호출자(Caller) 함수는 main()에 해당하며 원래 호출한(calling)에 해당한다. 피호출자(Calle) 함수는 printf()에 해당하며, 호출된(called)함수에 해당한다.

title

이런 호출자 함수가 피호출자 함수를 부르기 위해서는 피호출자 함수의 매개변수에 초기값(실인수)을 기술해야한다.

리턴문

return 0;

리턴문을 마지막으로 이 프로그램이 끝나는 이유는 int main(void);에서 intmain()가 하나의 정수를 리턴한다는 것을 나타내기 때문이다. main() 앞에 다른 자료형이 있다면 return할 값이 정수는 아닐 것이다. C표준은 main()가 이처럼 행동할 것을 요구한다. 이때 return 뒤에는 리턴값이 있어야 하고, 그 뒤에는 세미콜론(;)이 있어야 한다. main()에서 return을 빠트린 경우에 프로그램을 닫는 중괄호(})에 도달했을 때 0을 자동으로 리턴한다. 그래서 main()끝에 있는 return을 생략한 경우도 많이 볼 것이다. 그러나 다른 수에서는 생략할 수 없기 때문에, main()에서는 return을 사용하는 것이 일관성이 있어서 좋다.

세미콜론(;)

문장 끝 마다 찍어주는 세미콜론(;)은 그 라인이 하나의 C 문장(statement)임을 나타낸다. C에서 세미콜론은 단순히 문장을 분리 하는 것이 아니라, 문장의 일부다. 때문에 생략시 오류가 발생한다.




© 2020. by RIVER

Powered by RIVER