Основные конструкции языка Си

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

Рубрика Программирование, компьютеры и кибернетика
Вид курс лекций
Язык русский
Дата добавления 29.06.2016
Размер файла 165,3 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

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

1) известно количество параметров, которое передается как обязательный параметр;

2) известен признак конца списка параметров;

Пример1

Найти среднее арифметическое последовательности чисел

//известен признак конца списка параметров

#include<iostream.h>

float sum(int k, . . .)

{

int *p=&k;//настроили указатель на параметр k

int s=0;

for(;k!=0;k--)

s+=*(++p);

return s/k;

}

void main()

{

cout<<”\n4+6=”<<sum(2,4,6);//находит среднее арифметическое 4+6

cout<<”\n1+2++3+4=”<<sum(4,1,2,3,4);//находит среднее арифметическое 1+2+3+4

}

Для доступа к списку параметров используется указатель *p типа int. Он устанавливается на начало списка параметров в памяти, а затем перемещается по адресам фактических параметров (++p).

Пример 2.

//известен признак конца списка параметров

#include<iostream.h>

int sum(int k, . . .)

{

int *p=&k;//настроили указатель на параметр k

int s=*p;//значение первого параметра присвоили s

for(int i=1;p!=0;i++)//пока нет конца списка

s+=*(++p);

return s/(i-1);

}

void main()

{

cout<<”\n4+6=”<<sum(4,6,0);//находит среднее арифметическое 4+6

cout<<”\n1+2++3+4=”<<sum(1,2,3,4,0);//находит среднее арифметическое 1+2+3+4

}

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

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

Пример.

#include<iostream.h>

#include <string.h>

int max(int a,int b)

{

if(a>b)return a;

else return b;

}

float max(float a,float b)

{

if(a>b)return a;

else return b;

}

char*max(char*a,char*b)

{

if(strcmp(a,b)>0) return a;

else return b;

}

void main()

{

int a1,b1;

float a2, b2;

char s1[20];

char s2[20];

cout<<"\nfor int:\n";

cout<<"a=?";cin>>a1;

cout<<"b=?";cin>>b1;

cout<<"\nMAX="<<max(a1,b1)<<"\n";

cout<<"\nfor float:\n";

cout<<"a=?";cin>>a2;

cout<<"b=?";cin>>b2;

cout<<"\nMAX="<<max(a2,b2)<<"\n";

cout<<"\nfor char*:\n";

cout<<"a=?";cin>>s1;

cout<<"b=?";cin>>s2;

cout<<"\nMAX="<<max(s1,s2)<<"\n";

}

Правила описания перегруженных функций:

1) Перегруженные функции должны находиться в одной области видимости.

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

3) Функции не могут быть перегружены, если описание их параметров отличается только модификатором const или наличием ссылки.

Например, функции int&f1(int&,const int&){. . . } и int f1(int,int){. . . } - не являются перегруженными, т. к. компилятор не сможет узнать какая из функций вызывается: нет синтаксических отличий между вызовом функции, которая передает параметр по значению и функции, которая передает параметр по ссылке.

16. Шаблоны функций

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

template <class имя_типа [,class имя_типа]>

заголовок_функции

{тело функции}

Таким образом, шаблон семейства функций состоит из 2 частей - заголовка шаблона: template<список параметров шаблона> и обыкновенного определения функции, в котором вместо типа возвращаемого значения и/или типа параметров, записывается имя типа, определенное в заголовке шаблона.

Пример 1.

//шаблон функции, которая находит абсолютное значение числа любого типа

template<class type>//type - имя параметризируемого типа

type abs(type x)

{

if(x<0)return -x;

else return x;

}

Шаблон служит для автоматического формирования конкретных описаний функций по тем вызовам, которые компилятор обнаруживает в программе. Например, если в программе вызов функции осуществляется как abs(-1.5), то компилятор сформирует определение функции double abs(double x){. . . }.

Пример 2.

//шаблон функции, которая меняет местами две переменных

template <class T>//T - имя параметризируемого типа

void change(T*x,T*y)

{T z=*x;*x=*y;*y=z;}

Вызов этой функции может быть :

long k=10,l=5;

change(&k,&l);

Компилятор сформирует определение:

void change(long*x,long*y){ long z=*x;*x=*y;*y=z;}

Пример 3.

#include<iostream.h>

template<class Data>

Data&rmax(int n,Data a[])

{

int im=0;

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

if(a[im]<a[im])im=i;

return d[im];//возвращает ссылку на максимальный элемент в массиве

}

void main()

{int n=5;

int x[]={10,20,30,15};

cout<<”\nrmax(n,x)=”<<rmax(n,x)<<”\n”;

rmax(n,x)=0;

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

cout<<x[i]<<” “;

cout<<”\n”;

float y[]={10.4,20.2,30.6,15.5};

cout<<”\nrmax(n,y)=”<<rmax(n,y)<<”\n”;

rmax(4,y)=0;

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

cout<<y[i]<<” “;

cout<<”\n”;

}

Результаты:

rmax(n,x)=30

10 20 0 15

rmax(n,y)=30.6

10.4 20.2 0 15.5

Основные свойства параметров шаблона функций

1. Имена параметров должны быть уникальными во всем определении шаблона.

2. Список параметров шаблона не может быть пустым.

3. В списке параметров шаблона может быть несколько параметров, каждый из них начинается со слова class.

