Попробуйте часть кода до слова main заменить на #include "stdafx.h" //1 #include <iostream> //2 using namespace std; //3 |
Дополнительные сведения о системе программирования Borland C++ 3.1
12.1. Препроцессор и условная компиляция
В состав системы программирования BC 3.1 входит препроцессор cpp.exe, который выполняет следующую подготовительную работу перед компиляцией программы:
- включает в программу тексты указанных файлов;
- исключает из программы фрагменты, не удовлетворяющие заданным условиям;
- осуществляет предусмотренную замену (макроподстановка);
- заменяет Esc-последовательности их числовыми кодами;
- объединяет смежные символьные строки и устраняет символы переноса строк.
Все действия препроцессора диктуются директивами, которые программист включает в текст своей программы. Первым символом директивы является символ #.
Для включения в текст программы указанных файлов используется директива #include (от англ. – включить), допускающая два следующие формата:
#include <file_name> #include "file_name"
Угловые скобки являются указанием препроцессору, что поиск файла с заданным именем надо начинать с системного каталога (например, с каталога c:\bc\include ). Если в указанном каталоге файл file_name не обнаружен, то поиск продолжается сначала с текущего каталога, а затем по всем каталогам, перечисленным в директиве PATH операционной системы. Если имя файла заключено в двойные кавычки, то поиск начинается с текущего каталога.
Обычно, с помощью директивы #include к программе подключаются системные и пользовательские заголовочные файлы с расширением .h (от header – заголовок). Однако точно так же к программе можно подключить ранее заготовленный фрагмент исходного кода, оформленный в виде текстового файла с расширением .inc.
Замена одной цепочки символов в тексте программы на другую цепочку символов реализуется с помощью макроподстановки #define (от англ. – определить):
#define s1s2s3...sn q1q2...qm
При этом цепочка символов s1s2s3...sn в тексте исходной программы будет заменена на цепочку q1q2...qm. Пробелы перед замещающей цепочкой и в ее конце игнорируются. Замене не подвергаются значения строк и комментарии. Заменяющий фрагмент может оказаться и многостроковым. В этом случае в конце каждой строки помещается символ переноса – " \ ". В приведенной ниже программе содержится несколько наиболее характерных примеров использования директивы #define:
#include <stdio.h> #include <conio.h> #define Nmax 100 #define max(a,b) ((a)>(b))?(a):(b) #define print(a) printf("\n%s=%d\n",#a,a);\ getch() void main() { int x=5,y=8; int z=Nmax; int w=max(x*y,z); print(x); print(y); print(z); print(w); } //=== Результат работы === x=5 y=8 z=100 w=100
Обратите внимание на некоторые тонкости в приведенных подстановках.
Во-первых, аргументы макроопределения-функции max в замещающем выражении заключены в круглые скобки. Это позволяет использовать в конкретных обращениях нормальные арифметические выражения. Представим себе, что в программе достаточно часто приходится использовать операцию возведения в квадрат. Если для этой цели макроподстановку Square(x) определить без использования скобок (#define Square(x) x*x), то для арифметического выражения Square(1+z) результат такой подстановки даст 1+z*1+z=2*z+1, т.е. заведомо неправильное значение.
Наличие круглых скобок тоже не является стопроцентной гарантией правильности результата подстановки. Например, попытка обратиться к макросу max необычным образом сообщений об ошибках не вызовет, но и результат может оказаться далеким от истины:
w=max(x+=2,y+=3); //результат подстановки w=14 w=max(x+2,y+3); //результат правильный w=11
В бесскобочном макроопределении первое обращение привело бы к синтаксической ошибке ( Lvalue required – требуется левое значение).
Во-вторых, макроопределение max не зависит от типа используемых данных.
В-третьих, в макроопределении print использована довольно редко описываемая возможность вывода имени переменной ( #a – интерпретируется препроцессором как имя переменной a ). Если бы мы включили эту переменную в форматную строку ( printf("\na=%d",a); ), то ничего хорошего из этого бы не вышло. Так как на содержимое строк макроподстановка не распространяется, то при каждом обращении вместо имени очередной переменной выводился бы символ 'a'.
В операторе макроподстановки иногда используется операция склейки лексем (аналог того, что при работе со строками называют конкатенацией ):
#define Paste(a,b) a##b
В результате такой подстановки строка Paste(x,4) будет заменена на x4.
Две группы следующих директив используются для организации "условной компиляции":
#define...#ifdef...#ifndef...#undef #if...#elif...#elif...#else...#endif
На самом деле препроцессор компиляцией не занимается, он просто включает или отключает фрагменты исходной программы, которые в дальнейшем будут или не будут обрабатываться компилятором.
Если заглянуть в любой заголовочный файл из каталога ...BC\INCLUDE, например, в файл math.h, то в самом его начале (исключая комментарий по поводу авторских прав) находятся следующие строки:
#ifndef __MATH_H #define __MATH_H #if !defined(___DEFS_H) #include <_defs.h> #endif
В чем смысл двух первых строк? Сначала проверяется, была ли ранее объявлена подстановка для идентификатора __MATH_H (этот идентификатор является уникальным, т.к. он повторяет имя заголовочного файла). Если такого указания еще не было, то следующая строка объявляет о необходимости такой подстановки в строках программы, следующих ниже по тексту (не важно, что замещающее выражение пусто), и все остальное содержимое файла, который мы присоединяем по директиве #include <math.h>, будет включено в текст наше программы. Но если такая подстановка ранее была заявлена, то повторное подключение файла math.h не произойдет. Это позволяет избежать дублирования констант и других макроопределений, которые могли бы появиться из-за повторения заголовочного файла.
Примерно такую же функцию выполняют три следующие строки. Первая из них проверяет, не состоялось ли ранее присвоение значения идентификатору ___DEFS_H. Если такого действия еще не было, то файл defs.h будет загружен в оперативную память. В противном случае фрагмент программы до строки #endif не будет передан компилятору. Кстати, подобная тройка строк присутствует во многих заголовочных файлах, и такая проверка предупреждает повторную загрузку файла defs.h.
Более детально, первая группа директив препроцессора выполняет следующие действия:
Вторая группа директив предназначена для включения или отключения фрагментов программы пользователя средствами, напоминающими действие условного оператора if – then – else:
#if (условие_1) //если выполнено условие_1 фрагмент_1 #elif (условие_2) //если выполнено условие_2 фрагмент_2 #elif (условие_3) //если выполнено условие_3 фрагмент_3 ............. #else //если не выполнено ни одно из предшествующих условий фрагмент_k #endif //конец проверок
Если результат проверки дал положительный результат, то следующий за ним фрагмент программы будет передан компилятору. В противном случае это фрагмент исключается из текста исходной программы (не затирается, а просто не поступает на вход компилятора). Условия, которые задаются в директивах проверки, могут выполняться только над константными выражениями:
#define name 1 ............. #if (name==1)
Одним из достаточно частых применений группы #if...#endif является подключение или отключение отладочных выдач.