Этапы разработки программного обеспечения — КиберПедия 

Индивидуальные и групповые автопоилки: для животных. Схемы и конструкции...

Общие условия выбора системы дренажа: Система дренажа выбирается в зависимости от характера защищаемого...

Этапы разработки программного обеспечения



Шафеева О. П.

 

Технологии программирования

 

 

Учебное пособие

 

 

Омск 2007

 

УДК 004.43

ББК 32.973.26-018.1

Ш 30

 

Рецензенты:

В.Т. Гиль, канд.техн.наук, доцент;

С.С. Ефимов, канд.техн.наук, доцент

Шафеева О.П.

Ш 30 Технологии программирования. С++:

Учебное пособие. Омск: Изд-во ОмГТУ, 2007. 80 с.

 

 

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

Пособие предназначено для изучения технологии объектно-ориентированного программирования в дисциплине «Технологии программирования».

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

 

 

УДК 004.43

ББК 32.973.26-018.1

© О.П. Шафеева, 2007

© Омский государственный

технический университет, 2007

ВВЕДЕНИЕ

Основыми направлениями в программировании являются

― процедурное (структурное) программирование,

― модульное,

― объектно-ориентированное (ООП).

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

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

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

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

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



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

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

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

Операции

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

Таблица 1

Приоритеты операций

Вес Знак Наименование операции Тип операции Порядок
( ) [] .   -> вызов функции выделение элемента массива выделение элемента структуры или объединения выделение элемента структуры или объединения (объединения), адресуемой (го) указателем Выражение Слева направо
! ~ - ++ -- & * (тип) sizeof логическое отрицание побитовое отрицание изменение знака (унарный минус) увеличение на единицу (инкремент) уменьшение на единицу (декремент) определение адреса обращение по адресу преобразование типа определение размера в байтах унарные Справа налево
* / % умножение деление определение остатка от деления по модулю бинарные арифмети-ческие Слева направо
+ - сложение вычитание бинарные арифмет. Слева направо
<< >> сдвиг влево сдвиг вправо сдвига Слева направо
< <= > >= меньше чем меньше или равно больше чем больше или равно отношения Слева направо
== != равно неравно отношения Слева направо
& побитовая операция "И" поразрядная Слева направо

 



Продолжение табл. 1

Вес Знак Наименование операции Тип операции Порядок
^ побитовая «исключающее "ИЛИ"» поразрядная Слева направо
| побитовая "ИЛИ" поразрядная Слева направо
&& логическая операция "И" логическая Слева направо
|| логическая операция "ИЛИ" логическая Слева направо
? : условная операция тернарная Справа налево
= += -= *= /= %=   <<=   >>=   &=   |=   ^= простое присваивание сложение с присваиванием вычитание с присваиванием умножение с присваиванием деление с присваиванием выделение остатка от деления с при- сваиванием сдвиг двоичного числа влево с присваиванием сдвиг двоичного числа вправо с присваиванием побитовая операция "И" с присваиванием поразрядная операция "ИЛИ" c присваиванием исключающее "или" с присваиванием присваи-вания   бинарные   (для двоичных операндов) Справа налево
, операция "запятая" (соединения) Бинарная Слева направо

 

 

Операторы языка Си

1. Оператор "Выражение"имеет три формы

<идентификатор>=<выражение>; //выполяет простое присваивание;

<идентификатор><знак>=<выражение>; // составное присваивание,

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

реализует многоступенчатое присваивание справа налево.

Примеры: z=x; w+=2; /* w=w+2*/ s=t=1;

 

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

if (<выражение>) <оператор1;>

else (<оператор2>);

Пример: a) if (k != 0) k = k+1; b) if (k) k++;

else k = k-1; else k--;

 

3. Оператор цикла со счетчиком:

for (<выражение1>;<условие выполнения>;<выражение2>) <оператор>;

где <выражение1> - выражение инициализации параметра цикла,

<выражение2> - изменение параметра цикла,

Пример: for (i=0; i<100; i++) sum += x[i]; // sum=sum+x[i];

 

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

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

Пример: while (i<100) p++;

 

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

do {<операторы>} while ( <условие выполнения> );

Пример: do {

n *= i;

i++;

}

while (i <= 100);

6. Оператор безусловного перехода:

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

7. Оператор возврата из функции:

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

 

8.Оператор-переключатель:

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

case <консатнта1> : <группа операторов1>;

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

...

case <консатнтаN> : <группа операторовN>;

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

};

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

