Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 42
Текст из файла (страница 42)
Обычно, неименованные аргументы в определениях функций появляются либо вследствие произведенных упрошений, либо в качестве резерва на будущее. В обоих случаях само наличие реально неиспользуемого аргумента в определении функции позволяет не трогать при этом клиентский код, вызываюший функцию.
Можно определить функцию с модификатором (п11пе. Например: ййпе ии(ас((и(н) ( ге(игп (п<2) 7 1: и»(ас(п-1) ) Такие функции называют встраиваемыми (тйпе йгисйопз), так как модификатор (пйпе указывает компилятору, что он должен пытаться осуществлять прямое встраивание кода функцииуас() во все места, где эта функция вызывается, а не реализовывать этот код в единственном экземпляре, доступ к которому выполняется через стандартный механизм вызова. Умный компилятор может сгенерировать константу 720 на месте вызова уас ( б) . Из-за того, что могут быть объявлены встраиваемыми рекурсивные или взаимно рекурсивные функции, гарантировать реальное встраивание кода функции в каждое место ее вызова невозможно. В зависимости от степени интеллектуальности конкретного компилятора мы может реально получить для нашего примера и 720, и б"уас(5), и просто обычный вызовуас(б).
Чтобы обеспечить возможность встраивания в случае рядового (а не сверхинтеллектуального) компилятора и компоновшика, нужно размешать определение (а не 197 7.2. Передача аргументов только объявление) функции с модификатором 1пвие в той же самой области види- мости (59.2). Модификатор иаие не влияет на семантику функции. В частности, встраиваемые функции по-прежнему имеют уникальный адрес, так же как и их ста- тические переменные (57.1.2). 7.1.2. Статические переменные Локальные переменные инициализируются всякий раз, как только поток исполнения достигает точки их определения.
Это происходит при каждом вызове функции и каждый такой вызов располагает собственной копией локальной переменной. Если же локальная переменная объявляется с модификатором згапс, то единственный, статически размещаемый в памяти объект (5С.9) будет использоваться для представления этой переменной во всех вызовах функции. Такая переменная будет инициализироваться лишь однажды, когда поток управления первый раз достигнет точки ее определения. Например: »оЫ)'(гиг а) ( ювде(а--) згаФс (лги = О; )пгх=Ог )) инициализируется один раз гУ иниц-ся 'о'раз при каждом выэовв1() соиз ««и == ««и+«« ", х == " «х+«« ' ~и' (иг эиа(п П ( Х(з) ' ) В результате будет получен следующий вывод: и == О, н == ), х== О и ==г, я==О Статические переменные наделяют функции «долговременной памятью» без необходимости применения глобальных переменных, доступ к которым из других функций способен привести к их порче (510.2.4). 7.2.
Передача аргументов При вызове функции выделяется память под ее формальные аргументы, и каждый формальный аргумент инициализируется значением соответствующего фактического аргумента. Семантика передачи аргументов идентична семантике инициализации. В частности, типы фактических параметров проверяются относительно типов формальных параметров, и в случае необходимости выполняются либо преобразования стандартных типов, либо преобразования, определенные для пользовательских типов. Существуют специальные правила для передачи массивов Глава 7. Функции 198 (97.2.1), средства для передачи аргументов без проверки типов 67.6) и средства, предназначенные для задания аргументов по умолчанию (97.5).
Рассмотрим следующую функцию; юЫ)(тг га1, т1ь ге)) ( га!в+1 ге~н-; Когда осуществляется вызов 7О, выражение га1++ инкрементирует локальную копию первого фактического аргумента, в то время как выражение ге~++ инкрементирует непосредственно второй фактический аргумент. Например, гоЫ я () ( гпг 1=11 (пг /=11 з (1 )) ' ) увеличит), но не 1. Первый аргумент, 1, передается по значению (Ьу иа)ие), а второй аргумент, 1, передается по ссылке (Ьу ге)егепсе). Как упоминалось в 95.5, функции, которые модифицируют передаваемые им по ссылке аргументы, делают программу менее ясной и их следует, чаще всего, избегать (однако, см. 921.3.2). Правда, передача параметров большого размера по ссылке намного эффективнее их передачи по значению.
В таком случае следует объявлять аргументы с модификатором сопзг, дабы явным образом подчеркнуть, что передача аргументов по ссылке осуществляется исключительно ради эффективности, а не для того, чтобы функция могла их модифицировать: го!И)'(сопл1 Йагяеа агу) ( У здесь агя невозможно изменить без явного приведения типа ) Отсутствие модификатора сопяг в объявлении передаваемого по ссылке аргумента должно восприниматься как явно выраженное намерение модифицировать этот аргумент в теле функции: гоЫ я (1.агдеь агя) 1 УУ предполагается, что я() может изменить агя Аналогично, объявляя аргумент функции как указатель на константу, мы декларируем, что значение объекта, на который ссылается этот указатель, не будет модифицироваться в этой функции.
Например: тг лт1еп (сопл( сваг* ); /I количество символов в С-строке слог* мгсру (сваг* го, сопл( слог*)гот) 1 УУ копирование С-строк тг мгстр (сопя( сваг*, сопл( сваг*); У сравнение С-строк Практическая роль аргументов с модификатором сопзг возрастает с ростом размера программы. Следует отметить, что семантика передачи аргументов отличается от семантики присваивания. Это имеет значение лля передачи аргументов с модификатором 199 7.2. Передача аргументов соазг, для передачи аргументов по ссылке и для аргументов некоторых пользовательских типов (в10.4.4.1). Литералы, константы и аргументы, требующие преобразования типов, могут передаваться в функции с сопеть аргументами, и не могут передаваться в функции с не-соизгь аргументами.
Разрешение преобразования для аргументов, объявленных как соим Ть, гарантирует передачу любых значений типа Тчерез временные объекты (в случае необходимости). Например: 1(оаг Гааге (сопзгггоагь); 11 здг( в гог(гап-стиле с аргументом-ссылкой Запрещение преобразований лля не-сопеть аргументов (95.5) устраняет глупые ошибки, порождаемые созданием временных объектов.
Например: юЫ ирйаге ()гааге 1) у еггот константный аргумент У передача ссылки на г У еггог: требуется приведение типа Будь эти вызовы разрешены, ирггаге() молча изменила бы временные переменные, которые тут же были бы удалены. Для программиста это было бы, скорее всего, неприятным сюрпризом. 7.2.1. Массивы в качестве аргументов Если аргумент функции объявлен как массив, то при вызове ей передается у«азатель на первый элемент массива.
Например: Ыг мггеп (сопя( сваг* ) юЫ Г() ( сваг г ( ) = "ап иггау" г 1пг г = вгггеп(г) г 1пг 1' = зо 1еп ( ")у(сйогав ч ) ) Таким образом, аргумент типа Т[1 при передаче преобразуется к типу Т". Отсюда следует, что присваивание значения элементу массива-аргумента означает изменение элемента самого массива (а не копии элемента массива).
Другими словами, массивы отличаются от иных типов тем, что их нельзя передать в функцию по значению, Так как размер массива неизвестен в вызываемой функции, то это могло бы стать неудобством, но есть способы обойти проблему. Для С-строк имеется терми- юЫя(йоиб(е д) ( 11 Г г=Гваг( (2.0) Г гчузагг (г ); к=~ваге (д); ) юЫя(доиЫе и, Яоагг) ( ирдаге (2.
01); ардисе (г); ирдаге (И); ) й' перелача ссылки на временную перем-ю со значением 2.02 У передача ссылки на г д перелача ссылки на временную перем-ю со значением ((оа((гр Глава 7. Функции нальный нуль, и длина строки, тем самым„легко вычисляется. Для других массивов размер можно передавать дополнительным аргументом. Например: чоы сотриге1 (!пг* гес ргг, !п! вес яае); //один способ лиис! )гес ( !иг* ргг~ !п! в!се) )г гоЫ сотри!ег(соим Месь г) // другой способ В качестве альтернативы вместо массивов можно использовать тип гестог (53.7.1, 516.3) из стандартной библиотеки.
С многомерными массивами проблем больше (5С.7), но часто вместо них можно применить массив указателей, который не требует специального обращения. Например: сйаг* дау[) =("толч, чгие", "пей", "Йи", "ггр', чяа!", явипч); Но опять-таки, тип гесгог и другие типы стандартной библиотеки являются пре- красной альтернативой низкоуровневым встроенным типам — массивам и указате- лям. 7.3. Возвращаемое значение Функция должна возвращать значение, если только ее возврат не объявлен как яо!а (функция тат () является исключением — см. 53.2).