The Essentials of C++, David hunter

Chapter 6. Preprocessor Directives and Header Files 프리프로세서 지시문과 헤더 파일

6.1 The Preprocessor 프리프로세서

  • Before the translation phase of compilation begins a C++ program is first treated by the C++ preprocessor. The preprocessor performs several tasks, the most significant of which are:

  • 컴파일의 번역 단계가 시작되기 전에 C++ 프로그램은 먼저 C++ 전처리기에 의해 처리됩니다. 전처리기는 여러 가지 작업을 수행하는데, 그 중에서 가장 중요한 작업은 다음과 같습니다:

  1. Copy specified files into the current source file;
  2. Perform text subsitutions in the source file;
  3. Select sections of code to be compiled or skipped by the compiler.
  • Instructions to the preprocessor are given through “directives”. These are indicated by the “#” symbol and must not be preceded by white space.
  1. 지정된 파일을 현재 소스 파일로 복사합니다.
  2. 소스 파일에서 텍스트 치환을 수행합니다.
  3. 컴파일러에 의해 컴파일되거나 건너뛸 코드 섹션을 선택합니다.

6.2 Header files, #include, and Separate Compilation 헤더 파일, #include 및 분리 컴파일

  • The #include filename directive is used to copy a specified file into the file being processed. If the file name is enclosed in angle brackets the preprocessor will search for the file on the system “include file” search path. If the name is enclosed in double quotes the preprocessor will search for the file on the users current directory.

  • #include filename 지시문은 지정된 파일을 현재 처리 중인 파일로 복사하는 데 사용됩니다. 파일 이름이 꺽쇠 괄호로 묶여 있으면 전처리기는 시스템 “포함 파일” 검색 경로에서 파일을 찾습니다. 파일 이름이 쌍따옴표로 묶여 있으면 전처리기는 사용자의 현재 디렉토리에서 파일을 검색합니다.

#include <iostream>
#include "myfile.h"
  • The #include directive is most often used to provide programmers access to groups of related type definitions and function prototypes located in “header” files. Header files are traditionally given a file extension of “h” to distinguish them from source code files which are usually given an extension of “cc”, “cpp”, or “C”. C++ installation usually provide a large set of header files defining functions for tasks such as string manipulation, I/O, mathematical computation, and so forth.

  • #include 지시문은 주로 프로그래머에게 “헤더” 파일에 위치한 관련된 형식 정의와 함수 프로토타입에 액세스 권한을 제공하기 위해 사용됩니다. 헤더 파일은 일반적으로 “h” 파일 확장자로 지정되어 있으며 이는 일반적으로 “cc”, “cpp” 또는 “C” 확장자가 있는 소스 코드 파일과 구별하기 위한 것입니다. C++ 설치는 주로 문자열 조작, 입출력, 수학 계산 등과 같은 작업을 위한 함수를 정의하는 많은 헤더 파일 집합을 제공합니다.

  • Some header files are standard and will be included with every C++ installation. For example, the file “iostream.h” and “string.h” provide I/O and string manipulation functions respectively. These are discussed in later chapters. Some of the other standard header files are discussed at the end of this chapter, but a complete treatment is beyond the current scope. Programmers should consult their compiler documentation for a description of what has been included in their implementation.

  • 일부 헤더 파일은 표준이며 모든 C++ 설치에 포함됩니다. 예를 들어 “iostream.h” 및 “string.h” 파일은 각각 입출력 및 문자열 조작 함수를 제공합니다. 이러한 표준 헤더 파일 중 일부는 이 장의 끝에서 설명되지만 완전한 처리는 현재 범위를 벗어납니다. 프로그래머는 자신의 컴파일러 설명서를 확인하여 구현된 내용에 대한 설명을 참고해야 합니다.

  • Header files should contain only definitions and function prototypes. Associated with a header file will be a corresponding source code file that contains the complete specification of the functions whose prototypes appear in the header file. To create a C++ executable file, each source code file is compiled separately and the resulting object code files linked using a system linker. (Usually, the same program that provides compilation can also provide linking.)

  • 헤더 파일은 정의와 함수 프로토 타입만 포함해야 합니다. 헤더 파일과 관련된 것은 헤더 파일에 나오는 프로토 타입의 함수에 대한 완전한 명세를 포함하는 해당 소스 코드 파일이 있을 것입니다. C++ 실행 파일을 만들려면 각 소스 코드 파일을 별도로 컴파일하고 생성된 오브젝트 코드 파일을 시스템 링커를 사용하여 연결해야 합니다. (일반적으로 컴파일을 제공하는 프로그램은 연결도 제공할 수 있습니다.)

