Спецификации формата для функций семейства printf/ scanf
Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

Модификаторы формата

 

Совместно с спецификатором формата может использоваться модификатор формата. Модификаторы формата применяются для управления шириной поля вывода, отводимого для размещения значения переменной. Модификаторы — это одно или два числа, первое из которых задает минимальное количество позиций, отводимых под число (ширина поля вывода), а второе — сколько из этих позиций отводится под дробную часть числа (точность). Если указанного количества позиций для размещения значения недостаточно, автоматически выделяется большее количество позиций:

 

• %-minC или %minC;

• %min.precisionC или %min.precisionC.

Здесь С — спецификация формата из приведенной выше таблицы, min — число, задающее минимальную ширину поля. Смысл модификатора precision, также задаваемого десятичным числом, зависит от спецификации формата, с которой он используется:

• при выводе строки (спецификация %s) precision указывает максимальное число символов для вывода;

• при выводе вещественного числа (спецификации %f или %е) precision указывает количество цифр после десятичной точки;

• при выводе целого числа (спецификации %d  или %i), precision указывает минимальное количество выводимых цифр. Если число представляется меньшим числом цифр, чем указано в precision, выводятся ведущие (начальные) нули.

• при выводе вещественного числа (спецификации %d или %G) precision указывает максимальное количество значащих цифр, которые будут выводится.

Символ минус (-) указывает на то, что значение выравнивается по левому краю и, если нужно, дополняется пробелами справа. При отсутствии минуса значение выравнивается по правому краю и дополняется пробелами слева.

Перед спецификацией могут использоваться префиксы l и h, например, %lf, %hu.

Префикс h с типами d, i, о, х и X указывает на то, что тип аргумента short int, а с типом u - short unsigned int .

Префикс l с типами d, i, о, х и X указывает на то, что тип аргумента long int, с типом u - long unsigned int, а с типами е, Е, f, g и G - что тип аргумента double, а не float.

       Пример:

 

#include <stdio.h>

#include <conio.h>

int main()

