/* Prim6_4.cpp */
char* my_strcpy( char* destination, char* source)
{char* ret_ptr = destination;
while( *source)
*destination ++= *source++;
*destination = ‘\0’; /* перенос символа ‘\0’ */
return ret_ptr;
}
Если программисту привычнее манипулировать с элементами массива, предыдущую программу можно записать так:
/* Prim6_5.cpp */
char* my_strcpy( char destination[], char source[])
{char* ret_ptr = destination;
while( *source)
*destination ++= *source++;
*destination = ‘\0’; /* перенос символа ‘\0’ */
return ret_ptr;
}
Первую строку функции my_strcpy() можно записать и так:
char* my_strcpy( char* destination, char* source)
а для доступа к элементам по-прежнему использовать индекс.
Язык программирования С++ допускает использование переменного числа аргументов. Признаком функции с переменным числом аргументов является многоточие … в списке параметров прототипа функции. Встретив (…), компилятор прекращает контроль соответствия типов параметров для такой функции. Функция с переменным числом параметров должна имет способ определения точного их числа при каждом вызове.
Ниже приводится пример функции example, которая принимает переменное число аргментов и выводит на печать их количество и принятые значения. Число действительно переданных через стек значений задает первый аргумент:
/* Prim6_6.cpp */
#include <stdio. h>
void example( int, …);
void main( void)
{int var1 = 5, var2 = 6, var3 = 7, var4 = 8, var5 = 9;
example( 2, var1); /* вызов функции с двумя аргументами */
example( 3, var1, var2); /* та же функция имеет три аргумента */
example( 6, var1, var2, var3, var4, var5); /* а теперь – шесть */
}
void example( int arg1, …)
{int* ptr = &arg1; /* установка указателя на первый аргумент */
printf("\n Функции EXAMPLE передано всего %d аргумента(ов):\n", arg1); /* печать всех переданных в функцию фактических аргументов */
for( ; arg1; arg1--) {printf("%d", *ptr); ptr++;}
Запуск программы на выполнение дает такие результаты:
Функции EXAMPLE передано всего 2 аргумента(ов):
2 5
Функции EXAMPLE передано всего 3 аргумента(ов):
3 5 6
Функции EXAMPLE передано всего 6 аргумента(ов):
В данном примере первый аргумент используется, как счетчик количества аргументов.
6.7. Модификаторы cdecl и pascal
Обратите внимание на то, что для доступа к фактическим аргументам в функции example() используется продвижение указателя вперед. Именно в таком порядке расположатся в стеке копии аргументов при так называемом Си-порядке передачи аргументов в функцию: последний аргумент записывается в стек самым первым, затем в стек помещается предпоследний аргумент и так далее. Запись в стек выполняется по адресу, задаваемому парой регистров SS:SP. Но так как при выполнении команды PUSH содержимое регистра SP физически уменьшается, то в оперативной памяти копия первого аргумента в списке параметров расположена по меньшему адресу памяти, следующий аргумент – по большему адресу и т. д.
На Си-порядок передачи параметров указывает модификатор типа функции cdecl. Он устанавливается специальным параметром IDE (Main Menu – Options – Compile – Entry/Exit Code в Borland C++) или задается опцией командной строки(-p-). По умолчанию выбирается cdecl. Альтернативой С++-порядку передачи параметров является последовательность передачи параметров, принятая в языке Pascal: копии аргументов записываются в стек, начиная с первого. Как результат, копия первого аргумента имеет в оперативной памяти больший адрес, чем копия последнего. На такой порядок передачи параметров указывает модификатор типа функции pascal. Явное задание порядка передачи отменяет порядок передачи, принимаемый компилятором по умолчанию.
Модификаторы типа функции должны совпадать как в прототипе, так и в определении функции. В языке Pascal не поддерживается переменное число аргументов и функция с модификатором pascal должна иметь фиксированное число аргументов. Модификатор типа функции pascal требуется при разработке С++-функций, которые будут вызываться из пределов написанных на языке Pascal программ.
Функции с переменным числом аргументов должны иметь как минимум один фиксированный аргумент для того, чтобы была возможность «привязаться» к адресу копии этого аргумента в стеке.
6.8. Передача параметров функции по ссылке
В С++ появилась возможность передачи в функцию аргументов по ссылке. Это может быть использовано в двух случаях:
1) для передачи в функцию больших структур, чтобы избежать копирования аргументов в стек;
2) для передачи в функцию аргумента, который должен быть изменен функцией.
Правда, для обоих этих случаев можно воспользоваться указателем и передавать в функцию либо указатель на структуру (в первом случае), либо указатель на модифицируемый аргумент (второй случай). Но использование указателя влечет за собой дополнительные неудобства:
1) в функции для данного параметра надо выполнять операцию разадресации;
2) при вызове функции необходимо передавать адрес аргумента, а не сам аргумент.
Все эти неудобства устраняются использованием ссылки в качестве параметра.
Например, функция, меняющая местами два целых числа, в Си имеет вид:
void swap( int* a, int* b)
{
int temp = *a; // Требуются операции
*a = *b; // разадресации
*b = temp;
}
Вызов функции:
int x = 5, y = 17;
swap( &x, &y); // необходимо передавать адрес
Используя ссылки, эту функцию можно переписать в виде:
void swap( int &a, int &b)
{
int temp = a; // знак операции * не требуется
a = b;
b = temp;
}
main()
{
void swap( int&, int&);
int x = 5, y = 17;
swap( x, y); // знак операции & не требуется
При использовании ссылок в качестве передаваемых параметров есть два существенных недостатка.
Первый недостаток заключается в том, что фактический аргумент, переданный в функцию по ссылке, может быть изменен функцией без ведома вызывающей программы.
Второй недостаток передачи параметра в функцию по ссылке проявляется, если при вызове функции фактический аргумент не соответствует типу формального параметра. В этом случае С++ будет выполнять преобразование типа. Но для ссылок преобразование типа выполняется через создание промежуточной переменной. При выполнении операций с промежуточной переменной могут возникнуть трудно уловимые ошибки. Поэтому надо быть очень осторожным в использовании ссылок в функциях и без особой нужды их не применять.
6.9. Перегрузка функций
Совершенно новым средством языка С++ является возможность определять в программе несколько функций с одним и тем же именем (в Си все имена функций должны быть уникальны). Такая возможность очень полезна, когда в программе одни и те же операции должны быть выполнены над данными различных типов.
В С++ допускается определение нескольких функций с одинаковым именем при условии, что их сигнатуры различаются. Сигнатурой для функции являются количество и типы принимаемых параметров. Значит, перегружаемые функции должны отличатся либо по количеству, либо по типам параметров.
void swap( int&, int&);
void swap( double&, double&);
void swap(char&, char&);
Здесь объявлены три экземпляра перегружаемой функции, которые будут выполнять одинаковые действия, а именно менять местами, значения двух переменных, но каждая функция работает с данными разных типов. Далее в программе определены все три функции, например, так:
void swap( int&, int&)
{
int temp = a;
a = b;
b = temp;
}
void swap( double&, double&)
{
double temp = a;
a = b;
b = temp;
}
void swap(char&, char&)
{
char temp = a;
a = b;
b = temp;
}
Теперь для пользователя достаточно знать, что есть функция swap, которая может менять местами два данных, либо целых, либо символьных, либо плавающих двойной точности.
Компилятор при вызове функции по типу и количеству фактических аргументов определит, вызов какой функции нужно встраивать.
6.10. Указатель на функцию. Модификаторы near, far, huge
В гл. 4.3 рассмотрены указатели на данные и способы доступа к данным через указатели. Синтаксис языка С++ позволяет применять их и для вызова функций, используя указатели особого рода – указатели на функцию. Имя функции в Си – это указатель-константа на функцию, равный адресу точки входа (адресу первой машинной команды) функции. Помимо констант, возможно описание и указателей-переменных на функции:
return_type(*name)(arg_list);
где return_type – тип возвращаемого функцией значения; name – имя переменной-указателя на функцию; arg_list – необязательный список типов аргументов, передаваемых функции при ее вызове по значению указателя.
Указатели на функцию используются в случаях, перечисленных ниже.
1. Многие библиотечные функции в качестве аргумента получают указатель на функцию. Например, функция сортировки qsort() получает четвертым аргументом указатель на составленную пользователем функцию, выполняющую сравнение сортируемых элементов. при этом библиотечные функции задают типы для возвращаемого значения аргументов.
2. Использование указателей на функцию в качестве аргументов позволяет разрабатывать универсальные функции, например функции численного решения уравнений, численного интегрирования и дифференцирования.
3. Указатели на функцию могут использоваться для косвенного вызова резидентных программ, точка входа в которые записывается в известное место в оперативной памяти, например на место неиспользуемого вектора таблицы векторов прерывания.
В качестве примера приведена программа, которая для доступа к функциям difference() и sum() использует указатель на функцию:
/* Prim6_7.cpp */
#include <stdio. h>
int difference( int, int); /* прототип */
int sum( int, int); // прототип
void main(void)
{
int(*func_ptr)(int, int);
int var1 = 20, var2 = 5, ret;
func_ptr = difference;
ret = func_ptr( var1, var2);
printf("ret = %d\n", ret);
func_ptr = sum;
ret = func_ptr( var1, var2);
printf("ret = %d\n", ret);
}
int difference(int a, int b)
{ return (a – b); }
int sum(int a, int b)
{ return (a + b); }
Как и обычные переменные, указатели на функции могут объединяться в массивы. Как и обычные указатели на данные, указатели на функцию могут иметь тип near, far или huge. Указатель типа near занимает в памяти два байта, указатели far и huge – четыре байта. Тип указателя на функцию, устанавливаемый по умолчанию, зависит от модели памяти.
6.11. Стандартные математические функции
В данной таблице перечислены библиотечные функции для выполнения элементарных математических операций и управления реакцией на ошибку при вычислениях; их прототипы описаны в заголовочном файле <math. h>, а для некоторых из рассматриваемых функций - в файле<stdlib. h>.
Сделаем несколько общих замечаний по использованию перечисленных функций. Для тех из них, которые являются макро, включение в программу заголовочного файла обязательно. Для остальных функций отсутствие заголовочного файла вызывает предупреждение типа “Отсутствие прототипа для функции...”. Те функции, которые получают и возвращают значения типа double, будут работать корректно и для данных типа float. Попытки использовать функции с данными типа long double связаны с некорректным преобразованием типов и приводят к ошибкам.
Таблица 10
Таблица стандартных математических функций
Имя функции | Выполняемое действие |
Abs | Возвращает абсолютное значение типа int |
Acos | Вычисляет арккосинус аргумента, лежащего в диапазоне от -1 до 1, и возвращает угол в радианах от 0 до p |
Asin | Вычисляет арксинус аргумента, лежащего в диапазоне от -1 до 1, и возвращает угол в радианах от - p/2 до p/2 |
Atan | Вычисляет арктангенс аргумента и возвращает угол в радианах от - p/2 до p/2 |
atan2 | Вычисляет арктангенс отношения первого аргумента ко второму и возвращает угол в радианах от - p до p |
Cabs | Вычисляет модуль комплексного числа |
Ceil | Отыскивает ближайшее целое число, не меньшее аргумента типа double (округление в большую сторону) |
cos | Возвращает косинус угла, заданного в радианах |
cosh | Возвращает гиперболический косинус аргумента |
div | Делит одно целое число на другое и возвращает частное и остаток от деления |
exp | Вычисляет”е ” в степени аргумент, где аргумент имеет тип double |
fabs | Возвращает абсолютное значение аргумента типа double |
floor | Отыскивает ближайшее целое число, не большее аргумента типа double(округление в меньшую сторону) |
fmod | Вычисляет остаток от деления первого аргумента типа double на второй аргумент типа double при условии, что частное-наибольшее целое возможное число |
frexp | Возвращает нормализованную мантиссу и несдвинутый порядок числа типа double |
hypot | Возвращает длину гипотенузы прямоугольного треугольника |
labs | Возвращает абсолютное значение аргумента типа long int |
idexp | Возвращает число типа double, сформированное по заданным мантиссе и несдвинутому порядку числа |
idiv | Делит одно целое число на другое и возвращает частное и остаток от деления |
log | Вычисляет натуральный логарифм аргумента типа double |
log10 | Вычисляет десятичный логарифм аргумента типа double |
lrotl | Выполняет циклический сдвиг влево числа типа long unsigned на заданное число битов |
_lrotr | Выполняет циклический сдвиг вправо числа типа long unsigned на заданное число битов |
_matherr | Внутренний обработчик прерываний, вызываемый другими математическими функциями файла math. h при возникновении исклячительных ситуаций математики с плавающей точкой |
matherr | “Заглушка” для подключения собственного обработчика ошибок и исключительных ситуаций математики с плавающей точкой, который будет вызываться другими математическими функциями файла math. h при возникновении ошибок и исключительных ситуаций |
max | Возвращает максимальный из двух аргументов типа int |
min | Возвращает минимальный из двух аргументов типа int |
modf | Выделяет целую и дробную части числа типа double |
poly | Вычисляет для заданного аргумента, показателя степени и коэффициентов значение полинома |
pow | Вычисляет значение аргумент1 в степени аргумент2 |
pow10 | Вычисляет значение 10 в степени аргумент |
rand | Возвращает случайное целое число в диапазоне от 0 до константы RAND_MAX |
random | Возвращает случайное целое число в диапазоне от 0 до заданного аргументом значения |
randomise | Инициализирует генератор случайных чисел, используя текущее время, сообщаемое компьютером |
_rotl | Выполняет циклический сдвиг влево числа типа unsigned на заданное число битов |
_rotr | Выполняет циклический сдвиг вправо числа типа unsigned на заданное число битов |
sin | Вычисляет синус аргумента, заданного в радианах |
sinh | Вычисляет гиперболический синус аргумента |
sqrt | Вычисляет квадратный корень положительного аргумента типа double |
srand | Устанавливает начальное число для генерируемой последоваиельности случайных чисел |
tan | Вычисляет тангенс аргумента, заданного в радианах |
tanh | Вычисляет гиперболический тангенс аргумента |
6.12. Работа со строками символов
Borland С++ содержит богатую коллекцию функций для работы со строками символов.
Сделаем несколько общих замечаний относительно использования функций библиотек. Прототипы функций содержатся в заголовочном файле <string. h>. Функции работают с ASCIIZ-строками. Если в строке отсутствует нуль - терминатор, обработка строки может подолжаться сколь угодно долго. Функции в качестве аргументов, как правило, передаются указатели на строки. Если при выполнении функции выполняется перенос символов строки из места-источника в место-назначение, для строки в месте назначения должно предварительно резервироваться место в памяти. Копирование строк с использованием просто указателя, а не адреса начала предварительного описания массива - одна из самых распространенных ошибок программирования. Поэтому, работая со строками, будьте осторожны и обязательно обращайте внимание на предупреждения системы программирования типа “Подозрительное преобразование указателя” (suspicious pointer conversion) или “Использование указателя до инициализации” (Possible use of... before definition). При выделении места для строки-назначения следует выделить место и для нуль-терминатора.
Ниже в таблице перечислены функции Borland С++ для работы со строками символов, а далее приведены несколько их спецификаций.
Таблица 11
Таблица функций для работы со строками символов
Имя функции | Выполняемое действие |
stpcpy | Копирует строку 2 в строку 1 |
strcat | Присоединяет строку 2 в конец строки 1 |
strchr | Возвращает позицию первого вхождения символа в строку |
strcmp | Сравнивает строку 1 со строкой 2, различая прописные и строчные буквы |
strcmpi | Идентична strcmp |
strcpy | Копирует строку 2 в строку 1 |
strcspn | Возвращает позицию первого вхождения символа из заданного набора символов |
strdup | Выделяет память и делает копию строки |
strerror | Возвращает по заданному номеру системной ошибки указатель на строку текста сообщения об ошибке |
_strerror | Возвращает указатель на строку, образованную объединением произвольной строкии сообщения об ошибке в библиотечной функции |
stricmp | Сравнивает строку 1 со строкой 2, не различая прописные и строчные буквы |
strlen | возврвщает длину строки в байтах, не учитывая нуль-терминатор |
strlwr | Преобразует все символы строки в строчные буквы |
strncat | Присоединяет заданное число символов строки 2 в конец строки 1 |
strncmp | Сравнивает заданное число символов двух строк, различая прописные и строчные буквы |
strncmpi | Эквивалентна strnicmp |
strncpy | Копирует заданное число символов строки 2 в строку 1 |
strnicmp | Сравнивает заданное число символов двух строк, не различая прописные и строчные буквы |
strnset | Помещает заданный символ в заданное число позиций строки |
strpbrk | Отыскивает место первого вхождения любого символа из заданного набора |
strrchr | Отыскивает последнее вхождение любого символа в строке |
strrev | Реверсирует строку (создает прочитанную с последнего символа строку) |
strset | Помещает символ во все позиции строки |
strspn | Возвращвет позицию в строке первого символа, который не принадлежит заданному набору символов |
strstr | Отыскивает место первого вхождения строки 2 в строку 1 |
strtok | Возвращает указатель на лексему, ограниченную заданным разделителем |
strupr | Преобразует все буквы строки в прописные буквы |
7. Классы хранения и видимость переменных
7.1. Общие положения
Для каждого идентификатора существует как минимум два атрибута: тип и класс хранения. Тип определяет размер памяти, выделяемой компилятором для объекта и способ интерпретации выделенной памяти (например, как адрес памяти для указателей или массив однотипных элементов). Класс хранения определяет место, где объект располагается (внутренние регистры процессора, сегмент данных, сегмент стека), и одновременно время жизни объекта (например, все время выполнения программы или только время выполнения конкретной части кода программы). Класс хранения либо задается явно, либо компилятор «сам» определяет это по местоположению описания в тексте программы.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 |


