русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Основныепонятиясегментации


Дата добавления: 2013-12-23; просмотров: 936; Нарушение авторских прав


Лекция 8. http://any-book.org/download/12608.html

Лекция 7. http://any-book.org/download/12608.html

Лекция 6. http://any-book.org/download/12608.html

Лекция 5. http://any-book.org/download/12608.html

Ассоциативность

Операции

Ранг

 

1.

( ) [ ] (вызов функции, индекс массива)

2.

! ~ + - ++ -- sizeof nbg() (унарные)

3.

Операции с классами

 

4.

* / % ( бинарные)

5.

+ - (бинарные)

6.

<< >>

7.

< <= >= > ( отношения )

8.

= = != ( отношения )

9.

& ( побитовая конъюнкция )

10.

^ ( побитовое исключающее ИЛИ )

11.

| ( побитовая дизъюнкция )

12.

&& ( логическая конъюнкция )

13.

| | ( логическая дизъюнкция )

14.

? : ( условная операция )

15.

= *= /= %= += -= &= ^= |= <<= >>=

16.

, ( операция “запятая” )

 

Язык С++ предоставляет программисту возможность самостоятельно задавать порядок выполнения вычислений с помощью скобок. Не существует ограничения на вложенность скобок.

 

Пример.

 

// Ex_05.CPP - использование скобок

 

#include <iostream.h>

 

void main()

 

{ int x = 0, y = 0;

 

int a = 3, b = 34, c = 82;

 

x = a * b + c;

 

y = a * ( b + c );

 

cout << "x= " << x < '\n';

 

cout << "y= " << y < '\n';

 

}

 

Результаты:

 

x = 184



 

y = 348

 

5. Разделители.

 

В число лексических единиц языка входят разделители:

 

[ ] ( ) { } , ; : _ * = # &

 

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

 

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

 

В языке С++ выделяют скалярные и структурированные типы.

 

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

 

К структурированным типам относят массивы, структуры, объединения и классы. Что характерно для скалярных типов и чем они отличаются от структурированных ? Скалярный тип определяет единственное неделимое значение. Например: число, символ, адрес. Структурированный тип определяет конечное множество элементов. Например: массив из ста чисел. Перейдем к изучению скалярных типов.

 

Определение переменных.

 

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

 

- unsigned char ( беззнаковый символьный) - 1 байт;

- char (символьный ) - 1 байт;

- unsigned int (беззнаковый целый ) - 2 байта;

- int (целый) - 2 байта;

- unsigned long (беззнаковое длинное целое) - 4 байта;

- long (длинное целое) - 4 байта;

- float (вещественный) - 4 байта;

- double (вещественный с удвоенной точностью) - 8 байтов;

- long double (длинное вещественное с удвоенной точностью) - 10 байтов;

- void (отсутствие значения);

- enum (перечислимый) - 2 байта;

- bool (логический) - 1 байт.

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

 

Например:

char symbol = ‘\n’;

double pi = 3.141592653589;

long double s;

unsigned int a, b, c; // 0.. 65535

unsigned char ch; // 0..255

 

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

 

Например:

 

….

 

int i;

 

cout << sizeof (i); // 2

 

….

 

Для более удобного обозначения составных типов можно использовать ключевое слово typedef:

 

Typedef unsigned char cod;

 

Здесь COD - это новое имя типа unsigned char. Далее в программе можно сослаться на это имя при определении, например, переменных:

 

COD x,y;

 

В языке С++ из базовых типов можно создавать производные типы с помощью [ ] , ( ) и *, например, указатели, массивы, прототипы функций.

 

Пусть type – допустимый базовый тип, тогда type name [10] ; - это определение массива с именем name, который состоит из десяти элементов типа type.

 

Например:

 

int array[5]; // массив array из 5-ти целых чисел.

 

Выражения.

 

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

 

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

 

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

 

Арифметические операции: +, -, *, /, %, ++, -- .

 

Примеры записи арифметических выражений:

 

float x, ALPHA, BETA;

 

int Z1, Z2;

 

int MAS[4];

 

….

 

157 // целая константа

 

x // вещественная переменная

 

cos(x) // обращение к числовой функции

 

ALPHA*(BETA – 4.5) // вещественные выражения

 

Z1 % Z2 // целое выражение

 

MAS[2] + 1

 

Z1 ++

 

Z2

 

Порядок выполнения операций в выражении определяется их приоритетами. Использование скобок позволяет повысить приоритет операций, содержащихся внутри них. Для удобства можно использовать дополнительные скобки и пробелы при записи выражений.

 

Например:

 

I вариант

 

x = y / 3 – 34* t + 127;

 

II вариант

 

X = (y / 3) – (34*t) + 127;

 

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

 

В языке С++ существует неявное и явное преобразование типов.

 

Неявное преобразование выполняется компилятором в соответствии со следующими правилами.

 

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

 

(char,unsigned char, enum) -> int

 

Все остальные типы приводятся к типу самого большого операнда. Например, если один операнд имеет тип int , а другой - long int (long) , то тип int расширяется в тип long int. Или, если хотя бы один из операндов имеет тип double, любой другой приводится к типу double. После преобразования оба операнда будут иметь один и тот же тип.

 

Значения типа bool при использовании в целочисленном выражении автоматически преобразуются в целые числа 0 и 1. При обратном преобразовании нуль преобразуется в false, а не нулевое значение – в true.

 

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

 

Рассмотрим пример.

 

// Ex_6.CPP - преобразование типов

 

#include <iostream.h>

 

void main()

 

{ long k = 123456789;

 

float g = (float) k;

 

cout << “\n k = “ << k; // к = 123456789

 

cout << “\n g = “ << g; // g = 1.234567е+8

 

k = (long) g;

 

cout << “\n k = “ << k; // к = 123456792

 

}

 

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

 

Логическим операндом может быть логическая константа, логическая переменная, обращение к логической функции и выражение отношения.

 

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

 

>, <, >=, <=, !=, = =. Выражение отношения принимают значение true, если соотношение имеет место и false в противном случае.

 

Примеры записи выражений отношений:

 

int x,y,;

 

char ch;

 

bool b;

 

….

 

x < y

 

char != =’a’

 

b == false

 

Примеры записи логических операндов

 

false

 

b

 

x <= y

 

При записи логических выражений можно пользоваться следующими логическими операциями:

 

!, &&, ||, &, |, ^

 

Примеры записи логических выражений:

 

B != false && x = = y

 

X <= 1 || y >=0

 

Значением логического выражения является логическая константа true или false

 

Основные операторы языка С++

 

1. Оператор присваивания. Иногда его относят не к операторам, а к операциям. Используется для присваивания значений переменной.

 

1. Синтаксис:

 

<переменная> = <выражение>;

 

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

 

2. Семантика: вычисляется значение выражения, которое записывается по адресу, соответствующему переменной. Тип выражения приводится к типу переменной.

 

Например:

 

int x;

 

char ch;

 

float f;

 

 

ch = x; // 1 строка

 

x = f; // 2 строка

 

f = ch; // 3 строка

 

f = x; //4 строка

 

В 1 строке символьной переменной ch будут присвоены младшие 8 бит. Во 2 строке переменная х получит часть значения f.

 

|| При преобразовании вещественного числа в целое отбрасывается дробная часть.

 

В 3 строке восьмиразрядное целое число будет преобразовано в формат с плавающей точкой.

 

В 4 строке целое значение типа int будет преобразовано в формат с плавающей точкой. Следует отметить. Что при преобразовании целого числа в логическое значение типа bool 0 превращается в false, а отличное от нуля значение – в true

 

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

 

Напимер: int x,y,z; … x = y = z = 100;

 

2. .Пустой оператор. Он обозначается ’;’ , перед которой нет никакого оператора. Пустой оператор не выполняет никаких действий. Он используется там, где в соответствии с синтаксисом требуется присутствие оператора, а по смыслу никаких действий не должно выполняться. Пустой оператор чаще всего используется в качестве тела цикла, когда все действия описаны в заголовке оператора for:

 

for ( int i = 0, p =1; I < 5; i++, p* = i);<- пустой оператор

 

Перед каждым оператором можно записывать метку, которая отделяется от оператора символов “:” Метка – уникальный идентификатор, локализованный в сфере действия функции.

 

Например:

 

label : x = y = z – 3.0;

 

3.. Составной оператор. Это последовательность операторов, заключенных в фигурные скобки { …. }

 

1. Синтаксис:

 

{ <оператор>;

 

<оператор>;

 

…..

 

<оператор>;

 

}

 

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

 

Если среди операторов имеются определения объектов, то составной оператор превращается в блок. Внутри блока n составного оператора любой оператор заканчивается “;”

 

Примеры:

 

{ int a; char symbol = ‘a’; a = (int) symbol; cout << a; } // блок

 

{ x = y; z = 4*x + 3; cout << x << z; } // составной оператор

 

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

 

4. Операторы выбора. К операторам выбора относят условный оператор ( if … else) и переключатель (switch).

 

4.1 Условный оператор

 

1. Синтаксис:

 

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

 

<оператор1>;

 

Else

 

<оператор2>;

 

Здесь <выражение > - это логическое или арифметическое выражение, <оператор1> и <оператор 2> - любые операторы С++, в том числе блоки или составные операторы..

 

2. Семантика:

 

Вначале вычисляется значение <выражения>. Если оно “истина” (выражение # 0 ), то выполняется <оператор1>, в противном случае <оператор2>

 

Примеры.

 

If (x>0) if (x<0)

 

y= x; {

 

else x = -x;

 

y= -x; f(x+2);

 

}

 

else

 

{

 

int i = 2;

 

x *= I;

 

f(x);

 

}

 

Все объекты, определенные в блоке, локализованы в нем.

 

Например:

 

if (j > 0)

 

{

 

int i;

 

i = 2*j;

 

}

 

else

 

i = - j; // ошибка: видима внутри блока !

 

Пример. Вычислить частное от деления двух целых чисел.

 

// Ex_.CPP

 

#include <iostream.h>

 

Void main()

 

{

 

int a,b;

 

cout << “ Введите числитель: “ ;

 

cin >> a;

 

cout << “ Введите знаменатель: “ ;

 

cin >> b;

 

cf (b)

 

cout << “ Результат: “ << a / b << ‘\n’;

 

else

 

cout << “ на нуль делить нельзя. \n “ ;

 

}

 

Допустима сокращенная форма условного оператора, в которой отсутствует ветвь else.

 

Например:

 

if (a < 0 ) a = -a;

 

В этом случае при невыполнении условия никакие действия не выполняются.

 

Следует отметить, что <операто1> и <оператор2> могут быть условными без ветви else. Синтаксис языка предполагает, что при вложенных условных операторах каждая ветвь else соответствует ближайшему к нему оператору if.

 

Например:

 

if (….)

 

if (…)

 

……;

 

ilse

 

….;

 

Пример: Найти максимальное значение из трех вещественных чисел: x, y, z.

 

…..

 

if ( x < y)

 

if ( y < z)

 

max = z;

 

else

 

if ( x < z)

 

maz = z;

 

else

 

max = x;

 

….

 

4.2. Переключатели

 

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

 

1. Синтаксис.

 

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

 

{ case <константа1> : <оператор1>;

 

case <константа2> : <оператор2>;

 

……..

 

case <константа_n> : <оператор_n>;

 

default : <оператор>;

 

}

 

Здесь <выражение> и константы должны быть одного символьного или целого типа,

 

<оператор_i> - любой оператор С++.

 

2. Семантика. Вначале вычисляется значение <выражения>. Затем выполняется поиск оператора, которому предшествует константа, равная значению <выражения>. Если такой оператор найден, он выполняется, в противном случае осуществляется переход к оператору с меткой default. Если эта метка отсутствует, то не выполняется ни один из операторов. Если в конструкции выбора (альтернативе) не предусмотрен выход из переключателя с помощью оператора break, то в переключателе последовательно выполняются все операторы, начиная с той метки, на которую передано управление до тех пор, пока не встретится break или конец оператора switch В переключателе каждому оператору может предшествовать несколько констант выбора, которые отделяются друг от друга символом “:”.

 

 

Пример. Вывести на экран название нечетных цифр, не меньше заданной. Если задана цифра 2, то вывести 2,5,7,9 и т.д.

 

// Ex_.CPP

 

#include <iostream.h>

 

Void main()

 

{ int cifra;

 

cout << “Введите цифру : “;

 

cin >> cifra;

 

cout << ‘\n’;

 

switch (cifra)

 

{ case 0: case 1: cout << “один, ”;

 

case 2: case 3: cout << “три, ”;

 

case 4: case 5: cout << “пять, ”;

 

case 6: case 7: cout << “семь, ”;

 

case 8: case 9: cout << “девять. ”;

 

break;

 

default : cout << “ошибка !”

 

}

 

}

 

Результаты:

 

Введите цифру : 4

 

пять, семь, девять.

 

Введите цифру : а

 

Ошибка !

 

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

 

Действие, которое реализует оператор switch, можно описать с помощью вложенных условных операторов. Возникает вопрос : когда целесообразно использовать вложенные if-операторы, а когда switch ?

 

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

 

Например:

 

if ( x < 10 )

 

…… ;

 

else

 

if ( y > 0 )

 

…. ;

 

else

 

…. ;

 

 

 

Оператор цикла

 

В языке С++ определены три оператора цикла:

 

- цикл с предусловием;

 

- цикл с постусловием;

 

- итерационный цикл.

 

Оператор цикла с предусловием.

 

1) Синтаксис:

 

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

 