{

clrscr();

int int1 = 45, int2 = 13;

float f = 3.621;

double dbl = 2.23;

char ch = 'z', *str = "ramambahari";

printf(“int1 = %d| int2 = %3d| int2 = %-4d|\n”,int1,int2,int2);

printf ("int1 = %X| int2 = %3x| int2 = %4o|\n”,int1,int2,int2);

printf(“f = %f| f = %4.2f| f = %6.1f|\n”, f, f,f);

printf(“f = %g| f = %e| f = %+E|\n”, f, f, f);

printf(“dbl = %5.2lf | bdl = %e| dbl = %4.1G|\n”,dbl,dbl,dbl);

printf("ch = %c| ch = %3c| \n”,ch,ch);

printf(“str = %14s| \nstr = %-14s|\nstr = %s|\n”,str,str,str);

getch();return 0;

}

Результат работы программы:

int1 = 45| int2 = 13| int2 = 13 |

int1 = 2D| int2 = d| int2 = 15 |

f = 3.621000| f = 3.62 | f = 3.6 |

f = 3.621      I f = 3.621000e+000 | f = +3.621000E+000 |

dbl = 2.23 I dbl = 2.230000e+000 | dbl = 2                   |

ch = z| ch = z  I

str =     ramambahari|

str = ramambahari    |

str = ramambahari |

 

Замечание.

Кроме рассмотренных выше функций ввода/вывода существуют и другие.

Функции вывода puts и putchar.

Функция puts выводит на экран строку, оканчивающуюся символом перехода на новую строку: puts(“Привет Вася.”);

Функция, putchar, выводит на экран один символ и не до­бавляет \n. Оператор putchar(ch) эквивалентен printf("%c",ch).

В чем смысл использования puts и/или putchar взамен функции printf? Одной из причин является достаточно большой размер функции printf. Если только она вам действительно не нужна для цифрового вывода или специального форматирования, лучше воспользоваться функ­циями puts и putchar, что сделает вашу программу меньше и быстрее.

Функции ввода gets и getch.

Функция gets воспринимает любую информацию до тех пор, пока вы не нажмете клавишу Enter. Код, соответствующий этой клавише, в строку не вводится, но в конце строки добавляется нулевой символ \0.

Функция getch считывает с кла­виатуры один символ, не отображая его на экране (в отличие от функ­ции scanf и gets). Эта функция не передает символ в качестве пара­метра; она сама имеет тип char, и ее значение может быть непосредс­твенно присвоено переменной типа char, например: ch = getch ();

3. Заголовочный файл <iostream.h> содержит описание набора классов для управления вводом/выводом. В нем определены стандартные объекты-потоки cin для ввода с клавиатуры и cout для вывода на экран, а также операции помещения в поток « и чтения из потока ». Чуть позднее, мы рассмотрим ввод и вывод данных через потоки cin / cout.

Пример программы с использованием потоков cin / cout из библиотеки классов C++:

#include <iostream.h>

#include <conio.h>

void main()

{

int a;

cout << "Введите целое число\n";

cin >> a;

cout << "Вы ввели число " << a << " спасибо!";

getch();

}

Переменные и выражения

В любой программе требуется производить вычисления. Для вычисления значений используются выражения, которые состоят из операндов, знаков операций и скобок. Операнды задают данные для вычислений. Операции задают действия, которые необходимо выполнить. Каждый операнд является, в свою очередь, выражением или одним из его частных случаев, например, константой или переменной. Операции выполняются в соответствии с приоритетами. Для изменения порядка выполнения операций используются круглые скобки. Рассмотрим составные части выражений и правила их вычисления.

Переменные

Переменная — это именованная область памяти, в которой хранятся данные определенного типа. У переменной есть имя и значение. Имя служит для обращения к области памяти, в которой хранится значение. Во время выполнения программы значение переменной можно изменять. Перед использованием любая переменная должна быть описана.

Пример описания целой переменной с именем а и вещественной переменной х:

 

int a ; float x ;

 

Общий вид оператора описания переменных:

[класс памяти] [const] тип имя [инициализатор];

 

Рассмотрим правила задания составных частей этого оператора.

• Необязательный класс памяти может принимать одно из значений auto, extern, static и register. О них рассказывается чуть ниже.

• Модификатор const показывает, что значение переменной изменять нельзя. Такую переменную называют именованной константой, или просто константой.

• При описании можно присвоить переменной начальное значение, это называется инициализацией. Инициализатор можно записывать - со знаком равенства:

= значение

Константа должна быть инициализирована при объявлении. В одном операторе можно описать несколько переменных одного типа, разделяя их запятыми.

       Например:

short int а = 1;                  // целая переменная а

const char С = ‘С’ ; // символьная константа С

char s, sf = ‘f’ ;                 // инициализация относится только к sf;

float с = 0.22, x =3, su ;

 

Если тип инициализирующего значения не совпадает с типом переменной, выполняются преобразования типа по определенным правилам.

Описание переменной, кроме типа и класса памяти, явно или по умолчанию задает ее область действия. Класс памяти и область действия зависят не только от собственно описания, но и от места его размещения в тексте программы.

Область действия идентификатора — это часть программы, в которой его можно использовать для доступа к связанной с ним области памяти. В зависимости от области действия переменная может быть локальной или глобальной.

Если переменная определена внутри блока (напомню, что блок ограничен фигурными скобками), она называется локальной, область ее действия — от точки описания до конца блока, включая все вложенные блоки. Если переменная определена вне любого блока, она называется глобальной и областью ее действия считается файл, в котором она определена, от точки описания до его конца.

Класс памяти определяет время жизни и область видимости программного объекта (в частности, переменной). Если класс памяти не указан явным образом, он определяется компилятором исходя из контекста объявления.

Время жизни может быть постоянным (в течение выполнения программы) и переменным (в течение выполнения блока).

Областью видимости идентификатора называется часть текста программы, из которой допустим обычный доступ к связанной с идентификатором областью памяти. Чаще всего область видимости совпадает с областью действия. Исключением является ситуация, когда во вложенном блоке описана переменная с таким же именем. В этом случае внешняя переменная во вложенном блоке невидима, хотя он и входит в ее область действия. Тем не менее, к этой переменной, если она глобальная, можно обратиться, используя операцию доступа к области видимости - :: .

Для задания класса памяти используются следующие спецификаторы:

* auto — автоматическая переменная. Память под нее выделяется в стеке и при необходимости инициализируется каждый раз при выполнении оператора, содержащего ее определение. Освобождение памяти происходит при выходе из блока, в котором описана переменная. Время ее жизни — с момента описания до конца блока. Для глобальных переменных этот спецификатор не используется, а для локальных он принимается по умолчанию, поэтому задавать его явным образом большого смысла не имеет.

* extern — означает, что переменная определяется в другом месте программы (в другом файле или дальше по тексту). Используется для создания переменных, доступных во всех модулях программы, в которых они объявлены.

* static — статическая переменная. Время жизни — постоянное. Инициализируется один раз при первом выполнении оператора, содержащего определение переменной. В зависимости от расположения оператора описания статические переменные могут быть глобальными и локальными. Глобальные статические переменные видны только в том модуле, в котором они описаны.

* register — аналогично auto, но память выделяется по возможности в регистрах процессора. Если такой возможности у компилятора нет, переменные обрабатываются как auto.

 

       Например:

int а;                      // 1 глобальная переменная а

int main()

{

int b;          // 2 локальная переменная b

extern int x ; // 3 переменная x определена в другом месте

static int с; // 4 локальная статическая переменная с

а = 1 ;         // 5 присваивание глобальной переменной

int а;          // 6 локальная переменная а

а = 2;         // 7 присваивание локальной переменной

::а = 3;      // 8 присваивание глобальной переменной

return 0;

}

int x = 4;                // 9 определение и инициализация х

 

В этом примере глобальная переменная а определена вне всех блоков. Память под нее выделяется в сегменте данных в начале работы программы, областью действия является вся программа. Область видимости — вся программа, кроме строк 8-6, так как в первой из них определяется локальная переменная с тем же именем, область действия которой начинается с точки ее описания и заканчивается при выходе из блока. Переменные b и с — локальные, область их видимости — блок, но время жизни различно: память под b выделяется в стеке при входе в блок и освобождается при выходе из него, а переменная с располагается в сегменте данных и существует все время, пока работает программа.

Если при определении начальное значение переменных явным образом не задается, компилятор присваивает глобальным и статическим переменным нулевое значение соответствующего типа. Автоматические переменные не инициализируются.

Имя переменной должно быть уникальным в своей области действия (например, в одном блоке не может быть двух переменных с одинаковыми именами).

СОВЕТ

Не жалейте времени на придумывание подходящих имен идентификаторов. Имя должно отражать смысл хранимой величины, быть легко распознаваемым и, желательно, не содержать символов, которые можно перепутать друг с другом, например, 1 (строчная L) или I (прописная i).

Для разделения частей имени можно использовать знак подчеркивания. Не давайте переменным имена, демонстрирующие знание иностранного слэнга. Как правило, переменным с большой областью видимости даются более длинные имена (желательно с префиксом типа), а для переменных, вся жизнь которых проходит на протяжении нескольких строк исходного текста, хватит и одной буквы с комментарием при объявлении.

Описание переменной может выполняться в форме объявления или определения.

Объявление информирует компилятор о типе переменной и классе памяти, а определение содержит, кроме этого, указание компилятору выделить память в соответствии с типом переменной. В C++ большинство объявлений являются одновременно и определениями. В приведенном выше примере только описание 3 является объявлением, но не определением.

Переменная может быть объявлена многократно, но определена только в одном месте программы, поскольку объявление просто описывает свойства переменной, а определение связывает ее с конкретной областью памяти.

Операции

В таблице ниже приведен список основных операций, определенных в языке C++, в соответствии с их приоритетами (по убыванию приоритетов, операции с разными приоритетами разделены чертой). Остальные операции будут вводиться по мере изложения.

В соответствии с количеством операндов, которые используются в операциях, они делятся на унарные (один операнд), бинарные (два операнда) и тернарную (три операнда).

Все приведенные в таблице операции, кроме условной и sizeof, могут быть перегружены. О перегрузке операций рассказывается на с. 189.

Таблица 1.5. Основные операции языка C++

 

 

 

 

 

Полный перечень операций языка С++

 

 

Операции увеличения и уменьшения на 1 (++ и --). Эти операции, называемые также инкрементом и декрементом, имеют две формы записи — префиксную, когда операция записывается перед операндом (++i), и постфиксную (i++). В префиксной форме сначала изменяется операнд, а затем его значение становится результирующим значением выражения, а в постфиксной форме значением выражения является исходное значение операнда, после чего он изменяется.

 

#include <stdio.h>

#include <conio.h>

void main()

{

int х = 3, у = 3;

printf("Значение префиксного выражения: %d\n", ++х):

printf("Значение постфиксного выражения: %d\n", у++);

printf("Значение х после приращения: %d\n", х);

printf("Значение у после приращения: %d\n", у);

getch();

}

Результат работы программы:

 

Значение префиксного выражения: 4

Значение постфиксного выражения: 3

Значение х после приращения: 4

Значение у после приращения: 4

Операция определения размера sizeof предназначена для вычисления размера объекта или типа в байтах, и имеет две формы:

Sizeof выражение

sizeof ( тип )

Пример:

#include <iostream.h>

#include <conio.h>

void main()

{

float x = 1;

cout << "sizeof (float) =" << sizeof (float);

cout « "\nsizeof x ="<< sizeof x;

cout « "\nsizeof (x + 1.0) =" << sizeof (x +1.0);

getch();

}

Результат работы программы:

sizeof (float) = 4

sizeof x = 4

sizeof (x + 1.0) = 8

В последнем случае имеем 8, так как к переменной х типа float прибавляется вещественная константа 0.1, которая по умолчанию имеет тип doublе, поэтому происходит преобразование типов данных к более длинному, т.е. double. Для переменных типа double в оперативной памяти отводится 8 байт.

Операции отрицания (- , ! , ~).

Арифметическое отрицание (унарный минус -) изменяет знак операнда целого или вещественного типа на противоположный.

Логическое отрицание (!) дает в результате значение 0, если операнд есть истина (не нуль), и значение 1, если операнд равен нулю. Операнд должен быть целого или вещественного типа, а может иметь также тип указатель.

Поразрядное отрицание (~), часто называемое побитовым, инвертирует каждый разряд в двоичном представлении целочисленного операнда.

Деление (/) и остаток от деления (%).

Операция деления (/)применима к операндам арифметического типа. Если оба операнда целочисленные, результат операции округляется до целого числа, в противном случае тип результата определяется правилами преобразования (см. раздел «Выражения», с. 38, и приложение 3).

Операция остатка от деления (%) применяется только к целочисленным операндам. Знак результата зависит от реализации.

#include <stdio.h>

#include <conio.h>

void main()

{

int x = 11, у = 4;

float z = 4;

printf("Результаты деления: x/y=%d  x/z=%f\n", x/y, x/z):

printf("Остаток = %d\n", x%y);

getch();

}

Результат работы программы:

Результаты деления: x/y=2 x/z=2.750000

Остаток= 3

 

Операции сдвига ( << и >> ) применяются к целочисленным операндам. Они сдвигают двоичное представление первого операнда влево или вправо на количество двоичных разрядов, заданное вторым операндом. При сдвиге влево ( << ) освободившиеся разряды обнуляются. При сдвиге вправо (>>) освободившиеся биты заполняются нулями, если первый операнд беззнакового типа, и знаковым разрядом в противном случае. Операции сдвига не учитывают переполнение и потерю значимости.

Операции отношения (<, <=, >, >=, == , !=) сравнивают первый операнд со вторым. Операнды могут быть арифметического типа или указателями. Результатом операции является значение true или false (любое значение, не равное нулю, интерпретируется как true). Операции сравнения на равенство и неравенство имеют меньший приоритет, чем остальные операции сравнения.

Поразрядные операции (&, | , ^) применяются только к целочисленным операндам и работают с их двоичными представлениями. При выполнении операций операнды сопоставляются побитово (первый бит первого операнда с первым битом второго, второй бит первого операнда со вторым битом второго, и т д.).

При поразрядной конъюнкции, или поразрядном И (операщия обозначается &) бит результата равен 1 только тогда, когда соответствующие биты обоих операндов равны 1.

При поразрядной дизъюнкции, или поразрядном ИЛИ (операция обозначается |) бит результата равен 1 тогда, когда соответствующий бит хотя бы одного из операндов равен 1.

При поразрядном исключающем ИЛИ (операция обозначается ^) бит результата равен 1 только тогда, когда соответствующий бит только одного из операндов равен 1.

#include <iostream.h>

#include <conio.h>

void main()

{

cout << "\n 6 & 5 = " << (6 & 5);

cout << "\n 6 | 5 = " << (6 | 5);

cout << "\n 6 ^ 5 = " << (6 ^ 5;)

getch();

}

Результат работы программы:

6 & 5 = 4

6 | 5 = 7

6 ^ 5 = 3

Логические операции (&& и ||). Операнды логических операций И (&&) и ИЛИ (||) могут иметь арифметический тип или быть указателями, при этом операнды в каждой операции могут быть различных типов. Преобразования типов не производятся, каждый операнд оценивается с точки зрения его равенства нулю (операнд, равный нулю, рассматривается как false, не равный нулю — как true). Результатом логической операции является true (не ноль) или false (ноль).

Результат операции логическое И имеет значение true только если оба операнда имеют значение true.

Результат операции логическое ИЛИ имеет значение true, если хотя бы один из операндов имеет значение true.

Логические операции выполняются слева направо.

Если значения первого операнда достаточно, чтобы определить результат операции, второй операнд не вычисляется: if ( (q = = 0) && (cos(q) = =0) ) printf(“True”);

                                          else printf(“False”);

Операции присваивания (=, +=, -=, *= и т. д.). Операции присваивания могут использоваться в программе как законченные операторы.

Формат операции простого присваивания (=):

<Идентификатор> = <выражение>;

Сначала вычисляется <выражение>, стоящее в правой части операции, а потом его результат записывается в область памяти, указанную в левой части.

Замечание.

При присваивании производится преобразование типа выражения к типу <идентификатора>, что может привести к потере информации.

Условная операция ( ?:). Эта операция тернарная, то есть имеет три операнда. Ее формат: 

операнд_1 ? операнд_2 : операнд_3;

       Здесь операнд_1 операнд может иметь арифметический тип или быть указателем. Он оценивается с точки зрения его эквивалентности нулю (операнд, равный нулю, рассматривается как false, не равный пулю — как true). Если результат вычисления операнда_1 равен true, то результатом условной операции будет значение операнда_2, иначе — операнд3. Вычисляется всегда либо операнд_2, либооперанд_3. Их тип может различаться.

Например: определить максимум из двух целых чисел.

#inc1ude <stdio.h>

void main()

{     int a = 11, b = 4, max; 

max = (b > a) ? b : a;

printf("Наибольшее число: %d", max);

}

Преобразование типов данных

Стр.38, 231, приложение 3

       При вычислении различных типов выражений в языке С/С++ может происходить неявное и явное преобразование типов данных. Преобразование типов происходит, если в выражении участвуют операнды различного типа (более короткие типы преобразуются в более длинные для сохранения значимости и точности, что используется как тип результата), а это повлияет на результат операции.

В основе всех преобразований лежит изменение внутреннего представления величин (с потерей точности или без потери точности).

       Преобразования типов всегда происходит по определенным правилам:

       1) Любые операнды типа char, unsigned char или short преобразуются к типу int по правилам:

* char расширяется нулем или знаком в зависимости от умолчания для char; 

* unsigned char расширяется нулем; 

* signed char расширяется знаком; 

* short, unsigned short и enum при преобразовании не изменяются.

* Затем любые два операнда становятся либо int, либо float, double или long double. 

2) Если один из операндов имеет тип long double, то другой преобразуется к типу long double. 