17. Указатель на функцию

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

тип_функции(*имя_указателя)(спецификация параметров)

Примеры:

1. int f1(char c){. . . . }//определение функции

int(*ptrf1)(char);//определение указателя на функцию f1

2. char*f2(int k,char c){. . . .}//определение функции

char*ptrf2(int,char);//определение указателя

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

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

(*имя_указателя)(список фактических параметров);

Пример.

#include <iostream.h>

void f1()

{cout<<”\nfunction f1”;}

void f2()

{cout<<”\nfunction f2”;}

void main()

{

void(*ptr)();//указатель на функцию

ptr=f2;//указателю присваивается адрес функции f2

(*ptr)();//вызов функции f2

ptr=f1;//указателю присваивается адрес функции f1

(*ptr)();//вызов функции f1с помощью указателя

}

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

void (*ptr)()=f1;

Указатели н функции могут быть объединены в массивы. Например, float(*ptrMas[4])(char) - описание массива, который содержит 4 указателя на функции. Каждая функция имеет параметр типа char и возвращает значение типа float. Обратиться к такой функции можно следующим образом:

float a=(*ptrMas[1])(`f');//обращение ко второй функции

Пример.

#include <iostream.h>

#include <stdlib.h>

void f1()

{cout<<"\nThe end of work";exit(0);}

void f2()

{cout<<"\nThe work #1";}

void f3()

{cout<<"\nThe work #2";}

void main()

{

void(*fptr[])()={f1,f2,f3};

int n;

while(1)//бесконечный цикл

{

cout<<"\n Enter the number";

cin>>n;

fptr[n]();//вызов функции с номером n

}

}

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

Пример.

#include <iostream.h>

#include <math.h>

typedef float(*fptr)(float);//тип - указатель на функцию

float root(fptr f, float a, float b, float e)//решение уравнения методом половинного деления

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

{float x;

do

{

x=(a+b)/2;

if ((*f)(a)*f(x)<0)b=x; else a=x;

}

while((*f)(x)>e&&fabs(a-b)>e);

return x;

}

float testf(float x)

{return x*x-1;}

void main()

{

float res=root(testf,0,2,0.0001);

cout<<”\nX=”<<res;

}

18. Ссылки на функцию

Подобно указателю на функцию определяется и ссылка на функцию:

тип_функции(&имя_ссылки)(параметры) инициализирующее_выражение;

Пример.

int f(float a,int b){. . . }//определение функции

int(&fref)(float,int)=f;//определение ссылки

Использование имени функции без параметров и скобок будет восприниматься как адрес функции. Ссылка на функцию является синонимом имени функции. Изменить значение ссылки на функцию нельзя, поэтому более широко используются указатели на функции, а не ссылки.

Пример.

#include <iostream.h>

void f(char c)

{cout<<”\n”<<c;}

void main()

{

void (*pf)(char);//указатель на функцию

void(&rf)(char);//ссылка на функцию

f(`A');//вызов по имени

pf=f;//указатель ставится на функцию

(*pf)(`B');//вызов с помощью указателя

rf(`C');//вызов по ссылке

}

19. Типы данных, определяемые пользователем

19.1 Переименование типов

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

typedef тип имя_типа [размерность];

Примеры:

typedef unsigned int UNIT;

typedef char Msg[100];

Такое имя можно затем использовать также как и стандартное имя типа:

UNIT a,b,c;//переменные типа unsigned int

Msg str[10];// массив из 10 строк по 100 символов

19.2 Перечисления

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

enum [имя_типа] {список констант};

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

Пример:

Enum Err{ErrRead, ErrWrite, ErrConvert);

Err error;

. . . .

switch(error)

{

case ErrRead: …..

case ErrWrite: …..

case ErrConvert: …..

}

19.3 Структуры

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

Форматы определения структурного типа следующие:

1. struct имя_типа //способ 1

{

тип 1 элемент1;

тип2 элемент2;

. . .

};

Пример:

struct Date//определение структуры

{

int day;

int month;

int year;

};

Date birthday;//переменная типа Date

2) struct //способ 2

{

тип 1 элемент1;

тип2 элемент2;

. . .

} список идентификаторов;

Пример:

struct

{

int min;

int sec;

int msec;

}time_beg,time_end;

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

Во втором случае описание структуры служит определением переменных.

3) Структурный тип можно также задать с помощью ключевого слова typedef:

Typedef struct //способ 3

{

floar re;

float im;

}Complex;

Complex a[100];//массив из 100 комплексных чисел.

19.3.1 Инициализация структур

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

Примеры:

1. struct Student

{

char name[20];

int kurs;

float rating;

};

Student s={”Иванов”,1,3.5};

2. struct

{

char name[20];

char title[30];

float rate;

}employee={“Петров", “директор”,10000};

Работа со структурами

19.3.2 Присваивание структур

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

Student ss=s;

19.3.3 Доступ к элементам структур

Доступ к элементам структур обеспечивается с помощью уточненных имен:

Имя_структуры.имя_элемента

employee.name - указатель на строку «Петров»;

employee.rate - переменная целого типа со значением 10000

Пример:

#include <iostream.h>

void main()

{

struct Student

{

char name[30];

char group[10];

float rating;

};

Student mas[35];

//ввод значений массива

for(int i=0;i<35;i++)

{

cout<<”\nEnter name:”;cin>>mas[i].name;

cout<<”\nEnter group:”;cin>>mas[i].group;

cout<<”\nEnter rating:”;cin>>mas[i].rating;

}

cout<<”Raitng <3:”;

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

if(mas[i].name<3)

cout<<”\n”<<mas[i].name;

}

19.4 Указатели на структуры

Указатели на структуры определяются также как и указатели на другие типы.

Student*ps;

Можно ввести указатель для типа struct, не имеющего имени (способ 2):

Struct

{

char *name;

int age;

} *person;//указатель на структуру

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

Student *ps=&mas[0];

Указатель на структуру обеспечивает доступ к ее элементам 2 способами:

1.(*указатель).имя_элемента

2. указатель->имя_элемента

cin>>(*ps).name;

cin>>ps->title;

20. Битовые поля

Битовые поля - это особый вид полей структуры. При описании битового поля указывается его длина в битах (целая положительная константа).

Пример:

struct {

int a:10;

int b:14}xx,*pxx;

. . . .

xx.a=1;

pxx=&xx;

pxx->b=8;

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

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

21. Объединения

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

Пример

union{

char s[10];

int x;

}u1;

0

1

2

3

. . . .

9

x- занимает 2 байта

S - занимает 10 байтов

Рис.3. Расположение объединения в памяти

И s, и x располагаются на одном участке памяти. Размер такого объединения будет равен 10 байтам.

Пример1:

//использование объединений

enum paytype{CARD,CHECK};//тип оплаты

struct{

paytype ptype;//поле, которое определяет с каким полем объединения будет

// выполняться работа

union{

char card[25];

long check;

};

}info;

switch (info.ptype)

{

case CARD:cout<<”\nОплата по карте:”<<info.card;break;

case CHECK:cout<<”\nОплата чеком:”<<info.check;break;

}

22. Динамические структуры данных

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

- линейные списки;

- стеки;

- очереди;

- бинарные деревья;

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

Наиболее простой динамической структурой является линейный однонаправленный список, элементами которого служат объекты структурного типа (рис.4).

Информационное поле

адресное поле

Информационное поле

NULL

Рис.4. Линейный однонаправленный список

22.1 Линейный однонаправленный список

Описание простейшего элемента такого списка выглядит следующим образом:

struct имя_типа

{

информационное поле;

адресное поле;

};

Информационное поле - это поле любого, ранее объявленного или стандартного, типа;

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

Информационных полей может быть несколько.

Примеры.

1. struct Node

{

int key;//информационное поле

Node*next;//адресное поле

};

2. struct point

{

char*name;//информационное поле

int age;//информационное поле

point*next;//адресное поле

};

Каждый элемент списка содержит ключ, который идентифицирует этот элемент. Ключ обычно бывает либо целым числом (пример 1), либо строкой (пример 2).

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

- начальное формирование списка (создание первого элемента);

- добавление элемента в конец списка;

- добавление элемента в начало списка;

- удаление элемента с заданным номером;

- чтение элемента с заданным ключом;

- вставка элемента в заданное место списка (до или после элемента с заданным ключом);

- упорядочивание списка по ключу

и др.

Пример1. Создание и печать однонаправленного списка

#include <iostream.h>

#include<string.h>

//описание структуры

struct point

{char *name;//информационное поле

int age;//информационное поле

point*next;//адресное поле

};

point* make_point()

//создание одного элемента

{

point*p=new(point);//выделить память

char s[20];

cout<<"\nEnter the name:";

cin>>s;

p->name=new char[strlen(s)+1];//выделить память под динамическую строку символов

strcpy(p->name,s);//записать информацию в строку символов

cout<<"\nEnter the age";

cin>>p->age;

p->next=0;//сформировать адресное поле

return p;

}

void print_point(point*p)

//печать информационных полей одного элемента списка

{

cout<<"\nNAME:"<<p->name;

cout<<"\nAGE:"<<p->age;

cout<<"\n--------------------------------\n";

}

point* make_list(int n)

//формирование списка из n элементов

{

point* beg=make_point();//сформировать первый элемент

point*r;

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

{

r=make_point();//сформировать следующий элемент

//добавление в начало списка

r->next=beg;//сформировать адресное поле

beg=r;//изменить адрес первого элемента списка

}

return beg;//вернуть адрес начала списка

}

int print_list(point*beg)

//печать списка, на который указывает указатель beg

{

point*p=beg;//р присвоить адрес первого элемента списка

int k=0;//счетчик количества напечатанных элементов

while(p)//пока нет конца списка

{

print_point(p);//печать элемента, на который указывает элемент p

p=p->next;//переход к следующему элементу

k++;

}

return k;//количество элементов в списке

}

void main()

{

int n;

cout<<"\nEnter the size of list";

cin>>n;

point*beg=make_list(n);//формирование списка

if(!print_list(beg)) cout<<"\nThe list is empty";}//печать списка

Пример 2. Удаление из однонаправленного списка элемента с номером k (рис 5.).

Рис. 5. Удаление элемента с номером k из однонаправленного списка

point*del_point(point*beg,int k)

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

{

point*p=beg,//поставить вспомогательную переменную на начало списка

*r;//вспомогательная переменная для удаления

int i=0;//счетчик элементов в списке

if(k==0)

{//удалить первый элемент

beg=p->next;

delete[]p->name;//удалить динамическое поле name

delete[]p;//удалить элемент из списка

return beg;//вернуть адрес первого элемента списка

}

while(p)//пока нет конца списка

{

if(i==k-1)//дошли до элемента с номером k-1, чтобы поменять его поле next

{//удалить элемент

r=p->next;//поставить r на удаляемый элемент

if(r)//если p не последний элемент

{

p->next=r->next;//исключить r из списка

delete[]r->name;//удалить динамическое поле name

delete[]r;//удалить элемент из списка

}

else p->next=0;//если p -последний элемент, то в поле next присвоить NULL

}

p=p->next;//переход к следующему элементу списка

i++;//увеличить счетчик элементов

}

return beg;//вернуть адрес первого элемента}

22.2 Работа с двунаправленным списком

Рис. 6 Двунаправленный список

Пример 3.

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

#include <iostream.h>

struct point//описание структуры

{

int key;//ключевое поле

point* pred,*next;//адресные поля

};

point*make_list()

{

int n;

cout<<"n-?";cin>>n;

point *p,*r,*beg;

p=new (point);//создать первый элемент

beg=p;//запомнить адрес в переменную beg, в которой хранится начало списка

cout<<"key-?";cin>>p->key;//заполнить ключевое поле

p->pred=0;p->next=0;//запомнить адресные поля

for(int i=1;i<n;i++)//добавить элементы в конец списка

{

r=new(point);//новый элемент

cout<<"key-?";cin>>r->key;//адресное поле

p->next=r;//связать начало списка с r

r->pred=p;//связать r с началом списка

r->next=0;//обнулить последнее адресное поле

p=r;//передвинуть p на последний элемент списка

}

return beg;//вернуть первый элемент списка

}

void print_list(point *beg)

{

if (beg==0)//если список пустой

{

cout<<"The list is empty\n";

return;

}

point*p=beg;

while(p)//пока не конец списка

{

cout<<p->key<<"\t";

p=p->next;//перейти на следующий

}

cout<<"\n";

}

point* del_point(point*beg, int k)

{

point *p=beg;

if(k==0)//удалить первый элемент

{

beg=beg->next;//переставить начало списка на следующий элемент

if(beg==0)return 0;//если в списке только один элемент

beg->pred=0;//обнулить адрес предыдущего элемента

delete p;//удалить первый

return beg;//вернуть начало списка

}

//если удаляется элемент из середины списка

for(int i=0;i<k-1&&p!=0;i++,p=p->next);//пройти по списку либо до элемента с предыдущим номером, либо до конца списка

if(p==0||p->next==0)return beg;//если в списке нет элемента с номером k

point*r=p->next;//встать на удаляемый элемент

p->next=r->next;//изменить ссылку

delete r;//удалить r

r=p->next;//встать на следующий

if(r!=0)r->pred=p;//если r существует, то связать элементы

return beg;//вернуть начало списка

}

point* add_point(point *beg,int k)

{

point *p;

p=new(point);//создать новый элемент и заполнить ключевое поле

cout<<"key-?";cin>>p->key;

if(k==0)//если добавляется первый элемент

{

p->next=beg;//добавить перед beg

p->pred=0;//обнулить адрес предыдущего

beg->pred=p;//связать список с добавленным элементом

beg=p;//запомнить первый элемент в beg

return beg;//вернуть начало списка

}

point*r=beg;//встать на начало списка

for(int i=0;i<k-1&&r->next!=0;i++,r=r->next);//пройти по списку либо до конца списка, либо до элемента с номером k-1

p->next=r->next;//связать р с концом списка

if(r->next!=0)r->next->pred=p;//если элемент не последний, то связать конец списка с р

p->pred=r;//связать р и r

r->next=p;

return beg;//вернуть начало списка

}

void main()

{

point*beg;

int i,k;

do

{

cout<<"1.Make list\n";

cout<<"2.Print list\n";

cout<<"3.Add point\n";

cout<<"4.Del point\n";

cout<<"5.Exit\n";

cin>>i;

switch(i)

{

case 1:

{beg=make_list();break;}

case 2:

{print_list(beg);break;}

case 3:

{

cout<<"\nk-?";cin>>k;

beg=add_point(beg,k);

break;

}

case 4:

{

cout<<"\nk-?";cin>>k;

beg=del_point(beg,k);

break;

}

}

}

while(i!=5);

}

23. Ввод-вывод в С

Файл - это именованная область внешней памяти. Файл имеет следующие характерные особенности:

1. имеет имя на диске, что дает возможность программам работать с несколькими файлами;

2. длина файла ограничивается только емкостью диска.

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

Библиотека С поддерживает три уровня ввода-вывода:

- потоковый ввод-вывод;

- ввод-вывод нижнего уровня;

- ввод-вывод для консоли портов (зависит от конкретной ОС).

Рассмотрим потоковый ввод-вывод.

23.1 Потоковый ввод-вывод

На уровне потокового ввода-вывода обмен данными производится побайтно, т. е. за одно обращение к устройству (файлу) производится считывание или запись фиксированной порции данных (512 или 1024 байта). При вводе с диска или при считывании из файла данные помещаются в буфер ОС, а затем побайтно или порциями передаются программе пользователя. При выводе в файл данные также накапливаются в буфере, а при заполнении буфера записываются в виде единого блока на диск. Буферы ОС реализуются в виде участков основной памяти. Т .о. поток - это файл вместе с предоставленными средствами буферизации. Функции библиотеки С, поддерживающие обмен данными на уровне потока позволяют обрабатывать данные различных размеров и форматов. При работе с потоком можно:

1. Открывать и закрывать потоки (при этом указатели на поток связываются с конкретными файлами);

2. Вводить и выводить строки, символы, форматированные данные, порции данных произвольной длины;

3. Управлять буферизацией потока и размером буфера;

4. Получать и устанавливать указатель текущей позиции в файле.

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

23.2 Открытие и закрытие потока

Прежде, чем начать работать с потоком, его надо инициировать, т. е. открыть. При этом поток связывается со структурой предопределенного типа FILE, определение которой находится в файле <stdio.h>. В структуре находится указатель на буфер, указатель на текущую позицию и т. п. При открытии потока возвращается указатель на поток, т. е. на объект типа FILE. Указатель на поток должен быть объявлен следующим образом:

#include <stdio.h>

. . . . . . . .

FILE*f;//указатель на поток

Указатель на поток приобретает значение в результате выполнения функции открытия потока:

FILE* fopen(const char*filename,const char*mode);

где const char*filename - строка, которая содержит имя файла, связанного с потоком,

const char*mode - строка режимов открытия файла.

Например:

f=fopen(“t.txt”,”r”);

где t.txt - имя файла, r - режим открытия файла.

Файл связанный с потоком можно открыть в одном из 6 режимов

Режим

Описание режима открытия файла

r

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

w

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

a

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

r+

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

w+

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

a+

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

Поток можно открывать в текстовом (t) или двоичном режиме(b). В текстовом режиме поток рассматривается как совокупность строк, в конце каждой строки находится управляющий символ `\n'. В двоичном режиме поток рассматривается как набор двоичной информации. Текстовый режим устанавливается по умолчанию.

В файле stdio.h определена константа EOF, которая сообщает об окончании файла (отрицательное целое число).

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

- файл, связанный с потоком не найден (при чтении из файла);

- диск заполнен (при записи);

- диск защищен от записи (при записи) и т. п.

В этих случаях указатель на поток приобретет значение NULL (0). Указатель на поток, отличный от аварийного не равен 0.

Для вывода об ошибке при открытии потока используется стандартная библиотечная функция из файла <stdio.h>

void perror (const char*s);

Эта функция выводит строку символов, не которую указывает указатель s, за этой строкой размещается двоеточие пробел и сообщение об ошибке. Текст сообщения выбирается на основании номера ошибки. Номер ошибки заносится в переменную int errno(определена в заголовочном файле errno.h).

После того как файл открыт, в него можно записывать информацию или считывать информацию, в зависимости от режима.

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

int fclose(FILE*f);

Изменить режим работы с файлом можно только после закрытия файла.

Пример:

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

void main()

{

FILE *f;

char filename[20];

cout<<”\nEnter the name of file:”; cin>>filename;

if(f=fopen(filename,”rb”)==0)//открываем для чтения в бинарном режиме и проверяем

// возникает ли ошибка при открытии файла

{

perror(strcat“error in file :”,filename);//strcat складывает две строки

exit(0);//выход из программы

}

. . . . .

fclose(f);

}

Для текстового файла:

if(f=fopen(filename,”rt”)==0)//открываем для чтения и проверяем возникает ли ошибка при //открытии файла

if(f=fopen(filename,”r”)==0)//открываем для чтения и проверяем возникает ли ошибка при //открытии файла

23.3 Стандартные файлы и функции для работы с ними

Когда программа начинает выполняться, автоматически открываются несколько потоков, из которых основными являются:

- стандартный поток ввода (stdin);

- стандартный поток вывода (stdout);

- стандартный поток вывода об ошибках (stderr).

По умолчанию stdin ставится в соответствие клавиатура, а потокам stdout и stderr - монитор. Для ввода-вывода с помощью стандартных потоков используются функции:

- getchar()/putchar() - ввод-вывод отдельного символа;

- gets()/puts() - ввод-вывод строки;

- scanf()/printf() - форматированный ввод/вывод.

Функции рассматривались, когда мы рассматривали строковые и символьные данные. Теперь мы можем связать их со стандартными потоками: ввод осуществляется из стандартного потока stdin вывод осуществляется в стандартный поток stdout. Аналогично работе со стандартными потоками выполняется ввод-вывод в потоки, связанные с файлами.

23.4 Символьный ввод-вывод

Для символьного ввода-вывода используются функции:

- int fgetc(FILE*fp), где fp - указатель на поток, из которого выполняется считывание. Функция возвращает очередной символ в форме int из потока fp. Если символ не может быть прочитан, то возвращается значение EOF.

- int fputc(int c, FILE*fp), где fp - указатель на поток, в который выполняется запись, c - переменная типа int, в которой содержится записываемый в поток символ. Функция возвращает записанный в поток fp символ в форме int . Если символ не может быть записан, то возвращается значение EOF.

Пример:

#include <iostream.h>

#include <stdio.h>

#include <stdlib.h>

void main()

{

FILE *f;

char c;

char *filename=”f.txt”;

if((f=fopen(filename,”r”)==0)

{

perror(filename);exit(0);

}

while(c=fgetc(f)!=EOF)

putchar(c);//вывод с на стандартное устройство вывода

fclose(f);

}

23.5 Строковый ввод-вывод

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

1) char* fgets(char* s,int n,FILE* f), где

char*s - адрес, по которому размещаются считанные байты,

int n - количество считанных байтов,

FILE* f - указатель на файл, из которого производится считывание.

Прием байтов заканчивается после передачи n-1 байтов или при получении управляющего символа `\n'. Управляющий символ тоже передается в принимающую строку. Строка в любом случае заканчивается `\0'. При успешном завершении считывания функция возвращает указатель на прочитанную строку, при неуспешном - 0.

2) int puts(char* s, FILE* f), где

char*s - адрес, из которого берутся записываемые в файл байты,

FILE* f - указатель на файл, в который производится запись.

Символ конца строки (`\0') в файл не записывается. Функция возвращает EOF, если при записи в файл произошла ошибка, при успешной записи возвращает неотрицательное число.

Пример:

//копирование файла in в файл out

int MAXLINE=255;//максимальная длина строки

FILE *in,//исходный файл

*out;//принимающий файл

char* buf[MAXLINE];//строка, с помощью которой выполняется копирование

in=fopen(“f1.txt”,”r”);//открыть исходный файл для чтения

out=fopen(“f2.txt”,”w”);//открыть принимающий файл для записи

while(fgets(buf,MAXLINE,in)!=0)//прочитать байты из файла in в строку buf

fputs(buf,out);//записать байты из строки buf в файл out

fclose(in);fclose(out);//закрыть оба файла

23.6 Блоковый ввод-вывод

Для блокового ввода-вывода используются функции:

1) int fread(void*ptr,int size, int n, FILE*f), где

void*ptr - указатель на область памяти, в которой размещаются считанные из файла данные,

int size - размер одного считываемого элемента,

int n - количество считываемых элементов,

FILE*f - указатель на файл, из которого производится считывание.

В случае успешного считывания функция возвращает количество считанных элементов, иначе - EOF.

2) int fwrite(void*ptr,int size, int n, FILE*f), где

void*ptr - указатель на область памяти, в которой размещаются считанные из файла данные,

int size - размер одного записываемого элемента,

int n - количество записываемых элементов,

FILE*f - указатель на файл, в который производится запись.

В случае успешной записи функция возвращает количество записанных элементов, иначе - EOF.

Пример:

struct Employee

{

char name[30];

char title[30];

float rate;

};

void main()

{

Employee e;

FILE *f;

if((f=fopen(“f.dat”,”wb”))==NULL)

{

cout<<”\nCannot open file for writing”;

exit(1);

}

int n;

//запись в файл

printf(“\nN-?”);

scanf(“%d”,&n);

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

{

//формируем структуру е

printf(“\nname:”);scanf(“%s”,&e.name);

printf(“\ntitle:”);scanf(“%s”,&e.title);

printf(“\nrate:”);scanf(“%s”,&e.rate);

//записываем е в файл

fwrite(&e,sizeof(Employee),1,f);

}

fclose(f);

//чтение из файла

if((f=fopen(“f.dat”,”rb”))==NULL)

{

cout<<”\nCannot open file for reading”;

exit(2);

}

while(fread(&e,sizeof(Employee)1,f)

{

printf(“%s % s%f”, e.name, e.title, e.rate)

}

fclose(f);

}

23.7 Форматированный ввод-вывод

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

1) int fprintf(FILE *f, const char*fmt,. . .) , где

FILE*f - указатель на файл, в который производится запись,

const char*fmt - форматная строка,

. . . - список переменных, которые записываются в файл.

Функция возвращает число записанных символов.

2) 1) int fscanf(FILE *f, const char*fmt, par1,par2, . . .) , где

FILE*f - указатель на файл, из которого производится чтение,

const char*fmt - форматная строка,

par1,par2,. . . - список переменных, в которые заносится информация из файла.

Функция возвращает число переменных, которым присвоено значение.

Пример:

void main()

{

FILE *f;

int n;

if((f=fopen(“int.dat”,”w”))==0)

{

perror(“int.dat”);

exit(0);

}

for(n=1;n<11;n++)

fprinf(f,”\n%d %d”,n,n*n);

fclose(f);

if((f=fopen(“int.dat”,”r”))==0)

{

perror(“int.dat”);

exit(1);

}

int nn;

while(fscanf(f, ”%d%d”,&n,&nn))

printf(“\n%d %d”,n,nn);

fclose(f);

}

23.8 Прямой доступ к файлам

Рассмотренные ранее средства обмена с файлами позволяют записывать и считывать данные только последовательно. Операции чтения/записи всегда производятся, начиная с текущей позиции в потоке. Начальная позиция устанавливается при открытии потока и может соответствовать начальному или конечному байту потока в зависимости от режима открытия файла. При открытии потока в режимах “r” и “w” указатель текущей позиции устанавливается на начальный байт потока, при открытии в режиме “a” - за последним байтом в конец файла. При выполнении каждой операции указатель перемещается на новую текущую позицию в соответствии с числом записанных/прочитанных байтов.

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

int fseek(FILE *f, long off, int org), где

FILE *f - - указатель на файл,

long off - позиция смещения

int org - начало отсчета.

Смещение задается выражение или переменной и может быть отрицательным, т. е. возможно перемещение как в прямом, так и в обратном направлениях. Начало отсчета задается одной из определенных в файле <stdio.h> констант:

SEEK_SET ==0 - начало файла;

SEEK_CUR==1 - текущая позиция;

SEEK_END ==2 - конец файла.

Функция возвращает 0, если перемещение в потоке выполнено успешно, иначе возвращает ненулевое значение.

Примеры:

fseek(f,0L,SEEK_SET); //перемещение к началу потока из текущей позиции

fseek(f,0L,SEEK_END); //перемещение к концу потока из текущей позиции

fseek(f,-(long)sizeof(a),SEEK_SET); //перемещение назад на длину переменной а.

Кроме этой функции, для прямого доступа к файлу используются:

long tell(FILE *f);//получает значение указателя текущей позиции в потоке;

void rewind(FILE *f);//установить значение указателя на начало потока.

23.9 Удаление и добавление элементов в файле

Пример 1:

void del(char *filename)

{

//удаление записи с номером х

FILE *f, *temp;

f=fopen(filename,”rb”);//открыть исходный файл для чтения

temp=fopen(“temp”,”wb”)//открыть вспомогательный файл для записи

student a;

for(long i=0;.fread(&a,sizeof(student),1,f);i++)

if(i!=x)

{

fwrite(&a,sizeof(student)1,temp);

}

else

{

cout<<a<<" - is deleting...";

}

fclose(f); fclose(temp);

remove(filename);

rename(“temp”, filename);

}

Пример 2:

void add(char *filename)

{

//добавление в файл

student a;

int n;

f=fopen(filename,”ab”)открыть файл для добавления

cout<<"\nHow many records would you add to file?";

cin>>n;

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

{

прочитать объект

fwrite(&a,sizeof(student),1,f);//записать в файл

}

fclose(f);//закрыть файл

}

24. Вопросы к экзамену

1. Алгоритм и его свойства. Способы записи алгоритма. Программа. Языки программирования. Примеры алгоритмов и программ.

2. Структура программы на языке С++. Примеры. Этапы создания исполняемой программы.

3. Состав языка С++. Константы и переменные С++.

4. Типы данных в С++.

5. Выражения. Знаки операций.

6. Основные операторы С++ (присваивание, составные, выбора, циклов, перехода). Синтаксис, семантика, примеры

7. Этапы решения задачи. Виды ошибок. Тестирование.

8. Массивы (определение, инициализация, способы перебора).

9. Сортировка массивов (простой обмен, простое включение, простой выбор).

10. Поиск в одномерных массивах (дихотомический и линейный).

11. Указатели. Операции с указателями. Примеры

12. Динамические переменные. Операции new и delete. Примеры.

13. Ссылки. Примеры.

14. Одномерные массивы и указатели. Примеры.

15. Многомерные массивы и указатели. Примеры.

16. Динамические массивы. Примеры.

17. Символьная информация и строки. Функции для работы со строками (библиотечный файл string.h).

18. Функции ввод-вывода (scanf(), printf(), puts(), gets(), putchar(),getchar()).

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

20. Прототип функции. Библиотечные файлы. Директива препроцессора #include.

21. Передача одномерных массивов в функции. Примеры.

22. Передача многомерных массивов в функции. Примеры.

23. Передача строк в функции. Примеры.

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

25. Подставляемые функции. Примеры.

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

27. Перегрузка функции. Шаблоны функций. Примеры.

28. Указатели на функции. Примеры.

29. Ссылки на функции. Примеры.

30. Типы данных, определяемые пользователем (переименование типов, перечисление, структуры, объединения). Примеры.

31. Структуры. Определение, инициализация, присваивание структур, доступ к элементам структур, указатели на структуры, битовые поля структур.

32. Динамические структуры данных (однонаправленные и двунаправленные списки).

33. Создание списка, печать, удаление, добавление элементов (на примере однонаправленных и двунаправленных списков).

34. Потоковый ввод-вывод в С++. Открытие и закрытие потока. Стандартные потоки ввода-вывода.

35. Символьный, строковый, блоковый и форматированный ввод-вывод.

36. Прямой доступ к файлам.

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

25. Примеры задач для подготовки к экзамену

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

2. Дана последовательность целых чисел из n элементов. Найти:

· - среднее арифметическое;

· - (максимальное значение;

· - количество отрицательных элементов;

· -номер минимального элемента;

· -количество четных чисел;

· - минимальный из четных элементов этой последовательности.

3. Дана последовательность целых чисел, за которой следует 0. Найти:

· - среднее арифметическое;

· - (максимальное значение;

· - количество отрицательных элементов;

· -номер минимального элемента;

· -количество четных чисел;

· - минимальный из четных элементов этой последовательности.

4. Найти сумму чисел Фибоначчи, меньших заданного числа Q.

5. Напечатать N простых чисел.

6. Дан массив целых чисел. Найти:

· - среднее арифметическое;

· - (максимальное значение;

· - количество отрицательных элементов;

· -номер минимального элемента;

· -количество четных чисел;

· - минимальный из четных элементов этого массива.

7. Дан массив целых чисел. Перевернуть массив.

8. Дан массив целых чисел. Поменять местами пары элементов в массиве: 1и2, 3 и 4, 5 и 6 и т. д.

9. Циклически сдвинуть массив на К элементов влево (вправо).

10. Найти первое вхождение элемента К в массив целых чисел.

11. Удалить из динамической матрицы строку с номером K.

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

13. Сформировать динамический массив строк. Удалить из него строку с заданным номером.

14. Заданы координаты сторон треугольника. Если такой треугольник существует, то найти его площадь. Решить задачу с использованием функций.

15. Дан массив int a[100]. Удалить из массива все четные элементы.

16. Дан массив int *a.Удалить из массива все элементы, совпадающие с первым элементом, используя динамическое выделение памяти.

17. Найти количество цифр в строке символов, используя функции.

18. Удалить из однонаправленного (двунаправленного) списка элемент с заданным номером (ключом).

19. Добавить в однонаправленный (двунаправленный) список элемент с заданным номером.

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

21. Добавить в бинарный файл, в который записаны элементы типа

struct Student

{char name[20];int age;};

К элементов после элемента с заданной фамилией.

22. Удалить из текстового файла все четные строки.

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

24. struct Date

{сhar Month[15];int Day;}

В файле содержатся даты типа Date. Заменить все даты, у которых поле Month равно “Май”,”Июнь” или ”Июль” на даты, у которых поле Day не меняется а поле Month меняется на “Август”.

25. В текстовом файле заменить все строки, начинающиеся с буквы `f' на строки, начинающиеся с буквы `a'.

Размещено на Allbest.ru

...

Подобные документы

  • Основные этапы создания алгоритмов, представление в виде программы. Рассмотрение методов решения задач. Метод поэтапных уточнений. Различие между численными и логическими алгоритмами. Реализация цикла со счетчиком. Процесс разработки сложного алгоритма.

    презентация [1,3 M], добавлен 22.10.2013

  • Написание транслятора посредством языка Си, обрабатывающего конструкции integer, if Le then, записи (record), а также реализующего обработку new для выделения динамической памяти: разработка алгоритма реализации задачи, представление листинга программы.

    курсовая работа [171,7 K], добавлен 03.07.2011

  • Фаза "избавления" программы от ошибок. Задача обработки ошибок в коде программы. Ошибки с невозможностью автоматического восстановления, оператор отключения. Прекращение выполнения программы. Возврат недопустимого значения. Директивы РНР контроля ошибок.

    учебное пособие [62,3 K], добавлен 27.04.2009

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

    реферат [155,9 K], добавлен 19.10.2013

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

    курсовая работа [305,9 K], добавлен 03.07.2011

  • Функция записи в сжатое представление массива. Распечатка внутреннего представления матрицы. Результат работы программы при Xm=4. Построение графика зависимости T=F(Xm) по начальному значению времени выполнения алгоритма. Запись элементов в массив.

    лабораторная работа [471,8 K], добавлен 05.12.2015

  • Характеристика программирования с точки зрения процесса создания программы. Спецификация и определение требований к программе. Основные символы, используемые для представления алгоритма в виде блок-схемы. Особенности процесса поиска и устранения ошибок.

    лекция [234,9 K], добавлен 07.04.2011

  • Основные алгоритмические структуры. Запись алгоритма в словесной форме, в виде блок-схемы. Система команд исполнителя. Язык высокого уровня. Создание программы и её отладка. Интегрированные среды разработки: Integrated Development Environment, IDE.

    лекция [61,7 K], добавлен 09.10.2013

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

    курсовая работа [495,8 K], добавлен 09.06.2013

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

    реферат [192,2 K], добавлен 10.11.2014

  • Приемы и правила объектно-ориентированного программирования с использованием языка С++. Общие принципы разработки объектно-ориентированных программ. Основные конструкции языка С++. Разработка различных программ для Windows с использованием WIN32 API.

    учебное пособие [1,6 M], добавлен 28.12.2013

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

    реферат [687,5 K], добавлен 28.10.2011

  • Изучение теоретических основ разработки программы и правил выбора языка программирования. Рассмотрение основных задач по созданию сайта автоклуба. Основы разработки базы данных, создания web-дизайна, текстового наполнения сайта и его публикации.

    курсовая работа [687,9 K], добавлен 07.04.2014

  • Проведение сравнительного анализа языков программирования. Создание алгоритма и специфика составления математической модели программы "Механические часы, показывающие текущее время" в среде Microsoft Visual Studio 2010, на базе языка программирования С++.

    курсовая работа [408,9 K], добавлен 11.03.2013

  • Методика разработки и частичная реализация транслятора для языка "С" с использованием языка "С++", производящего разбиение на минимальные неделимые конструкции языка исходной цепочки символов основываясь на лексике языка. Анализ работы программы.

    курсовая работа [841,3 K], добавлен 19.03.2012

  • Элементы и переменные, используемые для составления записи в Паскале. Основные числовые типы языка Turbo Pascal. Составление блок-схемы приложения, программирование по ней программы для вычисления функции. Последовательность выполнения алгоритма.

    лабораторная работа [256,9 K], добавлен 10.11.2015

  • Словесное описание алгоритма программы. Открытие файла процедурой Rewrite, его проверка на наличие ошибок при открытии. Особенности построения диаграммы. Листинг программы, ее тестирование и отладка. Выполнение процедуры CloseFile при закрытии файла.

    контрольная работа [17,3 K], добавлен 11.06.2010

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

    курсовая работа [1,2 M], добавлен 19.02.2015

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

    контрольная работа [329,7 K], добавлен 16.10.2013

  • Описание алгоритма решения задачи графическим способом. Вывод элементов массива. Описание блоков укрупненной схемы алгоритма на языке Pascal. Листинг программы, а также ее тестирование. Результат выполнения c помощью ввода различных входных данных.

    контрольная работа [150,4 K], добавлен 03.05.2014

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.