6.3 Symbolic Constants 상수 기호

  • The #define directive can be used to define symbolic constants, as shown below.

  • #define 지시문을 사용하여 상수 값을 정의할 수 있습니다. 아래와 같이 보여집니다.

#define PI 3.1415927
#define MAX_SIZE 100
#define ERROR "An error has occured."
  • Symbols defined this way are quite different from those defined using the const declaration shown in chapter 1. The former exist only during the preprocessing stage, while the latter are compiled in much the same way that variables are.

  • 이 방식으로 정의된 심볼은 1장에서 설명된 const 선언을 사용하여 정의된 심볼과 매우 다릅니다. 전자는 전처리 단계에서만 존재하며, 후자는 변수와 유사한 방식으로 컴파일됩니다.

  • Symbolic constants defined this way instruct the preprocessor to perform text substitution. That is, with reference to the example above the preprocessor will simply substitute the string “3.1415927” whenever it encounters “PI” in the source file. After preprocessing, the resulting altered text is made the subject of the compiler, which consequently never “sees” PI.

  • 이렇게 정의된 심볼 상수는 전처리기에게 텍스트 대체를 수행하도록 지시합니다. 즉, 위의 예제를 참조하면 전처리기는 소스 파일에서 “PI”를 만날 때마다 단순히 문자열 “3.1415927”을 대체합니다. 전처리 후에 결과로 변경된 텍스트가 컴파일러의 주제가 되며, 따라서 컴파일러는 결코 “PI”를 “보지” 않습니다.

6.4 Macros 매크로

  • Macros are another from of text substitution that can be specified using the #define directive. A macro is defined with parameters and takes the general form:

  • 매크로는 #define 지시어를 사용하여 지정할 수 있는 텍스트 치환의 또 다른 형태입니다. 매크로는 매개변수와 함께 정의되며 일반적인 형식은 다음과 같습니다:

#define name(arg1, arg2, ...) substitute-text
  • For example, one might define the following:

  • 예를 들어, 다음과 같이 정의할 수 있습니다:

#define sqr(x) (x) * (x)
  • Subsequent to this definition, upon encountering the text sqr(a) the preprocessor substitutes (a) * (a), upon encountering sqr(a+b) it substitutes (a+b) * (a+b), and so forth. Since the preprocessor performs text substitution only, upon encountering sqr(“Hello world”) it would substitute (“Hello world”) * (“Hello world”) and proceed with its processing.

  • 이 정의 이후에 “sqr(a)” 텍스트를 만나면 전처리기는 “(a) * (a)”로 대체하고, “sqr(a+b)”를 만나면 “(a+b) * (a+b)”로 대체하며, 이와 같은 과정을 계속합니다. 전처리기는 텍스트 대체만 수행하므로 “sqr(“Hello world”)”를 만나면 (“Hello world”) * (“Hello world”)로 대체하고 처리를 계속합니다.

  • The last example illustrates that macros are potentially very unsafe constructs. In fact, they are a holdover from C. Since they provide functionality very similar to that provided by inline functions, there is no reason to use them in C++ and they should be avoided.

  • 마지막 예제에서 보듯이 매크로는 잠재적으로 매우 안전하지 않은 구조입니다. 실제로 매크로는 C에서 기원한 것이며, 인라인 함수와 매우 유사한 기능을 제공하기 때문에 C++에서 사용할 이유가 없으며 피해야 합니다.