3) Если один из операндов имеет тип double, то другой преобразуется к типу double. 

4) Если один из операндов имеет тип float, то другой преобразуется к типу float. 

5) Если один из операндов имеет тип unsigned long, то другой преобразуется к типу unsigned long.

6) Если один из операндов имеет тип long, то другой преобразуется к типу long. 

7) Если один из операндов имеет тип unsigned, то другой преобразуется к типу unsigned.

 

Для выполнения явных преобразований типа в C++ существует целая группа операций: const_cast, dynamic_cast, reinterpret_cast и static_cast, а также операция приведения типа, унаследованная из языка С (эта операция использовалась нами в лабораторных работах). Рассмотрим эти операции.

1) Операция приведения типов. Формат использования:

                              <тип> (<переменная>)

                              (<тип>) <переменная>

Результатом операции является значение заданного <типа>.

int а = 2; 

float b = 6.8; 

printf(“a = %f b = %d”, double (a), (int) b);

Величина a преобразуется к типу double, a переменная b — к типу int с отсечением дробной части.

2) Операция const_cast. Операция служит для удаления модификатора const. Как правило, она используется при передаче в функцию константного указателя на место обычного (изменяемого) формального параметра, не имеющего модификатора const, а это запрещено. Операция const_cast снимает это ограничение. Естественно, функция не должна пытаться изменить значение, на которое ссылается передаваемый указатель, иначе результат выполнения программы не определен.