Пример:

switch (operand) {

case 1: x *= y; break;

case 2: x /= y; break;

case 3: x += y; break;

case 4: x -= y; break;

case 5: x++;

case 6: x++; break;

case 7:

case 8:

case 9: printf("Not done\n"); break;

default: printf("Bug!\n");

exit(1);

}

9. Оператор break;

организует досрочный выход из операторов while, do, for или switch.

 

10. Оператор continue;

выполняет переход на седующую итерацию и выполняет первый оператор тела цикла while, do или for.

Пример:

for (i = 0; i < 20; i++) {

if (array[i] == 0)

continue;

array[i] = 1/array[i];

}

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

 

Пусть по ходу программы надо часто печатать значения типа int, double, char*. В стандартном Си потребовалось бы дать этим трем функциям различные имена, а вот в С++ можно написать «умную» функцию print, существующую как бы в трех видах.

# include <stdio.h>

Void print(int i)

{ printf("%d ", i); }

Void print(double x)

{ printf("%lf ", x); } // Можно и {printf("%f ", x); }

void print(char* s)

{ printf("%s ", s); }

Void main()

{

int j=5;

float pi=3.14159268;

double e=2.7183;

print(j);

print(pi);

print(e);

print("Hello!\n");

}

Компилятор сам выбирает, какую из трех (!) различных функций с именем print (по терминологии С++ «перегруженных» - overloaded) вызвать в каждом случае. Критерием выбора служат количество и тип аргументов, с которым функция вызывается, причем, если не удается найти точного совпадения (float), то компилятор выбирает ту функцию, при вызове которой « наиболее легко» выполнить для аргументов преобразование типа.

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

void f (int, int,);

int f(int, int); // ошибка.

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

Чтобы сообщить компилятору С++, что имена тех или иных функций не должна перегружатся, их следует объявить как extern «с»:

extern «с» int func 1(int); // отдельная функция

extern «с» // несколько функций

{

void func 2 (int);

int func3 ( )

double func4 (double);

};

Модификатор extern «с» можно использовать и при определении функций.

Функции, описанные как extern «с» не могут быть перегруженными.

 

Перегрузка операций

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

* ::(разрешение области видимости)?:(условная операция)

Тогда достаточно определить функцию с именем operator@ и требуемым числом и типами аргументов так, чтобы эта функция выполняла необходимые действия.

Пример. Переопределим («перегрузим») операцию сложения для данных типа string (строки).

# include <stdio.h>

# include <string.h>

const MAX_STR_LEN=80; // В С++ возможно определение констант

struct String // Структурный тип Строка

{

char s[MAX_STR_LEN]; // Массив символов - "Содержимое" Строки

int str_len; // Текущая длина строки

};

// Переопределим ("перегрузим") оператор сложения для данных типа String

 

String operator+(String s1, String s2)

{

String TmpStr; // для временного хранения

// Длина строки-результата сложения равна сумме длина складываемых строк.

// Позаботимся также о том, чтобы не выйти за границу отведенного массива

if ((TmpStr.str_len=s1.str_len+s2.str_len)>=MAX_STR_LEN)

{

TmpStr.s[0]='\x0';

TmpStr.str_len=0;

return TmpStr; // Вернем нулевую строку.

} // Иначе или далее сольем строки

strcpy(TmpStr.s, s1.s); //s1.s-> TmpStr.s

strcat(TmpStr.s, s2.s); // присоединим s2.s к TmpStr.s

return TmpStr; // и возвратим результат

}

Void main()

{

String str1, str2, str3;

strcpy(str1.s, "Перегрузка операторов -"); // Заполение содержимым

str1.str_len=strlen(str1.s); // Опеределение текущей длины

strcpy(str2.s, "это очень удобно!");

str2.str_len=strlen(str2.s);

printf("Первая строка: длина=%d, содержимое=%s\n",

str1.str_len, str1.s);

printf("Вторая строка: длина=%d, содержимое=%s\n",

str2.str_len, str2.s);

str3=str1+str2; // Используем перегруженный оператор!

// Компилятор, ориентируясь на типы слагаемых, генерирует код,

// эквалентный вызову str3=operator+(str1,str2);

printf("Третья строка:\n длина=%d, содержимое=%s\n",

str3.str_len, str3.s);

}

Результаты

Первая строка: длина =23, содержимое = Перегрузка операторов-

Вторая строка: длина =17, содержимое = это очень удобно!