6.5 Conditional Compilation 조건부 컴파일

  • A programmer can cause the preprocessor to select sections of code to be compiled or not compiled by using either the #ifdef(“if defined”) or #ifndef (if not defined) directives. The two have the same form. The #ifdef is shown below.

  • 프로그래머는 #ifdef(“if defined”) 또는 #ifndef (if not defined) 지시문을 사용하여 코드의 섹션을 컴파일하거나 컴파일하지 않도록 선택할 수 있습니다. 두 지시문은 동일한 형식을 가지고 있습니다. #ifdef는 아래와 같습니다.

#ifdef symbol
// ...some code...
#else
// ...some code...
#endif
  • The code contained within the “if” section of the block is compiled if the specified symbol is known to the preprocessor. If the symbol is not known, the code within the “else” section is compiled. The effect of the #ifndef directive is analogous. In either case, the #else is optional. Symbols can be defined by statements of the form #define symbol.

  • 지시문의 “if” 섹션에 포함된 코드는 지정된 심볼이 전처리기에 알려져 있으면 컴파일됩니다. 만약 심볼이 알려져 있지 않으면 “else” 섹션 내의 코드가 컴파일됩니다. #ifndef 지시문의 효과는 유사합니다. 어느 경우에도 #else는 선택 사항입니다. 심볼은 #define symbol 형식의 문장으로 정의할 수 있습니다.

  • These directives are very useful if a program must include machine specific section of code. The technique is illustrated below.

  • 이러한 지시문은 프로그램이 특정한 기계에 따라 코드의 특정 섹션을 포함해야 하는 경우에 매우 유용합니다. 이 기법은 아래에서 설명됩니다.

#ifdef MS_DOS
// code specific to MS_DOS
#endif
#ifdef UNIX
// code specific to UNIX
#endif
  • To compile this code for a specific platform, a programmer would include either the line #define MS_DOS or #define UNIX at the top of the source file. Only the appropriate blocks of code are then compiled.

  • 이 코드를 특정 플랫폼에 맞게 컴파일하려면 프로그래머는 소스 파일의 맨 위에 #define MS_DOS 또는 #define UNIX와 같은 줄을 포함해야 합니다. 그런 다음 적절한 코드 블록만 컴파일됩니다.

6.6 Avoiding Duplicate Definitions 중복 정의 피하기

  • Header files and the include directive provide a simple mechanisom for creating software libraries but can cause problems since it is easily possible for the same header files to be copied several times into the same source file. The problem is illustrated below.

  • 헤더 파일과 include 지시문은 소프트웨어 라이브러리를 만드는 간단한 메커니즘을 제공하지만, 동일한 헤더 파일이 동일한 소스 파일에 여러 번 복사되는 경우 문제가 발생할 수 있습니다. 이 문제는 아래에서 설명됩니다.

// This is file class.h
#include "student.h"
struct class_record {
    student_record students[100];
    int enrollment;
};
// ...
// This is file grade_book.cc
#include "student.h"
#include "class.h"
  • In the example, the definitions in “student.h” end up being copied into “grade_book.cc” twice: once as a direct consequence of #include “student.h” and indirectly from #include “class.h” because “class.h” also includes “student.h”. Compiler errors will be generated for every duplicate definition. Of course, a careful programmer could avoid this problem by not including “student.h” in “grade_book.cc”, but for large systems with many different header files it is difficult or impossible to keep an accurate track of which files are included in which other files.

  • 예시에서 “student.h”의 정의가 “grade_book.cc”에 두 번 복사됩니다. 한 번은 #include “student.h”의 직접적인 결과로, 다른 한 번은 “class.h”가 “student.h”를 포함하기 때문에 간접적으로 발생합니다. 중복된 정의로 인해 컴파일러 오류가 발생할 것입니다. 물론 신중한 프로그래머는 “grade_book.cc”에서 “student.h”를 포함하지 않음으로써 이 문제를 피할 수 있지만, 많은 다양한 헤더 파일이 있는 대규모 시스템의 경우 어떤 파일이 다른 파일에 포함되어 있는지 정확하게 추적하기 어렵거나 불가능합니다.

  • Because of this problem, it is common practice to associate with each header file a preprocessor symbol that severs as a flag to indicate whether or not the contents of that file have been compiled. This technique is illustrated on the next page. In the example, “grade_book.cc” file may contain more than one copy of “student.h”, but the definitions are compiled only the first time they are encountered.

  • 이러한 문제로 인해 각 헤더 파일에 컴파일된 내용을 나타내는 플래그로 사용되는 전처리기 심볼을 연관시키는 것이 일반적인 관행입니다. 이 기술은 다음 페이지에서 설명됩니다. 이 예에서 “grade_book.cc” 파일은 “student.h”의 복사본을 하나 이상 포함할 수 있지만 정의는 처음으로 만나는 경우에만 컴파일됩니다.

  • File “student.h”

