Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 42
Текст из файла (страница 42)
1!одсказка: определите функцию в виде последовательности операцийл, в точности такой, какую ввел пользователь. Последовательность можно хранить либо в виде строки, либо как список лексем, При вызове функции прочитайте и выполните эти операции. Если вы захотите, чтобы пользовательские функции принимали аргументы, вам придется придумать для этого соответствующую форму записи. ("1.5) Модифщ[ируйтс настольный калькулятор таким образом, чтобы в нем ис- по.льзовалась сз руктура ядтбо1 вместо статических перемеиньлх пигпбег иа1ие и к!г!пи иа1ие, (*1.5) Напишите программу, которая удаляет комментарии из программы на С«"-.
Пусть она читает цз слп, удаляет коллмеитарпи обоих видов (// п /* */) и записывает результат в соиб Не заботьтесь о внешнем виде вывода (это было бы другим и гораздо более сложным упражнением). Программа должна работать одинаково и для корректнлях, и для некорректных программ на ее входе. Не за- оудьте о вазможности наличия символов //, /* и */ в комментариях, строках и силлвольных константах. ("2) Просмотрите несколько программ, обращая внимание на различные стили отступов, задания имен н оформления комментариев. Функции Итерация — от человека, рекурсия — от Глогп. — Л. Питер Доич Объявлс ния и определения функций — передача аргументов — возвращаемые значения — перегрузка функций - .
разрешение неоднозначности — аргументы по умолчанию — в(г(агдз — указатели на функции — макросы — советы— упражнения. 7.1. Объявления функций Чтобы выполнить что-нибудь полезное в С+-:, как правило, вызывают функцию, Определить функцию — значит задать способ выполнения операции. К функции нельзя обратиться, если она не была предварительно объявлена. Обьявление функции задает имя функции, тип возвращаемого значения (еслн оно есть) и количество н типы аргументов, которые должны присутствовать при вызове функции. Например: Е(ет* пех! е!ет )), сЬаг* в!геру (сЬаг* го, сопв! сбаг')гот) эо(с(ехп )(пгГ Семантика передачи аргументов аналогична инициализации.
Производится проверка типов аргументов н, если необходимо, осуществляется неявное преобразование типов. Например: с(ои6(е вдг! )г(ои6(е) г(ои6(е вг2 = вуг! )2); //вызов гуггО с аргунентои иоиЫе(2) г(ои6(е вгу = где!)'три'); //ошибка: где!() требует аргул~епт типа ~Уои6(е Не следует недооценивать значение проверки соответствия н преобразования типов. Объявление функции может содержать имена аргументов. Это может оказаться полезным для читающего программу, но компилятор их просто игнорирует. Как упоминалось в у 4.7, указание ио(с( в качестве типа возвращаемого значения означает, что функция не возвращает значения. Глава 7. Функции 186 ?.1.1.
Определения функций Каждая функция, вызываемая в программе, должна быть где-нибудь определена (и только один раз). Определение функции является обьявлснием функции, в котором присутствует тело функции. Например: ехгегп ооЫ яшар (ЫР, и!"), О объявление ооЫятар (!пл р, лп!'с!) 77 определеное ( и!!='р, р= ч ) Типы в определении и объявлениях функции должны совпадать. Однако, имена аргументов не являются частью типа и не обязаны совпадать. Бывакл. функции, имеющие неиспользуемый аргумент: ооЫ яеагсб (гойе' 1, сопя! спас ллеу, сопя! сллаг') О треп|и!! аргуленгп не используется Как показано в примере, тот факт, что аргумент не используется, можно отразить, не указывая для него имени.
Обычно, неименованные аргументы появляются либо в результате упро!пения кода, либо когда планируется расширение возможностей функции. Б обоих случаях наличие аргумента, пусть и не используемого, обеспечивает независимость вызывающих программ от зтих изменений. Функцию можно определить со специфпкатором !п(!пе, Такие функции называются вппроенныжи. Например: т!Ые !и!)ас(!пгп) (ге!игл(п <21 71 пупс(п — !),) Спецификатор !п1(ле указывает компилятору, что оп должен пытаться квжд!ай раз генерировать в месте вызова код, соответствующий функции ~ас () (факториал), а не создавать отдельно код функции (единожды) и затем вызывать ее посредством обычного механизма вызова.
Хорошпй компилятор может сгенерировать константу 720 в месте вызова ас (б). Ввиду допустимости рекурсивных и взаимно рекурсивных встроенных функций, невозможно гарантировать, что в мс сте каждого вызова такой функции будег действительно осуществлено встраивание кода функции. Б зависимости от качества, один компилятор может сгенерировать 72Р, другой —. б*)ас (5), а третий — обычный вызов(ас (б).
Для того чтобы сделать возможным встраивание при отсутствии «сверхумно! о» компилятора н изощренных средств компоновки, определение (а не только объявление) встроенной функции должно находится в данной области видимости (9 9.2). Спецификатор !п1!пепе оказывает влияния на слсысл вызова функции. В частности, встроенная функция все равно имеет уникальный адрес, так же как ее стапшегкпе переменные (б 7.1.2).
7.1.2. Статические переменные Локальная переменная инициализируется в момент выполнения строки, содержащей ее определение. 1!о умолчанию зто происходит при каждом вызове функции п каждый 7.2. Передача аргументов 18? оо!О?(!л!а( ( шlи(е (а — ( ( з!алс ш! л = 0; юп!х= О; /1 инияиализирретсн один раз Оинилиазизиррелми 'а'раз лри каждая аьсзовеЗ(( сои!«'л== «л++«',х=="«х+з-"1л'; 1п! гааги (( ( у(з~, В результате булет напечатано: п== О,х== 0 л==!,х==0 п== 2,х == 0 Статическая переменная позволяет функции лпоынить о прошлом>, не создавая при этом глобальной переменной, к которой могли бы обратиться (и испортить ее) дру- ! ие функции (см. также б 10.2.4).
7.2. Передача аргументов Ири вызове функции выделяется память под ее формальные аргументы, и каждому формальному аргументу присваивается значение соответствующего фактического аргумента. Семантика передачи аргументов идентична семантике инициализации. В частности, проверяется соответствие типов формальных н фактических аргументов и при необходимости выполняются либо стандартные, либо определенные пользователем преобразования типов. Существуют специальные правила лля передачи массивов в качестве аргумента Я 7.2.1), среде~на для передачи аргументов, соответствие типов для которых не проверяется (Ч 7.б), и средства для задания аргументов по умолчанию Я 7.5).
Рассмотрим пример; оо! д г ((п ! па 1, !лИ ге1( ( оа1+з-, гез1-+; Нри вызовеД(( ва1++ увеличивает значение локальной копни первого фактического аргумента, в то время как герьь увеличивает значение второго фактического аргу- мента. Например, оо!Оя(( ( раз создается новая переменная, Если локальная переменная объявлена как з(а1!с, прп всех вызовах функции для хранения ее значения будет использоваться единственный, статически размещенный в памяти объект Я В.9). Она будет проннициалпзпрована только при первом выполнении строки, содержащей ее определение. Например: Глава 7. Функции 188 ио!ЙЯсопяг ЕагуеГ ага) //знанение агунельзя изл~енить без использования //явного иреойразовиния гтта Отсутствие сопя! в об ьявленин аргумента, передаваемого по ссылке, рассматривается как указание на то, что переменная модифицируется в фу нкппи: ооЫу)рагдезагу); лл/ предполагаель нто у() ллодифииирует ат Аналогично, объявление аргумента, являющегося указателем, с модификатором сопя!, говорит читателю, что значение объекта, иа который ссылается указатель, не изменится в функции.
Например: // колииеслпво силлволов в !!проке //копирование строк // сравнение слпрок спг я!г!еп )сопя! с/лаг'! с!заг" я1гсру )с!заг' 1о, сопя! с!!а~ /сот ! им япгстр )сопят и!лаг', сопя! с/саг") Использование константных аргументов становится все более важным по мере роста размера программы. Обратите внимание, что семантика передачи аргументов отличается от семантики присваивания.
Это важно для константных аргументов, аргументов, передаваемых по ссылке, и аргументов некоторых типов, определяемых пользователем (8 10.л).й!.1). Литералы, константы и аргументы, требующие преобразования типа, можно передавать как сопя!8 -аргументы и нельзя — в качестве не сопяЯ аргументов. Разрешение преобразования для аргумента типа соля! Т$ обеспечивают гарантию того, что аргументу будет присвоен тот же набор значений, что и аргументу типа Т; при необходимости значение будет передано через временную переменную. Например: /!оаг/яуг! )сопя!/)оаИ), //яуа в стиле Рог1гап с передаясо аргумента по ссьтке ио10 у )с!оиЫе лй /!оа! г = /луг! )2.0Я; // передает ссылку на вреден ную переменную, 0 со дар ясаи1 уло 2.0 ! // передаелп ссьллку на г // передает ссылку на врелленную перел~вянуло.
// содерясаи,ую/!оа! (д) г =/яусс )г); г =-/яугг )д), увеличит/, но не !. Первый аргумент, с, передан по значению, а второй,!' — по ссьите. Б ~ 5.5 упоминалось, что функции, которые модифицируют аргументы, переданные по ссылке, делают программу трудно читаемой и в болин!ивстве случаев пх слелует избегать (но см. з 21.3.2). Однако, большие объекты значительно эффективней передавать по ссылке, чем !ю значению.