일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 아두이노 컴파일러
- set
- Deque
- Stack
- 자료구조
- Array
- html
- stl
- 아두이노
- LineTracer
- Visual Micro
- 시스템프로그래밍
- 컴퓨터 그래픽스
- c++
- 운영체제
- priority_queue
- vector
- directx
- map
- Algorithm
- list
- WinAPI
- 아두이노 소스
- Arduino
- C언어
- 라인트레이서
- queue
- arduino compiler
- 수광 소자
- 통계학
- Today
- Total
Kim's Programming
C언어 - 함수(function)(3/4) 본문
전처리기
#include와 같은 명령을 전처리기(PreProcessor)라고 하며 이외에도 여러가지 종류의 전처리기가 있습니다. 우선은 간단한 #define과 #include만 먼져 알아보도록 하겠습니다. (추가로 #ifdef와 #programa도 있기만 나중에 하겠습니다.) 전처리기란 이름에서 볼 수 있듯 "앞서 먼저 처리하는 명령"이란 뜻을 가지고 있습니다. 역할로는 컴파일 전에 소스를 재작성 하는 역할을 합니다. 컴파일러가 소스를 읽기 전에 전처리기가 먼저 실행되어 컴파일하기 좋도록 소스의 모양을 정리하는데 전처리기는 코드는 생성하지 않고 소스를 재구성하는 역할을 합니다. 컴파일 전 단계에서 실행되기 때문에 몇몇 제약이 있는데 전처리문은 반드시 한 행을 모두 차지해야 하며 전처리문 뒤에 C코드를 쓸 수는 없습니다. 프리 포맷의 예외인데 주석은 코드가 아니므로 가능합니다.
컴파일러는 #include 명령을 먼저 처리하여 헤더파일을 모두 포함후 컴파일을 시작합니다. 그래서 #include<stdio.h>등을 제일 앞에 선언해두면 그안에 있던 printf, scanf등의 함수등 안에 선언 된 것을 사용할 수 있는 것입니다. 물론 stdio.h안에 있는 내용을 복사하여 앞쪽에다가 작성을 해버리면 #include를 쓴 것과 같은 효과는 낼 수 있습니다. 하지만 소스는 엄청나게 길어지게 되어 작성하기 매우 어려워 질 것입니다. 하지만 #include를 사용하면 포함시키게 되면 짧아지게 됩니다. #include명령은 괄호에 따라서 2가지의 형태로 나누어 지게 됩니다.
- #include<stdio.h> : C언어에서 제공하는 표준 헤더파일을 포함시키고자 할 때 <>괄호를 사용합니다. 컴파일러의 제공되는 표준헤더를 인클루드 시킬떄 사용합니다.
- #include"function.h" : 사용자가 만든 헤더파일을 포함시키고자 할 떄 " "괄호를 사용합니다. 이 괄호를 사용하면 소스 파일과 같은 디렉토리에서 헤더파일을 찾습니다. 직접 만든 헤더파일은 보통 소스와 같은 디렉토리에 두므로 " " 괄호를 이용하면 됩니다.
(사용자가 만든 헤더 인클루드는 잠깐 따로 설명하겠습니다.)
괄호의 형식에 따라서 헤더파일을 어디서 먼져 찾을 것인가의 검색순서가 달라집니다. 하지만 두 괄호는 그다지 엄격하게 구분할 필요는 없습니다. ""괄호를 사용했더라도 현재 디렉토리에 이 파일이 없으면 표준 헤더파일 디렉토리도 검색하며 반대로 <>괄호를 사용했더라도 현재 디렉토리도 같이 검색을 합니다. 예를 들어서 #include "stdio.h"라고 하여도 현재 디렉토리에 그런헤더가 없다면 곧바로 표준 헤더파일에서 찾아보기 떄문에 같은 결과가 나옵니다. 하지만 괄호에 따라서 결과가 달라지는 경우도 있는데, 이는 표준 헤더파일의 이름과 사용자 지정 헤더의 아름이 다를 경우입니다. 하지만 이런 경우를 제외하면 괄호를 꼭 사용할 필요는 없으나 관행상으로 구분하기 때문에 구분하는것이 바람직 합니다. #include 명령은 헤더파일을 인클루드 하기 위해 사용을 하지만 확장자가 cpp인 파일도 가능하며 txt 파일은 물론 텍스트로 이루어진 파일이기만 하면 불러올 수 있습니다. 예를들어서 아주 긴 배열 정의문이 있다고 했을 때 편집하기 힘들면 이 부분만 array.cpp (또는 array.txt등)의 파일을 불러와 따로 뗴어 내고 주 파일에서 #include " array.cpp"등으로 호출하면 됩니다. 또한 같은 경로 뿐만 아니라 다른 경로에 있는 파일의 인클루드도 가능합니다. 만약 function.h 라는 파일이 같은 레벨의 다른 디렉터리(func)에 저장이 되어있다면 #include "func/function.h" 따위로 선언하면 불러 올 수 있습니다. 또한 더 많은 안쪽 경로에 있으면 더 추가하여 인클루드가 가능합니다. 필요시에는 절대경로를 지정하여 인클루드 할 수 있지만, 만약 다른 컴퓨터로 프로젝트를 옮기게 되면 그 컴퓨터에서도 구성을 같게 만들어줘야 하기때문에 귀찮아집니다. 그렇기 때문에 프로젝트 디렉토리로 복사해서 포함시켜주는 것이 편해질 수 있는 방법입니다.
윈도우환경에서는 \(또는 \)를 이용하지만 #include문에서는 /(슬레쉬)를 이용하여 인클루드를 하게됩니다. 원래 C언어가 유닉스에서 만들어진 것이기 떄문에 유닉스의 디렉토리 구분자인 슬레쉬를 사용하도록 되어 있습니다. 비주얼 C++은 슬레쉬와 역 슬레쉬를 모두 인정하는데 가급적이면 표준에 맞게 /를 이용하는 것이 이식성에 유리합니다. 문자열 상수 내에서 \는 확장열 이므로 \\로 써야하지만 헤더파일의 경로를 표기할 때는 한번만 써도 상관없습니다. 사실 헤더파 파일명을 지정하는 표현식은 전처리단계에서 실행되어 컴파일 단계에서는 존재하지 않으므로 문자열 상수는 아닙니다.
#include명령으로 인클루드 시킬떄는 대문자 소문자는 상관이 없습니다. C언어는 대소문자를 구분하지만 윈도우즈에서는 대소문자를 구분하지 않기 떄문입니다. 그래서 #inlclude<stdio.h> 라고 하나 #include<STDIO.H>라고 작성으로 해도 똑같습니다. 하지만 유닉스나 리눅스 환경에서는 대소문자가 구분이 되기 때문에 가급적이면 원래 파일명과 같게 쓰는것이 바람직합니다.
#include 명령은 중첩이 가능합니다. 포함한 파일이 다른 파일을 포함하고 있다면 포함된 모든 파일이 주 파일로 읽혀 지게 됩니다. 예를들어 A가 B를 포함하고 있는 상태에서 A를 인클루드 하게되면 A 뿐만 아니라 B 까지도 주 파일로 읽혀 지게 됩니다.
#define
#define 전처리문은 매크로 상수를 정의하는데 기본 문법은 다음과 같습니다.
#define 매크로명 실제값
원하는 매크로이름을 입력한 다음 실제 값을 뒤에 써주면 되는데 예를 들어서 한달을 음력으로 하기위하여 30일로 지정하려면 이름은 MONTH로 지정하고 값을 입력하여 #define MONTH 30 이라고 정의하면 됩니다. 하지만 매크로 상수가 어디서 필요한지 알아보도록 하겠습니다.
1
2
3
4
5
6
7
8
9
10
11 |
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define MACH 1200.0
void main()
{
int speed;
printf("속도를 입력해주세요 (Km/h)\n");
scanf("%d", &speed);
printf("이 속도는 마하 %f입니다.\n", speed / MACH);
} |
cs |
위의 소스는 Km/h 단위의 시속을 입력 받아 이 속도를 마하로 환산하여 출력을 하는데 입력된 시속을 1200.0으로 나누면 됩니다. 소수점 이하까지 정확하게 나누기 하기 위해 1200.0이라는 실수 상수를 사용하였습니다. 다음 소스의 결과는 다음과 같이 나옵니다
위에서 MACH라고 정의 한것은 실수 1200.0을 정의 하여 사용했는데 이를 float형이나 double로 선언하여 대입하여도 괜찮은데 왜 이렇게 하였을까요? 단순합니다. 구분하기 쉽기 떄문입니다. MACH라고 하면 마하 속도 변환을 위한 값이라는 것을 알 수 있고 또한 3.14라는 단순한 pi 값보다는 PI라는 매크로 상수를 하면 더욱 정확한 계산이 가능하게 되며 1년이 몇초인지를 60*60*24*365를 계산해서 적어주느니 단순하게 ONEYEARSECOND라고 입력을 해주면 바로 31536000이라는 값이 입력되게 됩니다. 프로그래밍의 경우는 대부분이 영어로 작성되기 때문에 기본적인 영어는 알아야 한다는 것입니다. 만약 어떤 특정 값이 일정한데 여러 군데에 있었다고 하면 각 부분의 값들을 수정하는 것 보다 매크로를 이용하여 메크로만 수정하면 끝이 나게 됩니다. 최소한 2번 이상 사용하는 상수의 경우는 매크로 상수를 정의하여 소스를 작성하는 것이 좋은 것 같습니다. 다음 몇 가지는 매크로 상수를 사용할 때의 주의사항입니다.
- #define 문은 전 처리 문이지 코드를 생성하는 명령이 아닙니다. 그래서 행 끝에 세미콜론은 붙이지 않습니다. #define MACH 1200.0;이라고 써서는 안됩니다. 만약 이렇게 쓰게 된다면 세미콜론도 함께 매크로 값에 들어가게 됩니다. #include를 포함 모든 전처리문들도 세미콜론은 붙이지 않으며 주석을 제외한 다른문장들은 따라 올 수 없습니다.
- 매크로의 이름도 일종의 명칭이기 때문에 명칭 규치에 맞게 작성을 해야합니다. 중간에 공백이 들어간다거나 숫자로 시작한다거나 다른 명칠과 일치해서도 안됩니다. 매크로 상수는 다른 명칭과 구분될 수 있도록 관습적으로 대문자를 사용하는 것이 일반 적입니다. NUM, DAY, MACRO 등이 좋은 명칭이 될 수 있습니다.
- 매크로는 이름에는 공백이 허용 되지 않지만 매크로의 실제값에는 공백이 들어갈 수 있습니다. #define 전처리문은 매크로를 실제값으로 단순 치환할 뿐이므로 공백이 있든 한글을 사용하든은 상관이 없습니다. 매크로를 통해서 문장을 정의하면 다음과 같이 할 수 있습니다. #define Sentance "이 것은 매크로 입니다." 와 같이 정의하며 올바른 정의입니다. 실제 코드에서는 puts(Sentance)와 같은 식으로 사용이 가능합니다.
- 문자열 상수 내에 있는 매크로나 다른 명칭의 일부로 포함 된 경우는 치환이 되지 않습니다. #define LENGTH 130이라는 전처리문을 소스의 모든 LENGTH라는 것의 값을 130으로 치환 할 것입니다. 하지만 만약 printf("This LENGTH is = %d\n")라는 문장이 있어도 여기에서의 LENGTH는 매크로 상수와 이름이 같지만 문자열이기 때문에 치환을 하지 않습니다. 또한 LONGLENGTH라는 명칭을 가진 것 또한 포함은 되어 있어도 분리된 명칭이 아니기 떄문에 LONG130 등으로 바뀌지 않습니다.
- 매크로는 중첩이 가능합니다. 즉, 매크로 상수끼리 서로 참조가 가능합니다. 예시를 통해서 보곘습니다.
12#define A 12#define B (A*4)cs
A라는 것을 12로 정의하고 B의 값은 A에 4를 곱한 값으로 정의 했기 떄문에 48이라는 값을 가지게 될 것입니다. 하지만 한 방향방식의 컴파일러가 대부분이므로 순서를 지켜야지 사용 할 수있는 문장입니다. 만약 B가 위에 있으면 A가 정의가 되지 않았기 떄문에 올바르지 않게 됩니다. - 값을 가지지 않는 빈 매크로도 정의가 가능합니다.
#define Triangle
이 매크로는 값을 가지지 않으며 매크로 상수 자체만 존재합니다. 이렇게 값을 가지지 않는 매크로는 주로 조건부 컴파일 지시자와 함께 사용되며 존재하는가? 또는 존재하지 않는가? 로만 의미를 가지게 됩니다. 또한 아무 값도 가지지 않음을 명시할 때도 빈 매크로를 정의합니다.
모든 상수를 다 매크로 상수로 정의한 후 사용할 수도 있고 적당한 수준에서 상수를 직접 사용할 수도 있는데 이는 개인의 프로그래밍 취향에 따라서 결정하면 됩니다. 매크로 상수는 일괄적인 수정에 도움을 줍니다. 하지만 너무 남용하게 되면 편리한듯 하다가도 소스의 가독성이 떨어져 버리게 됩니다.
'Programming > C' 카테고리의 다른 글
기억 부류(Storage Class) (1/2) (0) | 2015.08.09 |
---|---|
C언어 - 함수(function)(4/4) (0) | 2015.08.07 |
C언어 - 함수(function)(2/4) (0) | 2015.07.31 |
C언어 - 함수(function)(1/4) (0) | 2015.07.30 |
C언어 - 연산자(2/2) (1) | 2015.07.22 |