Формат операции:

const_cast <тип> (<выражение>);

Обозначенный <тип> должен быть таким же, как и тип <выражения>, за исключением модификатора const. Обычно это указатель. Операция формирует результат указанного <типа>. 

Пример пользовательской функции:

void print ( int *р) // Функция не изменяет значение указателя *р

cout « *р; 

……………..

const int *р;

print ( p );   // Ошибка, поскольку р объявлен как указатель на константу

Операция const_cast используется в том случае, когда программист уверен, что в теле функции значение, на которое ссылается указатель, не изменяется. Естественно, если есть возможность добавить к описанию формального параметра модификатор const, это предпочтительнее использования преобразования типа при вызове функции.

3) Операция dynamic _ cast. Операция применяется для преобразования указателей родственных классов иерархии, в основном — указателя базового класса в указатель на производный класс, при этом во время выполнения программы производится проверка допус­ тимости преобразования. Формат операции: dynamic_cast <тип *> (выражение) Выражение должно быть указателем или ссылкой на класс, тип — базовым или производным для этого класса. После проверки допустимости преобразования в случае успешного выполнения операция формирует результат заданного типа, в противном случае для указателя результат равен нyлю, а для ссылки порождается исключение bad_cast. Если заданный тип и тип выражения не относятся к одной иерархии, преобразование не допускается. Преобразование из базового класса в производный, называют понижающим (downcast), так как графически в иерархии наследования принято изображать производные классы ниже базовых. Приведение из производного класса в базовый называют повышающим (upcast), а приведение между производными класса­ ми одного базового или, наоборот, между базовыми классами одного производ­ ного — перекрестным (crosscas

Повышающее преобразование

Выполнение с помощью операции dynamic_cast повышающего преобразования равносильно простому присваиванию:

Понижающее преобразование 

Чаще всего операция dynamic_cast применяется при понижающем преобразовании — когда компилятор не имеет возможности проверить правильность приведения. Производные классы могут содержать функции, которых нет в базовых классах. Для их вызова через указатель базового класса нужно иметь уверенность, что этот указатель в действительности ссылается на объект производного класса. Та­ кая проверка производится в момент выполнения приведения типа с использова­ нием RTTI (run-time type information) — «информации о типе во время выполне­ ния программы». Для того чтобы проверка допустимости могла быть выполнена, аргумент операции dynamic_cast должен быть полиморфного типа, то есть иметь хотя бы один виртуальный метод (см. с. 205).

Для полиморфного объекта реализация операции clynam1c_cast весьма эффектив­ на, поскольку ссылка на информацию о типе объекта заносится в таблицу вирту­ альных методов, и доступ к ней осуществляется легко. С точки зрения логики требование, чтобы объект был полиморфным, также оп­ равдано: ведь если класс не имеет виртуальных методов, его нельзя безопасным образом использовать, не зная точный тип указателя. А если тип известен, ис­ пользовать операцию dynam1c_cast нет необходимости. Результат примепения операции dynamic^cast к указателю всегда требуется про­ верять явным образом. В приведенном ниже примере описан полиморфный базо­ вый класс В и производный от него класс С, в котором определена функция f2. Для того чтобы вызывать ее из функции demo только в случае, когда последней передается указатель на объект производного класса, используется операция dynamic_cast с проверкой результата преобразования:

При использовании в этом примере вместо dynamic_cast приведения типов в сти­ ле С, например: С* с = (С*) р: проконтролировать допустимость операции невозможно, PI если указатель р на самом деле не ссылается на объект класса С, это приведет к ошибке. Другим недостатком приведения в стиле С является невозможность преобра­ зования в производный виртуального базового класса, это запрещено синтакси-чески. С помощью операции dynamic_cast такое преобразование возможно при условии, что класс является полиморфным и преобразование недвусмыслерню. Рассмотрим пример, в котором выполняется понижающее преобразование виртуального базового класса

 

Преобразование ссылок Для аргумента-ссылки смысл операции преобразования несколько иной, чем для указателя. Поскольку ссылка всегда указывает на конкретный объект, операция dynamic_cast должна выполнять преобразование именно к типу этого объекта. Корректность приведения проверяется автоматически, в случае несовпадения порождается исключение bad_cast:

Перекрестное преобразование Операция dynamic_cast позволяет выполнять безопасное преобразование типа между производными классами одного базового класса, например:

 

 

Классы С и D являются производными от класса В. Функции demo передается ука­ затель на класс D, являющийся на самом деле указателем на «братский» для него класс С, поэтому динамическое преобразование типа из D в С в функции demo завершается успешно.

При необходимости можно осуществить преобразование между базовыми клас­ сами одного производного класса, например:

Класс D является потомком В и С, поэтому содержит методы обоих классов. Если в функцию demo передается на самом деле указатель не на В, а на D, его можно пре­ образовать к его второму базовому классу С.

4) Операция static_cast . Операция stat1c__cast используется для преобразования типа на этапе компиля­ ции между:

целыми типами;

целыми и вещественными типами;

целыми и перечисляемыми типами;

указателями и ссылками на объекты одной иерархии, при условии, что оно однозначно и не связано с понижающим преобразованием виртуального базо­ вого класса.

 

Формат операции:

static_cast <тип> (выражение)

Результат операции имеет указанный тип, который может быть ссылкой, указа­ телем, арифметическим или перечисляемым типом. При выполнении операции внутреннее представление данных может быть моди­ фицировано, хотя численное значение остается неизменным. Например:

Такого рода преобразования применяются обычно для подавления сообщений компилятора о возможной потере данных в том случае, когда есть уверенность, что требуется выполнить именно это действие. Результат преобразования остает­ ся на совести программиста. Операция static_cast позволяет выполнять преобразования из производного класса в базовый и наоборот без ограничений: 

Преобразование выполняется при компиляции, при этом объекты могут не быть полиморфными. Программист должен сам отслеживать допустимость дальней­ ших действий с преобразованными величинами. В общем случае использование для преобразования указателей родственных классов иерархии предпочтительнее использовать операцию dynamic_cast. В этом случае если преобразование возможно на этапе компиляции, генерируется тот же код, что и для static_cast. Кроме того, dynam1c_cast допускает перекрестное пре­ образование, нисходящее приведение виртуального базового класса и произво­ дит проверку допустимости приведения во время выполнения.

