Г. Шилдт - Полный справочник по C++ (1109478), страница 27
Текст из файла (страница 27)
Следующая программа лемонсгрирует крайне опасную ошибку. Попробуйте сами найти ее. /* Программа содержит ошибку! */ ()1пс1пбе <вег(по.)з> аьпс1ис)е <всб(о.)з> (пе ша(п(чоЫ) ( с)заг *р1; с)заг в[80)з р1 до ( деев(в)з /* Считываем строку */ /* Вывалим десятичный эквивалент казсзого символа */ ы)з(1е(*р1) рг1псз(" %6", 'р1++); ) ы)з(1е(всгсшр(в, "готово")); гесогп 0; ) В этой программе указатель рз используется для вывода иа печать АЬСП-кодов символов, содержашихся н строке в. Проблема заключается в том, что адрес строки в присваивается указателю р1 только один раз, поэтому при втором проходе цикла он будет содержать алрос ячейки, на которой остановился в прошлый раз — вель алрес начала строки ему не присваивается снова.
В этой ячейке может быть записано все что уголио: символ из второй строки, другая переменная и даже инструкция программы! Исправить программу следует так. Глава 5. Указатели /* Теперь все правильно! */ еупс1ппе авступв.б> оупс1пбе <вес)во.и> Тпс та)п(тс)с() ( сиат р1; с)зпп в(ВО); с(с р1 = в; десв(в); /' считываем строку /* Выводим десятичный вквивалснт каждсго символа *' и)з11е(*р1) рх1псй(" Вс)", *р1а+) „.
) ии11е(встстр(в, "с(опе")); теептп О; ) Теперь в начале каждан итс)юции указатель р1 содержит адрх псрвото символа стрОки в1 Итак, при повторном использовании указателя сто необкодимо ипцизлизировать вновь. Неправильная работа с указателями может привести к тяжслыл( последствиям. Од пако зто нс значит, что от указателей слслуст отказаться. Просто нужно быть анима тельным и следить за тем, побы указатели содержали корректные адреса. 184 Часть ).
Основы языка С++: подмножество С ункции — это строительные блоки для всех программ на языке С и С++. В этой главе мы рассмотрим функции. предусмотренные станлартом языка С, включая темы, связанные с передачей аргументов, возвращением значений, прототипами и рекурсией. Свойства функций, характерные для языка С++, мы рассмотрим во второй части книги. В частности, там булут освещены вопросы, связанныс с перегрузкой функций и псрелачсй параметров по ссылке.
~~| Общий вид функции Общий вид Функции выглялит следующим образом. | тип козе(нляаемопг значение нмл Фтнкз(ии(список пирометров) ( тело фтнкиин ) Тип возвращаемого значения определяет тип переменной, которую возвращает функция. Функция может возвращать переменные любого типа, кролге массива. В списке параметров перечисляются типы аргументов и их имена, разделенные запятыми.
Вели функция не имев~ аргументов, ее список параметров пуст. В этом случае скобки все равно необходимы. В языке С/С++ с помощью одного операзора можно объявлять несколько переменных одинакового типа, разделяя их запятыми. В противоположность этому, каждый параметр функции должен быть объявлен отдельно. Таким образом, общий вид объявления параметров в заголовке функции выглядит так.
)(тип имя переменнайТ, тип имя переменнай2, ..., тип имл переменнои(У) Рассмотрим примеры правильного и неправильного обьявления параметров Функции. т(тпе й, (пс к, зле 1) /* Правильно */ т()пс т, Х, ттоае З) /* Неправильно *г ~"3 Область видимости функции Правила„определяющие область видимости (асора ги1ез), устанавливают, видит ли олна часть программы другую часть кода или данных и может ли к ним обращаться. Каждая функция представляет собои отдельный блок операторов. Код Функции является закрытым и недоступным для любых операторов, расположенных в других функциях, кроме операторов вызова.
(Например, невозможно перейти из однои функции в другую с помощью оператора посо,) Код, образующий тело функции, скрыл от остальной части программы, если он не используе~ глобальные переменные. Этот код не люжст влиять на другие функции, а они, в свою очередь, не могут влиять на него.
Иначе говоря, кол и ланные, определенные внутри некой Функции, никак нс взаимодействуют с кодом и данныгли, опрелсленными в другой функции, поскольку их области видимое~и не перекрываются. Переменные, опредсленныс внутри Функции, называются ланальныл~и (1оса1 чапаЫеэ). Они создаются при входе в функцию и уничтожаются при выходе из нее, Иными словами, локальные переменные не сохраняют свои значения между вызовами функции.
Единственное исключение из этого правила составляют статические локальные перемснпыс. Они хранятся в памяти вместе с глобальными переменными, но Часть 1. Основы языка С++: подмножество С их обласгь щ)димости ограничена функцией, в которой оии определены. (1лобальные и лохальпые перемсипыс подробно описаны в глане 2.) В языке С (и Сь+) невозможно опрсделить одну функцию внутри лругой. По этой причине с формальной точки зрения пи язык С, ии язык С++ нельзя счгггать блочпоструктурироваипыми. Е~ Аргументы функции Если Функция имеет аргументы, н ес заголовке должны быть объявлены переменпыс, принимающие их значения. Эти перемеппые иазынакпся грормальяыми ларамеам рами функции (Гогща! ращгпс(егз). Как и локальные переменные, оии создаются при входе в функцию и уничтожаются при выходе из пес.
Как показано в слелуюшем примере, объявления параметров размещаются после имени фупкции. /* Воэвражает 1, если символ с является гастью строки в, и О, если нет. */ упс зв ъп(сцаг *в, с)гаг с) ( иц11е("в) зт(*в==с) геесегг 1; е1ве в++: гесптп О; Функция 1в 1п() имеет лва параметра: в и с.
Опа возвращает 1, если символ с янлястся частью строки в, и О, если ист. Несмотря па то что эти переменные должиы„в основном, получать зиа ~ения аргументов, псрславасмых функции, их можно испольювщь наравне с другими локальными перслгеиныгяи, в частности, присваивать им значения или использовать в любых выражениях. Передача параметров по значению и по ссылке В языках программировапия суглествук)т два способа передачи аргументов н подпрограмму.
Первый из них известен как передача параметров ло зяачению (сай Ьу ча1ие). В этом случае формальному параметру подпрограммы присваивается копия значения аргумента, и нес изменения параметра никак ие отражаются иа аргументе. Второй способ называется лередачеи ларамеглроо ло ссьыке (саП Ьу геуегепсе), При использовании этого метода параметру присваивается адрес аргумента. Внутри подпрограммы этот алрос открывает лоступ к фактическому аргументу. Зто значит, что все изменения, которым подвергастся параметр, отражаются иа аргументе. По умолчанию в языке С/С+ ь применяется перелача по значению.
Как правило, это означает, что код, образуклдий тело функции, пс может изменять аргументы, указаииые при ее вызове. Рассмотрим следующую программу. $1пс1пс)е <вес)1о.)г> ьпе вг)х(ъпе х); 1пс па1п(чоЫ) ( ьпс Е=1ог ргьпет("НГ) ВГ(", с)х(е), Е): Глава 6. Функции теспхп О; В этом примере значение аргумента функции вове(), те. число 1О копируется в параметр х. После выполнения присваивания х = х х модифицируется только переменная х. Значение переменной е, указанной при вызове функции вою(), по-прежнему равно 10. Помните, что в функцию передастся лишь копия аргумента То, что произойдет с этой копией внутри функции, никак нс отразится на оригинале. Передача параметров по ссылке Хотя в языке С/С++ по умолчанию применяется передача параметров по значению, нх можно передавать и по ссылке.
Для этого в функция> вместо аргумента нужно передать указатель на него. Поскольку функция получает адрес аргумента, ее код может изменять значение фактического аргумента вне функции. Указатели передаются функции как обычные переменные. Естественно, они должны быть объявлены как указа~ели. Рассмотрим в качестве примера функцию вывр(). чозс) виар(5.пе *х, Ьпс *у) ( Ьпс ееюр; Еегпр = вх; *х = *у; Функция виар() может менять местами значения двух переменных„па которые ссылаются указатели х и у, поскольку она получает их адреса (а не копии). Внутри этой функции содержимое переменных можно извлекать, используя обычные операции над указателями.
Помните, что функции виар() (как и любой другой функции, параметрами которой являются указатели) перелаются адреса ареументав. Рассмотрим фрагмент. который демонстрирует правильный вызов функции виар(). чоЫ виар(упе *х, Ыс *у) зпт. паап(чоЫ) ( Ыс з, — 10; — 20; виар(аа, аб); /* Передаются адреса переменных Ь и З */ тегцтп 0; Часть 1. Основы языка С+и подмножество С Ьпс зцт(згс х) ( х =- х*х; гессен(х); ) /* Сохраняем значение, записанное по адресу х */ /* Записываем значение, записанное по адресу у, в ячейку с адресом х */ /* Записываем исходное значение, вани~аннов по адресу х */ На заметку В этом примере псрсмшшой з. присваивастсн значение )О, а переменной з — чис:- ло 20.
Затем Функции вчгар() псрсдаютсл адреса переменных 1 и з, а нс их значения. (Лля этого использустсл унарныи оператор пол) мнил алроса а.) После возврата управления из функции амар() псрсмснныс х и у оказываются (зсрсставлснными. «~у~уду~щ Язьск Счч ппзеоппет автомвпвчески передавать параметры по осыпке, испппь)МЙЙЙЙЙЙИ зуя моканизм ссылок, а не указатели. Это сеойсп>зо описываегпсл ео второй части справочнике Передача массивов в качесГве параметров «1пс1оде <второ.Ь> Е1пс1обе <стуре.)г> уоЫ рхЫт оррет(снах "в тЫд) тггс аазп(уозс() сиат в[ВО]; оесв(в)с рххпс оррет(в); рхзптс('*1пбгрока нэ прописных букв: Ъв', в)с хесохп Ог ) /" Выводит строку, состокыуп иэ поогнснь:х букв.
*/ уоЫ рт1пх оррет (с)сах квттЫО) тепзвтех зпс тох(с=о; всх1по(х]; гч.с) ( чбх1гр(С] =- Сооррех(втт(пс(х]) россусах(встхпо(с])с ) ) После вызова функции рт1пе оррех.() буквы, храняшисся в массиве в, заменяются прописными. Если вы нс хотите изменять содержимое массива в, перепишите программу следуюшим образом. Езпс1обе <втс(то.)1> Е1пс1ос)е <стуре.и> уоЫ рххпс пррех(сгсат *встЫр) Глава 6.
функции Массивы подробно описаны в главе 4. С)дсшко в этом разделс мы рассмотрим передачу массивов в качестве параметров функции', поскольку они представляют исклю° атис из обы шага правила, в соответствии с которым в языке С/Сч-ь всс параметры должны передаваться по значсншо. Если аргулсеитом функции является массив, си передастся сто адрес. Зтз ситуация прсдставлвет собой искл(очснис из обшспршипого правила передачи параметров по значсншо. Функция, получившая массив, получает доступ ко всем сто элементам и может его модифициров;пгп Рассмотрим в качестве примера функцию рт1ое пррех(), предназначсш(ую для преобразовании строчных букв в прописные (с кириллицей программа нс работает — г7/гим.
Ред.). зпт шаьг.(уоЫ) ( сЬаг я[80); дете(я)г ртьпт пррет(в)> рт1птг(">,пСтрока не изменилась: яя", я)> гесптп О> ) уоЫ ргьпт пррег(сЬаг *вттгпд) ( тедзятет гп Кот(г=ог вгтьпд[т); +ег) рцтснаг(топррет(втг1пд[т))); ) В этой версии программы содержимое массива в остается постоянным, поскольку его значение в функции рт1пе баррет () не изменяется.