C의 표준 입출력 라이브러리에 대한 정보를 첨가하기 위해 필요한 줄
1단계: 전처리(Processing)
2단계: 컴파일(Compiling)
3단계: 링크(Linking)
GCC 컴파일러는 가장 유명한 C 컴파일러중 하나로서, 리눅스에서 제공될 뿐만아니라 다른 수많은 플랫폼에서 사용가능한 컴파일러다. 이것은 유닉스 cc컴파일러와 유사하다. 예를들어 어떤 프로그램을 컴파일하기 위해서 다음과 같은 커맨드를 사용한다.
% gcc -o hello hello.c
커맨드 라인에서 컴파일러를 실행하는 방법의 대안은 IDE다. IDE는 편집, 컴파일, 링크, 실행, 심지어는 디버깅도 가능하게 해준다.
지시자에는 수많은 종류가 있지만 나중에 알아보도록 하자. 지금 알아볼것은 #include <stdio.h> 다. stdio.h는 C의 표중 입출력 라이브러리에 대한 정보를 포함하고 있다. C는 <stdio.h>같은 수많은 헤더를 가지고 있으며, 각 헤더들은 표준 라이브러리의 일부에 대한 정보를 가진다. <stdio.h>를 포함하는 이유는 C는 다른 언어들과 달리, 읽고, 쓰는 명령어가 내장되어 있지 않기 때문이다. 함수가 할 일을 라이브러리가 대신 해주고 있는 것이다.
함수는 두 종류로 나뉠 수 있다. 첫 번째는 프로그래머에 의해 작성된 함수, 두 번째는 C 내장함수다. 후자는 "라이브러리"에 속하기 때문에, 라이브러리 함수라고 불리기도 한다.
C 프로그램은 수많은 함수로 이뤄질 수 있으나, 반드시 main 함수만은 포함해야한다. main 은 특별하다.(begin? start? 무조건 main 이다. MAIN도 안된다)
main 은 상태코드(staus code)를 운영체제에 반환한다. KnK의 예시코드를 보자
#include <stdio.h>
int main(void)
{
printf("To C, or not to C: that is the question.\n");
return 0;
}
여기 main 앞의 int는 main함수가 정수를 반환한다는 의미다.
void는 main에 입력값이 필요하지 않음을 의미한다.
return 0는 두가지 효과가 있다. 하나는 main 함수의 종료를, 하나는 main 함수가 0을 반환하게 만드는 것이다. 물론 이게 없더라도 프로그램은 종료된다. 그러나 많은 컴파일러들가 경고메시지를 뿜을것이다.(함수가 정수값(int)을 반환하도록 정해졌으나 아무것도 반환받지 못했으므로)
위의 예시코드에서 구문은 딱 두가지 종류만 사용됬다. 첫번째는 return 구문이고, 하나는 함수호출(functional call) 이다. 모든 구문은 ;로 끝내며 구문의 끝을 구분한다. 복합문같은 예외도 있지만 나중에 살펴볼것이다.
주석은 /* ... */ 로 이뤄진다. 가독성을 위해 다음과 같이 주석을 다는것이 좋다
/* Name: ~~~~
Purpose: ~~~
Author:~~~
*/
아예 *표시로 박스를 만드는것도 좋다. 구문 옆에 다는경우 날개 주석(winged comment)라고 부른다.
C99 표준에서는 //로도 주석을 달 수 있다.(한줄밖에 못달지만, 주석 마감처리 안될 위험이 없다.)
모든 변수들은 자료형을 가지고 있으며, 어떤 형식의 자료를 가질지 구분해준다. 적절한 자료형을 선택하는것은 매우 중요하다. 어떤 자료가 저장될지, 어떤 연산을 수행 할 수 있는지에 영향을 미치기 떄문이다.
변수는 사용되기 전에 반드시 (컴파일러를 위해서라도) 선언되어야 한다. int height;나 float profit;처럼 말이다. 같은 자료형을 가지는 경우 다음과 같이 결합하여 선언할 수 있다.
int height, length, width, volume;
float profit, loss;
위의 예시코드들에서 main 함수에서는 선언을 포함하지 않았다. 선언을 포함하는 경우에는 구문 앞에서 해주어야 한다.
C99에서는 선언을 반드시 구문 이전에 할 필요가 없다. 단 구형 컴파일러와의 호환성을 생각하면 구문 앞에서 선언을 해주는 것이 좋다. C++ 이나 JAVA에서는 선언을 앞에서 하지 않는것이 일상이다.
변수는 할당을 통해 값이 주어진다. 예를들어
height = 8l
length = 12;
width = 10;
처럼 말이다. 여기서 8, 12, 10은 상수(constants) 다.
float 에 할당되는 상수는 부동소수점을 포함한다. 일반적으로 소수를 표현해서 할당하는것도 좋겠으나, 다음과 같이 f를 붙이는 것이 제일 좋다.(안붙이면 컴파일러에서 경고가 뜨기도 한다.)
profit = 2150.48f;
한번 변수 할당이 이뤄진 뒤에는 다른 변수를 만드는데 사용될 수 있다.
우리는 printf 를 통해 변수가 가지고있는 값을 출력할 수 있다. 예를 들어 height = h;에서 h는 현재 height변수의 값으로 printf 에서 호출된다.
printf("height: %d\n", height);
%d는 height의 값이 출력값에서 위치하는 자리를 나타낸다. \n의 위치를 살펴보면 %d의 뒤에 놓인것을 알 수 있다.
%d는 오직 정수 자료형을 나타낸다. float 자료형을 출력하려면 %f를 사용해야한다. 기본적으로는 소숫점 이하자리를 나타내며, 소숫점이하 일정 자릿수만 나타내기 위해서는 %와 f사이에 .숫자를 집어넣으면된다.(예시:%2f)
몇몇 변수들은 자동적으로 0으로 설정될 것이다. 그러나 대부분은 아니다. 초기화되지 않은 값을 사용하는것은 많은 오류를 발생시킬 가능성이 있다.(2568-30891과 같은)
우리는 할당을 통해 초기값을 변수에 설정 해줄 수 있다. 그러나 더 선언과 함꼐 설정하는 것이 더 쉬운 방법이다. 다만 각 변수는 각각의 초기값을 가지고 있어야한다. 예를들어 아래 코드에서 10은 width에만 대응한다.
int height, length, width = 10;
printf는 변수 내의 수를 표현할 뿐만 아니라, 어떠한(any) 연산(numeric)들도 출력가능하다. 이런특성은 프로그램을 간소화하고, 변수의 개수를 줄여준다. 예를들어
printf("%d\n", height*length*width);
dweight.c 프로그램은 한 가지 경우의 상자 무게만을 잴 수 있다. 이 프로그램이 유용하려면 사용자가 원하는 상자의 무게를 잴 수 있어야 한다. 이를 위해 우리는 scanf 함수를 사용할 것이다. printf와 scanf의 f는 formatted의 f다. 이 두 함수 모두 포맷 스트링의 사용이 필요하다. scanf함수는 입력값이 어떤 형식을 취하는지에 대해 알아야한다.
scanf("%d", &i) //int 자료를 읽어서 i에 저장
%d는 입력값을 정수형으로 받아들이도록 만든다. &는 지금 시점에서 설명하기 난해하다.
프로그램이 상수를 포함할 때, 이름을 정하는 것이 좋다. 예시코드들 중에서 166이라는 상수가 있다. 이 상수의 의미는 나중에 타인에게 그 의도가 불명확할 수 있다. macro definition으로 알려진 방법을 사용하여 이 상수에 이름을 붙여주자
#define INCHES_PER_POUND 166
#define은 전처리 지시자이기 때문에 ;로 끝나지 않는다.
프로그램 개발을 하는 이상, 반드시 변수, 함수, 매크로 혹은 다른 구조체들에 대해 이름을 선택해야한다. 이 이름들을 식별자라고 부른다. C에서 식별자는 알파벳, 숫자, 밑줄로 구성된다. 하지만 알파벳 혹은 밑줄로 시작해야한다.
[C99]에서는 유니버설 문자명을 포함할 수 있다.
C는 대/소문자를 구분한다.(case-sensitive)
가독성을 위해 변수명을 짓는법이 두 가지가 있다. 첫 번째는 빈칸을 _로 채우는 것이고, 하나는 이어서쓰되 대문자를 끼워넣는것이다.
C언어는 식별자의 길이에 제한을 두지 않으므로, 걱정할 필요없다.
다음의 키워드 들은 C 컴파일러에서 중요한 키워드들이므로 식별자로 사용할 수 없다. 이외에도 많이 있으며 필요하면 찾아볼 것.
auto, break, case, char, double, enum, switch ...
참고로 이 키워드들로 이뤄진 함수들은 모두 소문자로만 이뤄져 있다.(C99에서 추가된 사항 제외)
[WatchOut] 이 밖의 식별자에 관한 제한사항들을 눈여겨보아야한다. 몇몇 컴파일러들은 특정 식별자를 다룬다.(asm처럼 말이다)
C프로그램은 일종의 토큰 으로 이뤄진 연속이라고 생각될 수 있다. 아래의 예시는 7개의 토큰으로 이뤄져 있다고 볼 수 있다.
printf("Heigh: %d\n", height);
이 토큰 사이의 공백은 대부분의 경우에는 중요하지 않다. 극단적으로 토큰들은 띄어쓰기 없이 사용될 수 있다.(두 토큰을 결합하여 새 토큰을 만들지 않는한) 정말 극단적으로 main 함수를 한 줄로 작성할 수 있을것이다. 하지만 전처리 지시자는 각각 한 줄을 필요로하므로 프로그램 전체를 한 줄로 작성할 수는 없다.
다행히 C는 토큰 사이의 공백을 넣는것에 대해 매우 관대하다. 이 규칙은 굉장히 중요한 절차들을 가지고 있는데 다음과 같다.
1. 한 구문이 길어져서 몇줄이 되어도 상관없다. 2. 토큰 사이의 공백은 가독성을 높인다. 3. 들여쓰기는 신이다. 4. 줄을 건너띄어서 쓰는것은 논리적 흐름에 따라(logical unit) 프로그램을 파악하게 해준다.
추가로 토큰 내 공백, 개행은 유효하지 않다. 다만 문자열 내부 공백은 괜찮으나, 개행은 조금 문제된다. 문자열 내 개행의 경우, 조금 특별한 기술이 필요하다.
Q1. 컴파일러는 주석을 전부 지워서 인식하는가?
A1. 구식(Old) 컴파일러는 전부 지워서 인식한다. 하지만 보통은 공백 한 칸으로 인식한다.
Q2. 진짜 식별자 길이 제한이 없는가?
A2. "진짜" 없는건 아니다. C99 기준으로 63글자까지만 식별자로 인식한다. 즉 앞의 63글자가 같다면 다른 두 변수는 서로 같은 것으로 인식된다. 외부링크가 있는 경우에는 31글자까지 대소문자를 구분해서 인식한다.