4.4.6. Об"явление указателей
Синтаксис:
<type-specifier> *<declarator>;
Об"явление указателя определяет имя переменной типа указатель и тип об"екта, на который указывает эта переменная. Декларатор- <declarator> определяет имя переменной с возможной модификацией ее типа. Спецификатор типа- <type- specifier> задает тип об"екта, который может быть базового типа, типа структуры или совмещения.
Переменная типа указатель может указывать также на функции, массивы и другие указатели. Более полная информация о типах указателей дана в разделе 4.3.2. "Составные деклараторы".
Если указатель не используется до определения типа структуры или совмещения, то он может быть об"явлен ранее этого определения. Такие об"явления допускаются, поскольку компилятору не требуется знать размера структуры или совмещения, чтобы распределить память под переменную типа указатель. Указатель может быть об"явлен посредством использования тега структуры или совмещения (смотри ниже пример 4).
Переменная, об"явленная как указатель, хранит адрес памяти. Размер памяти, требуемый для адреса, и смысл адреса зависит от данной конфигурации машины. Указатели на различные типы не обязательно имеют одну и ту же длину.
Для некоторых реализаций используются специальные ключевые слова near, far и huge, чтобы модифицировать размер указателя. Об"явления, использующие специальные ключевые слова, были описаны в разделе 4.3.3. Информация о смысле ключевых слов дана в системной документации.
Примеры:
char *message; /* Example 1 */
int *pointers[10]; /* Example 2 */ int (*pointer)[10]; /* Example 3 */ struct list *next, *previous; /* Example 4 */
struct list { /* Example 5 */ char *token;
int count;
struct list *next;
} line;
struct id { /* Example 6 */ unsigned int id_no;
struct name *pname;
} record;
В первом примере об"является переменная- указатель поименованная message. Она указывает на величину типа char.
Во втором примере об"явлен массив указателей, поименованный pointers. Массив состоит из 10 элементов. Каждый элемент- это указатель на переменную типа int.
В третьем примере об"явлена переменная- указатель, поименованная pointer. Она указывает на массив из 10 элементов. Каждый элемент в этом массиве имеет тип int.
В четвертом примере об"явлены две переменныхуказателя, которые ссылаются на величины структурного типа list (смотри следующий пример). Определение типа с именем list должно находиться в пределах видимости об"явления.
В пятом примере об"является переменная с именем line, структурного типа, поименованного list. Тип структуры с именем list определяется тремя элементами. Первый элементэто указатель на величину типа char, второй- на величину типа int, а третийэто указатель на следующую структуру типа list.
В шестом примере об"является переменная с именем record, имеющая тип структуры с именем id. Заметим, что третий элемент с именем pname об"явлен как указатель на другой тип структуры с именем name. Это об"явление может появиться перед об"явление структуры с именем name.
Об"явление функцийСинтаксис:
[<type-specifier>]<declarator>([<arg-type-list>])[,<declarator>...];
Об"явление функции определяет имя, тип возврата функции и, возможно, типы и число ее аргументов. Об"явление функции также называется forward- об"явлением. Декларатор функции об"являет имя функции, а спецификатор типа задает тип возврата. Если спецификатор типа опущен в об"явлении функции, то предполагается, что функция возвращает величину типа int.
Об"явление функции может включать спецификаторы класса памяти extern или static.
Список типов аргументов.
Список типов аргументов- <arg-type-list> определяет число и типы аргументов функции. Синтаксис списка аргументов следующий:
<type-name-list>[,...]
Список имен типов- это список из одного или более имен типов. Каждое имя типа отделяется от другого запятой. Первое имя типа задает тип первого аргумента, второе имя типа задает тип второго аргумента и т. д. Если список имен типов заканчивается запятой с многоточием (,...), то это означает, что число аргументов функции переменно. Однако, предполагается, что функция будет иметь не меньше аргументов, чем имен типов, предшествующих многоточию.
Если список типов аргументов- <arg-type-list> содержит
только многоточие (...), то число аргументов функции является пе-
ременным или равно нулю.
Замечание:
Чтобы поддержать совместимость с программами предидущих версий, компилятор допускает символ запятой без многоточия в конце списка типов аргументов для обозначения их переменного числа. Запятая может быть использована и вместо многоточия для об"явления нуля или более аргументов функции. Использование запятой поддерживается только для совместимости. Использование многоточия рекомендуется для нового представления.
Имя типа- <type- name> для типов структуры, совмещения или базового типа состоит из спецификатора этого типа (такого как int ). Имена типов для указателей, массивов и функций формируются путем комбинации спецификатора типа с "абстрактным декларатором". Абстрактный декларатор- это декларатор без идентификатора. В разделе 4.9 "Имена типов" об"ясняется, каким об"разом формировать и интерпретировать абстрактные деклараторы.
Для того чтобы об"явить функцию, не имеющую аргументов, может быть использовано специальное ключевое слово void на месте списка типов аргументов. Компилятор вырабатывает предупреждающее сообщение, если в вызове такой функции будут специфицированы аргументы.
Еще одна специальная конструкция допускается в списке типов аргументов. Это фраза void *, которая специфицирует аргумент типа указатель. Эта фраза может быть использована в списке типов аргументов вместо имени типа.
Список типов аргументов может быть опущен. В зтом случае скобки после идентификатора функции все же требуются, хотя они и пусты. В этом случае в об"явлении функции не определяются ни типы, ни число аргументов в функции. Когда эта информация опускает-
ся, то компилятор не проверяет соответствия между формальными и фактическими параметрами при вызове функции. Более подробная информация дана в разделе 7.4 "Вызовы функций".
Тип возврата
Функции могут возвращать величины любого типа за исключением массивов и функций. Для этого посредством спецификатора типа"type-specifier" в об"явлении функции можно специфицировать любой тип: основной, структуру или совмещение. Идентификатор функции может быть модифицирован одной или несколькими звездочками (*), чтобы об"явить возвращаемую величину типа указателя.
Хотя функции и не допускают возвратов массивов или функций, но они могут возвращать указатели на массивы или функции. Функции, которые возвращают указатели на величины типа массив или функция, об"являются посредством модификации идентификатора функции квадратными скобками, звездочкой и круглыми скобками, чтобы сформировать составной декларатор. Формирование и интерпретация составных деклараторов рассматривались в разделе 4.3.2.
Примеры:
int add(int, int); /* Example 1 */
double calc(); /* Example 2 */
char *strfind(char *,...); /* Example 3 */
void draf(void); /* Example 4 */
double (*sum(double, double)) [3]; /* Example 5 */ int (*select(void)) (int) ; /* Example 6 */
char *p; /* Example 7 */
short *q;
int prt(void *);
В первом примере об"является функция, поименованная add, которая требует два аргумента типа int и возвращает величину типа int.
Во втором примере об"является функция, поименованная calc, которая возвращает величину типа double. Список типов аргументов не задан. В третьем примере об"является функция, поименованная strfind, которая возвращает указатель на величину типа char. Функция требует, по крайней мере один аргументуказатель на величину типа char. Список типов аргументов заканчивается запятой с многоточием, обозначающим, что функция может потребовать большее число аргументов.
В четвертом примере об"является функция с типом возврата void (нет возвращаемой величины). Список типов аргументов также void, означающий отсутствие аргументов для этой функции.
В пятом примере sum об"является как функция, возвращающая указатель на массив из трех величин типа double. Функция sum требует два аргумента, каждый из которых является величиной типа double.
В шестом примере функция, поименованная select, об"явлена без аргументов и возвращает указатель на функцию. Указатель возврата ссылается на функцию, требующую один аргумент типа int и возвращающую величину типа int.
В седьмом примере об"явлена функция prt, которая требует аргумент- указатель любого типа, и которая возвращает величину типа int. Любой указатель p или q могли бы быть использованы как аргументы функции без выдачи при этом предупреждающего сообщения.
Классы памятиКласс памяти переменной, которая определяет какой либо об"ект, имеет глобальное или локальное время жизни. Об"ект с глобальным временем жизни существует и имеет значение на протяжении всей программы. Все функции имеют глобальное время жизни.
Переменные с локальным временем жизни захватывают новую па-
мять при каждом выполнении блока, в котором они определены. Когда управление на выполнение передается из блока, то переменная теря-
ет свое значение.
Хотя Си определяет два типа классов памяти, но, тем не менее, имеется следующих четыре спецификатора классов памяти:
auto
register
static
extern
Об"екты классов auto и register имеют локальное время жизни. Спецификаторы static и extern определяют об"екты с глобальным временем жизни. Каждый из спецификаторов класса памяти имеет определенный смысл, который влияет на видимость функций и переменных в той же мере, как и сами классы памяти. Термин "видимость" относится к той части программы, в которой могут ссылаться друг на друга функции и переменные. Об"екты с глобальным временем жизни существуют на протяжении выполнения исходной программы, но они могут быть видимы не во всех частях программы. Видимость и связанная с ней концепция времени жизни рассмотрена в разделе 3.5.
Месторасположение об"явления переменной или функции внутри
исходных файлов также влияют на класс памяти и видимость. Говорят, что об"явления вне определения всех функций и переменных относятся к внешнему уровню, а об"явления внутри определений функций относятся к внутреннему уровню.
Точный смысл каждого спецификатора класса памяти зависит от того, находится ли об"явление на внешнем или внутреннем уровне и от того, об"явлен ли об"ект функцией или переменной. В следующем разделе описывается смысл спецификаторов класса памяти в каждом случае об"явления, а также об"ясняется режим умолчания, когда спецификатор класса памяти опущен при об"явлении переменной или функции.
Об"явления переменной на внешнем уровнеОб"явления переменной на внешнем уровне используют спецификаторы класса памяти static и extern или совсем опускают их. Спецификаторы класса памяти auto и register не допускаются на внешнем уровне.
Об"явления переменных на внешнем уровне- это определения переменных или ссылки на определения, сделанные в другом месте.
Об"явление внешней переменной, которое инициализирует эту
переменную (явно или неявно), называется определением этой переменной. Определение на внешнем уровне может задаваться в следующих различных формах:
-переменная на внешнем уровне может быть определена путем ее об"явления со спецификатором класса памяти static. Такая переменная может быть явно инициализирована константным выражением. Если инициализатор отсутствует, то переменная автоматически инициализируется нулем во время компиляции. Таким образом, об"явления static int k = 16; и static int k; оба рассматриваются как определения;
-переменная определяется, когда она явно инициализируется
на внешнем уровне. Например, int j = 3; это определение переменной.
Так как переменная определяется на внешнем уровне, то она видима в пределах остатка исходного файла, от места, где она определена. Переменная не видима выше своего определения в том же самом исходном файле ни в других исходных файлах программы, если не об"явлена ссылка, которая делает ее видимой.
Переменная может быть определена на внешнем уровне внутри исходного файла только один раз. Если задается спецификатор класса памяти static, то в других исходных файлах могут быть определены переменные с тем же именем. Так как каждое определение static видимо только в пределах своего собственного исходного файла, то конфликта не возникнет.
Спецификатор класса памяти extern используется для об"явления ссылки на переменную, определенную где-то в другом месте. Такие об"явления используются в случае, когда нужно сделать видимым определение переменной в других исходных файлах или выше места, где она определена в том же самом исходном файле. Так как ссылка на переменную об"явлена на внешнем уровне, то переменная видима в пределах остатка исходного файла от места об"явления ссылки.
В об"явлениях, которые используют спецификатор класса памяти extern, инициализация не допускается, так как они ссылаются на переменные, чьи величины уже определены.
Переменная, на которую делается ссылка extern, должна быть определена на внешнем уровне только один раз. Определение может быть сделано в любом из исходных файлов, составляющих программу.
Есть одно исключение из правил, описанных выше. Можно опус-
тить из об"явления переменной на внешнем уровне спецификатор класса памяти и инициализатор. Например, об"явление int n; будет правильным внешним об"явлением. Это об"явление имеет два различных смысла в зависимости от контекста.
1. Если где-нибудь в программе будет определена на внешнем уровне переменная с тем же именем, то об"явление является ссылкой на эту переменную, как если бы был использован спецификатор класса памяти extern в об"явлении.
2. Если нет такого определения, то об"явленной переменной распределяется память во время линкования и переменная инициализируется нулем. Если в программе появится более чем одно такое об"явление, то память распределится для наибольшего размера из об"явленных переменных. Например, если программа содержит два неинициализированных об"явления переменной i на внешнем уровне int i; и char i; то память во время линкования распределится под переменную i типа int.
Неинициализированные об"явления переменной на внешнем уровне не рекомендуются для файлов, которые могут быть размещены в библиотеку.
Пример:
/*****************************************************
SOURCE FILE ONE *****************************************************/
extern int i; /* reference to i
defined below */
main()
{
i++;
printf("%d\n", i); /* i equals 4 */
next();
}
int i = 3; /* definition of i */
next()
{
i++;
printf("%d\n", i); /* i equals 5 */
other();
}
/***************************************************** SOURCE FILE TWO
*****************************************************/
extern int i; /* reference to i in
first source file */
other()
{
i++;
printf("%d\n", i); /* i equals 6 */
}
Два исходных файла в совокупности содержат три внешних об"явления i. Одно об"явление содержит инициализацию- int i = 3; , где глобальная переменная i определена с начальным значением равным 3.
Самое первое об"явление extern в первом файле делает глобальную переменную видимой выше ее определения в файле.
Без об"явления extern функция main не смогла бы сослаться на глобальную переменную i. Об"явление extern переменной i во втором исходном файле делает глобальную переменную видимой в этом исходном файле.
Все три функции выполняют одну и ту же задачу: они увеличивают i на 1 и печатают получившееся значение. (Предполагается, что функция printf определена где-то еще в программе.). Печатаются величины равные 4, 5 и 6.
Если бы переменная i не была бы инициализирована,она бы была автоматически установлена в 0 при линковании. В этом случае напечатанные значения были бы равны 1, 2 и 3.
Об"явление переменной на внутреннем уровнеЛюбой из четырех спецификаторов класса памяти может быть использован для об"явления переменной на внутреннем уровне. Если спецификатор класса памяти опускается в об"явлении переменной на внутреннем уровне, то подразумевается класс памяти auto.
Спецификатор класса памяти auto об"являет переменную с ло-
кальным временем жизни. Переменная видима только в том блоке, где она об"явлена. Об"явления переменных auto могут включать инициализаторы. Переменные класса памяти auto автоматически не инициализируются, а инициализируются явно при об"явлении или присваивании начальных значений, посредством операторов внутри блока. Если нет инициализации, то величина переменной auto считается неопределенной.
Спецификатор класса памяти register сообщает компилятору о том, чтобы он распределил память под переменную в регистре, если это возможно. Использование регистровой памяти обычно приводит к более быстрому времени доступа и к меньшему размеру результирующего кода. Переменные, об"явленные с классом памяти register име-
ют ту же самую видимость, что и переменные auto.
Число регистров, которое может быть использовано под память переменных, зависит от машины. Когда компилятор встречает спецификатор класса памяти register в об"явлении, а свободного регистра не имеется, то для переменной распределяется память класса auto. Компилятор назначает переменным регистровую память в том порядке, в котором появляются об"явления в исходном файле. Регистровая память, если она имеется, гарантирована только для целого и адресного типов.
Переменная, об"явленная на внутреннем уровне со спецификатором класса памяти static,имеет глобальное время жизни и имеет видимость только внутри блока, в котором она об"явлена. В отличие от переменных auto, переменные, об"явленные как static, сохраняют свое значение при завершении блока.
Переменные класса памяти static могут быть инициализированы константным выражением. Если явной инициализации нет, то переменная класса памяти static автоматически устанавливается в 0. Инициализация выполняется один раз во время компиляции. Инициализация переменной класса памяти static не повторяется при новом входе в блок.
Переменная, об"явленная со спецификатором класса памяти extern, является ссылкой на переменную с тем же самым именем, определенную на внешнем уровне в любом исходном файле программы.
Цель внутреннего об"явления extern состоит в том, чтобы
сделать определение переменной внешнего уровня видимой внутри блока. Внутреннее об'явление extern не изменяет видимость глобальной переменной в любой другой части программы.
Пример:
int i = 1;
main()
{ /* reference to i, defined above */
extern int i;
/* initial value is zero; a is
visible only within main */
static int a;
/* b is stored in a register, if possible */ register int b = 0;
/* default storage class is auto */
int c = 0;
/* values printed are 1, 0, 0, 0 */ printf("%d\n%d\n%d\n%d\n", i, a, b, c);
other();
}
other()
{
/* i is redefined */
int i = 16;
/* this a is visible only within other */
static int a = 2;
a += 2;
/* values printed are 16, 4 */
printf("%d\n%d\n", i, a);
}
Переменная i определяется на внешнем уровне с инициализацией 1. В функции main об"явлена ссылка extern на переменную i внешнего уровня. Переменная класса памяти static автоматически
устанавливается в 0, так как инициализатор опущен. Вызов функции print (предполагается, что функция print определена в каком-то месте исходной программы.) печатает величины 1, 0, 0, 0.
В функции other, переменная i переопределяется как локальная переменная с начальным значением 16. Это не влияет на значение внешней переменной i. Переменная a об"является как переменная класса памяти static с начальным значением 2. Она не противоречит переменной a, об"явленной в функции main, так как видимость переменных класса памяти static на внутреннем уровне ограничена блоком, в котором она об"явлена.
Значение переменной увеличивается на 2 и становится равным 4. Если бы функция other была вызвана снова в той же самой программе, то начальное значение a стало бы равным 4. Внутренние переменные класса памяти static сохраняют свои значения, когда заканчивается выполнение блока, в котором они об"явлены.
Об"явление функции на внешнем и внутреннем уровняхФункции могут быть об"явлены со спецификаторами класса памяти static или extern. Функции всегда имеют глобальное время жизни.
Правила видимости для функций отличаются от правил видимости для переменных. Об"явления функций на внутреннем уровне имеют тот же самый смысл, что и об"явления на внешнем уровне. Это значит, что функции не могут иметь блочной видимости и видимость функций не может быть вложенной. Функция об"явленная как static,
видима только в пределах исходного файла, в котором она определяется. Любая функция в том же самом исходном файле может вызвать функцию static, но функции static из других файлов нет. Функция static с тем же самым именем может быть об"явлена в другом исходном файле.
Функции, об"явленные как extern видимы в пределах всех исходных файлов, которые составляют программу. Любая функция может вызвать функцию extern.
Об"явления функций, в которых опущен спецификатор класса памяти, считаются по умолчанию extern.
ИнициализацияВ об"явлении переменной может быть присвоено начальное значение посредством инициализатора. Величина или величины инициализатора присваиваются переменной.
Синтаксически, записи инициализатора предшествует знак равно (=)
=<initializer>
Могут быть инициализированы переменные любого типа. Функции не инициализируются. Об"явления, которые используют спецификатор класса памяти extern не могут содержать инициализатора.
Переменные, об"явленные на внешнем уровне, могут быть ини-
циализированы. Если они явно не инициализированы, то они устанавливаются в нуль во время компиляции или линкования. Любая переменная, об"явленная со спецификатором класса памяти static, может быть инициализирована константным выражением. Инициализация переменных класса static выполняется один раз во время компиляции. Если отсутствует явная инициализация, то переменные класса памяти static автоматически устанавливаются в нуль.
Инициализация переменных auto и register выполняется каждый раз при входе в блок, в котором они об"явлены. Если инициализатор опущен в об"явлении переменной класса памяти auto или register, то начальное значение переменной не определено. Инициализация
составных типов auto (массив, структура, совмещение) запрещена. Любое составное об"явление класса памяти static может быть инициализировано на внешнем уровне.
Начальными значениями для внешних об"явлений переменной и для всех переменных static как внешних так и внутренних должно быть константное выражение. Автоматические и регистровые переменные могут быть инициализированы константными или переменными величинами.
Базовые типы и типы указателейСинтаксис:
=<expression>
Величина выражения присваивается переменной. Для выражения допустимы правила преобразования.
Примеры:
int x = 10; /* Example 1 */
register int *px = 0; /* Example 2 */ int c = (3 * 1024); /* Example 3 */ int *b = &x; /* Example 4 */
В первом примере x инициализируется константным выражением 10. Во втором примере, указатель px инициализирован нулем, в результате чего получился "null" указатель. В третьем примере используется константное выражение для инициализации c. В четвертом примере инициализируется указатель b адресом другой переменной x.
Составные типыСинтаксис:
={<initializer-list>}
Список инициализаторов <initializer-list> - это последовательность инициализаторов, разделенных запятыми. Каждый инициализатор в последовательности- это либо константное выражение, либо список инициализаторов. Поэтому, заключенный в фигурные скобки список, может появиться внутри другого списка инициализации. Эта конструкция используется для инициализации элементов составных конструкций.
Для каждого списка инициализации значения константных выражений присваиваются в порядке следования элементов составной переменной. Когда инициализируется совмещение, то список инициализаторов представляет собой единственное константное выражение. Величина константного выражения присваивается первому элементу совмещения.
Если в списке инициализации меньше величин, чем их имеется в составном типе, то оставшиеся памяти инициализируются нулем. Если число инициализирующих величин больше чем требуется, то выдается ошибка.
Эти правила применяются к каждому вложенному списку инициализаторов, точно так же как и ко всей конструкции в целом.
Пример:
int p[4] [3] = {
{ 1, 1, 1 },
{ 2, 2, 2 }, { 3, 3, 3,}, { 4, 4, 4,},
};
В примере об"является массив p размерности 4 строки на 3 столбца. Элементы первой строки инициализируются 1, второй строки
2 и т. д. Заметим, что списки инициализаторов третьей и четвертой строк заканчиваются запятой. Последний список инициализаторов { 4, 4, 4,} также заканчивается запятой.
Эти дополнительные запятые допускаются, но не требуются. Требуются только те запятые, которые разделяют константные выражения и списки инициализации. Если список инициализаторов не структурирован под составной об"ект, то его величины присваиваются в том порядке, в котором подстыкованы элементы об"екта. Поэтому вышеприведенная инициализация эквивалентна следующей:
int p[4] [3] = {
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4
};
Фигурные скобки могут также появляться вокруг индивидуальных инициализаторов в списке.
Когда инициализируются составные переменные, то нужно позаботиться о том, чтобы правильно использовать фигурные скобки и списки инициализаторов. В следующем примере иллюстрируется более детально интерпретация компилятором фигурных скобок.
typedef struct {
int n1, n2, n3;
} triplet;
triplet nlist[2] [3] = {
{ { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }, /* Line 1 */ { { 10,11,12}, { 13,14,15}, { 15,16,17} } /* Line 2 */
};
В примере nlist об"является как массив структур, состоящий из двух строк и трех столбцов. Каждая структура состоит из трех элементов. Первая строка инициализации назначает величины первой строке массива nlist следующим образом:
1. Первая левая фигурная скобка Line 1 информирует компилятор о том, что это начало инициализации первой строки массива nlist(nlist[0]).
2. Вторая левая фигурная скобка означает то, что начинается инициализация первого элемента первой строки массива ( nlist[0] [0] ).
3. Первая правая фигурная скобка сообщает об окончании инициализации первого элемента- структуры nlist[0] [0]. Следующая левая фигурная скобка сообщает о начале инициализации второго элемента первой строки nlist[0] [1].
4. Процесс продолжается до конца Line 1 и заканчивается по последней правой фигурной скобке.
Аналогично, Line 2 назначает величины второй строке массива nlist.
Заметим, что внешние фигурные скобки инициализаторов Line 1 и Line 2 требуются. Следующая конструкция, в которой внешние фигурные скобки опущены будет неверной.
/* THIS CAUSES AN ERROR */
triplet nlist[2] [3] = {
{ 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, /* Line 1 */
{ 10,11,12}, { 13,14,15}, {16,17,18 } /* Line 2 */
};
В этом примере первая левая фигурная скобка в Line 1 стартует инициализацию nlist[0], которая является массивом из трех структур. Величины 1, 2, 3 назначаются трем элементам первой структуры. Когда встретится правая фигурная скобка (после величины 3), инициализация nlist[0] закончится и две оставшиеся структуры автоматически инициализируются нулем. Аналогично, { 4, 5, 6 } инициализирует первую структуру во второй строке nlist, а оставшиеся две структуры nlist[1] установятся в нуль. Когда компилятор встретит следующий список инициализации { 7, 8, 9 }, то это приведет к попытке инициализировать nlist[2]. Так как nlist содержит только две строки, то будет выдано сообщение об ошибке.
Примеры:
/******************* Example 1 *********************/
struct list {
int i, j, k;
float n[2] [3];
} x = {
1,
2,
3,
{4.0, 4.0, 4.0}
};
/******************* Example 2 *********************/
union {
char x[2] [3];
int i, j, k;
} y = {
{'1'},
{'4'}
};
В первом примере три элемента int структурной переменной x инициализированы 1, 2, и 3 соответственно. Три элемента первой строки массива m инициализированы как 4.0. Элементы второй строки инициализированы нулем по умолчанию.
Во втором примере инициализируется переменная y типа совмещения. Первым элементом совмещения является массив, для которого требуется составной инициализатор. Список инициализации {'1'} задает величины для первой строки массива. Поскольку в списке всего одна величина, то только первый элемент строки массива инициализируется символом 1 , а оставшиеся два элемента в строке инициализируются нулем (символом \0) по умолчанию. Аналогично, первый элемент второй строки массива x инициализируется символом 4, а оставшиеся два элемента в строке инициализируются нулем.
Строковые инициализаторыМассив может быть инициализирован строчным литералом.
Например,
char code[ ] = "abc";
инициализирует code как массив символов из четырех элементов. Четвертым элементом является символ \0, который завершает все строковые литералы.
Если специфицируется размер массива, а строка больше чем
специфицированный размер, то лишние символы отбрасываются. Следующее об"явление инициализирует переменную code, как трехэлементный массив символов:
char code[3] = "abcd"
В примере только три первые символа инициализатора назначаются для массива code. Символ d и сивол нуль отбрасываются.
Если строка короче, чем специфицированный размер массива, то оставшиеся элементы массива инициализируются нулем (символом \0).
Об"явления типовОб"явление типа определяет имя и элементы структурного или совмещающего типов или имя и перечислимое множество перечислимого типа.
Имя типа может быть использовано в об"явлениях переменных и функций в качестве ссылки на этот тип. Это полезно, когда многие переменные или функции имеют один и тот же тип.
Об"явление typedef определяет спецификатор типа для типа. Это об"явление используется для того, чтобы создавать более короткие или более осмысленные имена типов уже определенных в Си или об"явленных пользователем.
Типы структур, совмещений и перечисленийОб"явления типов структур, совмещений и перечислений имеют ту же самую общую синтаксическую форму, как и об"явления переменных этих типов. В об"явлении типа идентификатор переменной опущен, так как нет переменной которая об"является. Именем структуры, совмещения или перечисления является тег.
В об"явлении типа может появиться список об"явлений элементов- <member-declaration-list> или список перечисления-
<enum-list>, определяющие тип.
Сокращенная форма об"явления переменной, в котором tag ссылается на тип, определенный где-то еще, при об"явлении типа не используется.
Примеры:
/******************** Example 1 ********************/
enum status {
loss = -1,
bye,
tie = 0,
win,
};
/********************* Example 2 *******************/
struct student {
char name[20];
int id, claas;
};
В первом примере об"является тип перечисления, поименованный status. Имя типа может быть использовано в об'явлениях перменных типа перечисления. Идентификатор loss явно устанавливается в -1. Идентификаторы bye и tie ассоциируются со значением 0, а win принимает значение 1. Во втором примере об"является структурный тип, поименованный student. Теперь можно использовать такое об"явление, как struct student employee, чтобы об"явить структур-
ную переменную employee типа student.
Об"явления typedefСинтаксис:
typedef <type-spesifier><declarator>[,<declarator>...]; Об"явления typedef являются аналогом об"явления переменной,
за исключением того, что ключевое слово typedef заменяет спецификатор класса памяти.
Об"явление интерпретируется тем же самым путем, как об"явления переменной или функции, но <declarator> вместо того, чтобы стать переменной типа, специфицированного об"явлением, становится синонимом имени типа. Об"явление typedef не создает типов. Оно создает синонимы для существующих имен типов, которые были специфицированы другим способом. Любой тип может быть об"явлен с typedef, включая типы указателя, функции и массива. Имя с ключевым словом typedef для типов указателя, структуры или совмещения может быть об"явлено прежде чем эти типы будут определены, но в пределах видимости об"явления.
Примеры:
/******************** Example 1 ********************/
typedef int WHOLE;
/******************** Example 2 ********************/
typedef struct club {
char name[30];
int sise, year;
} GROUP;
/******************** Example 3 ********************/
typedef GROUP *PG;
/******************** Example 4 ********************/
typedef void DRAWE(int, int);
В первом примере об"является WHOLE как синоним для int .
Во втором примере об" является GROUP как структурный тип с тремя элементами. Так как специфицирован также тег clab, то имя GROUP и тег club могу быть использованы в об"явлениях.
В третьем примере используется предидущее имя typedef для об"явления адресного типа. Тип PG об"является как указатель на тип GROUP, который в свою очередь определен как структурный тип.
В последнем примере представлен тип DRAWE для функции не
возвращающей значения и требующей два аргумента типа int. Это означает, например, что об"явление DRAWE box; эквивалентно об"явлению void box(int, int);
Имена типовИмя типа специфицирует особенности типа данных. Имена типов используются в трех контекстах: в списках типов аргументов, при об"явлении функций, в вычислениях cast (преобразованиях типов), и в sizeof операциях. Списки типов аргументов рассматривались в
разделе 4.5. "Об"явления функций". Преобразования cast и операция sizeof обсуждаются в разделах 5.7.2. и 5.3.4. соответственно.
Именами для основных, перечисляющих, структурных и совмещающих типов являются спецификаторы типа для каждого из них. Имена для типов указателя, массива и функции задаются следующей синтаксической формой:
<type-specifier><abstract-declarator>
Абстрактный декларатор <abstract-declarator>- это декларатор без идентификатора, состоящий из одного или более модификаторов указателей, массивов и функций. Модификатор указателя (*) всегда появляется перед идентификатором в деклараторе, в то время как модификатор массива ([]) или функции ( () ) появляются после идентификатора. Таким образом, чтобы правильно интерпретировать абстрактный декларатор, нужно начинать интерпретацию с подразумеваемого идентификатора.
Абстрактные деклираторы могут быть составными. Скобки в составном абстрактном деклараторе специфицируют порядок интерпретации, подобно тому как это делается при интерпретации составных деклараторов об"явлений. Абстрактный декларатор, состоящий из пустых круглых скобок () не допускается, поскольку это двусмысленно. В этом случае невозможно определить находится ли подразу-
меваемый идентификатор внутри скобок, и в таком случае- это немодифицированный тип, или перед скобками, тогда- это тип функции. Спецификаторы типа, установленные посредством об"явлений typedef, также рассматриваются как имена типов.
Примеры:
long * /* Example 1 */
int (*) [5] /* Example 2 */
int (*) (void) /* Example 3 */
В первом примере задано имя типа как указатель на тип long. Во втором и третьем примерах показано каким образом скобки
модифицируют составные абстрактные деклараторы. В примере 2 задано имя типа для указателя на массив иэ пяти злементов. В третьем примере именуется указатель на функцию, не требующую аргументов и возвращающую значение типа int.
КОНТРОЛЬНЫЕ ВОПРОСЫ:1. Какие ошибки содержат следующие операторы?
enum State { on, off };
enum YesNo { yes, no};
enum DiskDriveStatus { on, off };
2. Верно или нет, что объявление следующего перечислимого типа неправильно?
enum YesNo { no = 0, No = 0, yes = 1, Yes = 1 };
3. Что не так в следующей программе?
#include <iostream.h>
int main()
{
int *p = new int;
cout << "Enter а number";
cin >> *p;
cout << "The square of " << *p << " = " << (*p * *p);
return 0;
}
Функции Объявление и определение функций- Общая форма определения функции такова:
возвращаемыйТип имяФункции(<список параметров>)
// обязателен тип возвращаемого значения
{
< объявление данных >
< тело функции>
return возвращаемоеЗначение; // - если возвращаемыйТип не void
}
- Выход из функции осуществляется по оператору return. Void-функции
могут не возвращать значения.
Список параметров:
[const] тип1 параметр1, [const] тип2 параметр2, ...
- Ключевое слово const предохраняет передаваемые по ссылке аргументы от случайного изменения.
Программа USERINFO.CPP иллюстрирует использование модификатора
// const в списке параметров
*/
struct userInfo
{
int age;
char name[150];
};
void processUserInfo(/*const*/ userInfo &ui)
// при снятии комментария будет сообщение об ошибке,
// поскольку модификатор const запрещает изменение параметра
{
if ( ui.age < 18 ) {
cout << "Значение параметра меньше 18" << endl;
return;
}
if ( ui.age < 21 )
ui.age = 21;
}
/*
Если функция вызывается до своего определения, обязательно должен быть задан прототип функции. Общая форма объявления функции:
возврТип имяФункции(<список параметров>);
При объявлении функции имена параметров могут быть опущены.
- Передача аргумента по ссылке позволяет функции изменять значение переданного аргумента и экономит память, так как при этом не создается локальная копия аргумента:
[const] тип1& параметр1, [const] тип2& параметр2, ...
void foo(int &); // - объявление функции - это ее прототип
int main()
{
int value = 5;
foo(value);
cout << value << endl;
return 0;
}
void foo(int &parm) // - определение функции вызов параметра по ссылке
{
++parm;
}
/* Результаты:
6
*/
void foo(int *); // пердача указателя
int main()
{
int value = 5;
foo(&value); // передается адрес
cout << value << endl;
getch();
foo(&value);
cout << value << endl;
getch();
return 0;
}
void foo(int* parm)
{
++*parm; // параметр - указатель
}
/* Результаты:
6
7
- Локальные переменные и константы существуют и действуют только в теле данной функции, где они объявлены. Объявление локальных переменных подобно объявлению глобальных переменных.
Программа LOCAL.CPP знакомит с понятием локальной переменной
- Ключевое слово static позволяет объявить переменную как статическую.
Статическая переменная является локальной переменной, но сохраняет свое значение между вызовами функции. Обычно статические переменные инициализируются. Начальные значения присваиваются перед первым вызовом функции, в которой определена статическая переменная.
Программа STATIC.CPP знакомит с понятием статической локальной переменной
- Макроопределения позволяют вам вводить компактные псевдо-функции, принимающие любые типы данных, поскольку компилятор не выполняет в этом случае проверку типов:
#define min(n1, n2) (((n1) < (n2)) ? (n1) : (n2))
#define max(n1, n2) (((n1) > (n2)) ? (n1) : (n2))
double num1 = 50, num2 = 5, rslt;
rslt = min(num1 / 2, num2 * 2);
- При объявлении функции с модификатором inline компилятор заменяет вызов функции ее телом. В этом смысле эти функции похожи на макросы.
Отличие состоит в том, что встроенные функции выполняют проверку типов данных.
Программа INLINE.CPP, иллюстрирующая применение встроенной функции
- Используя аргументы по умолчанию для некоторых параметров, при вызове функции вы можете не задавать аргументы для этих параметров; тогда им автоматически будут присваиваться значения по умолчанию.
Программа DEFARGS.CPP, иллюстрирующая применение аргументов по умолчанию
- Рекурсивными называются функции, которые вызывают сами себя. Количество рекурсивных вызовов должно быть ограничено, чтобы не столкнуться с проблемой нехватки памяти. По этой причине каждая рекурсивная функция должна выполнять проверку условия на окончание рекурсии.
Пример программы FACTOR.CPP, использующей рекурсивную функцию
- Перегрузка функций позволяет вам иметь несколько функций с одним именем, но с разными списками аргументов (список аргументов еще называется сигнатурой функции). Тип возвращаемого функцией значения не является частью сигнатуры.
Программа OVERLOAD.CPP, иллюстрирующая перегрузку функции
ТИПОВЫЕ ВОПРОСЫ С ОТВЕТАМИМожно ли в С++ объявлять вложенные функции?
Нет, так как это приводит к большим накладным расходам во время выполнения программы.
В каких случаях нужно использовать статические глобальные переменные?
Можете использовать их, где хотите. Когда вы объявляете статической глобальную переменную (которые я вам не советую использовать вообще), вы даете указание компилятору сделать ее невидимой для функций из других файлов. Такая переменная недоступна из других файлов вашего проекта.
Как расходуется память при обслуживании вызовов рекурсивной функции?
Исполняющая система использует стек для хранения временных данных, в том числе необходимых для генерирования вызова рекурсивной функции. Как и другие ресурсы, стек ограничен в своем размере. В результате при длинной цепочке вызовов рекурсивной функции стек может переполниться, что приведет к остановке программы из-за ошибок выполнения или переполнения стека.
ПРАКТИКУМ Контрольные вопросы1. Каков будет результат работы следующей программы? Что вы можете сказать по поводу функции swap?
*/
# include <iostream.h>
void swap(int i, int j)
{
int temp = i;
i = j;
j = temp;
}
int main()
{
int a = 10, b = 3;
swap (a, b);
cout << "а = " << a << " and b = " << b;
return 0;
}
/*
2. Каков будет результат работы следующей программы? Что вы можете сказать по поводу еще одной функции swap?
*/
#include <iostream.h>
void swap(int &i, int &j)
{
int temp = i;
i = j;
j = temp;
}
int main()
{
int a = 10, b = 3;
swap (a, b);
cout << "а = " << a << " and b = " << b;
return 0;
}
/*
3. Что за проблема возникнет со следующими перегруженными функциями?
*/
void inc(int &i)
{
i = i + 1;
}
void inc(int &i, int diff = 1)
{
i = + diff;
}
/*
4. Найдите ошибку в функции.
/*
double volume(double length, double width = 1, double height)
{
return length * width * height
}
/*
5. Найдите ошибку в функции.
*/
void inc (int &i, int diff = 1)
{
i = I + diff;
}
/*
6. В этой программе есть ошибка. Что это за ошибка и как ее исправить?
*/
# include<iostream.h>
int main()
{
double x = 5.2;
cout << x << " ^ 2 = " << sqr(x);
return 0;
}
double sqr( double х)
{ return x * x; }
/*
7. Попробуйте в функции вычисления факториала использовать операцию ?: .
Массивы// Листинг 6.1. исходный текст программы AVERAGE1.CPP
// Программа иллюстрирует использование одномерных массивов
// при расчете среднего значения.
#include <iostream.h>
const int MAX = 0x1FFF; //64K/8 - максимальный размер массива типа double ***
int main()
{
double array[MAX]; // объявление одномерного массива ***
int num_elem;
// Ввод количества обрабатываемых данных
do
{
cout << "Введите размер массива данных [2 ... "
<< MAX << "]: ";
cin >> num_elem;
cout << endl;
} while (num_elem < 2 || num_elem > MAX);
// Ввод данных
for (int ix = 0; ix < num_elem; ix++)
{
cout << "массив[" << ix << "]: ";
cin >> array[ix];
}
// Расчет среднего значения
double sum = 0;
for (ix = 0; ix < num_elem; ++ix)
sum += array[ix];
cout << endl << "Среднее: " << sum / num_elem << endl;
return 0;
/*
- При объявлении одномерных массивов им можно присвоить начальные значения. Список ИНИЦИАЛИЗАЦИИ должен быть заключен в фигурные скобки, а элементы в нем должны быть разделены запятыми. Можно при инициализации задать данных МЕНЬШЕ, чем размер массива. В этом случае компилятор автоматически присвоит нулевые значения тем элементам, которые вы не инициализировали. И вдобавок, если вы не укажете размерность инициализируемого массива, она будет определена по количеству элементов в списке инициализации.
*/
// Листинг 6.2. исходный текст программы AVERAGE2.CPP
// Программа иллюстрирует использование одномерных массивов
// при расчете среднего значения.
// Данные задаются при инициализации массива.
#include <iostream.h>
const int MAX = 10; //50
int main()
{
double array[MAX] = { 12.2, 45.4, 67.2, 12.2, 34.6, 87.4,
83.6, 12.3, 14.8/*, 55.5*/ };
int num_elem = MAX;
//double array[] = { 12.2, 45.4, 67.2, 12.2, 34.6, 87.4,
// 83.6, 12.3, 14.8, 55.5 };
//int num_elem = sizeof(array) / sizeof(array[0]);
double sum = 0;
for (int ix = 0; ix < num_elem; ++ix)
{
sum += array[ix];
cout << "массив[" << ix << "]: " << array[ix] << endl;
}
cout << endl << "Среднее: " << sum / num_elem << endl;
return 0;
}
- Объявление одномерных массивов в качестве параметров функции возможно в двух формах: массив-параметр фиксированной размерности и массив-параметр неопределенной длины (открытый массив), При объявлении параметром массива фиксированной размерности указывается размер массива. В этом случае передаваемые функции аргументы должны соответствовать параметру по типу и размеру. Массив- араметр неопределенной длины объявляется с пустыми скобками, означающими, что аргумент может быть любого размера.
(Листинг 6.3а. исходный текст программы MINMAX.CPP)
(Листинг 6.3. исходный текст программы MINMAX.CPP)
СОРТИРОВКА массива - ПРИМЕР в файле list6_4cpp.В результате сортировки элементы массива распределяются в порядке возрастания или убывания. Осуществлять поиск в сортированном массиве намного проще, чем в несортированном. Для сортировки массивов можно использовать эффективную встроенную функцию быстрой сортировки qsort.
- ПОИСК в массивеозначает нахождение в массиве элемента, совпадающего с заданным значением. Методы поиска делятся на две группы: для упорядоченных и неупорядоченных массивов. Метод линейного поиска применяется для неупорядоченных массивов, а метод двоичного поиска - для сортированных массивов. (Пример - list6_5.cpp)
Рассмотрим понятия ПАРАМЕТРОВ-ФУНКЦИЙ и УКАЗАТЕЛИ НА ФУНКЦИИ:
(Листинг 6.5. исходный текст программы SEARCH.CPP)
БИБЛИОТЕЧНЫЕ ФУНКЦИИ ПОИСКА и СОРТИРОВКИ в непрерывных массивах:*/
void *bsearch(const void *key, const void *base, size_t nelem,
size_t width, int (*fcmp)(const void*, const void*));
// key - указатель на искомый элемент,
// возвращаемое значение - указатель на элемент (0 - не найден)
// base - базовый адрес массива
// num - число элементов в массиве
// width - размер элемента
// fcmp - указатель на функцию сравнения элементов массива
// Функция возвращает указатель на элемент, а не значение индекса элемента
// Если элемент не обнаружен, возвращается 0.
// Для вычисления индекса можно использовать следующую формулу:
index = (searchRslt - arrayBase) / sizeof(arrayBase[0]);
void *lfind(const void *key, const void *base, size_t *num,
size_t width, int (*fcmp)(const void *, const void*));
void *lsearch(const void *key, void *base, size_t *num,
size_t width, int (*fcmp)(const void *, const void *));
// - если нет элемента, то он вставляется, поэтому возвращаемое значение
// всегда не ноль.
void qsort(void *base, size_t nelem,
size_t width, int (*fcmp)(const void *, const void *));
/*
- При объявлении многомерных массивов вам нужно указать тип массива, его имя и размер (заключенный в свою пару скобок) по каждому измерению. Нижнее значение индекса для любого измерения равно 0. Верхнее значение индекса по любому измерению равно количеству элементов поэтому измерению минус единица.
- Для того чтобы обратиться к многомерному массиву, Вам нужно задать его имя и правильные значения индексов. Каждый индекс должен быть заключен в свою пару скобок.
Пример работы с двумерным массивом:
(Листинг 6.6. Исходный текст программы MATRIX1.CPP)
- При объявлении многомерных массивов им можно присвоить начальные значения. Список ИНИЦИАЛИЗАЦИИ должен быть заключен в фигурные скобки, а элементы в нем должны быть разделены запятыми. Можно при инициализации задать данных меньше, чем размер массива, В этом случае компилятор автоматически присвоит нулевые значения тем элементам, для которых вы не указали начальные значения:
(Листинг 6.7. Исходный текст программы MATRIX2.CPP.)
- Объявление многомерных массивов в качестве параметров функции воз- можно в двух формах: массив-параметр фиксированной размерности и массив-параметр неопределенной длины по первому измерению. При объявлении параметром массива фиксированной размерности указывается размер массива по каждому измерению. В этом случае передаваемые функции аргументы должны соответствовать по типу и размеру параметру. Массив-параметр неопределенной длины объявляется с пустыми скобками для первого измерения, означающими, что передаваемый аргумент может быть любого размера по первому измерению. По другим измерениям размеры аргумента и параметра должны совпадать:
(Листинг 6.8. Исходный текст программы MATRIX3.CPP)
Строки и управление вводом/выводомЗдесь подробнее рассматриваются операции консольного ввода/вывода. C++, как и его предок — язык С — не определяет операции ввода/вывода как часть языка, а выносит операции консольного ввода/вывода в библиотеки ввода/вывода. Такие библиотеки в основном предназначены для работы в MS-DOS. Рассмотрим небольшую выборку функций ввода/вывода, объявляемых в заголовочных файлах STDIO.H и IOSTREAM.H.
Сегодня мы рассмотрим следующие темы:
· Форматированный потоковый вывод
· Потоковый ввод
· Функция printf
· Строки в C++
· Ввод строк
· Использование стандартной библиотеки функций для работы со строками
· Присвоение значений строкам
· Определение длины строки
· Конкатенация строк
· Сравнение строк
· Преобразование строк
· Перестановка символов в строке в обратном порядке
· Поиск символа
· Поиск подстроки
Форматированный потоковый выводC++ имеет целое семейство гибких библиотек функций ввода/вывода. Разработчикам языка было ясно, что функции ввода/вывода из STDIO.H, унаследованные из С, имеют ограничения при работе с классами (вы узнаете больше о классах в главе 8)., В результате в C++ было введено понятие потоков. Вспомним, что потоки, которые уже существовали в С, означают последовательность данных, передаваемых из одной части компьютера в другую. В программах, рассматриваемых ранее, вы видели операцию помещения в поток «, например — в стандартный поток вывода, cout. Встречалась вам и операция извлечения из потока », применяемая к стандартному потоку ввода, cin. В этом разделе мы познакомимся с потоковыми функциями width и precision, используемыми при форматировании вывода. Библиотеки потоков C++ содержат большое количество таких функций, позволяющих настроить ваш вывод.
Функция width задает ширину поля вывода. Общая форма использования функции width с потоком cout:
cout.width (widthOf Output);
Функция precision определяет количество значащих цифр после точки для чисел с плавающей точкой. Общая форма использования функции precision с потоком cout:
cout.precision(numberOfDigits) ;
Обратимся к примеру, программе OUT1.CPP, исходный текст которой приведен в листинге 1. Программа, в которую ничего не вводится, просто выводит форматированные целые числа, числа с плавающей точкой и символы с использованием функций width и precision.
Листинг 1. Исходный текст программы OUT1.CPP01 | // Программа иллюстрирует потоковый форматированный вывод в C++ |
02 | // с использованием функций width и precision |
03 | #include <iostream.h> |
04 | |
05 | int main() |
06 | { |
07 | int anInt = 67; |
08 | unsigned char aByte = 128; |
09 | char aChar = '@'; |
10 | float aSingle = 355.1112; |
11 | double aDouble = 1.131112e+002; |
12 | |
13 | // Вывод простых выражений |
14 | cout.width(3); cout << int(aByte) << " + "; |
15 | cout.width(2); cout << anInt << " = "; |
16 | cout.width(3); cout << (aByte + anInt) << endl; |
17 | |
18 | cout.precision(3); cout << aSingle << " / "; |
19 | cout << aDouble << " ="; |
20 | cout.width(7); cout.precision(4); cout << (aSingle / aDouble) << endl; |
21 | |
22 | cout << "Символьная переменная aChar: " |
23 | << aChar << endl; |
24 | return 0; |
25 | } |
Пример программной сессии:
Введите три числа через пробел: 123
Сумма чисел = 6
Среднее этих чисел = 2
Введите три символа: ABC
Вы ввели символы 'A', 'B', 'C'
Введите число, символ, и число: 12A34.4
Вы ввели 12 A 34.4
Введите символ, число и символ: A3.14Z
Вы ввели A 3.14 Z
В программе из листинга 2 объявляется четыре переменных типа double и три переменных типа char. Оператор вывода в строке 10 предлагает вам ввести три числа. Оператор ввода в строке 11 помещает введенные вами числа в переменные х, у и z. He забывайте, что при вводе чисел их нужно разделять пробелами. Либо вводите каждое число с новой строки. Первое введенное вами число будет помещено в переменную х, второе — в у, а третье окажется в переменной z. Данные в переменные заносятся в том порядке, в котором переменные перечислены в операторе ввода в строке 11. Оператор в строке 12 вычисляет сумму значений переменных х, у и z. Оператор вывода в строках 13 и 14 выводит сумму и среднее значение введенных вами величин.
Оператор вывода в строке 15 предлагает вам ввести три символа. Оператор (ввода в строке 16 последовательно размещает введенные символы в переменных с1, с2, с3. Использовать пробел для разделения вводимых символов не обязательно. Например, вы можете ввести данные и таким образом: 1А2, Bob и 1 D d. Оператор вывода в строках 17—19 выводит введенные вами символы, разделенные пробелами и заключенные в одинарные кавычки.
Оператор вывода в строке 20 предлагает вам ввести число, символ и число. Оператор ввода в строке 21 помещает ваши данные в переменные х, с1 и у. Пробел-разделитель здесь нужен только в том случае, если символ может быть интерпретирован как часть числа. Например, если вам нужно ввести число 12, символ «точка» и число 55, вам нужно набрать на клавиатуре 12 . 55. Вводимый символ «точка» лучше «заключить» в пробелы, чтобы быть уверенным, что поток вода не воспримет эту точку как точку, разделяющую в вещественном числе целую и дробную части. Оператор вывода в строке 22 выводит введенные вами данные разделенные пробелами.
Оператор вывода в строке 23 предлагает вам ввести символ, число и символ. Оператор ввода в строке 24 последовательно размещает введенные значения в переменных с1, х, с2. Пробел-разделитель здесь нужно использовать только в том случае, если символ может быть интерпретирован как часть числа. Например, если вам нужно ввести символ «-», число 12 и цифру 0, вам нужно набрать на клавиатуре 12 0. Оператор вывода в строке 25 выводит введенные вами данные, разделяя их пробелами.
Функция printfПросматривая программы, написанные разными людьми, вы часто можете встретить функцию printf. Этот стандартный оператор вывода пришел из языка С. Так как C++ является расширением С, эта функция поддерживается и в этом языке. Многие программисты до сих пор предпочитают использовать старую функцию printf, а не потоки ввода/вывода C++. Вот почему вам эта функция наверняка уже знакома. Но, помимо этого, эта функция имеет несколько очень мощных возможностей, и в ряде случаев она оказывается удобнее функций потоков. Прототип функций можно найти в заголовочном файле STDIO.H.
Функция printfОбщая форма объявления функции printf:
int printf(const char *format[, argument,... ]);
Параметр format является символьным массивом, содержащим выводимый текст. Кроме этого обязательного параметра, могут быть необязательные аргументы. Массив format может содержать специальные форматирующие символы, которые выполняют преобразование необязательных аргументов при выводе.
Функция printf является очень мощной функцией с богатыми возможностями форматирования вывода. В качестве первого шага в освоении ее возможностей рассмотрим Esc-последовательности, позволяющие представлять специальные символы. Esc-последовательность начинается с символа «\» — «обратная косая черта». Esc-коды представлены в таблице 1.
Таблица 1. Еsс - последовательности Последовательность | Десятичное значение | Шестнадцатеричное значение | Название |
\а | 7 | 0х07 | Звонок |
\b | 8 | 0х08 | Возврат назад |
\f | 12 | 0х0С | Перевод страницы |
\n | 10 | 0х0А | Новая строка |
\г | 13 | 0x0D | Возврат каретки |
\t | 9 | 0х09 | Табуляция |
\v | 11 | 0х0В | Вертикальная табуляция |
\\ | 92 | 0х5С | Обратная черта |
\' | 44 | 0х2С | Апостроф |
\" | 34 | 0х22 | Кавычка |
\? | 63 | 0х3 F | Знак вопроса |
\0 | Восьмеричное число, от 1 до 3 цифр | ||
\XHHH и \xhhh | 0xhhh | Шестнадцатеричное число |
Функция printf имеет специальные форматирующие спецификации (символы) для вывода переменных. Общий вид этих спецификаций таков:
% [flags] [width] [.precision] [F | N | h | l | L ] <символ типа>
Опции flags могут определять выравнивание, отображение знака числа при выводе, вывод десятичной точки и символов заполнения. Кроме того, эти флаги определяют префиксы для восьмеричных и шестнадцатеричных чисел. Возможные значения флагов приведены в таблице 2.
Таблица 7.2. Значения флагов строки формата функции printf Символ | Назначение |
- | Выравнивать вывод по левому краю поля |
+ | Всегда выводить знак числа |
Пробел | Выводить пробел перед положительным числом и знак минус — перед отрицательным |
# | Не влияет на вывод десятичных целых, для шестнадцатеричных чисел выводит префикс 0х или 0Х, перед восьмеричными целыми выводит ноль, десятичную точку для вещественных чисел. |
Спецификация width определяет минимальное количество выводимых символов. Если необходимо, используются заполнители — пробелы или нули. Когда значение для width начинается с нуля, printf использует в качестве заполнителей нули, а не пробелы. Если в качестве значения для width используется универсальный символ *, а не число, то printf подставляет на место этого символа значение, которое должно содержаться в списке аргументов. Это значение ширины поля должно предшествовать выводимому значению. Ниже приведен пример вывода числа 2, занимающего три позиции, согласно значению второго аргумента printf:
printf("%*d", 3, 2);
Спецификатор precision определяет максимальное количество выводимых цифр. В случае целого числа он определяет минимальное количество выводимых символов. Для precision также можно применить символ *, вместо которого будет подставлено значение из списка аргументов. Это значение точности представления должно предшествовать выводимому значению. Ниже приведен пример вывода числа с плавающей точкой 3.3244 с использованием десяти символов, как это задано вторым аргументом printf:
printf("%7.*f", 10, 3.3244);
Символы F, N, h, l и L являются символами размера, переопределяющими размер по умолчанию. Символы F и N применяются с указателями, far и near соответственно. Символы h, l, и L используются для указания соответственно типов short int, long или long double.
Символам типа данных должен предшествовать форматирующий символ %. В таблице 7.2 мы показали возможные значения флагов форматирующей строки printf. Символы типов данных перечислены в таблице 7.3.
Таблица 3. Символы типов данных строки формата функции printf Тип данных | символ типа | результат |
Символ | c | Один символ |
d | Десятичное целое со знаком | |
i | Десятичное целое со знаком | |
O | Восьмеричное целое без знака | |
N | Десятичное целое без знака | |
X | Шестнадцатеричное целое без знака; набор цифр - 0123456789abcdef | |
X | Шестнадцатеричное целое без знака; набор цифр - 0123456789ABCDEF | |
Указатель | P | Для указателей near выводит только смещение в виде: 0000. Указатели far отображаются в виде: SSSS:0000 |
Указатель на целое | N | |
Вещественное | F | Выводит величину со знаком в формате [-]dddd.dddd |
E | Выводит вещественную величину со знаком в экспоненциальном формате [-]d.dddde[+|-]ddd | |
Е | Выводит вещественную величину со знаком в экспоненциальном формате [-]d.ddddE[+|-]ddd | |
G | Выводит вещественную величину со знаком в формате f или е в зависимости от ее значения и заданной точности | |
G | Выводит вещественную величину со знаком в формате F или Е в зависимости от ее значения и заданной точности | |
Указатель | S | Выводит строку символов, пока не встретит нуль-терминатор строки |
Разберем небольшой пример. Программа OUT2.CPP, исходный код которой приведен в листинге 3, создана на основе программы OUT1.CPP. В этой программе используется форматированный вывод с использованием функции printf. Программа выводит те же числа, что и OUT1.CPP, используя три различных набора спецификаций преобразования.
Листинг 3. Исходный текст программы OUT2.CPP в файле List7-3.CPP 01 | // Программа, использующая printf для форматирования вывода |
02 | |
03 | #include <stdio.h> |
04 | |
05 | int main() |
06 | { |
07 | int anInt = 67; |
08 | Unsigned char aByte = 128; |
09 | char aChar = '@'; |
10 | Float aSingle = 355.0; |
11 | Double aDouble = 1.130e+002; |
12 | |
13 | Printf("%3d + %2d = %3d\n", |
14 | aByte, anInt, aByte + anInt ); |
15 | |
16 | Printf("Вывод использует спецификации преобразования %%lf :\n"); |
17 | Printf(" %6.4f / %10.4lf = %7.5lf\n", |
18 | aSingle, aDouble, aSingle / aDouble ); |
19 | |
20 | Printf("Вывод использует спецификации преобразования %%le :\n"); |
21 | printf(" %6.4e / %6.4le = %7.5le\n", |
22 | aSingle, aDouble, aSingle / aDouble ); |
23 | |
24 | printf("Вывод использует спецификации преобразования %%lg :\n"); |
25 | printf(" %6.4g / %6.4lg = %7.5lg\n", |
26 | aSingle, aDouble, aSingle / aDouble ); |
27 | |
28 | printf("Символьная переменная aChar: %c\n", aChar); |
29 | printf("ASCII-код %c: %d\n", aChar, aChar); |
30 | return 0; |
31 | } |
Пример вывода программы из листинга 3:
128 + 67 = 195
Вывод использует спецификации преобразования %lf :
355.0000 / 113.0000 = 3.14159
Вывод использует спецификации преобразования %le :
3.5500e+02 / 1.1300e+02 = 3.14159e+00
Вывод использует спецификации преобразования %lg :
355 / 113 = 3.1416
Символьная переменная aChar: @
ASCII-код @: 64
В программе из листинга 3 объявляется целый набор переменных различных типов. Оператор вывода в строках 13 и 14 выводит целые, используя спецификацию формата %d. В таблице 4 приведены результаты действия спецификаций преобразования из строки 13. Обратите внимание на то, что первая переменная была преобразована из типа unsigned char в тип integer.
Таблица 4. Результат действия спецификаций форматирования в функции printf из строки 13 Спецификация формата | Переменная | Тип данных | Тип после преобразования |
%3d | aByte | unsigned char | Int |
%2d | anInt | int | Int |
%3d | aByte + anInt | int | Int |
Оператор вывода в строке 17 выводит переменные aSingle, aDouble и выражение aSingle / aDouble, используя спецификации преобразования %6.4f, %6.41f и % 7.51f. Точность представления задается ими равной 4, 4 и 5 цифрам, а минимальная ширина поля 6, 6 и 7 цифрам соответственно. Две последних спецификации осуществляют преобразование величин двойной точности.
Оператор вывода в строке 21 подобен оператору из строки 17. Отличие состоит в том, что используется е-формат вместо f-формата. Соответственно три значения выводятся в экспоненциальном формате.
Оператор из строки 25 также похож на оператор из строки 17. Основное отличие состоит в том, что вместо f-формата используется g-формат. В результате первые два числа выводятся без дробной части, поскольку они являются целыми величинами.
Оператор вывода в строке 28 выводит содержимое переменной aChar по формату %с. Оператор вывода в строке 29 выводит ту же переменную aChar дважды, первый раз как символ, а второй раз как целое (или, если быть точным, выводится ASCII-код символа). Для этого используются спецификации преобразования %с и %d соответственно.
Массивы символов в C++
В C++ имеется специальный класс для работы со строками, которого, конечно, не было в языке С. В С строки вводились как массивы символов, ограниченные нуль-символом (ASCII-код которого равен нулю), поэтому большое количество программ, написанных на С, используют символьные массивы. Более того, и в C++, несмотря на то, что он имеет класс для работы со строками, находится применение массивам символов. Поэтому термин «строка» имеет два значения: строка в смысле C++ и строка как массив символов. Весь этот раздел будет посвящен тому, как нужно и не нужно использовать символьные массивы.
Символ '\0' также называют нуль-терминатором. Строки, оканчивающиеся нуль-терминатором, называют еще ASCIIZ-строками, где символ Z обозначает ноль — ASCII-код нуль-терминатора. Еще этот символ называют NUL-символом, поскольку этот термин является его именем в ASCII.
Все строки обязательно должны оканчиваться нуль-терминатором, и при объявлении размера массива необходимо это учитывать. Когда вы объявляете строковую переменную как массив символов, увеличьте размер массива на один символ для нуль-терминатора. Использование строк с конечным нулем также имеет то преимущество, что здесь отсутствуют ограничения, накладываемые реализацией C++. Кроме того, структура ASCIIZ-строк очень проста.
Ввод строкВ программах, которые мы рассматривали, операторы потокового вывода выводили строковые константы; C++ поддерживает потоковый вывод для строк как специального не-предопределенного типа данных. (Можно сказать, что это было сделано по требованию масс.) Операции и синтаксис для вывода строковых переменных остаются прежними. При вводе строк операция извлечения из потока » не всегда будет работать так, как вы ожидаете, поскольку строки часто содержат пробелы, которые игнорируются оператором ввода; поэтому вместо оператора ввода вам нужно использовать функцию getline. Эта функция вводит заданное количество символов.
Функция getlineПерегруженная функция getline объявляется следующим образом:
istreams getline( signed char *buffer,
int size,
char delimiter = '\n') ;
istreams getline( unsigned char *buffer,
int size,
char delimiter = '\n') ;
istream& getline( char *buffer,
int size,
char delimiter = '\n') ;
Параметр buffer является указателем на строку, в которую помещаются вводимые символы. Параметр size задает максимальное количество вводимых символов. Параметр delimeter определяет символ-ограничитель, при появлении которого ввод символов прекращается прежде, чем будут введены все size символов. Параметр delimeter имеет аргумент по умолчанию, равный '\n'. В случае ввода символов с клавиатуры этот символ появляется в потоке ввода при нажатии клавиши
Пример
#include <iostream.h> //см. файл Ex01.cpp
int main()
{
char name[80] ;
cout « "Enter your name: ";
cin.getline(name, sizeof(name) - 1);
cout « "Hello " « name « ", how are you?";
return 0;
}
Функции, объявленные в STRING. H
Стандартная библиотека для работы со строками содержит много полезных функций (объявляемых в STRING.H), разработанных коллективными усилиями многих программистов на С. В файлах заголовка STDIO.H и IOS-TREAM.H также имеются прототипы строковых функций. Комитетом ANSI/ISO C++ предложен класс для работы со строками. Строки этого класса больше похожи на строки в языках Pascal и BASIC. (Мы познакомимся с классами в День 8, а со строковым классом в День 11.) Этот раздел будет посвящен рассмотрению некоторых (ни в коей мере не всех) функций, объявленных в STRING.H.
Некоторые функции из STRING.H имеют несколько версий. Дополнительные версии этих функций, имеющих в имени префиксы _f, f или _ работают с указателями типа far. Этих версий вы не встретите в плоской, 32-битной модели памяти компилятора Borland.
Присвоение значений строкамC++ поддерживает два способа присвоения значений строкам. Вы можете присвоить строковой переменной строковую константу, произведя инициализацию при объявлении строки. Этот метод прост: требуется операция присваивания и строковая константа.
Инициализация строкиОбщий метод инициализации строки:
char stringVar[stringSize] = stringLiteral;
Пример
char a3tring[81] = "Borland C++ 5 in 21 days";
char Named = "Rene Kinner";
Второй способ присвоить значение строке — это вызвать функцию, которая копирует содержимое одной строки в другую, — не забывая при этом и нуль-символ. Эта функция называется strcpy. Она предполагает, что копируемая строка оканчивается символом NUL и прекращает копирование, как только встретит этот символ.
Функция strcpyПрототип функции strcpy таков:
char* strcpy(char *target, const char *source);
Функция копирует строку source в строку target. Функция предполагает, что целевая строка имеет размер, достаточный для того, чтобы вместить содержимое строки-источника.
Пример
char name[41] ;
strcpy(name, "Borland C++ 5");
Переменная name содержит строку "Borland C++ 5".
Функция strdupФункция strdup копирует одну строку в другую, при этом отводит необходимое количество памяти для целевой строки.
Прототип функции strdup таков:
char* strdup(const char *source);
Функция копирует строку source и возвращает указатель на строку-копию.
Пример
char *string1 = "Монархия в Испании";
char *string2;
string2 = strdup(string1);
После того, как будет отведено необходимое количество памяти для строки string2, строка string1будет скопирована в строку string2.
Функция strncpyБиблиотека строковых функций предлагает также функцию strncpy, копирующую заданное количество символов из одной строки в другую.
Прототип функции strncpy таков:
char * strncpy(char *target, const char *source, size_t num);
Функция копирует num символов из строки source в строку target. Функция не выполняет ни усечение, ни заполнение строки.
Пример
char str1[] = "Pascal";
char str2[] = "Hello there";
strcnpy(strl, str2, 5);
Переменная strl содержит строку "Hellol". Заметьте, что символ ‘l’ строки-приемника, следующий за скопированной частью строки, сохранился.
Определение длины строкиПри работе со строками часто бывает нужно знать длину строки.
Функция strlenФункция strlen возвращает количество символов в строке, в которое не включается нуль-терминатор.
Прототип функции strncpy таков:
size_t strlen (const char *string) ,
Функция strlen возвращает длину строки string. size_t — это имя, приписанное типу unsigned int оператором typedef.
Пример
char str[] = "1234567890";
size_t i;
i = strlen(str),
Переменной i будет присвоено значение 10.
Конкатенация строк
Операция конкатенации используется достаточно часто, когда новая строка получается объединением двух или более строк.
Присоединить одну строку к другой можно функцией strcat.
Функция strcatКонкатенация строк означает их последовательное присоединение друг к другу.
Прототип функции strcat таков:
char *strcat(char *target, const char *source) ;
Функция добавляет к содержимому целевой строки содержимое строки-источника и возвращает указатель на целевую строку. Функция предполагает, что целевая строка может вместить содержимое объединенной строки.
Пример
char string[81] ;
strcpy(string, "Turbo");
strcat (string, " C++");
Переменная string содержит строку "Turbo C++".
Функция strncatФункция strncat добавляет к содержимому целевой строки указанное количество символов из строки-источника.
Прототип функции strcat :
char *strncat(char *target, const char *source, size_t num);
Функция добавляет к содержимому целевой строки num символов из строки-источника и возвращает указатель на целевую строку.
char strl[81] = "Hello I am ";
char str2[41] = "Keith Thompson";
strncat(strl, str2, 5);
Переменная strl теперь содержит строку "Hello I am Keith".
Пример использования функций getline, strlen и strcat в файле List7_4.cpp (исходный код программы STRING.CPP). Программа выполняет следующие задачи:
· Предлагает вам ввести строку; ввод не должен превышать 40 символов
· Предлагает вам ввести вторую строку; ввод не должен превышать 40 символов
· Выводит число символов, содержащихся в каждой строке
· Присоединяет вторую строку к первой
· Выводит результат конкатенации
· Выводит длину объединенной строки
· Предлагает вам ввести символ для поиска
· Предлагает вам ввести символ для замены
· Выводит содержимое объединенной строки после замены символа
Сравнение строкПоскольку строки являются массивами символов, вы не можете применить операцию сравнения для проверки равенства двух строк. Библиотека функций STRING.H предлагает набор функций для сравнения строк. Эти функции сравнивают символы двух строк, используя для этого ASCII-коды символов. Это функции strcmp, stricmp, strncmp и strnicmp.
Вообще говоря, все функции сравнения работают одинаково: возвращают 0, если две строки совпали, отрицательную величину, если вторая строка больше по величине, и положительное значение, если большей оказалась первая строка.
Функция strcmpФункция strcmp выполняет сравнение двух строк с учетом регистра символов.
Прототип функции strcmp:
int strcmp(const char *strl, const char *str2);
Функция сравнивает строки strl и str2. Возвращает в качестве результата сравнения целую величину:
< 0 когда strl меньше, чем str2;
= 0 когда strl равна str2;
> 0 когда strl больше, чем str2.
Примерchar stringl[] = "Borland C++";
char string2[] = "BORLAND C++";
i = strcmp(string1, string2);
В последнем операторе переменной i присваивается положительное значение, так как string1 больше string2 (ASCII-коды символов в нижнем регистре больше ASCII-кодов символов в верхнем.)
Функция stricmpФункция stricmp выполняет сравнение двух строк, не учитывая регистра символов.
Прототип функции stricmp:
int stricmp(const char *strl, const char *str2);
Функция сравнивает строки strl и str2, не делая различия между символами в нижнем и верхнем регистре. Возвращает в качестве результата сравнения целую величину:
< 0 когда strl меньше, чем str24
= 0 когда strl равна str24
> 0 когда strl больше, чем str2.
Примерchar string1[] = "Borland C++";
char string2[] = "BORLAND C++";
int i = strcmp(string1, string2);
В последнем операторе переменной i присваивается значение 0, так как string1 и string2 отличаются друг от друга только регистром символов.
Функция strncmp выполняет сравнение заданного количества символов двух строк с учетом регистра символов.
Функция strncmpПрототип функции strncmp:
int strncmp(const char *strl, const char *str2, size_t num);
Функция сравнивает первые num символов строк strl и str2. Возвращает в качестве результата сравнения целую величину:
< 0 когда strl меньше, чем str2;
= 0 когда strl равна str2;
> 0 когда strl больше, чем str2.
Примерchar string1[] = "Borland C++";
char string2[] = "Borland Pascal";
i = stricmp(string1, string2, 9);
В последнем операторе переменной i присваивается отрицательное значение, так как значение "Borland С" меньше, чем "Borland Р".
Функция strnicmp
Функция strnicmp выполняет сравнение заданного количества символов двух строк без учета регистра символов.
Прототип функции strnicmp :
int strnicmp(const char *strl, const char *str2, size_t num);
Функция сравнивает первые num символов строк strl и str2, не делая различия в регистре символов. Возвращает в качестве результата сравнения целую величину:
< 0 когда strl меньше, чем str2;
= 0 когда strl равна str2;
> 0 когда strl больше, чем str2.
Примерchar string1[] = "Borland C++";
char string2[] = "BORLAND Pascal";
i = strnicmp(string1, string2, 7);
В последнем операторе переменной i присваивается значение 0, так как подстрока "Borland" отличается в этих строках только регистром.
Рассмотрим пример программы, в которой применяются функции сравнения строк. Программа из листинга 5 объявляет массив строк и присваивает им значения. Затем программа выводит исходный массив, сортирует его и выводит значения строк отсортированного массива. |
(см. List7_5.cpp - Исходный текст программы STRING2.CPP) Преобразование строк Функция strlwrПрототип функции strlwr:
char* strlwr (char *source)
Функция преобразует символы верхнего регистра в символы нижнего регистра в строке source. Другие символы не затрагиваются. Функция возвращает указатель на строку source.
Примерchar str[] = "HELLO THERE";
strlwr(str);
Переменная str теперь содержит строку "hello there".
Функция struprПрототип функции strupr:
char* strupr(char *source)
Функция преобразует символы нижнего регистра в символы верхнего регистра в строке source. Другие символы не затрагиваются. Функция возвращает указатель на строку source.
Примерchar str[] = "Borland C++";
strupr(str);
Переменная str теперь содержит строку "BORLAND С ++".
Обращение строкБиблиотека STRING.H предлагает функцию strrev для записи символов в строке в обратном порядке.
Функция strrevПрототип функции strrev:
char* strrev(char *str)
Функция обращает порядок символов в строке str и возвращает указатель на строку str. char str[] = "Hello";
strrev(str) ;
cout « str;
Будет выведено "olleH".
Рассмотрим программу, которая манипулирует символами в строке. List7_6.cpp показывает исходный текст программы STRING3.CPP. Программа выполняет следующие задачи:
· Запрашивает у вас ввод строки
· Отображает ваш ввод
· Выводит вашу строку в нижнем регистре
· Выводит вашу строку в верхнем регистре
· Отображает символы, которые вы ввели, в обратном порядке
· Выводит сообщение, что ваш ввод не содержит символов верхнего регистра, если это так
· Выводит сообщение, что ваш ввод не содержит символов в нижнем регистре, если это так
· Выводит сообщение, что ваша строка симметрична, если это так
Поиск символовБиблиотека STRING.H предлагает ряд функций для поиска символов в строках. Это функции strchr, strrchr, strspn, strcspn и strpbrk. Они осуществляют поиск в строках символов и простых символьных шаблонов.
Функция strchrФункция strchr определяет первое вхождение символа в строку.
Прототип функции strchr:
char* strchr(const char *target, int c)
Функция находит первое вхождение символа с в строку target. Функция возвращает указатель на символ в строке target, который соответствует заданному образцу с. Если символ с в строке не обнаруживается, функция возвращает 0.
Пример
char str[81] = "Borland C++";
char *strPtr;
strPtr = strchr(str, '+');
Указатель strPtr теперь содержит адрес подстроки "++" в строке str.
Функция strrchrФункция strrchr определяет последнее вхождение символа в строке.
Прототип функции strrchr:
char* strrchr(const char *target, int c)
Функция находит последнее вхождение символа с в строку target. Функция возвращает указатель на символ в строке target, который соответствует заданному образцу с. Если символ с в строке не обнаруживается, функция возвращает 0.
Примерchar str[81] = "Borland C++ is here";
char* strPtr;
strPtr = strrchr(str, '+');
Указатель strPtr теперь указывает на подстроку "+ is here " в строке str.
Функция StrspnФункция strspn возвращает число символов с начала строки, совпадающих с любым символом из шаблона.
Прототип для функции strspn:
size_t strspn(const char *target, const char *pattern)
Функция strspn возвращает число символов от начала строки target, совпадающих с любым символом из шаблона pattern.
Примерchar str[] = "Borland C++ 5";
char substr[] = "narlBod";
int index;
index = strspn(str, substr);
Этот оператор присваивает 8 переменной index, потому что первые восемь символов из str содержатся в подстроке substr.
Функция strcspnФункция strcspn просматривает строку и выдает число первых символов в строке, которые не содержатся в шаблоне.
Прототип функции strcspn:
size_t strcspn(const char* str1, const char* str2)
Функция strcspn просматривает строку str1 и выдает длину подстроки, отсчитываемой с начала строки, символы которой полностью отсутствуют в строке str2.
Примерchar strng[] = "The rain in Spain";
int i = strcspn(strng, " in");
Этот пример возвращает 3 (расположение первого пробела в строке strng) переменной i.
Функция strpbrkФункция strpbrk просматривает строку и определяет первое вхождение любого символа из образца.
Прототип функции strpbrk:
char* strpbrk(const char* target, const char* pattern)
Функция strpbrk ищет в строке target первое вхождение любого символа из образца pattern. Если символы из образца не содержатся в строке, функция возвращает 0.
Примерchar *str = "Hello there how are you";
char *substr = "hr";
char *ptr;
ptr = strpbrk(str, substr);
cout « ptr « endl;
Вы увидите на экране строку "here how are you", потому что 'h' встречается в строке str раньше, чем 'r'.
Поиск строкБиблиотека функций STRING.H предлагает для поиска подстроки в строке функцию strstr.
Функция strstrПрототип функции strstr:
char* strstr(const char *str, const char *substr);
Функция ищет в строке str первое вхождение подстроки substr. Функция возвращает указатель на первый символ найденной в строке str подстроки substr. Если строка substr не обнаружена в строке str, функция возвращает 0.
Примерchar str[] = "Hello there! how are you";
char substr[] = "how";
char *ptr;
ptr = strstr (str, substr);
cout « ptr « endl ;
Это приведет к выводу строки "how are you", поскольку в строке str , была обнаружена подстрока "how". Указатель ptr содержит адрес остатка первоначальной строки, начинающегося с подстроки "how".
Функция strtokБиблиотека функций для работы со строками имеет функцию strtok, которая дает вам возможность разбить строку на подстроки на основании заданного набора символов-ограничителей.
Подстроки иногда называются лексемами.
Прототип функции strtok:
char* strtok(char *target, const char * delimiters);
Функция разбивает строку на лексемы, согласно символам-ограничителям, заданным в параметре delimeters. В следующем примере показано, как работать с этой функцией и как получать лексемы, на которые была разбита строка. Функция strtok вводит символ '\0' после каждой лексемы. (Опять же не забудьте сохранить копию вашей строки в другой строковой переменной.)
Пример#include <stdio.h> // см. файл Ex02.cpp
#include <string.h>
int main()
{
char *str = "(Base_Cost + Profit) * Margin";
char *tkn = "+*()";
char *ptr = str;
printf("%s\n", str);
// Первый вызов функции
ptr = strtok(str, tkn);
printf("Лексемы этой строки: %s", ptr);
while (ptr)
{
// Первый аргумент должен быть равен нулю
if ((ptr = strtok(0, tkn)) != 0)
printf (",%s", ptr);
}
printf("\n");
return 0;
}
В результате выполнения этой программы на экран выводятся следующие строки:
(Base_Cost + Profit) * Margin
Лексемы этой строки: Base_Cost, Profit, Margin
Рассмотрим пример программы поиска символов и строк. Листинг 7 (List7_7.cpp) содержит исходный текст программы STRING4.CPP. Программа выполняет следующие задачи:
· Запрашивает у вас ввод основной строки
· Запрашивает строку поиска
· Предлагает вам ввести символ поиска
· Выводит линейку цифр и основную строку
· Выводит номер символа в основной строке, с которого начинается строка поиска *
· Выводит номер символа в основной строке, совпавшего с символом поиска.
Основы объектно-ориентированного программирования СИНТАКСИС ОСНОВНЫХ КОНСТРУКЦИЙ Объявление базовых классовВ С++ мы имеем возможность объявлять классы, которые инкапсулируют элементы-данные и функции-элементы. Эти функции изменяют и позволяют обращаться к значениям данных-элементов и выполняют другие задачи.
Базовый класс
Базовый класс определяется следующим образом (синтаксис):
class className
{
private:
<закрытые элементы-данные>
<закрытые конструкторы>
<закрытые функции-элементы>
protected:
<защищенные элементы-данные>
<защищенные конструкторы>
<защищенные функции-элементы>
public:
<открытые элементы-данные>
<открытые конструкторы>
<открытый деструктор>
<открытые функции-элементы>
};
Пример 1:
class point
{
protected:
double х;
double у;
public:
point(double xVal, double yVal);
double getX();
double getY();
void assign(double xVal, double yVal);
point& assign(point &pt);
};
Разделы класса
Классы С++ имеют три различных уровня доступа к своим элементам - как к данным, так и к функциям:
- Закрытые (частные) элементы
- Защищенные элементы
- Открытые элементы
К данным в закрытом разделе имеют доступ только функции-элементы класса.
Классам-потомкам запрещен доступ к закрытым данным своих 6азовых классов.
К данным в защищенной секции имеют доступ функции-элементы класса и классов-потомков. Данные из открытой секции находятся в области видимости функций-элементов класса, функций-элементов классов-потомков, и вообще доступны кому угодно.
Существуют следующие правила для разделов класса:
1. Разделы могут появляться в любом порядке.
2. Один и тот же раздел можно определять несколько раз.
3. Если не определен ни один раздел, компилятор (по умолчанию) объявляет все элементы закрытыми.
4. Помещать данные-элементы в открытый раздел следует только в том случае, если в этом есть необходимость, например, если это упрощает вашу задачу. Обычно элементы-данные помещаются в защищенный раздел, чтобы к ним имели доступ функции-элементы классов-потомков.
5. Используйте для изменения значений данных и доступа к ним функции-элементы. При использовании функции вы можете осуществлять проверку данных и, если нужно, изменять другие данные.
6. Класс может иметь несколько конструкторов.
7. Класс может иметь только один деструктор, который должен объявляться в открытом разделе класса.
8. Функции-элементы (в том числе конструкторы и деструкторы), состоящие из нескольких операторов, должны определяться вне объявления класса. Определение функции может содержаться в том же файле, в котором определяется класс. Это напоминает порядок работы с обычными функциями: задание прототипа и определение функции.
Конструкторы являются специфическим типом функций-элементов, тип возвращаемого значения для которых не указывается, а имя должно совпадать с именем класса-хозяина. Вызываются они при создании нового представителя класса. Деструктор вызывается для разрушения представителя класса.
При определении функции-элемента вы должны указать ее имя и имя ее класса. Сначала вы должны Сначала необходимо указать имя класса (т.н. квалификатор), а затем, через два двоеточия (::), имя функции. В качестве примера рассмотрим такой класс:
class point
{
protected:
double x;
double y;
public:
point(double xVal, double yVal);
double getX();
// другие функции-элементы
};
Определения конструктора и функций-элементов должны выглядеть так
point::point (double xVal, double yVal)
{
// операторы
}
double point::getX()
{
// операторы
}
После того, как вы объявили класс, вы можете использовать имя класса в качестве спецификатора типа данных при объявлении представителей класса. Синтаксис объявления тот же, что и при объявлении переменной.
В листинге 8.1 приведен исходный текст программы RECT.CPP. Программа предлагает вам ввести длину и ширину прямоугольника (в данном примере прямоугольник является объектом). Затем программа выводит значения длины, ширины и площади определенного вами прямоугольника.
КонструкторыКонструкторы и деструкторы в С++ вызываются автоматически, что гарантирует правильное создание и разрушение объектов класса.
Общий вид (синтаксис) объявления конструктора:
class className
{
public:
className(); // конструктор по умолчанию
className(const className &c); // конструктор копии
className(<список параметров>); // остальные конструкторы
};
Пример 2:
// Замечание: Здесь только объявление класса без описания объявленных
// функций-параметров
class point
{
protected:
double x;
double y;
public:
point();
point(double xVal, double yVal);
point(const point &pt);
double getX();
double getY();
void assign(double xVal, double yVal);
point& assign(point &pt);
};
int main()
{
point p1;
point p2(10, 20);
point p3(p2);
p1.assign(p2);
cout << p1.getX() << " " << p1.getY() << endl;
cout << p2.getX() << " " << p2.getY() << endl;
cout << p3.getX() << " " << p3.getY() << endl;
return 0;
}
Конструктор копии создает объект класса, копируя при этом данные из существующего объекта класса.
В С++ имеются следующие особенности и правила работы с конструкторами:
1. Имя конструктора класса должно совпадать с именем класса.
2. Нельзя определять тип возвращаемого значения для конструктора, даже тип void.
3. Класс может иметь несколько конструкторов или не иметь их совсем.
4. Конструктором по умолчанию является конструктор, не имеющий параметров, или конструктор, у которого все параметры имеют значения по умолчанию.
Рассмотрим два примера с фрагментами объявления конструкторов.
// класс с конструктором без параметров
class point1
{
protected:
double x;
double y;
public:
point1();
// другие функции-элементы
};
// конструктор класса имеет параметры со значениями по умолчанию
class point2
{
protected:
double x;
double y;
public:
point2(double xVal = 0, double yVal = 0);
// другие функции-элементы
};
... (изложение), аналитический (что мы узнали? Как это можно применить в своих знаниях, в практике, в дальнейшей жизни?…), дискуссионный методы («Порассуждаем?»). Дидактические требования к лекции, методика её подготовки и чтения. Безусловно, помимо таких составляющих, как личность учителя, знание им предмета, эрудиция, на результат, итоговый выход проведения лекции, будет оказывать её основная ...
... вес, объем, урожайность. Абсолютная шкала является безразмерной шкалой отношений. Тема 2. Вариационные ряды. Пример 1. Приведем оценки 45 студентов по курсу статистика в порядке сдачи экзамена: 5 3 3 4 2 4 4 3 5 4 4 5 5 4 4 3 3 3 2 5 5 4 4 4 3 4 3 4 5 4 4 4 4 3 3 4 3 4 3 2 3 2 3 3 3 При таком представлении информации трудно делать какие-либо выводы об успеваемости. Произведем группировку данным ...
... подводит итоги. Выбор варианта лекции определяется образовательными целями, индивидуальным стилем преподавателя. Чтобы получить обратную связь, понять, что думают, чувствуют студенты, преподавателю следует искать способ активизации аудитории, мотивирования студентов к дальнейшему обучению. Выводы Лекция – это важнейшая форма обучения, характеризуемая логически стройным, последовательны
... – 5); · принцип приоритета общечеловеческих ценностей перед профессиональными (4). Очевидно, что данное деление условно, но оно удобно для классификации. Заключение Лекции присущи три основные педагогические функции, которые определяют ее возможности в учебном процессе: познавательная, развивающая и организующая. Познавательная функция выражается в возможности средствами лекции ...
0 комментариев