Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Согласно принципам фон Неймана компьютер должен....docx
Скачиваний:
4
Добавлен:
01.08.2019
Размер:
131.24 Кб
Скачать

Void swap(int & a, int & b)

Амперсанды перед именем переменной означают, что эта переменная является не локальной переменной, а ссылкой на переменную, указанную в качестве параметра при вызове функции. Теперь при вызове swap(p,q) переменные a и b являются синонимами для переменных p и q, и изменение их значений влечет изменение значений p и q. А вот вызывать функцию в виде swap(3,5) уже нельзя, поскольку 3 и 5 — это константы, и сделать переменные синонимами констант нельзя.

Однако в языке C (не C++) вообще не было такого понятия, как передача параметров по ссылке. Для того, чтобы реализовать функцию, аналогичную swap в рассмотренном примере, необходимо было передавать адреса переменных p и q, а сама функция при этом должна быть объявлена, как

Void swap(int * a, int * b)

Поскольку в этом случае функция swap знает физические адреса в оперативной памяти переменных p и q, то разыменовав эти адреса функция swap сможет изменить значения самих переменных p и q.

Теперь вспомним, что в C++ массивы и указатели — это почти одно и то же. А поскольку передача массива по значению, то есть создание копии всего массива является трудоемкой операцией (особенно для больших массивов), то вместо массива всегда передается указатель на его начало. То есть два объявления функции void f(int A[10]) и void f(int * A) абсолютно идентичны. В любом случае функция f получит указатель на начало массива, а, значит, будет способна изменять значения его элементов.

27. Символьные строки представляют один из наиболее полезных и важных типов данных. В языке С++ символьная строка является символьным массивом, который заканчивается нуль-символом (\0). Если строка должна содержать N символов, то в описании нужно указать N+1 элемент.

Для работы со строками существует специальная библиотека, описание которой находится в файле string.h. Рассмотрим наиболее часто используемые функции из этой библиотеки.

strlen (s) - определяет длину строки (без нуль-символа). s – указатель на строку символов. strcat (s1, s2) – объединяет две с троки. s1, s2 – указатели на строку символов. Копия второй строки присоединяется к концу первой, и это объединение становится новой первой строкой. Строка s2 остается без изменения, а s1должна быть достаточно большой, чтобы разместить две строки. strcmp (s1, s2) – сравнивает содержимое двух строк. s1, s2- указатели на строку символов. Эта функция возвращает 0, если строки одинаковые. Если строки разные, то функция возвращает разницу в кодах у первой пары несовпадающих символов. strcpy (s1, s2) – копирова ние строки: строка, на которую указывает второй аргумент, копируется в строку, на которую указывает первый. strchr (s, c) – ищет в строке s первое вхождение символа с и возвращает указатель на этот символ, если не обнаружит, то возвращает NULL. strstr (s1, s2) – ищет в строке s1 первое вхождение подстроки s2 и возвращает указатель на найденную подстроку, если не обнаружит, то возвращает NULL.

28. Структуры и объединения.

Синтаксис структур. Структуры в языке С аналогичны массиву тесно связанных атрибутов. Однако, в отличие от массива, структура позволяет иметь смешанные атрибуты различных типов данных. Структура создается при помощи ключевого слова struct, за которым следует имя_типа (имя структуры) и список элементов. Описание структуры заканчивается точкой с запятой, т.к. оно является оператором:

struct имя_типа { тип_элемента1 имя_элемента1; тип_элемента2 имя_элемента2; тип_элемента3 имя_элемента3; … };

Пример описания структуры:

struct stboat { char type[16]; // тип char model[16]; // модель char title[20]; // имя int year; // год выпуска double price; // цена };

Эта структура описывает новый тип данных stboat и содержит три текстовых поля, одно целое и одно действительное. С описанной структурой не связывается никакая переменная. Для создания новой переменной используется оператор (слово struct является необязательным):

struct имя_структуры имя-переменной;

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

struct sboat used_boat;

Если со структурой связывается только одна переменная, то ее можно объявить непосредственно в описании структуры перед точкой с запятой. Имя_типа в этом случае можно опустить:

struct { char type[16]; // тип char model[16]; // модель char title[20]; // имя int year; // год выпуска double price; // цена } used_boat;

Доступ к элементам структуры. Для обращения к отдельным элементам структуры используется операция обращения к члену структуры – “точка”. Она разделяет имя структурной переменной и имя поля, например

used_boat.year=1955; printf(“%lf”,used_boat_price);

Элементы структуры обрабатываются также, как и любые другие переменные С, необходимо только использовать операцию “точка”. Можно создавать массивы структур и указатели на структуру, для обращения к элементам структуры предусмотрен оператор “->” (стрелка, он состоит из знаков минус и больше).

sboat boat_array[10]; boat_array[3].year=1980; sboat *pboat; pboat=&used_boat; pboat->price=12570.25;

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

struct имя_типа { тип_элемента1 

имя_поля1:колич_бит1,

имя_поля2:колич_бит2, имя_поля3:колич_бит3, …; тип_элемента2 … };

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

struct keybits { unsigned char shift :1, ctrl :1, scrool:1, lock :1; };

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

union имя_типа { тип_элемента1 имя_элемента1; тип_элемента2 имя_элемента2; тип_элемента3 имя_элемента3; … };

К членам объединения можно обращаться так же, как и к членам структур, либо через операцию “точка”, либо через операцию “->” – для указателей:

union many_types ( char c[4]; int ivalue[2]; float fvalue; } type1; type1.fvalue=1.5; printf(“%i %i”,type1.ivalue[0],type1.ivalue[1]);

Дополнительные средства (typedef и enum). При помощи оператора typedef можно связать новые типы данных с существующими:

typedef double real;

После такого описания можно использовать real вместо double. Использовать typedef необходимо с осторожностью. Слишком много новых типов могут ухудшить читаемость программы. Перечисляемый тип данных enum позволяет определить список последовательных целых чисел, каждое из которых имеет собственное имя. Объявление перечисляемого типа выглядит следующим образом:

enum имя_типа {имя1=знач1, имя2=знач2, имя3=знач3, …}переменная;

Здесь имя1, имя2,… – это имена констант. Им можно присваивать целочисленные значения. Если значения отсутствуют, то предполагается, что они последовательно увеличиваются на единицу, начинаясь с нуля. Память под эти константы во время выполнения не выделяется, поэтому удобно использовать этот оператор для создания констант, если не указывать имя_типа и переменную:

enum (с28=28, с29,с30,c31);

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

enum months

{Jan=1,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec}

current_month;

current_month=Dec; //используется константа 12 int diff=(int)current_month-2; //тип enum автоматически не преобразуется в int 

Поскольку имена эквивалентны последовательным целым значениям, то с ними можно выполнять арифметические операции. Фактически в данном примере переменной current_month присваивается целочисленное значение 12.

29.

Битовое поле — в программировании число, занимающее некоторый набор битов, напрямую не адресуемый процессором. Например: при 8-битном байте первые два поля протокола IP — версия и IHL — будут битовыми полями. На машинах с 32-битным байтом все поля IP-пакета (кроме IP-адресов отправителя и получателя) будут битовыми.

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

Компиляторы, как правило, ограничивают работу с битовыми полями только извлечением значения битового поля и записью значения в битовое поле, а само битовое поле воспринимается как беззнаковое число. Реальный порядок следования битовых полей в структуре является системно-зависимым: в одних компиляторах битовые поля могут быть расположены начиная с младших битов, а в других — со старших.

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

Если память ограничена, то в одном байте можно хранить несколько булевых переменных (принимающих значения ИСТИНА и ЛОЖЬ);

Некоторые устройства передают информацию о состоянии, закодированную в байте в одном или нескольких битах;

Для некоторых процедур шифрования требуется доступ к отдельным битам внутри байта.

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

Битовое поле может быть членом структуры или объединения. Оно определяет длину поля в битах. Общий вид определения битового поля такой:

тип имя : длина;

Здесь тип означает тип битового поля, а длина — количество бит, которые занимает это поле. Тип битового поля может бытьint, signed или unsigned. (Кроме того, в соответствии со стандартом С99, у битового поля еще может быть тип _Вооl.)

Информацию в байте состояния можно представить с помощью следующего битового поля:

struct status_type {

unsigned delta_cts: 1;

unsigned delta_dsr: 1;

unsigned tr_edge: 1;

unsigned delta_rec: 1;

unsigned cts: 1;

unsigned dsr: 1;

unsigned ring: 1;

unsigned rec_line: 1;

} status;

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

status = get_port_status();

if(status.cts) printf("Разрешение на передачу");

if(status.dsr) printf("Данные готовы");

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

status.ring = 0;

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

Нет необходимости давать имя каждому битовому полю. Таким образом можно легко получать доступ к нужному биту, обходя неиспользуемые. Например, если вас интересуют только биты cts и dsr, то структуру status_type можно объявить таким образом:

struct status_type {

unsigned : 4;

unsigned cts: 1;

unsigned dsr: 1;

} status;

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

В структурах можно сочетать обычные члены с битовыми полями. Например, в структуре

struct emp {

struct addr address;

float pay;

unsigned lay_off: 1; /* временно уволенный или работающий */

unsigned hourly: 1; /* почасовая оплата или оклад */

unsigned deductions: 3; /* налоговые (IRS) удержания */

};

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

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

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

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

 

Для получения доступа к бинарному файлу(потоку) в MVS нужно:

Создать поток соответствующего типа:

ifstream- для ввода из файла ;

ofstream- для вывода в файл;

fstream   - для обмена с файлом в двух направлениях.

 

Связать его с файлом данных и открыть (open) для работы в определенном режиме, с обязательным указанием двоичного режима ios::binary(по умолчанию потоки открываются в текстовом режиме):

void ifstream::open(const char *имя_файла, openmode режим=ios::in|ios::binary);

void ofstream::open(const char *имя_файла, openmode режим=ios::out|ios::trunс|ios::binary);

void fstream::open(const char *имя_файла, openmode режим=ios::in|ios::out|ios::binary);

 

где имя_файла – имя файла, в которое может входить спецификатор пути;

режим – задает режим открытия файла (см. лаб. раб. №10).

Обмен данными с файлом через поток: запись в поток; чтение из потока; управление состоянием потока.

 

Для записи в поток в C++ используется метод write:

basic_ostream<_Elem, _Tr>& write( const char_type *_Str, streamsize _Count );

 

Для чтения из потока используется метод read:

basic_istream<_Elem, _Tr>& read( const char_type *_Str, streamsize _Count );

_Countчисло выводимых (вводимых) в поток байт

_Strсимволы выводимые(вводимые) в (из) поток(а)

 

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

для потоков открытых на ввод:

basic_istream<_Elem, _Tr>& seekg( pos_type _Pos );

basic_istream<_Elem, _Tr>& seekg( off_type _Off, ios_base::seekdir _Way );

 

для потоков открытых на вывод в C++ пишется так:

basic_ostream<_Elem, _Tr>& seekp( pos_type _Pos );

basic_ostream<_Elem, _Tr>& seekp( off_type _Off, ios_base::seekdir _Way );

 

_Posпозиция в потоке для чтения (номер байта)

_Off  позиция относительно _Way.

_Wayодно из перечислений ios_base  (beg, cur, end):

перечисление

описание

ios_base::beg

начало файла

ios_base::end

конец файла

ios_base::cur

текущая позиция

 

Например:

ofstream fout(“my.dat”,ios::ate|ios::binary);

//устанавливаем указатель на начало потока

fout.seekp(0);

fout.write((char *) &el,sizeof el);

 

ifstream fin(“my.dat”,ios::ate|ios::binary);

//передвигаем указатель на 10 байт перед концом потока

fin.seekg(-10, ios_base::end);

fin.read((char *) &el,sizeof el);

 

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

Для закрытия потока используется функция close().

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

В языке C имеется 4 класса памяти:

- extern (внешние переменные);

- auto (автоматические переменные);

- register (регистровые переменные);

- static (статические переменные);

Класс памяти

Класс памяти определяет:

место размещения переменной (ОП или регистры)

область действия имени (идентификатора), связанного с переменной

видимость объекта

время существования

тип компоновки (связывания)

Явно задать класс памяти можно с помощью спецификаторов: auto, register, static, extern.

Область действия (ОД) имени

ОД – это часть программы, в которой можно обращаться к данному имени (сфера действия имени в программе).

Рассмотрим все случаи:

Имя определено в блоке (локальные переменные): ОД - от точки определения до конца блока;

Формальные параметры в определении функции (форм. пар. - это локальные переменные функции) : ОД параметров – блок тела функции;

Метки операторов: ОД – функция;

Глобальные объекты: ОД - вся программа от точки их определения или от точки их описания;

Формальные параметры в прототипе функции: ОД- прототип.

32. input output

33. Предположим, что наша программа с именем nameprog.exe была запущена из командной строки со следующими аргументами:

>nameprog par1 par2 par3

Тогда первый аргумент главной функции argc будет равен 4 (имя программы входит в список параметров командной строки). Второй аргумент функции mainпредставляет собой строковый массив, элементами которого являются отдельные компоненты командной строки:

argv[0]

argv[1]

argv[2]

argv[3]

"nameprog"

"par1"

"par2"

"par3"

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

>pkzip –a –r arch.zip qq1.doc qq2.doc

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

double mid(double x,double y)

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

double mid(double x, double y)

{ return (x+y)/2.; }

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

void print_v(int *a,int n)

{ int j;

printf("\n");

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

printf("%8d",a[j]);

printf("\n");

}

Если перед именем функции не указан ни один из стандартных типов и отсутствует спецификатор void, то считается, что функция возвращает значение типа int.

34. Технология структурного программирования в самой краткой формулировке есть нисходящее проектирование, т.е. выстраивание текста программы, точнее алгоритмической компоненты, от общего к частному, от внешней конструкции к внутренней. Естественно, что надо знать, из чего выстраивать. В идеале, у опытного программиста действительно очередная нужная конструкция появляется «из головы». Но это не значит, что он не имеет общего плана действий и обобщенного представления процесса, который реализуется проектируемой программой.

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

·        формулировка целей (результатов) работы программы;

·        образное представление  процессы ее работы (образная модель);

·        выделение из образной модели фрагментов: определение переменных и их смыслового наполнения, стандартных программных контекстов.

Любая программа представляет собой структуру, построенную из трёх типов базовых конструкций:

последовательное исполнение — однократное выполнение операций в том порядке, в котором они записаны в тексте программы;

ветвление — однократное выполнение одной из двух или более операций, в зависимости от выполнения некоторого заданного условия;

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

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

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

Разработка программы ведётся пошагово, методом «сверху вниз».

Сначала пишется текст основной программы, в котором, вместо каждого связного логического фрагмента текста, вставляется вызов подпрограммы, которая будет выполнять этот фрагмент. Вместо настоящих, работающих подпрограмм, в программу вставляются «заглушки», которые ничего не делают. Полученная программа проверяется и отлаживается. После того, как программист убедится, что подпрограммы вызываются в правильной последовательности (то есть общая структура программы верна), подпрограммы-заглушки последовательно заменяются на реально работающие, причём разработка каждой подпрограммы ведётся тем же методом, что и основной программы. Разработка заканчивается тогда, когда не останется ни одной «затычки», которая не была бы удалена. Такая последовательность гарантирует, что на каждом этапе разработки программист одновременно имеет дело с обозримым и понятным ему множеством фрагментов, и может быть уверен, что общая структура всех более высоких уровней программы верна. При сопровождении и внесении изменений в программу выясняется, в какие именно процедуры нужно внести изменения, и они вносятся, не затрагивая части программы, непосредственно не связанные с ними. Это позволяет гарантировать, что при внесении изменений и исправлении ошибок не выйдет из строя какая-то часть программы, находящаяся в данный момент вне зоны внимания программиста.

35. В C++ также допускается использование перегруженных функций. Под перегрузкой понимается создание нескольких прототипов функции, имеющих одинаковое имя. Компилятор различает их по набору аргументов.  Перегруженные функции оказываются весьма полезными, когда одну и ту же операцию необходимо выполнить над аргументами разных типов. В следующей программе создаются два прототипа функции с одним именем и общей областью видимости, но аргументами разных типов.  При вызове функции adder() будут обрабатываться данные либо типа int, либо типа float.   // // overload.cpp  // Эта программа на языке C++ содержит пример перегрузки функции. // #include ‹iostream.h› int adder (int iarray[]);  float adder (float f array []);  int main() { int iarray[7] = {5,1, 6, 20,15,0, 12);  float farray[7] = {3.3,5.2,0.05,1.49,3.12345,31.0,2.007};  int isum; float fsum; isura = adder (iarray) ;  fsum= adder (f array) ; cout<< "Сумма массива целых чисел равна " << isura << "\n";  cout<< "Сумма массива дробных чисел равна " << fsum<< "\n"; return ( 0 ).;  } int adder(int iarrayt]) { int i; int ipartial; ipartial = iarray[0];  for(i= 1; i < 7; i++) ipartial += iarray[i];  return(ipartial); } float adder (float farrayf]) { int i;  float f partial, fpartial = f array [0];  for(i= 1; i < 7; i++) fpartial += farray[i];  return (fpartial) ; } 

Перегрузка функций

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

Перегрузка функции (function overloading) - это определение двух или более функций с одинаковым именем, но разными наборами параметров.

Функции, имеющие общее имя, называются перегруженными (over-loaded).

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

  // объявления функций для программы на Си   int multiplyInt(int num1, int num2);   float multiplyFloat(float num1, float num2);   short multiplyShort(short num1, short num2);

Не проще было бы иметь одну функцию multiply(), достаточно «умную», чтобы определить, когда вы хотите перемножить shortint или long? В С++ это возможно благодаря перегрузке функций. Вот как выглядят объявления для перегруженных функций:

  // объявления в С++   int multiply(int num1, int num2);   float multiply(float num1, float num2);   short multiply(short num1, short num2);

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

  float х = 1,5;   float у = 10.5;   float result = multiply(х, у);

Компилятор видит, что функции передаются два числа типа float и вызывает соответствующую версию multiply(). Аналогичным образом, если передаются два целых числа, будет вызвана целочисленная версия multiply().

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