<тело цикла>;

 

Здесь <выражение> - это арифметическое или логическое выражение,

 

<тело цикла> - это либо Олин оператор ( в том числе и пустой), который заканчивается “;”, либо составной оператор, либо блок.

 

2) Семантика: сначала вычисляется значение выражения. Если оно #0 или истина, то выполняется тело цикла. Затем повторяется вычисление значения выражения и выполнение тела цикла, пока значение выражения не станет = 0 или false. Как только выражение становится ложным, управление передается оператору, следующему за этим циклом.

 

Пример. Вычислить сумму первых К натуральных чисел.

 

S = 1+2+…+K

 

#Ex_9.CPP - применение оператора while

 

#include <iostream.h>

 

#include <conio.h>

 

void main()

 

{

 

int S = 0, k;

 

cin >> k;

 

int i = 0;

 

while ( i < k )

 

S +=++i;

 

cout << “\n S = “ << S;

 

getch();

 

}

 

Пример. Решить уравнение

 

0,25 x3 + x – 1,25 = 0; x € [0,2]

 

методом итераций.

 

#Ex_10.CPP - метод итераций

 

#include <iostream.h>

 

#include <conio.h>

 

#include >math.h>

 

void main()

 

{

 

float x, y, x0, eps;

 

int n = 1; // счетчик итераций

 

cout << “\n Введите х0 и eps ”;

 

cin >> x0 >> eps;

 

y = x0;

 

x = 1.25 – 0.25*y*y*y;

 

while ( fabs(y-x)>eps)

 

{

 

y = x;

 

x = 1.25 – 0.25*y*y*y;

 

n++;

 

}

 

cout << “\n x = “ << x << “n= “ << n;

 

getch();

 

}

 

Оператор цикла с постусловием.

 

1) Синтаксис:

 

do

 

<тело цикла>

 

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

 

2) Семантика: Сначала выполняется тело цикла. Затем вычисляется значение выражения. Если оно #0 или истина, то вновь выполняется тело цикла. Если выражение ложно. То осуществляется переход к следующему за циклом оператору.

 

Решим предыдущие примеры с помощью оператора цикла с постусловием.

 

. . . . . .

 

{ int S = 0, k; { float x, y, x0, eps;

 

cin >> k; int n = 0;

 

int i = 1; cin >> x0 >> eps;

 

do y = x0;

 

{ S += i; do

 

i++; { x = y;

 

} y = 1.25 – 0.25*x*x*x;

 

while ( i<= k); n++;

 

. . . }

 

} while ( fabs(y-x) > eps);

 

cout << “\n y= “ << y << “n= “ << n;

 

}

 

Оператор итерационного цикла

 

1).Синтаксис:

 

for (<инициализация цикла>; <выражение-условие>;

 

<список выражений>)

 

<тело цикла>;

 

Здесь <инициализация цикла> - последовательность определений и выражений, разделенных запятыми; <выражение-условие> - это либо арифметическое выражение, либо отношение, либо пусто ( но “;” сохраняется); <список выражений> - последовательность выражений, разделенных запятыми.

 

2) Семантика: Вначале выполняется инициализация цикла. Затем вычисляется значение выражения-условия. Если оно #0 или истина, то выполняется тело цикла. Затем вычисляются значения выражений из списка. Далее снова вычисляется значение выражения-условия, и цикл повторяется до тех пор, пока это значение не будет равным нулю или false.

 

Пример. Найти сумму квадратов первых К членов натурального ряда:

 

I вариант

 

II вариант

 

В языке С++ разрешается опустить любой элемент заголовка. Например:

 

for ( int i = 0, s = 0; i < k;) s +=++i*i;

 

Здесь отсутствует список выражений.

 

Еще пример.

 

int i = 1, s = 0;

 

for ( ; i < k; i++) s +=i*i;

 

В этом операторе отсутствует инициализация.

 

Пример бесконечного цикла:

 

for ( ; ; );

 

Следует заметить, что выражение – условие всегда проверяется в начале цикла. Если при первой же проверке окажется, что оно равно false, то код тела цикла не выполняется ни разу.

 

Например:

 

for ( int c = 10, c < 5; c++) count c;

 

Оператор count c; не будет выполняться.

 

Условным выражением, которое управляет циклом, может быть любое выражение, при этом оно может не включать параметр цикла.

 

Например, реализуем метод итераций с использованием цикла for.

 

{ . . .

 

cin >> xo >> eps;

 

y = xo;

 

x = 1.25 – 0.25*y*y*y; int n;

 

for ( n = 0; fabs(y-x) > eps; n++)

 

{ y = x;

 

x = 1.25 – 0.25*y*y*y;}

 

cout << x << n;

 

}

 

При объявлении переменной в заголовке цикла важно помнить: эта переменная видима только в теле цикла.

 

В языке С++ разрешена любая глубина вложений циклов.

 

Пример: Найти множители целых чисел в диапазоне от2 до 10.

 

//EX_10.CPP - вложенные циклы

 

#include <iostream.h>

 

void main()

 

{

 

for ( int i = 2; i <=10; i++)

 

{ cout << ‘\nмножители числа “ << i << “:”;

 

for ( int j = 2; j < i; j++)

 

if ( ( i%j) == 0 ) cout << j << “ “;

 

cout << “\n”;

 

}

 

}

 

Результат:

 

Множители числа 2:

 

Множители числа 3:

 

Множители числа 4: 2

 

Множители числа 5:

 

Множители числа 6: 2 3

 

Множители числа 7:

 

Множители числа 8: 2 4

 

Множители числа 9: 3

 

Множители числа 10: 2 5

 

8. Операторы передачи управления.

 

goto - безусловная передача управления

 

return - возврат из функции

 

break - выход из цикла или переключателя

 

continue - переход к следующей операции.

 

Оператор goto

 

1.Синтаксис:

 

goto <идентификатор>;

 

Здесь <идентификатор> - это имя метки, расположенной в той же функции, где используется оператор goto.

 

2.Семантика:

 

Передает управление оператору, помеченному меткой.

 

Следует заметить, что в программировании вполне можно обойтись без этого оператора. Но иногда можно упростить программный код с его помощью.

 

Например:

 

. . .

 

for (. . . ) {

 

for (. . . ) {

 

while (. . . ) {

 

if ( . . . ) goto stop;

 

. . .

 

}

 

}

 

}

 

Stop: cout << “Error \n”; ….

 

Чтобы заменить инструкцию goto, нужно выполнить ряд дополнительных проверок.

 

Ограничения:

 

1. запрещено обходить описания с инициализацией объектов.

 

2. блоки можно обходить целиком.

 

Например:

 

. . .

 

goto label; // ошибка

 

float x = 0.0;

 

goto label; // правильно

 

{ int n = 0;

 

x = (n+1)*x+x;

 

}

 

label: count << “\nx=” << x;

 

. . . .

 

3. Можно передавать управление из внешних блоков во внутренние, однако при этом нельзя обходить инициализацию внутри блока.

 

. . .

 

goto label1 ; // нельзя

 

. . .

 

{ int i = 10 ; // начало блока

 

. . .

 

label1:

 

. . .

 

goto label2; // нельзя

 

char ch = ‘a’;

 

. . .

 

label2:

 

. . .

 

} // конец блока

 

Оператор return

 

Выполняет выход их функции.

 

Синтаксис:

 

return < выражение>;

 

или

 

return;

 

Здесь < выражение> скалярного типа. Если возвращаемое функцией значение имеет тип void, то < выражение> не указывается.

 

float cub (float z) // вычислить куб числа

 

{ return z*z*z;}

 

void cub_print (float z) // вывести на экран куб числа

 

{ cout << “\ cub = “ << z*z*z;

 

Return;

 

}

 

Оператор break

 

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

 

Например:

 

void main ()

 

int t;

 

for ( t = 0; t < 100; t++) {

 

if ( t == 10 ) break;

 

cout << t << “ “;

 

}

 

Return;

 

}

 

Результат:

 

0 1 2 3 4 5 6 7 8 9

 

При использовании вложенных циклов оператор break обеспечивает выход только из самого внутреннего цикла.

 

Оператор break используется в переключателях, когда нужно организовать ветвление.

 

// EX_11.CPP - оператор break в переключателе

 

#include <iostream.h>

 

void main()

 

{ int ic; // восьмеричная цифра

 

cin >> ic;

 

cout << “\n” << ic;

 

switch ( ic )

 

{ case 0: cout << “- нуль”; break;

 

….

 

case 7: cout << “- семь”; break;

 

default : cout << “ –это не восьмеричная цифра”;

 

}

 

}

 

Оператор continue

 

Этот оператор используется в операторах цикла для организации “досрочного ” выхода из текущей итерации. Он выполняет принудительный переход к следующей итерации, опуская выполнение оставшегося программного кода.

 

Например:

 

void main()

 

{

 

int x;

 

for ( x=0; x<=100; x++) {

 

if (x % 2 ) continue;

 

cout << x << “ “;

 

}

 

}

 

Здесь выводятся только четные числа.

 

В циклах while и do-while оператор continue передает управление непосредственно инструкции, проверяющей условное выражение, после чего цикл выполняется как обычно. А в цикле for после выполнения оператора continue сначала вычисляется инкрементное выражение, а затем условное. И только после этого цикл будет продолжен.

 

 

Массивы

 

Массив – это структура данных, состоящая из фиксированного числа элементов одного типа.

 

Определение одномерного массива имеет следующий синтаксис:

 

<тип> <имя массива> [<размеры> ];

 

Здесь <тип> - базовый тип элементов массива; <имя массива > идентификатор; <размер> - целое выражение, которое определяет количество элементов массива.

 

Например:

 

int mas[5]; // массив из пяти целых чисел

 