// File "student.h"
#ifndef STUDENT_H
#define STUDENT_H
struct student_record {
    int id;
    char classification;
    float gpa;
};
void read_student(student_record&);
// ...
#endif // STUDENT_H
  • File “class.h”
// File "class.h"
#ifndef CLASS_H
#define CLASS_H
#include "student.h"
struct class_record {
    student_record students[100];
    int enrollment;
};
// ...
#endif // CLASS_H
  • File “grade_book.cc”
// File "grade_book.cc"
#include "student.h"
#include "class.h"
int main() {
    student_record s;
    class_record c;
    // ...
    return 0;
}

6.7 Some Useful Library Files 유용한 라이브러리 파일

6.7.1 ctype.h

  • The header file “ctype.h” defines several functions for testing character data. In the table below, the specified function is true if the argument is in the specified range.

  • 헤더 파일 “ctype.h”는 문자 데이터를 검사하기 위한 여러 함수를 정의합니다. 아래 표에서 지정된 함수는 인수가 지정된 범위에 속하면 참(True)입니다.

Table 6.1
  • 문서 원본: 오류 있어 보임.
functions description
int isalnum(int) upper or lower case letter or digit
int islower(int) lower case letter
int isalpha(int) upper or lower case letter
int isprint(int) a printing character, 0x20-0x7E
int isascii(int) low order byte range 0-127
int ispunct(int) a punctuation character (isspace or iscntrl)
int iscntrl(int) a delete or control character, 0x7F or 0x00 - 0x1F
int isspace(int) space, tab, carriage return, newline, vertical tab, or form feed
int isdigit(int) a digit
int isupper(int) an upper case letter
int isgraph(int) like isprint except the space character is excluded
int isxdigit(int) is a hex digit, 0-9 and A-F
  • 인터넷에서 찾은 것
Function Description
isalnum(int c) 알파벳 문자 또는 숫자인 경우 참(True)
isalpha(int c) 알파벳 문자인 경우 참(True)
iscntrl(int c) 제어 문자인 경우 참(True)
isdigit(int c) 숫자인 경우 참(True)
isgraph(int c) 그래픽 문자인 경우 참(True)
islower(int c) 소문자인 경우 참(True)
isprint(int c) 출력 가능한 문자인 경우 참(True)
ispunct(int c) 구두점인 경우 참(True)
isspace(int c) 공백 문자인 경우 참(True)
isupper(int c) 대문자인 경우 참(True)
isxdigit(int c) 16진수 숫자인 경우 참(True)
  • In addition to these, the file defines functions int toupper(int) and int tolower(int). These convert letters to upper and lower case, respectively. They return an unchanged copy of their argument if it is not a character in the expected range.

  • 이 파일은 위에서 언급한 함수들 외에도 int toupper(int)와 int tolower(int) 함수를 정의합니다. 이 함수들은 각각 문자를 대문자와 소문자로 변환합니다. 인수가 예상 범위 내의 문자가 아닌 경우, 함수들은 인수를 변경하지 않은 채 반환합니다.