Третья строка:длина=40, содержимое= Перегрузка операторов - это очень удобно!

Для чисел знак «+»выполняет обычное арифметическое сложение.

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

Если при написании программ на С++ часто приходиться создавать множество почти одинаковых функций для обработки данных разных типов, то используя ключевое слово template («шаблон»), можно задать компилятору образец, по которому он сам сгенерирует код, необходимый для конкретного типа. Компилятор создаст подходящую функцию, когда "узнает" какой тип аргументов класса Т подходит в конкретном случае. Т может быть и именем простого типа данных

 

Пример.

# include <stdio.h>

# include <string.h>

template <class T> void swap(T &a, T &b)

{

T c; // создать переменную для временного хранения значения

c=b; b=a; a=c; //обменять Перед обменом

} // i = 0 j = 1

void main() // x=0.000 y=1.000

{ int i = 0, j =1; // s1=first s2 = second

double x = 0.0, y = 1.0;

char *s1= "first", *s2 = "second"; // После обмена

printf("\t Перед обменом: \n " " i=%d j=%d\n " " x= %f y= %f \n "

" s1= %s s2= %s \n ", i, j, x, y, s1, s2); // i=1 j=0

swap( i, j ); // x=1.000 y=0.0000

swap( x, y ); // s1= second s2= first

swap( s1, s2 );

printf("\t После обмена: \n " " i=%d j=%d \n" "x=%f y=%f\n"

"s1=%s s2=%s\n", i, j, x, y, s1, s2);

}

Для примера «ОБМЕН» можно организовать обмен значениями через указатели:

# include <stdio.h>

void OBMEN(int *x, int*y)

{

int t;

t = *x;

*x = *y;

*y = t;

}

Void main()

{

int a = 5, b = 9;

OBMEN(&a, &b);

printf("\n a= %d b= %d", a, b);

}

Void main()

{

int a, kub, qv;

cin>>a; // ввод из потока

qv=fun(a,&kub); //исходное число, адрес ячейки, куда поместить

//второе значение

cout<<"для числа " << a<< "\t квадрат=" <<qv<<"\t куб=" <<kub;

}

int fun(int k, int* p)

{

*p=k*k*k; // значение куба помещаем по адресу *p

return k*k;

}

 

Стандартные типы даных

Объявление типа данных имеет формат вида

[<класс памяти>] <тип><идентификатор_1>

