Сложные программы на си

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

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

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

КАК ЧИТАТЬ И ПОНИМАТЬ С/C++ КОД?

№ Задача Автор решения Статус
1 Аналоги строковых функций С++ admin
2 Выполнить сортировку массива двумя способами liliia
3 Действия над считанными из файла матрицами
4 Квадратная матрица, главная и побочные диагонали admin
5 Класс зоомагазин
6 Класс Массив, описывающий одномерный массив NaikoN
7 Класс Матрица, описывающий двумерный массив admin
8 Класс множество: трехмерный массив admin
9 Класс Призма admin
10 Класс, реализующий операции со строками liliia
11 Массив структур «Знак зодиака» admin
12 Матрица, главные диагонали admin
13 Найти простые числа, используя Решето Эратосфена admin
14 Наследование, работа супермаркета admin
15 Номер min элемента, произведение элементов массива admin
16 Переписать положительные элементы матрицы admin
17 Переставляя столбцы заданной матрицы, расположить их в соответствии с ростом характеристик admin
18 Построение графика функции
19 Самое часто встречающееся слово в тексте admin
20 Слова сформированные из первых букв слов каждой строки файла admin
21 Удалить из массива дубликаты элементов admin

Поделиться:

Создание сборщика мусора | С

Новое

  • Особенности Qt: слоты и сигналы, описание QObject и QApplication, виды окон и т.д.
  • Первая программа на Qt:
  • Введение — графическая библиотека Qt
  • Наследование классов
  • Перегрузка операторов в С++ (часть 2)

Источник: cppstudio.com

Сложные программы на си

Построение нового, собственного типа переменных.

Цель. Построить новый тип из уже имеющихся.

1 Структуры языка Си

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

1.1 Переменные

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

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

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

Так, если нужно объявить одну точку, то можно написать код и так:

1 double x,y; // о д н а т о ч к а

Но, если точек больше, например две, то придется например вводить индекс:

1 double x0,y0; // п е р в а я т о ч к а
2 double x1,y1; // в т о р а я т о ч к а

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

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

1 struct point_2d
2 <
3 double x, y; // с о с т а в н ы е ч а с т и о б ъ е к т а
4 >;

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

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

Когда нужный тип данных определен объявления переменных можно записать следующим образом:

1 struct point_2d p; // о д н а т о ч к а
2 struct point_2d p0, p1; // е щ ̈e д в е т о ч к и .

В первой строчке была объявлена переменная p, а во второй строчке объявлено сразу две переменные p0 и p1. Все перечисленные переменные (p, p0 и p1) имеют тип struct point_2d, т.е. являются структурами (составными объектами) с именем (типом) point_2d.

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

Переопределение Заметим, что при объявлении приходится всегда писать вначале термин struct до название самого нашего типа (point_2d). Этого можно избежать использую другую языковую конструкцию языка Си: typedef . Она позволяет заменять любой сложный тип на более короткое название, сокращение:

Читайте также:
Таблица программ для стиральной машины канди

1 typedef int myintarray[5];
2 // .
3 myintarray a; // У a т и п int [5].
4 a[0]=5;
5 a[4]=a[0]+1;
6 myintarray b=;

В последнем примере сложный тип (целочисленный массив из 5 элементов) заменяется на некое обозначение (myintarray). Последнее позволяет далее в программе использовать именно его, а не громоздкое первоначальное (в котором легко ошибиться и в какой-то из очередных объявлений вместо 5 написать, например, 4). В частности, когда решено изменить размер массива (придется везде поменять 5 на, например, 4). Последнее означает, что хорошей практикой проектирования программного обеспечения является использование конструкции typedef.

По аналогии с выше написанном, рассматриваемую конструкцию можно применить и к структурам:

1 typedef struct point_2d point2d;
2 // Т е п е р ь м о ж н о т а к :
3 point2d p0, p1; // б е з т е р м и н а struct .

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

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

1 struct line_2d
2 <
3 // Н а ч а л ь н а я и к о н е ч н а я т о ч к а п р я м о й ( о т р е з к а )
4 point2d p0, p1;
5 double weight;
6 >;
7 typedef struct line_2d line2d;