6.7.2 assert.h

  • The header file “assert.h” defines a macro called assert which is used to specify truth conditions at various places in a program. These “assertions” state properties that should be true if the program is operating correctly, as in the example below.

  • 헤더 파일 “assert.h”는 프로그램의 여러 위치에서 진실 조건을 지정하는 데 사용되는 assert라는 매크로를 정의합니다. 이러한 “단언문”은 프로그램이 올바르게 작동하는 경우 참이어야 하는 속성을 나타냅니다. 예제는 아래와 같습니다

// return first character in a list
char first(ListCell* List)
{
    assert(List != NULL);
    // return ListCell->contents; // 원본
    return List->contents; // 수정
}
  • If an assertion is encountered and its specified condition is evaluated as false. the program is terminated and an appropriate error message displayed. (The particular message is machine dependent, but usually it specifies the function and line where the failed assertion occured.)

  • 만약 단언문(assertion)이 만나지고 지정된 조건이 거짓으로 평가된다면, 프로그램은 종료되고 적절한 오류 메시지가 표시됩니다. (특정 메시지는 기계에 따라 다르지만, 일반적으로 실패한 단언문이 발생한 함수와 라인을 지정합니다.)

  • Assertions are extremely useful debugging aids but will increase the size of an object file. Therefor, the macro is designed so that assert statements will be compiled only if the symbol NODEBUG is not defined.

  • 단언문(assertions)은 디버깅을 지원하는 매우 유용한 도구이지만 객체 파일의 크기를 늘릴 수 있습니다. 따라서 이 매크로는 ‘NODEBUG’라는 기호가 정의되지 않은 경우에만 단언문이 컴파일되도록 설계되었습니다.

6.7.3 math.h

  • The file “math.h” contains functions useful for performing mathematical computations. Some of them are given below. Typically, several variations of each function are defined to operate with different data types. The functions below accept arguments of type double.

  • 파일 “math.h”에는 수학 계산에 유용한 함수들이 포함되어 있습니다. 그 중 일부는 아래에 나열되어 있습니다. 일반적으로 각 함수의 여러 가지 변형이 다른 데이터 유형과 함께 작동하도록 정의되어 있습니다. 아래의 함수들은 double 유형의 인수를 허용합니다.

Table 6.2
function description
sin, cos, tan Trigonometric function
asin, acos, atan Arc-trigonometric functions
sinh, cosh, tanh Hyperbolic functions
log, log10 Natural and base-10 logarithm
exp e to the power of
ceil Finds smallest integer not less than x
floor Finds largest integer not greater than x
sqrt Square root
abs Absolute value
pow(base, exponent) Power(exponentiation)

6.7.4 stdlib.h

  • This file contains a miscellaneous group of definitions, too numerous to list exhaustively. Some of the more frequently used functions are given in the table below.

  • 이 파일에는 수많은 정의가 포함되어 있어 모두 나열하기 어렵습니다. 자주 사용되는 몇 가지 함수는 다음 표에 나와 있습니다.

Table 6.3
function description
int atoi(char* s) Convert a string of characters to an integer
float atof(char* s) Convert a string of characters to float
int exit(int) Exit the program and return the specified value
RAND_MAX A symbol representing the largest random number generated by rand()
NULL Symbol representing an “empty” pointer
int rand() Return a pseudo-random integer in the range 0 to MAX RAND
srand(unsigned int) Set the seed value used by rand()
double strtod(char* s) Convert character string to double
long strtod(char* s) Convert character string to long
unsigned long strtod(char* s) Convert character string to unsigned long
#include <iostream> 
#include <cstdlib>
#include <ctime>

int main()
{
    int heads = 0, tails = 0;
    time_t t = time(NULL);
    srand(static_cast<unsigned>(t));
    for (int i = 1; i <= 100; ++i)
    {
        if (rand() < RAND_MAX / 2)
            ++heads;
        else
            ++tails;
        std::cout << "Heads: " << heads << ", Tails: " << tails << std::endl;
    }
    return 0;
}
  • A program illustrating the use of the random number generator is given above. The program simulates the toss of 100 coins. The function srand should be called only once. The rand function produces a pseudo-random sequence of integers based on the seed value. If the program is given the same see value each time it is run, it produces the same sequence of “random” integers. Therefore, it is common to use the system clock to set the random number seed.

  • 난수 생성기 사용 예제가 아래에 제공되었습니다. 이 프로그램은 100개의 동전 던지기를 시뮬레이션합니다. srand 함수는 한 번만 호출해야 합니다. rand 함수는 시드(seed) 값을 기반으로 의사 난수 순서를 생성합니다. 프로그램이 실행될 때마다 동일한 시드 값을 제공하면 “랜덤” 정수의 동일한 순서를 생성합니다. 따라서 랜덤 숫자 시드를 설정하기 위해 시스템 클럭을 사용하는 것이 일반적입니다.

6.7.5 time.h

  • This file contiains a number of functions and definitions that can be used to access the computer system clock. Some of these are give below. Following that table, a program is presented that illustrates the used of the time functions.

  • 이 파일에는 컴퓨터 시스템 클럭에 액세스하는 데 사용할 수 있는 여러 함수와 정의가 포함되어 있습니다. 그 중 일부는 아래에 나와 있습니다. 그 표 다음에는 시간 함수의 사용 예제가 제시됩니다.

Table 6.4
function description
time t A type identifier used to represent time values
clock t A type identifier used to represent clock values
CLOCK_PER_SEC A symbolic constant representing the number of processor clock cycles per second
struct tm A structure type with the following fields:
  int tm_sec;
  Seconds int tm_min;
  Minutes int tm_hour;
  Hour(0-23) int tm_mday;
  Day of month (1-31) int tm_mon;
  Month(0-11) int tm_year;
  Year (calendar year minus 1900) int tm_wday;
  Weekday (0-6; Sunday = 0) int tm_yday;
  Day of year (0-256) int tm_isdst;
  0 if daylight savings time is not in effect
struct tm*localtime Given a pointer to a time_t variable, returns a tm structure with fields appropriately set
(const time_t*tpr)  
clock_t clock() Returns the number of clock ticks since program execution. Returns -1 if the information is not available.
time_t time(time_t*tm) Gives the current time in seconds, elapsed since 00:00:00 GMT, January 1, 1970. It stores that value in the location *tm, provided that tm is not null pointer.
function description
time_t 시간 값을 나타내는 데 사용되는 형식 식별자
clock_t 클럭 값을 나타내는 데 사용되는 형식 식별자
CLOCK_PER_SEC 초당 프로세서 클럭 주기를 나타내는 상수
struct tm 다음 필드를 가지는 구조체 형식:
  int tm_sec;
  초 int tm_min;
  분 int tm_hour;
  시(0-23) int tm_mday;
  월 일(1-31) int tm_mon;
  월(0-11) int tm_year;
  연도(달력 연도에서 1900을 뺀 값) int tm_wday;
  요일(0-6; 일요일 = 0) int tm_yday;
  연중 일 수(0-256) int tm_isdst;
  일광 절약 시간이 적용되지 않을 경우 0
struct tm* localtime time_t 변수에 대한 포인터를 주면 필드가 적절하게 설정된 tm 구조체를 반환합니다.
(const time_t* tpr)  
clock_t clock() 프로그램 실행 이후의 클럭 틱 수를 반환합니다. 정보를 사용할 수 없는 경우 -1을 반환합니다.
time_t time(time_t* tm) 현재 시간을 GMT 기준으로 1970년 1월 1일 00:00:00부터 경과된 초 단위로 반환합니다. tm이 널 포인터가 아닌 경우 해당 값을 *tm 위치에 저장합니다.
#include <ctime>
#include <iostream>
using namespace std;

int main(void)
{
    time_t timer;
    struct tm *tblock;
    // get time in seconds
    timer = time(NULL);

    // create a tm structure
    tblock = localtime(&timer);

    // write out data
    cout << tblock->tm_mon + 1 << "/" << tblock->tm_mday << "/" << tblock->tm_year + 1900 << "\n"; 

    clock_t ck = clock();

    // write out seconds elapsed since program execution
    cout << ck / CLOCKS_PER_SEC;
}