5) Операция reinterpret_cast Операция reinterpret_cast применяется для преобразования не связанных между собой типов, например, указателей в целые или наоборот, а также указателей типа void* в конкретный тип. При этом внутреннее представление данных оста­ ется неизменным, а изменяется только точка зрения компилятора на данные.  Формат операции:

reinterpret_cast <тип> (выражение)

Результат операции имеет указанный тип, который может быть ссылкой, указа­ телем, целым или вещественным типом.

 

Различие между static^cast и re1nterpret_cast позволяет компилятору произво­ дить минимальную проверку при использовании stat1c_cast, а программисту — обозначать опасные преобразования с помощью re1nterpret_cast. Результат пре­ образования остается на совести программиста.

Динамическое определение типа 

Механизм идентификации типа во время выполнения программы (RTTI) позво­ ляет определять, на какой тип в текущий момент времени ссылается указатель, а также сравнивать типы объектов. Для доступа к RTTI в стандарт языка введена операция typeid и класс type_1nfoi. Формат операции typeid:

 typeid (тип) typeid (выражение)

Базовые конструкции алгоритмического языка С/С++

Оператор «выражение».

Любое выражение, завершающееся точкой с запятой, рассматривается как оператор, выполнение которого заключается в вычислении выражения. Частным случаем выражения является пустой оператор ; (он используется, когда по синтаксису оператор требуется, а по смыслу — нет). Примеры:

i++;            //

а* = b + с;

funс(i, к);

 

Операторы ветвления

1) Условный оператор if Условный оператор if используется для разветвления процесса вычислений на два направления. Структурная схема оператора приведена на рис. 1.5. Формат оператора: 

if ( выражение ) оператор_1; [else оператор_2;] 

Сначала вычисляется выражение, которое может иметь арифметический тип или тип указателя. Если оно не равно нулю (имеет значение true), выполняется оператор_1, иначе — оператор_2. После этого управление передается на оператор, следующий за if. Одна из ветвей может отсутствовать, логичнее опускать вторую ветвь вместе с ключевым словом еlse. Если в какой-либо ветви требуется выполнить несколько операторов, их необходимо заключить в блок, иначе компилятор не сможет понять, где заканчивается оператор if. Блок может содержать любые операторы, в том числе описания и другие условные операторы (но не может состоять из одних описаний). Необходимо учитывать, что переменная, описанная в блоке, вне блока не существует.

Пример 1.

if (а<0) b = 1;                                                                                          //1

if (a<b && (a>d || a = =0 )) b++; else {b *= a; a = 0;}                            //2

if (a<b) { if (a<c) m = a; else m = c;} else { if (b<c) m = b; else m = c;} //3

if (a++) b++;                                                                                            //4