float A[10; // массив из десяти вещественных чисел.

 

Для доступа к элементу одномерного массива используется конструкция

 

<имя массива> [индекс]

 

Индекс определяет позицию элемента внутри массива.

 

|| В языке С++ индекс первого элемента массива равен 0

 

В памяти компьютера элементы одномерного массива располагаются последовательно.

 

Пример. Разместить целые числа 0…5 в массиве ARRAY

 

// Ex_12.CPP – работа с массивами

 

#include <iostream.h>

 

void main ()

 

{

 

int ARRAY [6];

 

int i;

 

for ( i = 0; i < 6; i++)

 

ARRAY[i] = i;

 

// вывод на экран элементов массива

 

for ( i = 0; i < 6; i++)

 

cout << “ARRAY[“ << “]: “ << ARRAY[i] << ‘\n”;

 

}

 

Результат:

 

ARRAY [0]: 0

 

ARRAY [1]: 1

 

……………….

 

ARRAY [5]: 5

 

Замечание: в С++ нельзя присваивать один массив другому.

 

Например:

 

Float x[7], y[7];

 

……

 

x = y ; // ошибка

 

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

 

for ( i = 0; i < 7; i++)

 

x[i] = y[i];

 

Замечание: компилятор С++ не контролирует выход за границы массива. Программист сам должен следить за значениями индексов, иначе программа будет нарушена.

 

Например:

 

Float MAS[10];

 

for ( i = 0; i < 100; i++)

 

MAS[i] = i+5;

 

Здесь будет выполнено 100 итераций, несмотря на то, что массив MAS имеет длину 10.

 

Было принято решение: поскольку С++ - язык профессионалов, то компилятор не будет тратить время на проверку принадлежности индекса определенному диапазону.

 

Пример: Вычислить математическое ожидание дискретной случайной величины

 

x = { x0, x1, …. xk-1 }

 

В данном примере длина массива К- переменная величина. Прежде, чем решать эту задачу, рассмотрим способы определения массива с переменной длиной.

 

I способ

 

const int k = 10;

 

float x[k];

 

II способ

 

int k; float x[100];

 

cin >> k;

 

III способ

 

#define k 10;

 

float x[k];

 

// Ex_13.CPP

 

#include <iostream.h>

 

void main()

 

{ const int k = 10;

 

float x[k], mo;

 

int i;

 

for ( i = 0; i < k; i++) cin >>x[k];

 

mo = 0;

 

for ( i = 0; i < k; i++) mo +=x[k];

 

mo /= k;

 

cout << “ \n Математическое ожидание = “ << mo << ‘\n’;

 

}

 

В С++ можно использовать многомерные массивы.

 

Определение многомерного массива:

 

<тип> <имя массива> [<размер1>][<размер2>] … [<размерn>];

 

Например:

 

int MATRIC[2][3]; // матрица целых чисел размерностью 2х3

 

Для доступа к элементу многомерного массива используется конструкция

 

<имя массива> [<индекс1>][<индекс2>] … [<индексn>];

 

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

 

Например:

 

Int A[2][3];

 

A[0][0], A[0][1],A[0][2],

 

A[1][0], A[1][1],A[1][2]

 

Пример. Задана матрица A[n x n] целых чисел. Написать программу транспортирования этой матрицы.

 

// Ex_14.CPP

 

#include <iostream.h> - транспонирование матрицы

 

#include <stdlib.h> // randomize(), random()

 

#include <conio.h> // clrscr;

 

#define n 4

 

void main()

 

{

 

clrscr();

 

int A[n][n];

 

int i, j;

 

randomize(); // инициализация генератора случайных чисел.

 

// создание матрицы

 

for ( i = 0; i < n; i++)

 

for ( j = 0; j < n; j++)

 

A[i][j] = random();

 

int S; // для обмена элементов

 

for ( i = 0; i < n; i++)

 

for ( j = i+1; j < n; j++)

 

{ S = A[i][j];

 

A[i][j] = A[j][i];

 

A[j][i] = S;

 

}

 

// Вывод матрицы на экран

 

cout << “n транспонированная матрица :” << ‘\n’;

 

for ( i = 0; i < n; i++)

 

for ( j = 0; j < n; j++)

 

cout << “\n” << A[i][j];

 

}

 

Чаще всего одномерные массивы используются для создания символьных строк.

 

Строки.

 

В С++ поддерживается два типа строк. Первый предполагает, что строка – это символьный массив, который завершается символом с нулевым кодом (‘\0’). Компилятор автоматически добавляет этот символ в конец строки.

 

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

 

Пример:

 

char buffer[10]; // определение строки из 9 символов и нулевого байта.

 

В памяти компьютера символы строки размещаются в последовательных байтах.

 

Например:

 

Рассмотрим особенности вода и вывода строк с помощью операций >> и << соответственно.

 

При вводе строки с клавиатуры с помощью операции >> набираются любые символы до нажатия клавиши Enter

 

Например:

 

char line[255];

 

cin >> line;

 

_______________.

 

fbcdef <enter>

 

Однако применение оператора >> иногда приводит к ошибке.

 

Например:

 

……

 

{ char str[80];

 

cin >> str;

 

cout << str;

 

}

 

____________.

 

abcd efgh (enter)

 

Результат:

 

abcd

 

Дело в том, что оператор cin >> прекращает считывание строки, как только встречает символ пробела. Вместо пробела он заносит в строку ‘\0’, тем самым завершая строку.

 

Для решения этой проблемы можно использовать библиотечную функцию gets():

 

gets(<имя массива>);

 

Эта функция считывает все вводимые символы до нажатия клавиши enter. Для вызова этой функции необходимо подключить библиотеку <stdio>.

 

Пример:

 

//Ex_15.CPP - использование gets()

 

#include <iostream.h>

 

#include <stdio.h>

 

void main()

 

{ char str[80];

 

cout << “Введите строку : “;

 

gets(str);

 

cout << str;

 

}

 

Результат:

 

Введите строку : abcd efgh

 

abcd efgh

 

Замечание: Если при вводе строки с помощью функции gets() или оператора >> число выводимых символов превышает допустимую длину строки, то на экран выводится сообщение об ошибке.

 

Например:

 

….

 

{ char str[5];

 

Cin >> str;

 

Cout << str;

 

….

 

}

 

___________.

 

abcdefgh (enter) - ошибка

 

Наряду с функцией gets() можно использовать для ввода строк функцию getline().

 

Формат обращения к функции getline():

 

getline(<имя массива>,<число символов>);

 

Пример:

 

//Ex_16.CPP - использование getline()

 

#include <iostream.h>

 

void main()

 

{ char myString[4]; // 3 символа +’\0’

 

cout << “\n Введите строку : “ << ‘\n’;

 

cin.getline(myString,4);

 

cout << myString;

 

}

 

Результат:

 

Введите строку : abc

 

abc

 

Оператор вывода << переносит на экран строку до нулевого байта ‘\0’.

 

Большинство функций для работы со строками содержится в библиотеке <string>.

 

1. Функция strcpy()

 

Формат вызова:

 

strcpy(str1, str2);

 

Копирует посимвольно строку str2 в строку str1. Копирование прекращается в случае достижения нулевого байта строки. str2, поэтому длина str2 не должна превышать длину строки str1.

 

Пример:

 

char str[40];

 

ctrcpy(str,”Проверка копирования”);

 

2. Функция strcat().

 

Формат вызова:

 

strcat(str1, str2);

 

Содержимое строки str2 присоединяется к содержимому строки str1.

 

Пример:

 

char str[80];

 

strcpy(str,”Для продолжения ”);

 

strcat(str,” нажмите клавишу ”);

 

3. Фкнкция strcmp()

 

Формат вызова:

 

strcmp(str1, str2);

 

Сравнивает строки str1 и str2 и возвращает целое значение, различая строчные и прописные буквы:

 

Пример: ( инициализация строк !)

 

char str1[]=”Ошибка в базе данных”;

 

char str2[]=”Ошибка в Базе данных”;

 

int i = strcmp(str1,str2); // i>0 , т.к. ’Б’ < ‘б’

 

4. Функция stricmp()

 

Сравнивает строки, не различая регистры символов.

 

Формат вызова:

 

strcimp(str1, str2);

 

Возвращает одно из следующих значений:

 

Пример:

 

char str1[]=”Moon”;

 

char str2[]=”MOON”;

 

int i = stricmp(str1,str2); // i=0

 

5. Функция strupr()

 

Формат вызова:

 

strupr(str);

 

Преобразует строку str к верхнему регистру и возвращает полученную строку.

 

Пример:

 

char str[]=”abcde”;

 

strupr(str);

 

cout << str; // ABCDE

 

6. Функция strlwr()

 

Формат вызова:

 

strlwr(str);

 

Преобразует строку str к нижнему регистру и возвращает полученную строку.

 

Пример:

 

char str[]=”ABRACADABRA”;

 

strlwr(str);

 

cout << str; // ABRACADABRA

 

7. Функция strchr()

 

Формат вызова:

 

strchr(str,ch);

 

Производить поиск символа ch в строке str и в случае успешного поиска возвращает позицию первого вхождения символа в строку. Если указанный символ не найден, функция возвращает нуль. Поиск символа начинается с начала строки.

 

8. Функция atoi().

 

Формат вызова:

 

atoi(str);

 

Преобразует строку str в число типа int. Если в строке встречается символ, который не может быть преобразован, то функция возвращает 0

 

9. Функция atol().

 

Формат вызова:

 

atol(str);

 

Преобразует строку в число типа long.

 

Пример:

 

char str[]=”70 000”;

 

long l = atoll(str);

 

cout << l // 70000

 

10. Функция atof().

 

Формат вызова:

 

atof(str);

 

Преобразует строку str в число с плавающей точкой типа double.Строка должна быть представлена в формате:

 

[пробелы] [знак] [цифры].[цифры] [е] [знак] [цифры]

 

+-

 

11. Функции ltoa(), itoa().

 

Выполняют обратное преобразование чисел (ling и int) в строки.

 

Формат вызова:

 

ltoa(number, str, r);

 

itoa(number, str,r);

 

Здесь r -

 

Основание системы счисления.

 

Пример:

 

int numb = 98765;

 

char str[10];

 

itoa(numb, str, 10); // десятичная с.с.

 

cout << numb << ‘\n’ << str;

 

12. Функция strlen().

 

Формат вызова:

 

Strlen(str);

 

Вычисляет длину строки без учета нулевого байта.

 

Рассмотрим пример использование строковых функций.

 

//Ex_.CPP

 

#include <iostream.h>

 

#include <string.h>

 

void main()

 

{ char s1[80], s2[80];

 

strcpy(s1, “C++”);

 

strcpy(s1, “ – это язык программирования.”);

 

cout << “Длина строк: ” << strlen(s1);

 

cout << “ “ << strlen(s2) << ‘\n’;

 

if (! Strcmp(s1,s2))

 

cout << “Эти строки равны. \n”;

 

else

 

cout << “Эти строки не равны. \n”;

 

strcat (s1,s2);

 

cout << s1 << ‘\n’;

 

strcpy(s2,s1);

 

cout << s1 << “ и “ << s2 << “\n”;

 

if (!strcmp(s1,s2))

 

cout << “строки s1 и s2 теперь одинаковы. \n “;

 

}

 

Результат:

 

Длины строк : 3 29

 

Эти строки не равны.

 

С++ - это язык программирования

 

С++ - это язык программирования. и С++ - это язык программирования.

 

Строки s1 и s2 теперь одинаковы.

 

Пример.

 

Задана строка, состоящая из слов, разделенных символом “пробел”. Написать программу вывода на экран первого символа каждого слова.

 

//Ex_17.CPP - обработка строк

 

#include <iostream.h>

 

#include <string.h> // strlen()

 

#include <conio.h> // getch(), clrscr()

 

void main()

 

{ const int n = 80;

 

char str[n];

 

clrscr();

 

cout << “\n Введите строку : “ << ‘\n’;

 

cin.getline(str, sizeof(str));

 

cout << “\n str = “ << str << ‘\n’;

 

cout << str[0] << ‘\t’; // первый символ первого слова

 

int i = 1;

 

while (i <= strlen(str))

 

{

 

If (str[i] == ‘\x20’)

 

Cout << str[i+1] << ‘\t’;

 

i++;

 

}

 

Getch();

 

}

 

Результаты:

 

Введите строку : abcd

 

str = abcd

 

a

 

Введите строку: ab def kl123

 

str = ab def kl123

 

a d k

 

Пример.

 

Задана строка, состоящая из m слов. Каждое слово содержит n символов.

 

Написать программу вывода на экран слова, начинающего с заданного символа а

 

//Ex_18.CPP

 

#include <iostream.h>

 

#include <conio.h> // getch(), clrscr()

 

#define m 5

 

# define n 3

 

void main()

 

{ clrscr();

 

int i, j;

 

char S[m*n]; // заданная строка

 

char a;

 

for ( i=0; i<m*n; i++) cin >> S[i];

 

cout << “Исходная строка \n”;

 

cin >> a;

 

i = 0;

 

while (( S[i] != a) && (I<m*n))

 

{ i+=n;}

 

if (S[i] == a)

 

{ cout << “\n слово \n”;

 

for (j = i; j<i+n; j++) cout << S[j];

 

}

 

else cout << “\n Нет такого слова \n”;

 

}

 

}

 

 