В данном определении структуры задается объект отрезок двумя точками. Также добавлено ещё одно поле weight с отличным от предыдущих типом double. Последним показана возможность использования в структурах полей с разным типом данных.

Этим полем можно задавать, например, вес (важность данного отрезка). Последнее важно например в задачах на графах.

1.2 Вычисления

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

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

Инициализация Переменные стандартных типов, как мы знаем, можно инициализировать в момент объявления:

1 double x0=1,y0=2; // п е р в а я т о ч к а
2 double x1=3,y1=y0+2.; // в т о р а я т о ч к а

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

1 point2d p0=, p1=;
2 line2d ll0=<, , 3.0>;
3 line2d ll1=

;

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

Простые вычисления Для начала покажем как выполнять простейшие вычисления, а именно – вычисления над полями сложных переменных. Все конечно сводится к переменной имеющий встроенный тип, например, числовой тип.

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

1 // П а р а л л е л ь н ы й с д в и г :
2 x0 += x1; // П о м н и м о т о м , ч т о о б ъ е к т с о с т о и т
3 y0 += y1; // и з б о л е е ч е м о д н о й п е р е м е н н о й .

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

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

1 // П а р а л л е л ь н ы й с д в и г :
2 p0.x += p1.x; // У к а з ы в а е т с я и м я п е р е м е н н о й , а д а л е е ч е р е з
3 p0.y += p1.y; // т о ч к у (.) и м я п о л я .

Такой подход явно более удобен.

В ещё более явно это проявляется, когда нужно копировать весь объект. Например

1 point2d left_most;
2 if ( p0.x < p1.x )
3 left_most = p0;
4 else
5 left_most = p1;

1.3 Ввод/вывод сложных типов

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

Но тем не менее покажем как это делать.

Вывод данных Стандартные функции вывода (printf и тому подобное) естественно ничего не знают о нашем типе данных. Поэтому и вывести его самостоятельно не смогут:

1 // Т а к в ы в е с т и к о м п о н е н т ы т о ч к и н е п о л у ч и т с я :
2 printf(«(%f, %f)», p0); // О ш и б к а .

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

1 // В ы в о д и м з н а ч е н и я к о м п о н е н т о в п е р в о й т о ч к и :
2 printf(«(%f, %f)», p0.x, p0.y); // П е ч а т а е м п о л я п е р в о й
3 printf(«(%f, %f)», p1.x, p1.y); // т о ч к и , а п о т о м в т о р о й .
4 // Г л а в н о е н е о ш и б и т ь с я п р и к о п и р о в а н и и с т р о ч к и :
5 printf(«(%f, %f)», p0.x, p1.y); // Н е т с о о т в е т с т в и я !

Читайте также:
Как выйти из программы одиночества

Ввод данных Продолжая предыдущее, такой же подход необходимо помнить при вводе данных:

1 // С ч и т ы в а е м к о м п о н е н т ы п е р в о й т о ч к и :
2 scanf(«%lf%lf», y0); // С о о т в е т с т в у ю щ и е п е р е м е н н ы е
3 scanf(«%lf%lf», y1); // С ч и т а л и в т о р у ю т о ч к у .
4 // Т а к о й к о м а н д о й с ч и т а е т с я с к о р е е в с е г о н е т о , ч т о х о т е л и
5 scanf(«%lf%lf», y1); // Н е т с о о т в е т с т в и я !

Упражнение. Реализовать сортировку по какому-либо из полей.

1.4 Функции

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

1 // Р а с с т о я н и е д о т о ч к и
2 double dist= sqrt( x ∗ x + y ∗ y );

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

1 // Р а с с т о я н и е д о п е р в о й т о ч к и
2 double dist = sqrt( x0 ∗ x0 + y0 ∗ y0 );
3 // Н е п р а в и л ь н ы м б у д е т н а п и с а т ь :
4 double dist2 = sqrt( x1 ∗ x1 + y0 ∗ y0 );

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

1 double length( double x, double y)
2 <
3 return sqrt( x ∗ x + y ∗ y);
4 >
5 // . и д е т н е к и й к о д
6 // п р и в ы з о в е ф у н к ц и и н у ж н о п е р е д а т ь с о о т в е т с т в у ю щ и е
7 double l = length(x0, y0); // п о л я о б ъ е к т а , к о м п о н е н т ы .

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