if (b>a) max = b; else max = a;                                                                //5

Здесь:

1) Пропущен else – краткая форма записи оператора if.

2) Запись условия состоящего из нескольких простых условий.

3) Запись вложенных операторов if, причем фигурные скобки необязательны.

4) Показано, что условием может быть не только логическое выражение, а любое арифметическое выражение.

5) Полная форма записи оператора if.

Пример2. Производится выстрел по мишени, изображенной на рис. 1.6. Опреде­ лить количество очков.

#include <iostream.h>

#include <conio.h>

void main()

{

float x, у;

int kol;

cout << "Введите координаты выстрела \n";

cin >> x >> у;

if ( x*x + y*y < 1 ) kol = 2;

else if( x*x + y*y < 4 ) kol = 1;

else коl = 0;

cout << “\n Очков: “ << kol;

getch();

}

 

Замечание.

1) Необходимо внимательно следить за записью условий использующих проверку на равенство (= =), так как возможно использование  простого присваивания (=), например,

if (a=l)b=0;

Синтаксической ошибки нет, так как операция присваивания формирует результат, кото­ рый и оценивается на равенство/неравенство нулю. В данном примере присваивание переменной b будет выполнено независимо от значения переменной а. Поэтому в выражениях проверки переменной на равенство константе константу рекомендуется записывать слева от операции сравнения: if (1= =а) b=0;

2) Вторая ошибка — неверная запись проверки на принадлежность диапазону. Например, чтобы проверить условие 0<х<1, нельзя записать его в условном операторе непосредственно, так как будет выполнено сначала сравнение 0<х, а его результат (true или false, преобразованное в int) будет сравниваться с 1. Правильный способ записи: шf(0<x && х<1)...

2) Оператор switch. Оператор switch (переключатель) предназначен для разветвления процесса вычислений по нескольким направлениям. Структурная схема оператора приведена на рис. 1.7.

Формат оператора:

switch ( <выражение> )

{

case <значение_1> : [<список_операторов_1>]

case < значение_2> : [<список_операторов_2>]

………………………..

case < значение_N> : [<список_операторов_N>]

[default: <список операторов N+1>]

}

 

Выполнение оператора начинается с вычисления <выражения> (оно должно быть целочисленным), а затем управление передается первому оператору из списка, значение которого совпало с вычисленным. После этого, если выход из переключателя явно не указан, последовательно выполняются все остальные ветви. Выход из переключателя обычно выполняется с помощью операторов break или return. Оператор break выполняет выход из самого внутреннего из объемлющих его операторов switch, for, while и do. и do. Оператор return выполняет выход из функции, в теле которой он записан.

Все перечисленные <значения> должны быть разными, но быть одного целочисленного типа. Несколько меток могут следовать подряд. Если совпадения не произошло, выполняются операторы, расположенные после слова default (а при его отсутствии управление передается следующему за switch оператору).

Пример: Написать программу, которая дописывает слово рубль в правильном падеже.

 

 

Дата: 2019-02-02, просмотров: 612.