Инициализация массивов

 

В языке С++ предусмотрена возможность инициализации массивов двумя способами: либо с указанием размера массива, либо без явного указания.

 

Формат инициализации:

 

<тип><имя массива>[<размер>] = {<список значений>};

 

Здесь тип каждого значения инициализации должен совпадать с базовым типом массива.

 

Примеры:

 

int INTMAS[10] = {1,2,3,4,5,6,7,8,9,10};

 

int ARRAY[6] = {1,2,3}; // 6 элементов

 

В массиве ARRAY проинициализированы только три элемента, остальные не определены.

 

Если в определении массива задан его размер. То количество начальных значений не должно превышать его длину.

 

Пример:

 

float B[4] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; //ошибка

 

char STR[7] = {‘a’, ‘b’, ‘a’, ‘b’, ‘a’, ‘b’,’\0’}; // верно

 

Строки можно инициализировать еще одним способом:

 

char <имя массива>[<размер>] = “<строка>”;

 

Например:

 

char STR[7] =”abcdef”;

 

Это определение строки эквивалентно следующей конструкции:

 

char STR[7] = {‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’,’\0’};

 

Многомерные массивы инициализируются по аналогии с одномерными.

 

При инициализации многомерных массивов начальные значения нужно размещать в порядке расположения элементов в памяти компьютера.

 

Например:

 

int MATR[5][2] = { 1,1,

 

2,4,

 

3,9,

 

4,16,

 

5,25

 

};

 

Здесь MATR[0][0]=1; MATR[0][1]=1; MATR[1][0]=2; MATR[1][1]=4; …

 

MATR[4][0]=5; MATR[4][1]=25.

 

При инициализации многомерного массива список инициализатров каждой размерности можно заключать в {…}.

 

Например:

 

int MATR[5][2] = { { 1,1},

 

{2,4},

 

{3,9},

 

{4,16},

 

{5,25}

 

};

 

При использовании подгрупп инициализаторов недостающие члены подгруппы инициализируются нулевыми значениями.

 

Язык С++ позволяет не указывать длину массива. Компилятор определяет её автоматически путем подсчета количества инициализаторов.

 

Например:

 

int MAS[ ] = {1,2,3,4}; // 4 элемента

 

int STR[ ] = ”abcdefgh”; // 9 элементов

 

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

 

char e[] =”www.weather.com”;

 

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

 

При “безразмерной” инициализации многомерных массивов необходимо указать все данные, за исключением крайней слева размерности.

 

Например:

 

int MATR[0][2] = {

 

1,1,

 

2,4,

 

3,9,

 

4,16,

 

5,25

 

};

 

С помощью операции sizeof(<имя массива>) можно определить размер массива в байтах. Так как элементы массива имеют один и тот же тип, то количество элементов массива можно определить так:

 

sizeof(<имя массива>)/sizeof(<имя массива>[0]);

 

Массивы строк.

 

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

 

Например:

 

char str[30][80]; // 30 строк по 80 символов

 

gets(str[2]); - ввести третью строку массива str.

 

Для доступа к конкретному символу можно использовать следующего конструктора:

 

cout << str[2][3]; // вывод 4-го символа 3-ей строки.

 

Пример использования телефонного справочника

 

#include <iostream.h>

 

#include <stdio.h>

 

void main()

 

{ int i; // пример цикла

 

char numbers[10][80] = {

 

“Маша”, “129-34-01“,

 

“ Олег”, “371-16-55“,

 

“Наташа”, “241-25-09“,

 

“Саша”, “971-18-04“,

 

“Лена”, “962-01-10“,

 

};

 

cout << “Ввдите имя :”;

 

cin >> str;

 

for (i = 0; i<10; i+=2)

 

if (!=strcmp(str,number[i]))

 

{ cout << “Телефонный номер : ”; << numbers[i+1] << “\n”;

 

break;

 

}

 

if (i==0 10) cout << “Отсутствует в справочнике. \n”;

 

}

 

Указатели

 

Указатель на языке С++ - это объект, значением которого является адрес. Во внутреннем представлении адрес может занимать 2 или 4 байта в зависимости от реализации языка.

 

Определение указателя имеет следующий синтаксис:

 

<type>*<имя указателя>;

 

Здесь <type> - это тип объекта, на который ссылается указатель; <имя указателя> -это идентификатор, * - операция обращения по адресу.

 

Например:

 

int *ptrint; // адрес целого числа, занимающего в памяти 2 байта

 

float *ptrfl; // адрес вещественного числа, занимающего в памяти 4 байта

 

char *ptrch; // адрес символа, занимающего в памяти 1 байт

 

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

 

Например:

 

char *p1, *p2, *p3;

 

При определении указателя можно выполнять его инициализацию:

 

<type>*<имя указателя>=<инициализирующее выражение>;

 

или

 

<type>*<имя указателя>=(<инициализирующее выражение>);

 

В качестве инициализирующего выражения можно использовать:

 

- явно указанный адрес;

 

- указатель, имеющий значение;

 

- выражение, значением которого является адрес.

 

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

 

Примеры.

 

char *ptrchr =(char *)0xF000FFFF; // явно указанный адрес

 

int *ptrint(NULL);

 

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

 

&<операнд>

 

Здесь унарный оператор ‘&’ возвращает адрес операнда. Этот оператор применим к объектам, имеющим имя и размещенным в памяти компьютера.

 

Например:

 

float e = 15.4462;

 

float *pe = &e; // указателю ре присваивается адрес переменной е при его

 

// определении

 

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

 

Например:

 

cout << “\n e = “” << e;

 

cout << “\n *pe = “” << *pe; //значение, записанное по адресу ре

 

Результат:

 

е = 15.4462 00

 

*ре = 15.4462 00

 

Если указатель получил нулевое значение, то обращаться по адресу нельзя.

 

Итак, выражение для обращения по адресу имеет следующий вид:

 

*<имя указателя>

 

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

 

Пример:

 

void main()

 

{

 

int t = 3200;

 

int *ptr = &t;

 

int value = *ptr; // присваивание значения с помощью указателя

 

cout << “value=” << value << ‘\n’;

 

}

 

Результат:

 

value = 3200

 

Рассмотрим еще один пример.

 

Пусть определен указатель:

 

char *p;

 

Этот указатель не инициализирован, поэтому нельзя написать

 

*p = ‘+’ // ошибка, адрес не определен.

 

Для того, чтобы исправить ошибку, можно поступить следующим образом:

 

1). P = (char *) 0xb8ff0000; //присвоить указателю явный адрес.

 

2) воспользоваться оператором new:

 

P = new char; // зарезервировать место в памяти под объект (символ) и адрес его

 

// запомнить в указателе р.

 

Теперь станут корректными следующие операторы: *p = ‘+’; или cin >> *p;

 

До сих пор мы рассматривали указатели, связанные с конкретным типом данных и размером требуемой памяти для размещения объекта.

 

В языке С++ можно определять указатели, не связывая их с конкретным типом данных:

 

void *vp;

 

Такой указатель можно связывать с различными типами данных.

 

Пример.

 

// Ex_23.CPP

 

#include <iostream.h>

 

void main()

 

{ void *vp;

 

int i = 77;

 

float e = 2.718282;

 

vp = &i; // настроились на целый тип int

 

cout << “\n *(int *)vp = ” << *(int *)vp;

 

vp = &e; // настроились на вещественный тип float

 

cout << “\n *(float *)vp = ” << *(float *)vp;

 

}

 

Результат:

 

*(int *)vp = 77

 

*(float *)vp =2/718282

 

В языке С++ разрешено присваивание указателю типа void * значения любого указателя, но не наоборот.

 

Пример.

 

void *vp;

 

int *ip;

 

….

 

ip = vp; // нельзя

 

….

 

vp = ip; // можно

 

….

 

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

 

- сложение указателя и константы;

 

- вычитание из указателя константы;

 

- вычитание указателей;

 

- инкремент;

 

- декремент.

 

Рассмотрим эти операции.

 

В языке С++ можно применять операцию вычитания к указателям одного типа и к указателю и целой const.

 

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

 

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

 

Пример.

 

//Ex_24.CPP

 

#include <iostream.h>

 

void main()

 

{ int a = 1, b = 2;

 

int *pa = &a, *pb = &b;

 

cout << “\n pa = “ << pa; // адрес а

 

cout << “\n pb = “ << pb; // адрес b

 

cout << “\n (pa-pb) = “ << (pa-pb);

 

cout << “\n“ << (int)pa – (int)pb;

 

}

 

Результат:

 

pa = 0xfff4

 

pb = 0xfff2

 

(pa – pb) = 1

 

|| Существует различие между разностью однотипных указателей и разностью числовых значений этих указателей

 

|| Переменная, определенная в тексте программы позднее, имеет меньший адрес. Компилятор размещает объекты в обратном порядке, т.к. использует стэк.

 

Если в программу Ex_18.CPP внести изменения:

 

cout << “\n(pb – pa) = “ << (pb – pa);

 

cout << “\n“ << (int)pa) – (int)pa;

 

то результат будет другим:

 

(pb – pa) = -1;

 

-2

 

Из указателя можно вычитать целое число. При этом числовое значение указателя уменьшится на величину

 

k * sizeof(type)

 

где k – вычитаемое , type - тип объекта, к которому отнесён указатель.

 

Аналогично выполняется операция сложения указателя с целым числом.

 

|| В языке С++ запрещено суммирование двух указателей.

 

// Ex_25.CPP

 

#include <iostream.h>

 

void main()

 

{ float zero = 0.0, pi = 3.1415, e = 2.7182;

 

float *ptr = &e;

 

cout << “\n ptr = “ << ptr << “*ptr = “ << *ptr;

 

cout << “\n (ptr+1) = “ << (ptr+1) << “*(ptr+1)=” << *(ptr+1);

 

cout << “\n (ptr+2) = “ << (ptr+2) << “*(ptr+2)=” << *(ptr+2);

 

}

 

Результат:

 

ptr = 0xffea *ptr = 2.7182

 

(ptr + 1) = 0xffee *(ptr+1) = 3.1415

 

+ 4 байта

 

а = 1010; e = 1110

 

(ptr+2) = 0xfff2 *(ptr+2) = 0

 

+ 4 байта

 

f2 = 11110010

 

-

 

ee = 11101110

 

_____________

 

100 => 4

 

Декремент ( -- ) и инкремент ( ++ ) изменяют конкретное численное значение указателя типа type на величину

 

sizeof(type),

 

где type * -тип указателя.

 

Таким образом указатель перемещается к соседнему объекту с меньшим или большим адресом.

 

Сравнение указателей

 

Указатели можно сравнивать, используя операции сравнения ‘= =’ и ‘<’ и ‘<’.

 

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

 

Замечание.

 

Работать с указателями нужно аккуратно, поскольку объекты, чьи определения размещены последовательно, не всегда размещаются в памяти друг за другом. Язык С++ это не гарантирует.

 

Лекция 9

 

Указатели и массивы

 

В языке С++ указатели и массивы тесно связаны между собой.

 

По определению, имя массива является указателем – константой, значением которого является адрес первого элемента массива ( с индексом 0). Таким образом, для любого массива соблюдается правило:

 

<имя массива> = &<имя массива> = &<имя массива>[0]

 

Поскольку <имя массива> - указатель, то к нему можно применять арифметические операции. Тогда действие индексных скобок в записи

 

&<имя массива>[<индекс>]

 

можно объяснить так:

 

*(<имя массива> + <индекс>)

 

Индекс элемента определяет смещение относительно начала массива.

 

Пример:

 

int z[3];

 

Тогда *(z+0) – это обращение к первому элементу z[0],

 

*(z+1) – это обращение ко второму элементу z[1],

 

*(z+2) – это обращение к третьему элементу z[2].

 

Необходимость использования круглых скобок в выражении (z+i) обусловлена тем, что приоритет у операции ‘*’ выше, чем у ‘+’.

 

Итак, в С++ предусмотрены 2 способа доступа к элементам массива и арифметики указателей.

 

Следует отметить, что арифметические операции над указателями выполняются быстрее, чем индексирование массивов. Поскольку быстродействие в программировании часто является определяющим фактором, при выборе решения, то использование указателей для доступа к элементам массива – характерная особенность многих С++ - программ. Кроме того, иногда указатели позволяют написать более компактный код по сравнению с использованием индексирования массивов.

 

Пример.

 

Вывести на экран символы заданной строки в столбик.

 

#include <iostream.h>

 

void main()

 

{ char X[ ] = “abcdef”;

 

int i = 0;

 

while ( *(X + i) != ‘\0’) // X[i] != ‘\0’

 

cout << “\n” << *(x+i++); // X[i++]

 

}

 

Результат:

 

a

 

b

 

c

 

d

 

e

 

f

 

Индексирование указателя.

 

В языке С++ указатель, который ссылается на массив, можно индексировать так, как если бы это было имя массива.

 

Рассмотрим пример.

 

// Ex_ CPP

 

#include <iostream.h>

 

void main()

 

{ char *p;

 

char X[80] =”abcdef”;

 

int i = 0;

 

p = X; // присваиваем указателю р адрес начала массива

 

while ( p[i] != ‘\0’)

 

cout << “\n” << p[i++];

 

}

 

При выполнении этой программы указатель Р индексируется, что допустимо, поскольку выражение p[i] идентично по своему действию *(p+i).

 

Доступ к элементам многомерных массивов возможен также с помощью [ ] и с помощью указателей.

 

В общем случае переменная с индексами

Z[i][j][k]

 

соответствует выражению

 

*(*(*(z+i)+j)+k)

 

Пример.

 

Пусть задана матрица А[2x3] целых чисел. Выведем её значения на экран, используя указатели.

 

#include <iostream.h>

 

void main()

 

{ int a[2][3] = {0,1,2,3,4,5};

 

cout << “a[0][0] =” << *(*(a+0)+0) << “\n”;

 

cout << “a[0][1] =” << *(*(a+0)+1) << “\n”;

 

cout << “a[0][2] =” << *(*(a+0)+2) << “\n”;

 

cout << “a[1][0] =” << *(*(a+1)+0) << “\n”;

 

cout << “a[1][1] =” << *(*(a+1)+1) << “\n”;

 

cout << “a[1][2] =” << *(*(a+1)+2) << “\n”;

 

}

 

Результат:

 

a[0][0] = 0

 

a[0][1] = 1

 

a[0][2] = 2

 

a[1][0] = 3

 

a[1][1] = 4

 

a[1][2] = 5

 

Указатели можно связывать с массивами как во время определения, так и в процессе выполнения программы.

 

Для этого существуют следующие возможности:

 

1) <type>*<имя указателя> = <имя уже определенного массива типа type>;

 

2) <type>*<имя указателя> = new <type>[ <размер массива>];

 

Например:

 

1) int array[ ] = { 10,20,30,50 }; // массив

 

int *ptrarray = array ; // указатель связали с массивом

 

2) int *ptrarray = new int[4]; // указатель связали с выделенным участком памяти

 

Указатели и строки

 

В языке С+_+ строка представляется в виде одномерного символьного массива, последним элементом которого является нулевой байт.

 

Если для инициализации символьного массива используется строка

 

char array[ ] =”<иниц. строка>”;

 

то адрес первого символа строки становится значением указателя – константы array.

 

Если строка используется для инициализации указателя типа char*:

 

char *ptr = “<иниц. строка>”;

 

или

 

char *ptr = {“<иниц. строка>”};

 

или

 

char *ptr =( “<иниц. строка>”);

 

то адрес первого элемента строки становится значением указателя ptr

 

Пример.

 

#include <iostream.h>

 

#include <string.h> // strlen()

 

void main()

 

{ char *array1 = “0123456789”; // массив символов инициализируется строкой

 

int k = strlen(array1) + 1;

 

char *array2 = new char[k];

 

for ( int i = 0; i < k; )

 

array2[i+1] = *array1++;

 

cout << “array2=” << array2;

 

}

 

Результат:

 

0 1 2 3 4 5 6 7 8 9

 

В программе определен и инициализирован массив – строка array1. С помощью операции new выделена память для такого же по типу и размерам массива, связанного с указателем array2. Доступ к элементам массива, связанного с указателем array2 реализован с помощью [ ], а к элементам второго массива с помощью операции *. Указатель array1 меняется, перемещаясь по элементам массива, поэтому нельзя вывести array1 (будет пустая строка).

 

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

 

Например:

 

int number[10];

 

int i;

 

For ( i = 0; i<10; i++ )

 

{ *number = i; //правильно

 

number++; // ошибка

 

}

 

Дело в том, что имя массива – это константный указатель и его модифицировать нельзя.

 

Замечание.

 

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

 

При объявлении можно любой указатель инициализировать нулевым значением:

 

int *p = 0;

 

а затем проверять его: if ( p == 0) …. else …;

 

Можно создать указатель, который будет ссылаться на другой указатель, а тот на конечное значение. Получается цепочка указателей.

 

Например:

 

{

 

int x, *p, **q; // q – это переменная, ссылающаяся на указатедь

 

x = 10;

 

p = &x;

 

q = &p;

 

cout << **q;

 

}

 

Результат:

 

 

Выводы: обращаться с указателями нужно очень осторожно:

 

1) перед использованием указателя его необходимо проинициализировать;

 

2) тип объекта адресуемого указателем, должен совпадать с его базовым типов;

 

3) не следует выполнять операции с нулевыми указателями.

 

Введение в функции.

 

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

 

В общем случае программа на языке С++ - это совокупность функций, каждая из которых должна быть объявлена и определена до её использования.

 

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

 

Прототип имеет следующий синтаксис:

 

<тип функции><имя функции>(<список формальных параметров>);

 

Здесь <тип функции> - это любой тип, за исключением массива. Если функция не возвращает никакого значения, необходимо указать void.

 

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

 

<тип><имя функции>

 

или

 

<тип><имя параметра> = <умалчиваемое значение>

 

Если функция не имеет параметров, то список либо отсутствует, но скобки остаются, либо указывается слово void.

 

В конце прототипа ставится “,”

 

// Имена параметров могут отсутствовать в списке.

 

Примеры прототипов функций.

 

void print ( int value );

 

float min ( float x, float y );

 

int sqr( int x );

 

float max ( float, float y ); // опустили имя

 

void write (void0;

 

Определение функции, кроме объявления, содержит тело функции.

 

Синтаксис определения функции:

 

<тип функции><имя функции>(<список формальных параметров>){ <тело функции>}

 

Здесь <тело функции> - это либо составной оператор, либо блок. Если функция не выполняет никаких действий, то телом функции является { }.

 

Имя функции со списком формальных параметров называется сигнатурой.

 

Следует заметить, что в отличие от прототипа, в определении функции в списке формальных параметров нужно указывать их имена.

 

Определение функции можно разместить после главной функции в том же файле, а можно сохранить в отдельном файле с с помощью препроцессорной директивы:

 

#include “<имя файла>”

 

Возврат из функции к инициатору её вызова осуществляется при обнаружении закрывающей фигурной скобки или с помощью оператора return, который имеет две формы:

 

return <выражение> ;

 

или

 

return ;

 

Здесь <выражение> - это возвращаемой функцией значение. Тип возвращаемого значения определяется типом функции. Если функция имеет тип void , то <выражение> в операторе return опускается. Можно опустить и сам оператор.

 

В теле функции может быть несколько операторов return. Оператор return можно использовать в главной функции main(). Если тип возвращаемого его результата отличен от void, то при благоприятном завершении программы возвращается значение 0, в противном случае – не 0.

 

Примеры определений функций.

 

1) float min ( float x, float y );

 

{ if ( x < y ) return x; // два оператора return

 

else return y;

 

}

 

2) int sqr ( int x )

 

{ return x*x ; // один оператор return

 

}

 

3) void print ( int value )

 

{ cout << “\n” << value; // нет оператора return

 

}

 

Вызов функции выполняется с помощью конструкции

 

<имя функции>(<список фактических параметров>);

 

Здесь <имя функции> - это имя функции из ее заголовка;

 

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

 

Рассмотрим механизм активизации функции на примере.

 

Создадим программу, включающую объявление и определение функции вох(), вычисляющей объем параллелепипеда , и главную функцию main(), в теле которой организуем многократный вызов функции вох().

 

#include <iostream.h>

 

void box( int length, int width, int height);

 

void main()

 

{ box (7,20, 4);

 

box (50,3,2);

 

box (8,6,9);

 

}

 

void box( ( int length, int width, int height)

 

{

 

cout << “Объем параллелепипеда = “ << length*width*height << “\n”;

 

}

 

При вызове функции :

 

1. формальным параметром присваиваются значения аргументов;

 

2. выполняются операторы тела функции;

 

3. управление передается конструкции, записанной в теле основной функции за конструкцией вызова функции.

 

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

 

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

 

1. количество и тип фактических параметров (аргументов) должны строго соответствовать количеству и типу формальных параметров (параметров)ж

 

2. аргументы должны перечисляться в том же порядке, в каком перечислены соответствующие им параметры.

 

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

 

#include <iostream.h>

 

int box( int length, int width, int height);

 

void main()

 

{

 

int answer;

 

answer = box( 10,11,3);

 

cout << “объем параллелепипеда = ” << answer;

 

}

 

int box( int length, int width, int height);

 

{

 

return length*width*height;

 

}

 

Результат:

 

Объем параллелепипеда = 330

 

В этом примере значение, возвращаемое инструкцией return, становится значением выражения box() в операторе присваивания.

 

Эту программу можно переписать в более эффективном варианте :

 

cout << “объем параллелепипеда = ” << box( 10,11,3);

 

В общем случае конструкцию вызова функции, возвращающей значение указанного типа, можно использовать в любом выражении, например:

 

int sum = box(1,2,3) + box(10,20,30);

 

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

 

#include <iostream.h>

 

int box( int length, int width, int height);

 

{

 

return length*width*height;

 

}

 

void main()

 

{

 

int answer;

 

answer = box( 10,11,3);

 

cout << “объем параллелепипеда = ” << answer;

 

}

 

Здесь функция box() определена до её использования в функции main(). Таким образом, её определение служит и ее прототипом, поэтому нет необходимости в отдельном включении прототипа в программу.

 

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

 

Как уже отмечалось выше, определения функций можно размещать в отдельных файлах, а затем подключать к тексту с главной функцией с помощью директивы

 

#include “<имя файла>”

 

Рассмотрим пример. Разместим функцию f() в отдельном модуле, а затем подключим её к главной функции.

 

определение функции f()

 

void f( float a, floa b)

 

{ cout << “\n “ << a << “ “ << b << “\n”; }

 

Сохраним её в файле “FUNC.CPP”

 

Создадим файл с главной функцией

 

#include <iostream.h>

 

#include <conio.h> // clrscr()

 

#include “FUNC.CPP”

 

void f( floata, float b); // прототип

 

void main()

 

{ float x, y;

 

clrscr();

 

cin >> x >> y;

 

f(x,y);

 

cout.precision(2);

 

cout << x << “ “ << y;

 

}

 

_________________________

 

12.345 34.23478 Enter

 

Результат:

 

12.345 34.234779 // вывод функцией f()

 

12.35 34.23

 

В этом примере можно было не объявлять в главном файле прототип функции f().

 

Можно прототипы необходимых функций также хранить в отдельном файле, а затем подключать к своей программы с помощью директивы

 

#include “<имя файла>”

 

Функции с переменным количеством параметров

 

В языке Си++ допустимы функции с неопределенным количеством параметров.

 

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

 

Например, формат прототипа:

 

<тип><имя функции>(<список формальных параметров>,..);

 

Каждая функция, тем не менее, должна знать количество параметров и их типы.

 

Один из подходов предусматривает передачу в функцию значения реального количества фактических параметров. Переход от одного фактического параметра к другому выполняется с помощью указателей.

 

Пример. Написать функцию, которая суммирует произвольное количество целых чисел.

 

// Ex_22.CPP

 

#include <iostream.h>

 

long SUMMA ( int k,…) // к -число параметров

 

{ int *ptrk = &k; // адрес параметра к

 

long total = 0;

 

for ( ; k; k--) total +=*(++ptrk);

 

(инициализации нет, т.к. используется к из списка параметров)

 

Return total;

 

}

 

void main()

 

{ << cout << “\n SUMMA(2,6,4) =” << SUMMA(2,6,4);

 

<< cout << “\n SUMMA(6,1,2,3,4,5,6) =” << SUMMA(6,1,2,3,4,5,6);

 

}

 

Результат:

 

SUMMA(2,6,4) = 10

 

SUMMA(6,1,2,3,4,5,6) = 21

 

Лекция 10.

 

Области видимости функций

 

Правила действия областей видимости любого языка программирования позволяют управлять доступом к объекту из различных частей программы. В С++ определены две основных области видимости: локальная и глобальная.

 

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

 

Тело функции является программным блоком, который начинается с “{“ и завершается “}”. Каждая функция определяет собственную область видимости.

 

Пример.

 

#include <iostream.h>

 

void f(); // прототип функции

 

void main( )

 

{ int value = 10; // локальная по отношению к main()

 

cout << “ Значение value в функции main():” << value << “|n”;

 

f();

 

cout << “ Значение value в функции main():” << value << “|n”;

 

}

 

void f() // определение функции

 

{ int value = 88; // локальная в функции f()

 

cout << “ Значение value в функции f():” << value << “|n”;

 

}

 

Результат:

 

Значение value в функции main() : 10

 

Значение value в функции f() : 88

 

Значение value в функции main() : 10

 

Каждая переменная value известна только своему блоку.

 

Поскольку локальные переменные создаются с каждым входом и разрушаются с каждым выходом из программного блока, они не хранят своих значений между активизациями блоков. Локальные переменные не сохраняют своих значений между вызовами функций.

 

Если локальная переменная инициализируется в блоке, то она инициализируется при каждом входе в блок.

 

Пример.

 

#include <iostream.h>

 

void f(); // прототип функции

 

void main( )

 

{ for ( int I = 0; I < 3; i++) f();}

 

void f() // определение функции

 

{ int num = 99;

 

cout << num << “\n”;

 

num++;

 

}

 

Результат:

 

 

 

 

Локальная переменная num инициализируется при каждом обращении к функции.

 

Локальные переменные можно объявлять в любой части блока. Они остаются локальными по отношению к этому блоку.

 

Пример:

 

int main() {

 

int x = 19;

 

if (x==19)

 

{ int y=20; // блок

 

cout << “x+y=” << x+y << “\n”;

 

}

 

y = 100; // ошибка: у здесь не известна, она вне области видимости блока.

 

return 0;

 

}

 

Локальные переменные можно объявлять в любой части блока, главное, чтобы это произошло до её использование использования.

 

Например :

 

void main()

 

{ cout << “Введите первое число” ;

 

int a;

 

cin >> a;

 

cout << “Введите второе число”;

 

int b;

 

cin >> b;

 

cin << “a*b=” << a*b << ‘\n’;

 

}

 

Если имя переменной, объявленной во внутреннем блоке, совпадет с именем переменной, объявленной во внешнем блоке, то внутренняя переменная переопределяет внешнюю в пределях видимости внутреннего блока.

 

Пример.

 

int main()

 

{ int i, j;

 

i = 10;

 

j = 100;

 

if ( j>0) {

 

int i; i = j/2;

 

cout << “Внутренняя переменная i :” << i << ‘\n’;

 

}

 

cout << “Внешняя переменная i :” << i << ‘\n’;

 

}

 

Результат:

 

Внутренняя переменная i:50

 

Внешняя переменная i:10

 

Формальные параметры функции существуют в пределах области видимости функции, они локальны по отношению к функции.

 

Поскольку локальные переменные известны только в пределах функции, то возникает вопрос: а как создать переменную, которую могли бы использовать сразу несколько функций ? Необходимо создать переменную в глобальной области видимости. Глобальная область видимости - это декларативная область, которая заполняет пространство вне всех функций.

 

Глобальные переменные известны на протяжении всей программы, и они поддерживают свои значения во время выполнения всего кода программы. Следовательно их область видимости – вся программа.

 

Глобальные переменные объявляют вне всех функций, например, до функции main(). Формально они должны быть определены до их первого использования.

 

Пример.

 

#include <iostream.h>

 

void f1();

 

void f2();

 

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

 

void main ()

 

{

 

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

 

for ( i = 0; i < 10; i++)

 

{ count = i*2;

 

f1()(;

 

}

 

} // конец функции main(()

 

void f1()

 

{ cout << “count: “ << count; // доступ к глобальной переменной

 

cout << ‘\n’;

 

f2();

 

}

 

void f2()

 

{

 

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

 

for (count=0; count<3; count++)

 

cout <<’.’;

 

}

 

Результат:

 

count : 0

 

.. . count: 2

 

.. . count: 4

 

. . .

 

.. . count: 18

 

. . .

 

В этом примере функция main() и функция f1() используют глобальную переменную count. Но в функции f2() объявляется локальная переменная count, которая внутри функции перекрывает глобальную и не оказывает на неё никакого влияния.

 

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

 

- они занимают память в течение всего времени выполнения программы и не только тогда, когда действительно необходимы;

 

- использование большого количества глобальных переменных может привести к ошибкам в программе;

 

- использование глобальной переменной вместо локальной делает функцию менее универсальной.

 

Передача указателей и массивов

 

в качестве аргументов функций.

 

Чтобы передать функции указатель, необходимо задать в списке формальных параметров параметр типа указатель. Фактическим параметром для него будет адрес объекта.

 

Пример.

 

#include <iostream.h>

 

void f(int *j); // параметр- указатель j на целое число

 

void main()

 

{ int i;

 

int *p;

 

p = &i;

 

f(p); // аргумент-указатель на целое число i

 

cout << i;

 

}

 

void f(int *j);

 

{ *j = 100;

 

}

 

В результате переменная i получит значение 100.

 

Можно не использовать переменную р и короче написать программу:

 

void main()

 

{ int i;

 

f(&I); // фактический параметр-адрес переменной i

 

cout << i;

 

}

 

void f(int *j)

 

{ *j = 100;}

 

Если аргументом функции является массив, то при вызове такой функции ей передается только адрес первого элемента массива. Мы знаем, что имя массива – это указатель на первый элемент этого массива.

 

Существует три способа объявления формального параметра, который принимает в качестве значения указатель на массив.

 

1). Формальный параметр можно объявить как массив, тип и размер которого совпадет с типом и размером фактического массива.

 

Пример.

 

.

 

#include <iostream.h>

 

void display ( int mas[10]);

 

void main()

 

{ int t[10], i;

 

for ( i=0; i<10; i++)

 

t[i] = I;

 

display(t); // передаем функции массив

 

}

 

void display (int mas[10])

 

{ int i;

 

for ( i=0; i<10; i++)

 

cout << mas[i] << “ “;

 

}

 

Несмотря на то, что формальный параметр mas объявлен как целочисленный массив, компилятор автоматически преобразует его в указатель на целое число.

 

2). Формальный параметр – массив объявляется как безразмерный массив:

 

void display (int mas[]) // параметр – безразмерный массив

 

{ int i;

 

for ( i=0; i<10; i++)

 

cout << mas[i] << “ “;

 

}

 

Поскольку компилятор С++ не обеспечивает проверку нарушения границ массива, то можно не указывать реальный размер массива. Целочисленный массив mas также автоматически преобразуется в указатель на целое число.

 

3). При передаче функции массива её формальный параметр можно объявить как указатель. Этот вариант чаще всего используется профессиональными программистами.

 

void display (int *mas) // указатель на целое число

 

{ int i;

 

for ( i=0; i<10; i++) cout << mas[i] << ‘ “;

 

}

 

Такой способ передачи параметра-массива основывается на том факте, что указатели можно индексировать.

 

Итак, если массив используется в качестве аргумента функции, то функции передается адрес этого массива. Это означает, что код функции может изменить реальное содержимое массива, используемого при вызове функции.

 

Пример.

 

#include <iostream.h>

 

void cube(int *n, int length); // n – адрес массива, length – длина его

 

/* функция возводит в куб элементы массива */

 

void main()

 

{ int i, nums[10];

 

for ( i=0; i<10; i++)

 

nums[i] = i+1;

 

count << “Исходное содержимое массива :”;

 

for (i=0; i<10; i++) cout << nums[i] << “ ‘;

 

cout << ‘\n’;

 

cube (nums,10); // передаем функции адрес массива nums

 

cout << “Изменненое содержимое :”;

 

for (i=0; i<10; i++) cout << nums[i] << “ “;

 

}

 

//Возводим в куб элементы массива

 

void cube( int *n, int length)

 