1 double dist( double x0, double y0, double x1, double y1)
2 <
3 double dx = x1 − x0; // р а з н и ц а м е ж д у п е р в ы х к о м п о н е н т
4 double dy = y1 − y0; // м е ж д у в т о р ы м и . А к к у р а т н о !
5 return sqrt( dx ∗ dx + dy ∗ dy);
6 // В м е с т о э т о г о м ы м о г л и б ы в ы з в а т ь :
7 // return length ( dx , dy );// Т а к п р а в и л ь н е е .
8 >
9 // . и д е т н е к и й к о д
10 // п р и в ы з о в е ф у н к ц и и н у ж н о п е р е д а т ь с о о т в е т с т в у ю щ и е
11 double d = dist(x0, y0, x1, y1); // п о л я о б ъ е к т о в , д в у х !.

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

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

1 double length2(point2d p)
2 <
3 return sqrt( p.x ∗ p.x + p.y ∗ p.y);
4 >
5 // . и д е т н е к и й к о д
6 // п р и в ы з о в е ф у н к ц и и н у ж н о п е р е д а т ь п р о с т о
7 double l = length(p0); // с о о т в е т с т в у ю щ и й о б ъ е к т а .

1 double dist2(point2d p0, point2d p1)
2 <
3 double dx = p1.x − p0.x; // р а з н и ц а м е ж д у п о л я м и
4 double dy = p1.y − p0.y; // м е ж д у в т о р ы м и . А к к у р а т н о !
5 return sqrt( dx ∗ dx + dy ∗ dy);
6 // В м е с т о э т о г о м ы м о г л и б ы в ы з в а т ь :
7 point2d dd = ;
8 return length( dd ); // Т а к п р а в и л ь н е е .
9 >
10 // . и д е т н е к и й к о д
11 // п р и в ы з о в е ф у н к ц и и н у ж н о п е р е д а т ь с о о т в е т с т в у ю щ и е
12 double d = dist( p0, p1); // д в а о б ъ е к т а .

Читайте также:
Как выключить компьютер с сохранением открытых программ

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

1 double dist2(point2d ∗ p0, point2d ∗ p1)
2 <
3 double dx = p1 − >x − p0 − >x; // р а з н и ц а м е ж д у п о л я м и
4 double dy = p1 − >y − p0 − >y; // м е ж д у в т о р ы м и . А к к у р а т н о !
5 return sqrt( dx ∗ dx + dy ∗ dy);
6 // В м е с т о э т о г о м ы м о г л и б ы в ы з в а т ь :
7 point2d dd = ;
8 return length( dd ); // Т а к п р а в и л ь н е е .
9 >
10 // . и д е т н е к и й к о д
11 // п р и в ы з о в е ф у н к ц и и н у ж н о п е р е д а т ь с о о т в е т с т в у ю щ и е
12 double d = dist( p1); // д в а о б ъ е к т а .

Вместо точки (.) используется указатель (->).

Упражнение. Реализовать функцию swap.

Источник: xn--80akaaied0aladi2a9h.xn--p1ai

Усложняя стандартный пример

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

Рассмотрим следующую задачу:

Считать из файла input.txt массив целых чисел, разделенных пробельными символами. Отсортировать их и записать в файл output.txt

Можно написать следующее решение:

#include #include #include int main() < // открываем input.txt для чтения std::ifstream fin(«input.txt»); // открываем output.txt для записи std::ofstream fout(«output.txt»); // объявление и инициализация пустого целочисленного вектора std::vectorv; // сложная магия, благодаря которой из потока чтения вставляются элементы в конец вектора std::copy(std::istream_iterator(fin), std::istream_iterator(), std::inserter(v, v.end())); // алгоритм сортировки std::sort(v.begin(), v.end()); // сложная магия, благодаря которой элементы из вектора копируются в поток записи std::copy(v.begin(), v.end(), std::ostream_iterator(fout, » «)); return 0; >