[ [ = <нач.значение1;> ] [ , <идентификатор_2>[ = нач.зн.2 ] ...];

Класс auto(автоматический) используется для описания локализованных в блоке переменных. Область действия ограничена той функцией или блоком, в котором она объявлена. Класс extern- внешний, используется для явного описания глобальных переменных или для определения ссылок на внешние переменные. Класс register - регистровый, описывает переменные, хранящиеся в сверхбыстродействующей памяти на регистрах; область действия - аналогична переменным класса auto. Переменные классаstatic (статитический) подобно автоматическим переменным локализуются в блоке или функции, где они описаны, но при выходе из блока значения сохраняют.

Примеры объявлений:

static char c, b; register int x=0;

 

Одни и те же стандартные типы могут иметь эквивалентные названия. Их классификация приведена в таблице 2.

 

Таблица 2

Типы данных

Обозначение Размер (байт) Диапазон Тип данных
char, signed char -128...127 Символьный со знаком
unsigned char 0...255 Символный без знака
short, short int, signed short , signed short int -32768... Короткое целое со знаком
unsigned short, unsigned short int 0...65535 Короткое целое без знака
int, signed, signed int 1, 2, 4 Зависит от реализации Целое
unsigned, unsigned int 1, 2, 4 Зависит от реализации Целое без знака
long, signed long, long int -2147483648… Длинное целое со знаком
unsigned long 0...4294967295 Длинное целое без знака
float -3.4e-38... 3.14e+38 Вещественное число с плавающей точкой
double -1.7е-308..1.7е308 Вещественное число удвоенной точности
long double   -3.4e-4932... 3.4e4932 Длинное вещественное число удвоенной точности

 

Структуры

 

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

Структура в языке С/С++ аналогична по смыслу записи в ТР. Вид ее описания:

struct <имя структуры>

{

тип <имя_поля1>; // Определение структуры

тип <имя_поля2>; // является оператором,

тип <имя_поляN>; // поэтому заканчивается

}; // точкой с запятой.

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

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

Пример определения структуры STUDENT (сведений о студенте)

struct STUDENT {

сhar Name[30];

char Groupe[5];

int Age;

};

Такая запись не задает никакой переменной и выделения памяти не производится. Под именем STUDENT задаетcя частный вид структуры или шаблон структуры, т.е. определен новый тип struсt STUDENT. Для объяв­ления конкретных переменных этого типа можно написать:

struct <имяструктуры> <список переменных>;

Пр. struct STUDENT stud1, stud2;

Переменные также могут задаваться одновременно с шаблоном

struct STUDENT {

сhar Name[30];

char Groupe[5];

int Age;

} stud1, stud2;

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

Внешние статические структуры можно инициировать, помещая следом за определением список начальных значений элементов:

struct stud1="Графкин Е.","В-227",18;

Доступ к элементу поля осуществляется с помощью операции "точка" или "выделения элемента": <имя_переменной_стр>.<имя_поля>

Пр. strcpy(stud2.name, "Петров К.");

Для печати содержимого поля группы элемента структуры запись бу­дет иметь вид: printf(" %c", stud1.Grupe);

Для пятого го элемента этого поля - printf("%c",stud1.Grupe[4]);

На практике структурные переменные объединяются в массивы струк­тур. Для объявления массива структур сначала задается шаблон структу­ры, далее объявляется массив:

struct stud1 sudents[40]; // в памяти создается 30 переменных [0..29].

 

Если объявлены две переменные типа структура с одним шаблоном, то их можно присваивать друг другу: stud2=stud1;

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

 

Пример: void main() struct St1 m,k;

{struct St1 {int a; struct St2 l;

char b; k . a=1;

}; k . b=’f’;

struct St2 {int a; m=k; // верно

char b; l=k; // неверно

}; l . a=k . a; l . b=k . b; // верно

 

Переменная типа структура может быть глобальной, локальной или фор-мальным параметром. Любое поле структуры может быть пара­метром функции func1(int k.a); параметром может являтся и адрес поля func2(int &k.a).

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

struct <имя структуры> * <имя указателя>;

Например, struct St*uk; // uk – переменная типа указатель на структуру St.

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

Указателю можно присвоить адрес переменной uk=&m.

Для получения значения поля а переменной m используется операция доступа к полю:

(*uk).a или uk->a;

Структура операции доступа к полю по указателю

переменная_указатель -> имя_поля; ( перем_указ -> элемент_структуры;)

 

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

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

struct Adr сhar

{ city[30];

int ind;

char adres[40];

};

struct STUDadr сhar Name[30];

{ struct Adr addr;

char groupe[6]; } st1, st2;

Adr-шаблон структуры, определеный перед объявлением структуры STUDadr.

Для присвоения значения элементу ind структуры STUDadr значения надо записать: st1.addr.ind=50;

 

 

Поля битов

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

Доступ к биту обеспечивают поля битов (bit fields) – это специальный тип членов структуры, в котором определено, из скольких битов состоит каждый элемент. Полем считается последовательность соседних двоичных разрядов в числе типа int или unsigned (signed). Оно объявляется как элемент структуры.

Основная форма объявления структуры битовых полей

struct < имя структуры >

{ <тип имя 1>: <ширина>;

¼

<тип имя N>: <ширина>;

};

где <ширина> – целое число от одного до 16; <тип> – int или unsigned. <Имя> может отсутствовать, тогда отведенные биты не используются (пропускаются). Длина структуры всегда кратна восьми.

Пример 1. Для переменной obj будет выделено восемь бит, но используется только первый.

Struct onebit

{

unsigned b: 1;

}obj;

Пример 2.

struct М{ // значения диапазонов полей

int а:4; // а[-8, 7]

int b:1; // b[- 1, 0]

unsigned с:5; // с[0,31]

int :2; // пусто

int d :2; // d[-2,1]

unsigned е :2 } // е[0,3]

Расположение полей в структуре М из 16 бит получится следующее

 

А b с не спользуется d е
0 … 3 5 … 9 10 11 12 13 14 15

 

Операции для полей битов такие же, как и для структур:

Присваивание. Пусть struct М byte; то byte.а=3;

byte.b=0;

Доступ к полю(точка) byte.с=30;

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

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

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

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

Struct { int f1:1

int : 2;

int f2:1;

:Æ; stb.f3 заполняется в следующем int.

int f3:1 ;

} stb;

 

Объединения

 

Struct byte

{

unsigned b1:1;

unsigned b2:1; // целое без знака

unsigned b3:1;

unsigned b4:1;

unsigned b5:1;

unsigned b6:1;

unsigned b7:1;

unsigned b8:1;

}; //Определена структура битовое поле

union bits {

char ch;

struct byte b;

} u;

void decode(union bits b); // Прототип функции

void main()

{

do { u.ch=getche();

printf(": ");

decode(u);

} while (u.ch!='q'); // цикл повторять, пока не будет

} // введен символ "q"

void decode(union bits p) // функция

{

printf("%d%d%d%d%d%d%d%d",p.b.b8,p.b.b7,p.b.b6,

p.b.b5, p.b.b4,p.b.b3,p.b.b2,p.b.b1);

printf("\n");

}

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

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

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

Список переменных может быть пустым.

Пример объявления. enum test{test1, test2, test3,test4};

enum test t;

Каждое из имен test1… test4 представляет собой целую величину (если они не определены по-другому). По умолчанию они соответствуют 0,1,2,3.

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

Пример. еnum Ages {stas=18, ira, alex=19,Nina= alex-1};

Когда нет явного инициализатора, то применяется правило по умалчиванию:

каждая следующая перечислимая константа увеличивается на единицу (+1) по сравнению с предшествующей. В примере вычислится ira=19; Nina=18;

С переменными перечислимого типа можно проводить следующие операции:

– присвоить переменную типа enum другой переменной того же типа;

провести сравнение с целью выяснения равенства или неравенства;

некоторые арифметические операции с константами типа enum.

Пример. i= test4-test2;

Оператор print f(¢¢%d %d”, test1, test4); выдаст на экран числа 0 и 3.

t= test3;

t++ ; // теперь t= test4;

t -=2 ; // t = test2;

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

#include <stdio.h> //Пример использования перечислений.

void main()

{

enum test {t1,t2,t3,t4,t5} t;

printf("t1=%d t4=%d\n", t1,t4); // 0 3

t=t1; printf("t=t1 = %d\n ", t); // 0

t++; printf("t++ = %d\n ", t); // 1

t=t4; t-=2;

printf("t4=%d t-=2 = %d\n ", t4, t); // 3 1

t--; printf("t-- = %d\n ", t); // 0

t=t4; t=t-t2; printf("t= = %d\n ", t); // 2

}

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

Пример.enum{ false, true} boolean; объявляет переменную boolean с допустимыми значениями false, true.

Основное назначение перечислений улучшить читаемость программы.

 

Typedef struct st

{

char name [30];

char group [4];

int god;

} STUDENT;

Теперь для определения переменной можно записать st AN;

или STUDENT AN;

Область действия зависит от расположения оператора typedef. Если определение находится внутри функции, то область действия локальна и ограничена этой функцией. Если вне, то – глобальна.

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

Пример1.

typedef char arr [40]; // FIO- массив символов

arr FIO, *adres; // adres- указатель на массив символов

Это эквивалентно char FIO[40], *adres;

Пример2.

typedef int* Pi; //объявлен новый тип Pi-указатель на целое.

typedef void (*pfn) ( ); //объявлен новый тип pfn-указатель на функцию, не-

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

typedef void (*pfnI) (int); // объявление типа pfnI-указатель на функцию с

одним аргументом типа int, невозвращающую значения

typedef void (*pptn[10]) (); //объявление типа pptn- массив из 10 указателей на

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

Файлы

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

FILE *fopen(char *fname, char *mode);

где *fname– имя файла, *mode– режим (табл.3).Функция возвращает указа-тель (ссылку) на файл, который должен быть предварительно описан.

Например, FILE *fu; *fuуказатель на файловый тип

Пример. Объявим указатели на переменные файлового типа

FILE *uin, *uout; // (указатели на переменные файлового типа)

uin = fopen(“name1”, “r”); //открыть файл “name1” для чтения и далее

идентифицировать как uin

uout = fopen(”name2”, “w”); //открывается для записи и связывается с

идентификатором uout.

Если производиться открытие несуществующего файла, то он создается.

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

# include <stdio.h>// работа с файлами и константа NULL (FILEOPEN.C)

# include <stdlib.h>//для ф-ии exit()

Void main()

{

FILE *fp;

if ((fp=fopen("test","w"))==NULL)

{

puts("Не могу открыть файл\n"); // печать строки

exit(1);

}

puts("Файл открыт\n");

}

Таблица 3

Таблица режимов для открываемого файла

Режим Действие
“r” Открыть для чтения
“w” Создать для записи
“a” Открыть для добавленияв существующий файл
“rb” Открыть двоичный файл для чтения
“wb” Создать двоичный файл для записи
“ab” Открыть двоичный файл для добавления
“r+” Открыть для чтения и записи
“w+” Создатьдля чтения и записи
“a+” Открыть для добавления или создать для чтения и записи
“r+b” Открыть двоичный файл для чтения и записи
“w+b” Создать двоичный файл для чтения и записи
“a+b” Открыть двоичный файл для добавления или создать для чтения и записи
“rt” Открыть текстовыйфайл для чтения
“wt” Создать текстовый файл для записи
“at” Открыть текстовый файл для добавления
“r+t” Открыть открыть текстовый файл для чтения и записи
“w+t” Создать текстовый файл для чтения и записи
“a+t” Открыть текстовый файл для добавления или создать для чтения и записи

 

В библиотеке <stdio.h> определены также следующие функции работы с файлами ( fp- указатель на файл, возвращаемый функцией):

fopen( ) - открытие файла;

int fclose(FILE *fp) - закрытиефайла. Возвращает нуль, если операция выполнена успешно и иное значение в противном случае. Функция является рекомендуемой, поскольку файлы при нормальном завершении закрываются автоматически;

int puts(int ch, FILE *fp) -записать символа типа intв поток. Возвращается записанный символ, если операция была успешной. Если произошла ошибка возвращается EOF;

int gets(FILE *fp) - прочесть символ типа intиз потока. Возвращает EOF, если достигнут конец файла, или произошла ошибка при чтении файла;

int feof(FILE *fp)- возвращает значение «истинно», если достигнут конец файла и нуль в противном случае;

Пример. while (!feof(fp)) {ch=getc(fp);}

Выполняется ввод символа, пока не достигнут конец файла;

int ferror(FILE *fp) - возвращает значение нуль, если обнаружена ошибка. Рекомендуется для обнаружения ошибок чтения и записи после каждой операции с файлами;

int fprintf(FILE *fp, const, char *string, …)- форматированная запись в файл. Содержит указатель на файл и управляющую строку со списком;

int fscanf(ILE *fp, const,char *string) –форматированное чтение из файла;

unsign fread(void *buf, int bytes, int c, FILE *fp) - читает блок данных из потока (буферный обмен); buf - указатель на область памяти, откуда проис-ходит обмен информации. с- количество единиц записи длиной bytes, которая будит считываться.

unsign fwrite(void *buf, int bytes, int c, FILE *fp) - пишется блок данных в поток;

int remove(char *filename)- уничтожается файл, возвращается нуль при успешной операции;

unsign rewind( FILE *fp)- устанавливается указатель на начало файла;

int fseck( FILE *fp, long numbyte, int orig) - установить указатель позиции файла в заданное место, numbyte - количество байт от точки отсчета (0,1,2), orig – макрос 0 - начало, 1- текущая позоция, 2 - конец;

void abort() - <stdlib.h> - немедленное прекращение программы без закрытия файлов и без освобождения буферов.

 

2.10 Операторы динамического распределения памяти в С++

 

В С++ введены 2 «интеллектуальных» оператора new и delete, освобождающие про-граммиста от необходимости явно использовать библиотечные функции malloс, calloс и free.

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

New type_name [(инициатор)]; илиNew (type_name [(инициатор)]);

Возвращает указатель на объект а= new int[n] для неизвестного числа элементов. Оператор delete освобождает ранее выделенную память. Размер занятого блока для правильной работы delete, записывается в его начало и обычно занимает дополнительно 4 байта.

Примечание: Следует помнить, что реальный размер занятого блока не произволен, а кратен определенному числу байт (в С++3.0-16), поэтому, с точки зрения расхода памяти невыгодно резервировать много блоков под небольшие объекты.

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

# include <stdio.h> //

# include <iostream.h> // для в\в в С++

intmain()

{

int *u_i;

double *u_d;

char *string;

int str_len=80;

u_i=new int; //Зарезервировать место под переменную типа int и

//присвоить u_i ее адрес (Значение *u_i не определено)

u_d=new double(3






Организация стока поверхностных вод: Наибольшее количество влаги на земном шаре испаряется с поверхности морей и океанов (88‰)...

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

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

Общие условия выбора системы дренажа: Система дренажа выбирается в зависимости от характера защищаемого...





© cyberpedia.su 2017-2020 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав

0.079 с.