{ while (length) {

 

*n = (*n)*(*n)*(*n);

 

length--;

 

n++;

 

} // конец функции

 

Результат:

 

Исходное содержимое массива : 1 2 3 4 5 6 7 8 9 10

 

Измененное содержимое: 1 8 27 64 125 261 343 512

 

Здесь элементы массива nums (7 29 1000) были изменены операторами тела функции cube(), поскольку ей передается адрес массива nums.

 

Передача функциям строк

 

Поскольку строка в С++ - это символьный массив, завершающийся нулевым байтом, то при передаче функции строки реально передается только указатель типа char* на начало этой строк.

 

Пример.

 

Определим функцию strInvert(), которая инвертирует регистр букв строки, т.е. заменяет строчные символы прописными, а прописные – строчными.

 

#include <iostream.h>

 

#include <string.h> // копирован6ие strcpy()

 

#include <ctype.h> // функции над строками

 

void strInvert(char *str); // параметр – строка

 

void main()

 

{ char str[80]; // исходная строка

 

strcpy(str, “This Is A Test”);

 

strInvert(str); // преобразуем строку

 

cout << str; // вывод преобразованной строки

 

}

 

void strInvert(char *str)

 

{

 

while (*str) // пока не нулевой байт

 

{ if (isupper(*str)) // возвращает true, если буква верхнего регистра

 

*str = tolower(*str); // возвращает символ в нижнем регистре

 

else

 

if (islower(*str)) // возвращает true, если буква нижнего регистра

 

*str = toupper(*str); // возвращает символ в верхнем регистре

 

str++; // переход к следующему символу

 

} // конец while

 

} // конец функции

 

Результат:

 

tHIS iS a tEST

 

Возвращение функциями указателей

 

Функции могут возвращать указатели. Чтобы вернуть указатель, функция должна объявить его тип в качестве типа возвращаемого значения.

 

Например:

 

int *f(); // прототип функции f(), возвращающей указатель на целое число

 

Если функция возвращает указатель, то значение выражения в операторе return должно быть указателем.

 

Пример. Определим функцию get_substr(), которая возвращает указатель на первую подстроку, которая совпадает с заданной. Если подстрока не найдена, то функция возвращает нулевой указатель.

 

#include <iostream.h>

 

char *get_substr( char *sub, char *str); // прототип

 

void main()

 

{ char *substr; // указатель на символ

 

substr = get_substr(“ab”, “cd_ablm”);

 

if (substr)

 

cout << “\nЗаданная подстрока найдена : “ << substr;

 

else

 

cout << “\nЗаданная подстрока не найдена : “;

 

}

 

char *get_substr(char *sub, char *str)

 

{ int I; // номер символа

 

char *p, *p2, *start;

 

for ( i=0; str[i]; i++)

 

{ p = &str[i];

 

start = p;

 

p2 = sub;

 

while (*p2 && *p2 == *p)

 

{ p++;

 

p2++;

 

}

 

/* если обнаружен конец p2-строки (т.е. подстроки), то искомая подстрока найдена */

 

if (!*p2)

 

return start; // указатель на начало подстроки

 

} // переход к следующему символу

 

return 0; // совпадение не обнаружено

 

}

 

Результат :

 

Заданная подстрока найдена: ablm

 

Лекция 11.

 

Рекурсивные функции

 

Функция называется рекурсивной. Если в процессе выполнения составляющих ее операторов она обращается к себе.

 

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

 

Пример. Написать рекурсивную функцию вычисления К !

 

long fact ( int k)

 

{ if ( k>0 ) return 0;

 

If (k = =0 ) return 1;

 

return k*fact(k-1);

 

}

 

Сравним эту функцию с итерационной версией.

 

long fact ( int k)

 

{ if ( k<0 ) return 0;

 

If ( k = = 0 ) return 1;

 

long answer = 1;

 

for ( int t = 1; t<=k; t++)

 

answer = answer*t;

 

return answer;

 

}

 

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

 

Например: требуется вычислить fact(3).

 

В этом примере простое решение появляется при n4=0.

 

Иногда удается рекурсивную процедуру сделать более компактной, чем итерационную.

 

Однако рекурсия требует больше времени и памяти при вычислениях.

 

Пример. Написать рекурсивную функцию вычисления целой степени вещественного числа.

 

double stepen (double a, int n)

 

{ if ( n = = 0 ) return 1;

 

if ( a = = 0 ) return 0;

 

if ( n > 0 ) return a*stepen(a,n-1);

 

if ( n < 0 ) return stepen(a,n+1) / a;

 

}

 

Подставляемые функции

 

Обычно при вызове функции компилятор включает в код программы команду передачи управления на тело функции.

 

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

 

Такие функции называются подставляемыми.

 

Синтаксис определения подставляемой функции:

 

inline<тип><имя функции>(<список формальных параметров>)

 

<тело функции>

 

Пример:

 

inline float r ( float x, float y)

 

{ return sqrt (x*x + y*y);}

 

В некоторых реализациях компиляторов вводятся ограничения на подставляемые функции:

 

- функция не должна быть рекурсивной;

 

- функция не должна содержать операторы цикла, переключатели и т.д.

 

Передача аргументов функции

 

В языках программирования обычно реализуют два способа передачи аргументов функции: по значению и по ссылке.

 

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

 

При передаче аргументов по ссылке функция обрабатывает сам аргумента, поскольку ей передается адрес аргумента, а не его значение. Это значит, что изменения, внесенные в формальный параметр в теле функции, изменяет сам аргумент.

 

Рассмотрим эти способы на примерах.

 

// Передача аргумента по значению.

 

#include <iostream.h>

 

int f ( int x );

 

void main()

 

{ int arg = 10.0;

 

cout << “Значение функции f(arg) = “ << f(arg) << “\”;

 

cout << “Значение аргумента arg = “ << arg << “\”;

 

}

 

Int f( int x)

 

{ x = 2*x;

 

return x;

 

}

 

Результат:

 

Значение функции f(arg) = 20

 

Значение аргумента arg = 10

 

В этом примере аргумент arg сохранил своё значение при выходе из функции f().

 

Для организации передачи аргумента по ссылке используется ссылочный формальный параметр, который объявляется в заголовке функции с помощью знака ‘&’.

 

//Передача аргумента по ссылке

 

#include <iostream.h>

 

void f ( int &i) // &i – ссылочный прараметр

 

{ i = 10; }

 

// Значение 10 присваивается переменной, на которую ссылается i

 

Void main()

 

{ int value = 1;

 

cout << “Исходное значение value :” << value << ‘\n’;

 

f (value); // передаем адрес переменной value

 

cout << “Новое значение value :” << value << ‘\n’;

 

}

 

Результат:

 

Исходное значение value: 1

 

Новое значение value : 10

 

Замечания.

 

1. В теле функции ссылочный параметр используется непосредственно, без ‘*’. i=10; эквивалентно по смыслу *i = 10;

 

2. При вызове функции аргумент записывается без знака ‘&’. f(value) ; эквивалентно по смыслу f(&value);

 

Функция может возвращать ссылку в качестве результата. Это означает, что она возвращает неявный указатель на значение, передаваемое его в операторе return.

 

Пример.

 

# include <iostream.h>

 

int & f(); // возвращает ссылку на целое число

 

int value = 100; // глобальная переменная

 

void main()

 

{ cout << f() << ‘\n’;

 

/* вывести на экран значение, записанное по адресу, возвращаемому

 

функцией f() */

 

int x;

 

x = f();

 

/* переменной х присвоить значение по ссылке, которую возвращает

 

функция f() */

 

f() = 200;

 

/* по адресу, возвращаемому функцией f(), записать

 

значение 200 */

 

cout << f() << ‘\n’;

 

}

 

int &f()

 

{ return; } // возвращает ссылку на value

 

Результат:

 

 

 

 

Замечание.

 

Нельзя возвращать ссылку на локальную переменную.

 

Например:

 

int &f()

 

{ int i = 10;

 

return i; } // ошибка

 

При завершении функции переменная i выйдет за пределы видимости и ссылка на неё будет не определена.

 

Лекция 12

 

Перегрузка функций

 

В С++ несколько функций могут иметь одинаковые имена, но при условии, что их параметры будут различными. Такую ситуацию называют перегрузкой функций, а функции, которые в ней задействованы – перегруженными.

 

Перегруженные функции должны отличаться типами и (или) числом параметров.

 

Рассмотрим пример.

 

# include <iostream.h>

 

void f( int i ); // один целочисленный параметр

 

void f( int i, int j); // два целочисленных параметра.

 

void f( double k); // один параметр типа double

 

void main()

 

{ f(10); // вызов f(int )

 

f(10,20); // вызов f(int,int)

 

f(12.23); // вызов f(double)

 

}

 

void f( int i)

 

{ cout << “В функции f(int), i=” << i << ‘\n’;}

 

void f(int i, int j)

 

{ cout << “В функции f(int, int), i=” << i << ‘\n’;

 

cout << “, j=” << j << ‘\n’;}

 

void f(double k)

 

{ cout << “В функции f(double), k=” << k << ‘\n’;}

 

Результат:

 

В функции f(int), i=10

 

В функции f(int,int), i=10, j=20

 

В функции f(double), k=12.23

 

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

 

Перегрузка функций широко используется в ООП. Кроме того, чтобы понять выигрыш от перегрузки функций, рассмотрим пример. Допустим, что нужно реализовать функцию, которая возвращает результат отрицания аргумента для различных типов аргумента: int, float, long. Традиционно мы бы написали три функции с разными именами. Перегрузка функций позволяет обойтись только одним именем:

 

int otr ( int n );

 

float otl ( float n );

 

long otl ( long n );

 

Благодаря этому, программисту нужно помнить только одно имя, а не три.

 

В С++ предусмотрено автоматическое преобразование типов при перегрузке функций.

 

# include <iostream.h>

 

void f(int x);

 

void f(double x);

 

void main()

 

{ int i = 10;

 

double d = 10.1;

 

short s = 99;

 

float r = 11.5F;

 

f( i ); // вызов версии f(int)

 

f( d ); // вызов версии f( double )

 

// далее автоматическое преобразование типов

 

f( s ); // short => int и вызов f(int)

 

f( r ); // float => double и вызов f(double)

 

}

 

void f( int x ) {

 

cout << “В функции f(int): “ << x << “\n”;}

 

void f( double ) {

 

cout << “В функции f(double): “ << x << “\n”;}

 

Результат:

 

В функции f(int): 10

 

В функции f(double): 10.1

 

В функции f(int): 99

 

В функции f(double): 11.5

 

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

 

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

 

/* создание перегруженных функций println() и print()

 

для некоторых типов данных */

 

# include <iostream.h>

 

void println( bool b);

 

void println( int i);

 

void println( double d);

 

void print( bool b);

 

void print( int i);

 

void print( double d); void print(char ch);

 

void main()

 

{ println (true);

 

println (10);

 

println (123.23);

 

print (false); print(‘ ‘);

 

print (88); print(‘ ‘);

 

print (100.01);

 

}

 

void println( bool b)

 

{ if (b) cout << “истина\n”;

 

else cout << “ложь\n”;

 

void println( int i)

 

{ cout << i << “\n”;}

 

void println( double d)

 

{ cout << d << “\n”;}

 

void print( bool b)

 

{ if (b) cout << “истина”;

 

else cout << “ложь”;

 

void print( int i)

 

{ cout << i ;}

 

void print( char ch)

 

{ cout << ch;}

 

void print( double d)

 

{ cout << d;}

 

Результат:

 

истина

 

 

123.23

 

ложь 88 100.01

 

Аргумента, передаваемые функции по умолчанию

 

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

 

Пример.

 

# include <iostream.h>

 

void myfunc( int x=0, int y=0);

 

void main()

 

{ myfunc(1,2);

 

myfunc(10);

 

mufunc();

 

}

 

void myfunc (int x, int y)

 

{ cout << “x=” << x << “,y=” << y << “\n”;}

 

Результат

 

x=1, y=2

 

x=10, y=100

 

x=0, y=100

 

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

 

void f( int a = 1, int b) ; // неверно

 

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

 

Значения, передаваемые по умолчанию, должны быть заданы только один раз и причем при первом объявлении функции.

 

Передача аргументов по умолчанию применяется при перегрузке функций.

 

Пример.

 

Определим две версии стандартной функции strcat(): одна будет присоединять всё содержимое одной строки к концу другой, а вторая функция использует один аргумент – количество присоединяемых символов:

 

void mystrcat( char *s1, char *s2, int len);

 

void mystrcat( char *s1, char *s2);

 

Используя возможность передачи аргументов по умолчанию, можно создать только одну функцию:

 

# include <iostream.h>

 

# include <string.h>

 

void mystrcat( char *s1, char *s2, int len=0);

 

void main()

 

{ char str1[80]=”это тест. ”;

 

char str2[80]=”’0123456789”;

 

mystrcat(str1,str2,5);

 

cout << str1 << ‘\n’;

 

strcpy(str1,”это тест. ”);

 

mystrcat(str1,str2); // присоединяем всю строку, поскольку len=5

 

cout << str1 << ‘\n’;

 

}

 

void mystrcat( char *s1, char *s2, int len)

 

{ while (*s1) s1++; //находим конец строки

 

if ( len == 0) len = strlen(s2);

 

while (*s2 && len) {

 

*s1 = *s2; // копируем символы

 

s1++;

 

s2++;

 

len --;

 

}

 

*s1=’0\’; // добавляем нулевой байт

 

}

 

Результат

 

это тест.01234

 

это тест.0123456789

 

Перегрузка функций и неоднозначность

 

Компилятор С++ иногда не знает, какую перегруженную функцию выбрать: возникает неоднозначность.

 

Пример.

 

. . .

 

float f( float i );

 

double f( double i );

 

void main()

 

{ cout << f(10.1) << “ “; // неоднозначности нет

 

// компилятор 10.1 => double автоматически и вызывается double f(double)

 

cout << f(10); // неоднозначность: к какому виду приводить 10? Ошибка !

 

}

 

. . .

 

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

 

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

 

Пример.

 

int f( int i );

 

int f( int i, int j=1);

 

void main()

 

{ cout << f(4,5) << “ “; // неоднозначности нет

 

cout << f(10); // неясно, то ли считать параметр j задаваемым

 

// по умолчанию, то ли вызывать версию f()

 

// с одним параметром

 

}

 

Лекция 13

 

Ввод-вывод в языке С++

 

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

 

Библиотеку функций языка Си подключают к программе с помощью препроцессорной директивы

 

# include <stdio.h>,

 

а библиотеку ввода-вывода для языка Си++ включают с помощью директивы

 

# include <iostream.h>

 

Библиотеку ввода-вывода для языка Си++ написана на этом языке с применением средств ООП.

 

При включении в программу файла iostream.h происходит формирование объектов cin и cout и программисту становятся доступными средства для ввода и вывода информации. При выполнении операции ввода осуществляется преобразование сигналов от клавиатуры в двоичные коды внутреннего представления данных. Операция вывода данных на экран дисплея выполняет преобразование двоичных кодов в символы.

 

Операция ввода на языке Си++ имеет следующий формат:

 

сin >> <имя объекта базового типа>;

 

К базовому типу относятся: int, long, float, double, char и др.

 

Операция вывода имеет следующий формат:

 

cout << <выражение базового типа>

 

Пример.

 

. . . .

 

{ int a; float b;

 

cin >> a;

 

cout << “\n a= “ << a;

 

cin >> b;

 

cout << “\n b= ‘ << b;

 

. . .

 

}

 

Особенности ввода и вывода данных

 

1) использование операций сдвига для ввода и вывода данных не изменяет их приоритет.

 

Например:

 

сout << 5+6*7-a;

 

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

 

Пример:

 

cout << (3+4>5);

 

Здесь нужны скобки, т.к. приоритет операции сравнения меньше, чем ранг оператора <<.

 

2) Для ввода последовательности значений можно использовать цепочки операций:

 

Например:

 

int i, j, k;

 

cin >> i >> j >> k;

 

1). 1 2 3 <enter>

 

2). 1 <enter>

 

2 <enter>

 

3 <enter>

 

3). При вводе и выводе массивов необходимо использовать циклы.

 

Например:

 

float a[5];

 

for ( int i=0; i<5; i++) cin >> a[i]; // ввод

 

for ( int i=0; i<5; i++) cout <<”\n” cin >> a[i]; // ввод

 

Для ввода на клавиатуре можно набрать:

 

0.2 0.3 0.4 0.5 <enter>

 

Рассмотрим особенности ввода и вывода целых чисел, вещественных чисел и строк.

 

4). При вводе и выводе целых чисел существует ограничение на длину внешнего и внутреннего представления.

 

Например:

 

a). long g;

 

cin >> g;

 

cout << “\n g= ” << g;

 

_______________________.

 

3.4 5.6 7.89 <enter> (.42.94.96.7295)

 

Результат:

 

g=123456789

 

б). int i;

 

cin >> i;

 

cout << “\n i= ” << i;

 

.______________________

 

123456789 <enter>

 

Результат:

 

i = -13035 - неправильное значение

 

b). При выводе целых констант учитываются ограничения компилятора.

 

Например:

 

cout << 123456789; // верно : 123456789

 

cout << 98765432100; // ошибка

 

5). При вводе целых чисел их можно набирать на клавиатуре в десятичном, восьмеричном и шестнадцатеричном виде.

 

Например:

 

. . . .

 

{ int a;

 

cin >> a;

 

cout << “\n a= “ << a;

 

}

 

._____________________

 

1).077777<enter>

 

a= 32767

 

2). -0x7FFF <enter>

 

a = -32767

 

6). При вводе вещественных чисел допустимы все их формы записи.

 

Например:

 

. . . .

 

{ float pi;

 

cin >> pi;

 

cout << “\n pi= “ << pi

 

}

 

.______________________

 

<enter>

 

или

 

3.1415e0 <enter>

 

или

 

+3.1415e0 <enter>

 

или

 

0.3.415e+1 <enter>

 

Результат:

 

Pi = 3.1415

 

7). Если вещественное значение слишком мало, то вывод происходит в экспоненциальном формате.

 

Например:

 

. . . .

 

{ float t;

 

cin >> t;

 

cout << “\n t= “ << t;

 

}

 

.____________________

 

0.0….01 <enter>

 

Результат:

 

t = 1e-23

 

8). Символьная строка – массив типа char, завершающийся нулевым байтом. При вводе строки с клавиатуры набираются символы до нажатия клавиши <enter>.

 

Например:

 

char line[81];

 

cin >> line;

 

cout << line;

 

.______________

 

abcdef

 

Результат:

 

abcdef

 

Замечание. При вводе строки с помощью оператора >> пробел заменяется нулевым байтом.

 

Оператор вывода << переносит строку на экран до нулевого байта.

 

При выводе данных допускается их форматирование.

 

Форматирование данных при выводе

 

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

 

int i=1, j=2, k=5;

 

cout << “\n” << i << j << k;

 

Результат:

 

1 2 5

 

Для улучшения читаемости можно в операторе вывода включить пробелы:

 

cout << “\n” << i << “ “ << j << “ “ << k;

 

1 2 5

 

Вещественные числа по умолчанию выводятся на экран с фиксированной точкой.

 

Например:

 

cout << “\n 1.0 / 3.0 = ” << 1.0/3.0;

 

на экране появится:

 

/ 3.0 = 0.333333 ( знаков по умолчанию)

 

Форматы представления выводимой информации могут быть изменены с помощью флагов форматирования. Флаги реализованы в виде отдельных битов целого числа типа long.

 

Например:

 

0х0001 - при таком значении флага операция ввода >> будет игнорировать пробелы.

 

0х0002 – вывод с выравниванием по левому краю.

 

0х0004 – вывод с выравниванием по правому краю ( по умолчанию).

 

0х0010 - десятичная система счисления.

 

0х0020 – восьмеричная система счисления.

 

0х0040 – шестнадцатеричная система счисления.

 

0х0100 – при выводе вещественных чисел обязательно печатать десятичную точку, даже

 

если дробная часть равна 0.

 

0х0400 – печатать знак + при выводе положительных чисел.

 

0х0800 – использовать для вывода вещественных чисел формата с плавающей точкой.

 

0х1000 – использовать формат с фиксированной точкой при выводе вещественных чисел.

 

Количество цифр в дробной части определяется заданной по умолчанию точностью

 

Флаги могут обрабатываться как по отдельности, так и группами (используют операцию ИЛИ ‘|’ )

 

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

 

char fill(); - возвращает текущий символ заполнения пустых позиций поля вывода;

 

char fill(char); - заменяет символ заполнения;

 

long flags(); - возвращает текущий набор битов флагов;

 

long flags(long); - устанавливает биты флагов;

 

long unsetf(long); - сбрасываются биты флагов;

 

int precision(); - возвращает текущее значение точности при выводе вещ.чисел;

 

int precision(int); - устанавливает по значению n точность;

 

int width(); - возвращает min ширину поля вывода;

 

int width(int); - устанавливает ширину поля вывода.

 

Примеры.

void main()

 

{ int a, b;

 

cin >> a >> b;

 

cout.width (4);

 

cout << a << “ “;

 

cout.width (6);

 

cout << b

 

}

 

._______________________

 

12 345 <enter>

 

Результат

 

 

void main()

 

{ float a, b;

 

cin >> a >> b;

 

cout.precision (2);

 

cout << a << “ “ << b;

 

}

 

._______________________

 

15.3456 12.34567 <enter>

 

Результат:

 

15.35 12.35

 

 

void main()

 

{ float a=2.7689547;

 

cout << “\n a= “ << a << “\n”

 

cout.flags(0x0800); // вывод в эксп. формате

 

cout << a ;

 

}

 

Если внести изменения в строку установки флагов:

 

cout.flags(0x0800|0x0400); // вывод в эксп. формате cсо знаком +

 

Результат:

 

а = 2.768955

 

+ а = 2.768955е+00

 

 

void main()

 

{ fint h=10;

 

cout << “\n h= “ << h << “\n”

 

cout.flags(0x0020); // восьмеричный код

 

cout << h ;

 

}

 

Результат:

 

h=10

 

 

 

void main()

 

{ fint h;

 

Cin >> h;

 

cout << “\n h= “ << h << “\n”

 

cout.width(4);

 

cout << h ;

 

}

 

.___________________________

 

12 <enter>

 

Результат:

 

 

##12

 

Следует отметить, что существует еще один способ изменения флагов с помощью манипуляторов. Манипулятор – это функция , вызов которой можно использовать в качестве правого операнда для операции ввода-вывода.

 

Различают манипуляторы без параметров и с параметрами.

 

Манипулятор без параметров:

 

dec – установка флага десятичной с.с.

 

hex – шестнадцатеричной с.с

 

oct – восьмеричной с.с.

 

ws - извлечение из входного потока пробелов

 

end – при выводе включается символ новой строки.

 

Например:

 

cout << 15 << hex << 15 << oct << 15 << dec << 15;

 

150xF01715

 

Манипуляторы с параметрами определены в файле iomanip.h

 

Примеры::

 

setprecision( int n) - задает точность представления вещественных чисел;

 

setw( int n) - задает min ширину поля вывода:

 

setfill( int n) - задает код символа-заполнителя;

 

resetiosflags( long l) - сбрасывает флаги.

 

Пример.

 

# include <iostream>h>

 

# include <iomanip.h>

 

void main()

 

{ cout << setprecision(3) << 12.345678;

 

cout << setw(4) << 12;

 

}

 

Результат:

 

 

`

 

 

Рекламные ссылки

Рассмотрим пример, когда программа использует одно адресное пространство.

 

 

программа использует одно адресное пространство

Недостатки такой системы:

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

 

Эти проблемы можно решить, если дать каждому участку независимое адресное пространство, называемое сегментом.

Рассмотрим то же пример с использованием сегментов:

 

 

Сегментированная память

Каждый сегмент может расти или уменьшаться независимо от других.

Сегмент - это логический объект.

В этом случае адрес имеет две части:

· номер сегмента

· адрес в сегменте

Преимущества сегментации:

· Сегменты не мешают друг другу.

· Начальный адрес процедуры всегда начинается с (n,0). Что упрощает программирование.

· Облегчает совместное использование процедур и данных.

· Раздельная защита каждого сегмента (чтение, запись).

 



<== предыдущая лекция | следующая лекция ==>
Элементов массива. | Сегментация с использованием страниц: IntelPentium


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 1.048 сек.