Несколько слов о «магии» в коде:

  • Одной из основ библиотеки являются итераторы, а также полуинтервалы, ими определяемые. По семантике (читай — по поведению) они совпадают с указателями. То есть, опреатор разыменования * вернет вам элемент, на который ссылается итератор, ++ переведет итератор на следующий элемент. В частности, любой контейнер представляется его концевыми итераторами [begin, end), где begin указывает на первый элемент, end — за последний;
  • Алгоритмы, работающие с контейнерами, в качестве параметров принимают начало и конец контейнера (или его части);
  • Алгоритм копирования copy просто переписывает элементы из одного полуинтервала в другой. Если в целевом контейнере не выделена память, то поведение непредсказуемо [copy];
  • Функция inserter вставляет значение в контейнер перед указанным итератором [inserter]
  • istream_iterator и ostream_iterator предоставляют доступ к потокам в стиле контейнеров [istream_iterator, ostream_iterator]

В файле input.txt хранится список, содержащий информацию о людях: фамилия, имя, возраст (каждая строка это запись, данные разделены пробелом). Считать эти данные в массив, отсортировать их по возрасту и записать в файл output.txt. Вывести на экран информацию о человеке, чей возраст более 20, но менее 25 лет.

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

    Объявить структуру данных. — можно определить что-то служное, но в конкретном случае достаточно struct:

struct man< std::string firstname, secondname; size_t age; >;
std::ostream operator >> (std::istream p)< in >> p.firstname >> p.secondname >> p.age; return in; >
bool comparator(const man p2)
struct predicate < size_t begin, end; predicate(int p1, int p2): begin(p1), end(p2) <>bool operator ()(const man return (p.age >begin) (p.age < end); >>;

int main() < std::ifstream fin(«input.txt»); std::ofstream fout(«output.txt»); std::vectorv; std::copy(std::istream_iterator(fin), std::istream_iterator(), std::inserter(v, v.end())); std::sort(v.begin(), v.end(), comparator); std::copy_if(v.begin(), v.end(), std::ostream_iterator(std::cout, «n»), predicate(20, 25)); std::copy(v.begin(), v.end(), std::ostream_iterator(fout, «n»)); return 0; >

Как можно заметить, изменения функции main минимальные, касаются только типа элементов вектора. Плюс добавлен вызов алгоритма copy_if. Этот полезный алгоритм появился со стандартом С++11, он копирует элементы из одного контейнера в дргой только те элементы, которые удовлетворяют условию.

Какие можно сделать из этого выводы?

  1. Знание и активно использование алгоритмов стандартной библиотеки существенно ускоряет разработку (точнее говоря, доводит до автоматизма).
  2. Полезным является объявление различных конструкторов и операторов копирования для структур данных. Они используются в различных ситуациях, в частности, при вставке элементов в контейнеры.
  3. Для удобства можно перегрузить операторы ввода и вывода, а также оператор сравнения и оператор упорядочения.
  4. Функторы — мощный инструмент, который позволяет реализовать функции с «памятью» или дополнительным поведением
  5. … возможно, еще каккие-либо.

Весь код программы:

an_example.cpp

#include #include #include #include #include #include struct man< std::string firstname, secondname; size_t age; >; std::ostream operator >> (std::istream p)< in >> p.firstname >> p.secondname >> p.age; return in; > bool comparator(const man p2) < return p1.age < p2.age; >struct predicate < size_t begin, end; predicate(int p1, int p2): begin(p1), end(p2) <>bool operator ()(const man return (p.age >begin) (p.age < end); >>; int main() < std::ifstream fin(«input.txt»); std::ofstream fout(«output.txt»); std::vectorv; std::vector::iterator i; std::copy(std::istream_iterator(fin), std::istream_iterator(), std::inserter(v, v.end())); std::sort(v.begin(), v.end(), comparator); std::copy_if(v.begin(), v.end(), std::ostream_iterator(std::cout, «n»), predicate(20, 25)); std::copy(v.begin(), v.end(), std::ostream_iterator(fout, «n»)); return 0; >

Библиография:

  1. Stepanov Al. Lee Meng, The Standard Template Library, 1995
  2. CPP Reference, copy
  3. CPP Reference, inserter
  4. CPP Reference, istream_iterator
  5. CPP Reference, ostream_iterator
  6. Wiki, функтор
  • STL
  • с++ программирование

Источник: habr.com

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru