l ----------------------------------------------------------- Лабораторная работа 4. ПОДГОТОВКА И ВЫПОЛНЕНИЕ ПРОГРАММ В ОС UNIX.

advertisement
l
----------------------------------------------------------Лабораторная работа 4.
ПОДГОТОВКА И ВЫПОЛНЕНИЕ ПРОГРАММ В ОС UNIX.
Разработка и подготовка программ в ОС UNIX производится традиционным
способом и характеризуется следующими этапами:
1. Подготовка текста программы одним из имеющихся в наличии редакторов текста.
Мы рассмотрим возможности визуального редактора VI, традиционно поставляемого со
всеми версиями ОС UNIX. Однако вы можете использовать и более современный
оконный редактор EMACS или его версию XEMACS, работающую в XWINDOWS и близкую по
своим возможностям редакторам WORD. С возможностями таких редакторов вы можете
ознакомиться по справочной системе, которая имеется в машине.
2. Компиляция текста с выявлением синтаксических и семантических ошибок. Надо
отметить, что хотя разработчики ОС UNIX указывают возможность использования всех
известных языков программирования, реально поставляется лишь язык С, с
возможностями которого мы и познакомимся в данной лабораторной работе. С
возможностями этого языка, кроме данного описания вы можете познакомиться по
имеющейся в компьютере справочной системе.
3. Запуск и отладка программы с использованием символьного отладчика GDB,
работающего практически во всех версиях ОС UNIX как с командной строки, так и с
XWINDOWS.
Экранный визуальный редактор VI.
Целью данной работы является ознакомление с основными возможностями
стандартизованной расширенной версии базового редактора текстовых файлов ОС
UNIX. Основная практическая цель - выполнение индивидуальных заданий в рамках
сеанса работы в среде vi, суть которых состоит в выполнении ввода текста,
выполнении изменений введенного текста в файле, удаление фрагментов текста в
файле, контекстного поиска/замены в текстовом файле, выполнении сервисных
процедур настройки среды редактора vi.
Визуальный редактор vi является идеологическим потомком стандартного для
системы UNIX строчного редактора ed. Для работы в редакторах такого типа
необходимо знать, в каком из режимов - командном или текстовом - находится
пользователь, запросивший ресурсы редактора. В командном режиме объектом работы
редактора является файл, в текстовом - строка (строки) файла. Собственно работа
с редактором vi начинается в вызова
$ vi [<имя_редактируемого_файла>],
где <имя редактируемого файла> - имя текстового файла, по отношению
к которому выполняются модифицирующие этот текст процедуры редактора vi; при
отсутствии в системе файла с таким именем он будет создан для начала на время
сеанса с vi, при наличии - vi отобразит первые строки этого файла на экране
видеотерминала; в любом случае после вызова редактора он переходит в командный
режим.
Для пользователя, находящегося в командном режиме визуального редактора vi,
возможно выполнение следующих, необходимых для редактирования, действий:
- в редактируемом файле (перемещение курсор);
- контекстный поиск и замена в файле;
- выход из редактора с сохранением или без сохранения изменений в файле;
- ввод команд редактирования в позиционированной области файла.
Для возврата из текстового режима в командный пользователю, находящемуся в
текстовом режиме, достаточно хотя бы один раз нажать клавишу <ESC>. Для
пользователя, находящегося в текстовом режиме визуального редактора vi,
доступны следующие, необходимые для редактирования, действия:
- ввод (вставка) текста с позиции/после позиции, отмеченной
маркером;
- ввод (вставка) текста во вновь образованную пустую строку над
строкой файла, отмеченной маркером.
После перехода в командный режим позиционирование маркера производится
путем нажатия клавиш <h>(влево), <j>(вниз), <k> (вверх), <l>
(вправо), расположенных на стандартной QWERT - клавиатуре подряд. Возможно
нажатие комбинации nnh, nnj, nnk, nnl, вызывающие сдвиг маркера
в выбранном направлении сразу на nn позиций. Если заранее известен номер в
позиционируемой строки файла, то для перемещения в эту строку можно
воспользоваться комбинацией нажимаемых клавиш xxG, где xx - номер строки.
Простое нажатие клавиши G перемещает указатель на самую последнюю строку файла,
а нажатие <CTRL>G выводит на экран номер текущей строки (той, где позиционирован
маркер).
Контекстный поиск, т.е. поиск некоторой последовательности символов,
например abcd, производится путем набора символа slash ("/"),
сразу за которым набирается последовательность контекста (abcd), после
чего нажимается клавиша <ВВ>. В результате курсор передвинется вперед
по файлу к ближайшей последовательности abcd, если такая существует.
Переход к следующей последовательности контекста - по нажатию клавиши
N - вперед по файлу. Возможен вариант этой команды с поиском
назад по файлу с заменой символа slash на ?, тогда нажатие клавиши n
вызовет переход к следующему контексту назад по файлу и клавиши N вперед по файлу. Для замены слова, в котором позиционирован курсор, на
любое другое необходимо ввести команду, задаваемую комбинацией клавиш
CW, после чего, до нажатия клавиши <ESC>, редактор переходит в текстовой режим
ввода заменяющей последовательности.
Для перевода редактора vi в режим ввода/вставки текста сразу за
позицией курсора в редактируемом файле служит команда а, а для выполнения
ввода/вставки текста в позиции курсора служит команда i. После
ввода любой из этих команд редактор переходит в текстовой режим до нажатия
клавиши <ESC>, возвращающей его снова в командный режим. В текстовом режиме
пользователь имеет возможность непосредственного ввода с клавиатуры символов
текста, пользоваться клавишей "Забой" (<Backspace> или <<-> в зависимости от
типа клавиатуры) и клавишами табуляции. Для перевода редактора vi из командного
режима в режим ввода текста во вновь образуемую пустую строку над
позиционированной служит команда О, а для тех же действий над позиционированной
строкой - команда о. В обоих случаях будет вначале образована пустая строка,
курсор будет перемещен в первую позицию этой новой строки и vi переключится в
текстовый режим до нажатия <ESC>.
Для одновременного выполнения контекстного поиска и замены служат
более сложные командные последовательности. Эти командные последовательности вводятся в зоне команд редактора vi. В этой же зоне вводятся
команды выхода из редактора. Для помещения курсора в зону команд
(обычно это самая нижняя из видимых строк видеотерминала, она не
является частью редактируемого файла) необходимо в командном режиме набрать символ (нажать клавишу) двоеточие (":"). Так, для замены <cтарой_последовательности_символов> вперед по файлу и замены только этой
первой найденной последовательности служит последовательность
:n, Ns/<старая_последовательность_символов>/<новая_последовательность_символов>/S <ВВ>
то же, но с изменением направления просмотра - назад по файлу
:n,
Ns?<старая_последовательность_символов>?<новая_последовательность_символов>?S
<ВВ>
Выполнение в зоне команд действий по записи изменений и выхода из
редактора кодируется последовательностью:
: wq
а для отказа от записи изменений, сделанных во время редактирования,
используется последовательность:
:q!
В режиме использования зоны команд возможно обращение непосредственно из визуального редактора vi к некоторым командам OC UNIX с
возвратом в командный режим vi путем нажатия либо <ВВ> (нормальное завершение вызова команды ОС UNIX), либо <CTRL>D (анормальное завершение
и возврат в vi при любых условиях). Так, для получения списка работающих в системе можно не выходя из vi выполнить следующее:
- перейти в командный режим;
- набрать последовательность
:! who <ВВ>
В общем случае формат вызова следующий
:!<команда> <ВВ>
Командный интерфейс визуального редактора vi гораздо богаче рассмотренных здесь случаев, однако уже этих примеров достаточно, чтобы
осознать гибкость этого программного средства.
Программирования на C в UNIX.
Поскольку UNIX развивался, C развивался также, и много лет, C был
единственным языком для развития программного обеспечения в системах UNIX.
Деннис Ритчи AT&T Лаборатории Бэлла создавал язык программирования C в
1970-ых, чтобы совместить выразительные возможности высокого языка с
эффективностью программирования ассемблера. В то время, языки типа PL/1 IBM,
которые включили возможности делать почти все, были в моде. В AT&T понимали,
что языки должны быть просты, изящны, и эффективны, принимая во внимание, что
расширения должны быть ограничены библиотечными программами. Эта та же самая
философия мотивировала оригинальный проект UNIX той же самой группой. C был
достаточно эффективен для использоваться на месте языка ассемблера для
развития UNIX, ведя к мобильности UNIX и его широкому успеху.
Начиная с его развития, C стал выдающимся языком программирования для
большинства задач по нескольким причинам. ANSI, Американский Институт
Национальных эталонов, стандартизировал C так, чтобы программа C,
которая выполняется на одной машине, вероятно будет работать на другой машине
без модификации. Далее, потому что C - маленький язык, трансляторы C были
написаны в каждой популярной архитектуре (а также и на не популярных). Много
операционных систем прибывают с транслятором C и никаких других языков
программирования не содержат. C - гибок, что делает его удобным для
программирования.
Программы C могут вызывать библиотеки, написанные на других языках, типа
Паскаля и ФОРТРАН, что позволяет программистам использовать обширный массив
предварительно написанного библиотечного программного обеспечения. C также
популярен среди программистов, потому что он мощен, и все же краток.
Многое из проекта было предназначено, чтобы делать программирование быстрее и, в
результате, C очень снисходителен в разрешении некоторых синтаксических
структур. Эта мягкость является, и притягивающей и опасной. Мир полон
программистами C, кто потеряли много часов бездействия, потому что они записали:
if ( 1= 1) {
вместо
if (1 == 1) {
Последний оператор проверяет, если переменная 1 равна одному, затем это
значение сравнивается с 1 и устанавливает что они равны). Таким образом, Вы
имеете, и назначение и проверку вместе, что не являлось намерением
программистов. Однако, основное преимущество, которое C имеет по другим языкам его скорость. Принимая во внимание, что в PL/1 отношение кода / ассемблер равно
25 (т.е. одна строка PL/1 производит 25 строк ассемблера), это же соотношение в
С меньше почти вдвое! Более того, программы C имеют тенденцию производить
короткий код ассемблера. Конечно, эти числа изменяются для различного архитектур
(например, механизм RISC мог бы иметь 1:4 отношение (коэффициент)). Его
простота разрешила изготовителям транслятора сосредотачиваться на оптимизации
C, так что сгенерированный код, имеет тенденцию быть высоко оптимизированным.
Наконец, потому что C - относительно простой язык, процесс компилирования очень
быстр.
Здесь мы будем использовать ANSI C. Это имеет некоторые старые конструкции
(структурные компоненты), которые мы останемся свободными, но время от времени я
буду указывать их в случае, если Вы должны столкнуться с ними.
Первая программа C
Начнем с первой программы, которую большинство программистов C записывает:
#include <stdio.h>
main () {; /* это 1ый комментарий */
Printf ("Hello Wor1d! \n "); /* печатают содержательное сообщение */
}
Эта программа, когда она откомпилирована и выполнена, произведет выход:
Привет Мир!
Мы теперь обсуждаем, как заставить это случиться. Под UNIX (и в многих других
операционных системах), транслятор C называется cc. Перед компилированием,
сохраните программу в файле, чей название заканчивается через .C , типа hello.
Тогда, компилируйте программу, печатая при подсказке:
% сс hello.c
Если Вы не сталкиваетесь с какими либо ошибками, программа откомпилирована,
создавая исполняемый файл, называемый A.OUT. Вы будете только видеть другую
подсказку, и Вы могли делать, чтобы видеть, работало ли это. По умолчанию,
все трансляторы C создают a.out. Вы можете тогда выполнять программу, печатая:
%A.out Привет Мир! %
Конечно, если Вы хотели назвать ваш исполняемый файл кое-чем другим чем
a.out, Вы могли переименовывать файл в этом пункте, но Вы можете также заставить
транслятор C делать это для Вас:
% cc -o hello hello.c
-o опция сообщает транслятору C, что нужно создавать исполняемый файл,
называемый hello вместо a.out.
Теперь, давайте посмотрим на программу непосредственно. Мы сначала обращаем
внимание, что комментарии начинаются с /* и кончаются с */. Это - всегда истина,
и комментарии могут простираться вне одиночной линии. Что-нибудь внутри
комментариев игнорируется, независимо от содержания. Программы C обычно
распространяются на несколько строк для удобства чтения. Таким образом, это - та
же самая программа:
#include <stdio.h>
main () {printf (" Hello World! \n ");} /* та же самая программа */
Фактически, большинство переходов на другую строку не нужны, но наш код был
бы при этом не читабелен.
В начале программы - строка #include <stdio.h>, которая сообщает C о
необходимости включать стандартные заголовки библиотеки ввода и вывода. Хотя это
не строго требуется для этой программы, это - всегда хорошая идея включить stdio
(явный стандарт I-0) всякий раз, когда любой вход или выход необходимы.
Реальная программа начинается со слова main (), который говорит, что это основная часть программы. ХОРОШО, так как это - единственная часть программы! Мы
размещаем тело основной программы во внутренние фигурные скобки {и},
которые являются разделителями блока в C. Всякий раз, когда новый блок кода
начинается, это начинается с {, и } заканчивает блок. Это - тот же самое, что
begin и end в Паскале. В этом случае, только одна инструкция - внутри блока, и
это - printf инструкция, которая используется для вывода в C. Этот специфический
выход - строка Hello Word!, за которой следует RETURN (это - то, чем \n
является строки). Без return, программа работала бы подобно этому:
% A.out Привет Мир
И ваша следующая подсказка была бы на той же самой строке что и выход
программы. Каждая инструкция в C заканчивается с точкой с запятой.
Начало с переменными в C имеет несколько различных стандартных моделей для
переменных. Все переменные должны быть объявлены прежде, чем они используются,
как в большинстве языков программирования. Имеется пример некоторых
переменных и их объявлений:
Main () {
int myScore; /* myScore - переменная, содержащаяся в отдельном целом числе */
int yourScore; /* так же- yourScore */
char mylnitiat; /* mylnitial -отдельный символ */ /*, Вы можете объявлять, что
большое количество переменных того же
самого типа сразу */ char your initial, another Character Variable;
YourScore = 6; /* устанавливает your Score */
MyScore = yourScore * 2; /* я имею дважды(вдвое), что Вы имеете */
myinitial = 'J';
Yourlnitial = 'М.';
/* Печатать значение */ printf (" конечное значение равно:\n ");
Pr1ntf (" %c: %d. %c: %d\n ", my_initial. MyScore. Yourlni'tial,
YourScore);
if (myScore > yourScore) {
Printf ("я побеждаю! \n);
}}
Если бы выполнено, эта программа печатала бы:
Конечное значение:
J: 12, M: 6 я побеждаю!
Эта программа иллюстрирует несколько новых концепций. Имена переменной
ограниченной длины (обычно 16 или 32 символа), и они должны начинаться с символа
и содержать только символы, цифры, и символы подчеркивания. Эта программа
содержит две целых переменной (myScore и yourScore) и три переменной, которые
могут содержать единственный символ каждый. Программа назначает значение (6) на
yourScore и затем вычисляет myScore от этого значения. Тогда устанавливается
myScore к этому значению и yourinitial к J и М, соответственно. В
C отдельные символы разграничиваются одинарными кавычками, а строчки
разграничиваются двойными кавычками. Программа тогда печатает конечную строку,
используя версию printf инструкции, которая описана позже. Тогда, условный блок
кода выполняется, если состояние выполняется. В этом случае, условие
(состояние), что myScore больший чем ваш Score, держится, и блок с printfC'I
w1n! \n ") также выполняется.
C имеет несколько других общих типов данных кроме int и char, включая чисела
с плавающей запятой float и double для чисел с плавающей запятой с двойной
точностью. В зависимости от вашей машины, они используют больше или меньшее
количество битов для представления целых чисел и поэтому имеют больший диапазон
или требуют меньшего количества пространства.
Избегайте long и short, если Вы не нуждаетесь в них. Они делают ваш код
менее переносимым.
Если мобильность не беспокоит, то обычно более легким для использования
является double для всех переменных с плавающей запятой.
Составные типы: массивы и структуры.
Один из отличительных признаков C - то, что массивы завершаются нулем. Это
означает, что первый элемент массива - номер 0, и энный элемент массива
пронумерован n-1:
Main() {
int grades [3]; /* массив трех элементов, пронумерованных 0, 1, 2 */
char initials[2];
Grades[0] = 10; /*законный */
grades [-1] = 9; /* незаконный - негативный индекс */
grades[3] = 12; /* незаконный - массив идет от 0 до 2 */
/* Две инструкции относительно одной строке - не очень читаемые */
intials [0] = 'J'; imtials [l] = 'A';
Printf (" Студент #0 Имеет номер %d\n ", grades[O]);
}
Элементы массива - точно так же как индивидуальные переменные, как показано в
примере. Везде Вы могли бы использовать целое число, Вы можете теперь
использовать одноэлементный массив. Группировка нескольких переменных вместе
под одним названием часто удобна. Например, предположите, что
студент имеет фамилию, имя, временный ранг, и десять степеней лаборатории. Мы
можем определять новую структуру, которая связывает все эти части, называемые
полями:
Struct student {
char firstinitial, Lastinitial;
int midterm, iabs [10]; *}; /* Вы должны поставить заключительную точку с
запятой */
Это определяет новый тип с четырьмя полями, одно из которых - массив. Такие
объявления новых типов должны идти между # <stdio.h> и началом основной
программы. Чтобы объявлять и использовать переменную этого типа, мы записываем:
main () {
Struct student goodOne; /* объявляет variabt'e "goodOne" типа " struct студент "
*/
goodOne.firstinitial='T'; /* устанавливает поле */
goodOne.iabs[5]=-33; /* устанавливает другое поле (шестой лаборатории) */
/* можно взять середину строки*/
printf ("Your sixth tab score is: %d\n", GoodOne.iabs[5]);
Структуры позволяют единственной переменной иметь несколько полей, что удобно.
Присвоение и переприсвоение
Одно из мощнейших средств C - то, что он позволяет переменным одного типа быть
назначенным на другой непосредственно. Например:
Int 1; /* целое число */
double f; /* число с плавающей запятой с двойной точностью */
i = 3;
f = i;/* f = 3.0 */
f = f + 1.7; /* f = 4.7 */
i= f; /* от 1 до 4 (остаточный член понижен) */
Иногда Вы должны преобразовать явно один тип в другой. Вы можете достигать
этого через CAST-операцию
f = 3/4; /*это целочисленное деление и f = 0, из-за округления до целого
числа*/
f = 3.0/4.0; /* это - деление с плавающей точкой, f = .75 */
f = 1/4; /* целочисленное деление снова */
f=((float))1)/4.0; /*"cast"1 конвертирует 1 к числу с плавающей точкой и
выполняет деление с плавающей точкой */
Если у вас сомнение, то лучше явно использовать CAST операцию. Не
рассчитайте, что C выполнит это за вас.
Одно из наиболее обычных действий в программировании должно увеличивать или
уменьшить целую переменную
1. C имеет четыре специальных действия для этого:
1++; /* увеличивают на 1 */
++ 1; /* также увеличивают на 1 */
1--; /* уменьшает на 1 */
--1; /* также уменьшают на 1 */
Разница между двумя версиями каждой операции - то, что первая делает
приращение после возвращения переменной, в то время как вторая делает это перед
возвращением переменной. Если выполняется эта единственная операция, как
показано здесь, то нет никакой разницы. Рассмотрите это:
i = 1; j = i ++; /* после этого. i= 2, j = 1 */
i= 1; j = ++ i; /* после этого. i = 2, j = 2 */
Операции управления.
C использует структурные компоненты управления, характерные для блочноструктурированных языков. Т.е. for loop, while loop, case statement и условные
операторы. Условный оператор обеспечивает условное выполнение:
if (x > 10) {
/* Этот код выполняется, если x больше чем 10 */}
else {
/* Этот код выполняется, если x меньше или равно 10*/}
ELSE часть условного оператора необязательная. Если только единственная
инструкция находится в тогдашнем блоке или в ELSE-блоке, Вы можете опустить
фигурные скобки:
if (x > 10) printf ("x больший чем 10\n ");
Размещение фигурных скобок вокруг отдельной инструкции лучше для совместимости,
если Вы решили прибавлять вторую инструкцию в этом блоке, Вы не будете забывать
прибавлять фигурные скобки. Обычно, слово THEN не появляется в любых условных
инструкциях в C. Однако если Вы используете THEN для расширения языка, Вы можете
включать директиву препроцессора, чтобы вынудить транслятор игнорировать каждое
вхождение THEN. Строка
#define THEN
решит проблему.
Вы должны быть внимательным, тем не менее, чтобы ни одна из ваших переменных
не включла THEN как подстроку. В WHILE условное выражение должно всегда быть в
круглых скобках. Это может быть составное вовлечение выражения "
логическое и && логическое или II, и логическое отрицания (!). Предположим, что
мы имеем три булево выражения, называемые A, B, и C. Каждое из этих выражений
является или истиной или ложью. Рассмотрите составное выражение:
if (A&& B || C)
В C, предшествование оператора дает " логический или " самое низкое
предшествование, сопровождаемое " логической и ", и затем логическое отрицание с
самым высоким предшествованием. С использованием круглых скобок это - можно
описать так
if ((A&& B) || (!C)
Вы можете читать это как " Если A и B обе верны или если C не верна или оба
не веры " скорее чем
if (A&& (B || (! C)))
Который интерпретируется как " A и "B или не C" верны). "
Что это значит для выражения верно или не верно? Напрмер,
( X> 3)
верно, если переменная x, например, 5. Однако в C любое выражение отличное от
нуля, рассматривается истинным любое нулевое выражение считается ложным. Это
означает, что можно было записывать следующее:
if (x) {
Что означает " если x - не нуль. " Кроме того, инструкции присваивания
возвращают им назначенные значения, что и объясняет, почему пример во введении
- проблема.
C имеет условные циклы, наиболее общим является цикл FOR. Например, чтобы
печатать первые десять значений I*I.
for (i = 1; i < = 10; i++) {/* повторяют десять раз */
printf (" %d возведение в квадрат i %d\n.1", i, i*i);
}
Код внутри блока выполняется неоднократно (в этом случае, десять раз). Перед
стартом, i установлен равным одному. Каждый раз через, прежде, чем блок
выполняется, i визуально проконтролирован, если i - все еще меньше или равно
десяти. Если условие держится, блок выполняется. После того, как i увеличено на
1, цикл перезапускается, если i - меньше или равно десяти.
Общий формат для инструкции - три отделенных точкой с запятой выражения. Первый
- задает начальное значение цикла.
Второе поле - условие, которое проверяется каждый раз прежде, чем блок
выполняется. Цикл выполняется, пока условие сохраняется. Третье поле инструкция модификации, выполняется каждый раз после того, как блок выполняется.
Каждое из этих полей необязательно, так что мы могли бы записывать:
for (;;) ...
Который не установил бы что-нибудь заранее, не проверит никакое условие каждый
раз по завершении входящих в тело цикла команд, и не изменил бы что-нибудь после
цикла. Это вызвало бы бесконечный цикл, поскольку это не имеет
никакого условия завершения.
C также имеет цикл while:
while (x > 2) {
/* Делать это пока x больше чем 2 */}
который является эквивалентным:
for (; x > 2;) {
/* Делать это пока x больше чем 2 */}
Мы показали, как гибко C может использовать FOR и WHILE. Гибкость не обязательно
подразумевает ясность. Будьте внимательны!
Указатели и динамические переменные.
Переменные Указатели содержат адреса местоположений, где данные сохранены, в
противоположность данным непосредственно. Любой тип данных может иметь указатель
на данные этого типа:
int *p; /* p - указатель на целое число */
char *s; /* s - указатель на символ */
struct student *t; /* t - указатель на структуру СТУДЕНТЫ */
Это размещает пространство для двух указателей, но не для местоположений, на
которые они указывают. Чтобы делать это, память должно быть ассигновано,
используя распределение памяти. Наиболее популярная функция - malloc (), которая
берет множество байтов, чтобы ассигновать и возвращает указатель на эти байты:
s = (char *) (malloc(d)); /* предполагает, что char 1 байт */
p= (int *) (malloc (sizeof (int))); /* адрес слова */
t = (struct student *) (malloc (sizeof (struct student)));
Количество отводимого пространства строго соответствует типу указателя. Первая
строка ассигнует один байт памяти и задает точку s к нему. Вторая строка
ассигнует необходимое число байтов для int и делает точку p к его
местоположению. Третья строка ассигнует достаточно байтов для записи и делает
точку t к этой записи.
Чтобы использовать указатели, мы должны разыменовать или отслеживатьть их:
*s = "X"; /* назначает байт на который s-указывает /
*p = -14;
(*p) ++; /* p теперь указывает на местоположение, содержащее -13 */
p = 33; /* не делайте это - вы записываете поверх указателя */
/*, следующие инструкции делают одно и то же */
(*t).iabs [3] = 100;
T- > iabs [3] = 100;
free() - библиотечная программа для освобождения пространства:
free(s); free (p) /* плохая идея освобождать то же самое пространство
дважды(вдвое) */
Для новичков, указатели необходимы в трех местах. Первый - для входа,
описанного в следующей секции. Второй - для проходящих параметров к функциям,
описанным в секции после этого. Третье общее использование - для cтрок.
Строки - только массивы символов в C, а массивы - действительно только указатели
на куски непрерывной памяти(хранения). Таким образом, следующие инструкции
эквивалентны:
char str [10]; /* строка 10 символов */
char *str; /* неинициализированная строка */
sir = (char *) matloc(str); /* пространство, зарезервированное для 10 символов
*/
Второй подход имеет преимущество, т.к.размер строки может быть
откорректирован в оперативно в течение трансляции. Обычно, в C, строки
снабжаются нулевым символом в конце. Это означает, что последний символ
строки должен быть специальный символ, называемый пустым множеством, которое
может быть написано как \0.
str [0] = 'H'; /* мы можем использовать обозначение массива */
str [l] = '1'; /* мы объявляли строку, использующую char *str */
str [2] = '\0 ; /*. ПУСТОЙ признак конца */
Чтобы распечатывать строку, мы можем использовать printf () с форматом %s:
Printf (" строка: %s.\n ", str);
Обратите внимание, что мы не указываем, что мы устанавливаем Char указатель на
printf, потому что, именно это ожидается для строки. Будет напечатано
строка: H.
В C двойые кавычки определяют статические строки. Чтобы копировать статическую
строку в переменную более быстро чем один символ одновременно, используйте
strсpy ():
char str [10]; /* достаточно пространства для 9 символов */
strcpy (str, " строка "); /* копирует " строка " в str '*/
strcpy (str, " Длинная строка "); /* бедствие - не достаточно пространства */
strcpy() не ассигнует никакое пространство для строки, и Вы, должны или
использовали MALLOC(), чтобы разместить или объявить строку как массив. Другие
функции для обработки строк обеспечивают более сложные(искушенные)
действия, но они - вне области(контекста) этой главы.
Пробуйте быть гибким с вашими строками. Когда вы в сомнении, ассигнуйте
большее количество памяти чем, Вам нужно при этом следует помнить, что пустой
признак конца берется за один символ.
При отладке программы происходила необычная неустойчивая работа. После
расхода нескольких часов поиска, я нашел проблему. При распределении памяти,
чтобы копировать строку, я ассигновал только длину строки непосредственно и не
включал пространство для завершающего символа пробела. Произошла запись вне
конца моего массива и для завершающего места не нашлось. Чтобы исправлять это,
строка стала
str=malloc (strlen (inbuf) +1)
Ввод и вывод
Мы уже имели некоторый опыт с printf () для вывода. Printf () берет
форматированную строку и переменные и печатает форматный вывод. Формата строки
указывает просмотр вывода и любых литеральных данных. Переменные обозначены с
%c для символа, %d для целого числа, %f для плавающей запятой, и %s для данных
строки.
printf ("Мое название -%s, и я -%d роста(футы)\n ", name, height);
В этом примере, название должно быть строкой, и это определяет %s строке
вывода, высота должна быть целым числом, заменяя %d. Имеются намного больше
сложные выборы, чтобы управлять, например, ширина полей вывода.
Для ввода, scanf() - подобно printf(). Она берет формат строки и переменных и
ждет пользователя, чтобы впечатать значения. Значения читаются в переменные. Как
правило, формат строки более прост для SCANF, потому что мы только
даем список переменных для ввода:
int first, second, third;
printf (" P1 вводит три целых числа: ");
/* нет перевода каретки */
scanf (" %d %d %d ", &first. &second, &third);
Printf (" сумма чисел 1s %d\n ", first + second + third);
Это запрашивает пользователя ввести три числа, которые читаются в переменные.
Обратите внимание на амперсант (&) перед именами переменной. Если Вы читаете в
скалярных переменных типа float, int и char, Вы должны снабжать
переменные с &. Это передает указатель на переменную вместо переменной
непосредственно, по причинам, описанным в следующей секции. Cтроки, с другой
стороны, не должны иметь амперсанта:
int score;
char name[80];
printf (" Вводят score и name\n ");
scanf (" %d %s ", &score, name);
Будьте внимательным, чтобы иметь достаточно пространства для размещения
строки. Если Вы не делаете этого, ваша программа потерпит крах.
Если ваша программа терпит крах "Ошибка Шины(канала)," наиболее вероятно, что
вы забыли помещать & перед скалярной переменной в scanf().
Функции
Мы уже описали несколько общих функций C, но Вы можете проектировать ваш
собственные функции. Функция имеет возвращаемое значение и некоторое число
параметров и должна быть объявлена прежде, вызова
int max(int a, int b) (/* возвращается большее из двух чисел */
if (a > b) return a;
else return b;
}
Это - функция берет два целочисленных параметра и возвращается больший.
Например:
x =max(y, 3);
Если функция не возвращает ничего, должно быть объявлено, чтобы возвратить
специальный "пустой" тип называемый VOID:
void anno(int n) {/*, неоднократно печатает раздражающее сообщение */
int i; /* локальная переменная */
for (i = 0; i < n; i++) {
Printf ("Ain't annoyin '? \n ");
}}
Переменные, объявленные внутри функции локальны к этой функции и доступны
только в ее пределах. Вы можете или объявлять переменные глобально перед
функцией (и вне основной программы) или Вы можете передавать их
как параметры, как в предыдущих примерах.
Вы можете изменять параметры, но эти модификации будут восстановлены на
выходе. Функции C поэтому называют как "передача параметра по значению ". Если
Вы должны изменить параметр внутри функции, Вы должны ввести в заблуждение C,
пропуская указатель на параметр вместо параметра непосредственно:
void paramExam(int x, int *y) {/* y - указатель, x - нет */
X- -; /* ИЗМЕНЕНИЕ Х НЕ ОТРАЖЕНО ВО ВНЕШНЕЙ ПРОГРАММЕ */
(*y) ++; /* изменение У отраженается вне */
}
main () {
int i,j;
i = 0; j = 3;
ParamExam(i, &j); /* помещают и передавют указатель на j */
Printf (" %d %d\n", I, J); /* " 0 и 4 " */}
Теперь мы понимаем, почему scanf ожидает амперсанд перед его скалярными
объяснениями, т.к. это изменяет
переменные. Мы также знаем, почему объявления указателя типа строк не нуждаются
в амперсанде.
Разработка программного обеспечения в C на UNIX.
Одна из сильных сторон C - то, что Вы можете помещать код в различные файлы.
Вы можете размещать каждую функцию в ее собственный файл и компилировать
отдельно, экономя время, в конечном счете. Если Вы берете этот подход, Вы
должны объявить заголовочные файлы там, где они используются. Разбивая
предыдущий пример, мы назовем файл, возможно, с расширением .C:
void paramExam (int x, int *y) {/* y - указатель, x - неn */
x- -; /* не будет отражен dj внешней программе */
(*y) ++; /* будет отражен вне */
И файл, называемый main.C:
void paramExam (int x, int *y); /* объявляют функцию */
main() {
int 1. J;
i = 0; j = 3;
ParamExam(i, &j); /* помещают и передавать указатель на j */}
Поскольку мы используем paramExaminmam(), мы должны объявить это в этом файле.
Чтобы компилировать нашу программу, мы должны компилировать каждый файл с
расширением .C:
cc -c param.c
cc -c main.c
Это создает два obj-файла param.o и main.o. Чтобы объединять obj-файлы мы
должны связать их:
cc -o myProgram param.o matn.o
Хотя это - три шага трансляции вместо одного, каждый шаг быстрее. Далее,
предположите, что мы теперь изменяем main.c, но не param.c. Мы только должны
перетранслировать main.C и перередактировать без перетранслирования
param.c.
cc -c mai'n.c
cc -o myProgram param.o main.o
MAKE ПРОГРАММА, ОПИСАННАЯ В СПРАВОЧНОЙ СИСТЕМЕ, АВТОМАТИЗИРУEТ ЭТУ ЗАДАЧУ.
РАЗДЕЛИТЕ ВАШИ ПРОГРАММЫ НА
ЧАСТИ, ЧТОБЫ СОХРАНИТЬ СЕБЯ ОТ ВОЛНЕНИЙ.
GDB Отладчик
GDB - отладчик, который может делать отладку C и C ++ программы. Этот
отладчик также называется GDB и работает с выполнимыми файлами, произведенными
компиляторами другими, чем GCC. Кроме того, GDB частично поддерживает и другие
языки типа Modula-2, ФОРТРАН, и Паскаль.
Вы можете использовать GDB многими способами. Родной интерфейс пользователя
- интерфейс командной строки, вот почему Emacs умеет работать с GDB. Вы можете
использовать Emacs, как интерфейс GDB.
GDB обеспечивает Вас четырьмя главными услугами, которые помогают найти ошибки
в вашей программе:
• Он позволяет Вам начинать вашу программу и определять что - нибудь, что могло
бы затрагивать поведение.
• Он останавливает ваши программы на условиях, которые Вы сами установили.
• Он исследует текущий статус вашей программы в момент ее остановки.
• Он позволяет Вам проводить изменения в вашей программе так, чтобы Вы могли
проверять, как эти изменения затронут ошибку и остальную часть программы.
Вызов GDB
Вы можете вызывать GDB тремя основными способами:
- Gdb <программа>
- Gdb < программа> <ядро файла >
- Gdb < программа> <pid >
Первым способом, Вы вызываете GDB с <программой> в качестве аргумента, где
<программа> - имя выполнимого файла.
Вторым способом, Вы вызываете GDB с <программой> и < основные файлы >, где <
ядро файла > - имя основного файла (обычно называемое ядром, но имя может также
определять что-нибудь еще). Основной файл содержит снимок точного статуса вашей
программы, когда она потерпела крах. Вызов GDB и определение основного файла
поставит программу в точно то же самое состояние внутри GDB.
Создание основных файлов для некоторых программ может быть невозможно. Иногда,
Вы должны поймать коварный тип ошибки, которая является неустойчивой и не
вызывает крах вашей программы.
Третий способ вызывать GDB будет удобным в этих случаях. Этим третьим
способом Вы вызываете GDB с <программой> и <pid>, где <pid> - номер
идентификации процесса управления. Когда избран этот путь, GDB примет на себя к
управление, обрабатывает и начинает исследовать процесс, для которого Вы можете
затем делать отладку.
Мы собираемся исследовать сессию отладки образца с GDB, используется
программа, которая приведена ниже. Программа, которая производит Ошибку
Сегментации
#include < stdio. h >
#include < fcntl. h >
#include <errno.h>
int openbuffer (filename)
char * filename;
int fd;
if ((fd = open (filename, 0_RDONLY)) <) 0
{
fprintf (stderr, " Не может открыть % s, errno = < % > d \n ",filename,
errno);
exit (-2);
return (fd);
}
int readbuffer (fd, buffer, size)
int fd;
char *buffer;
int size;
{ int retc;
if ((retc = read (fd,buffer, size))! = size)
if (retc < 0)
{
fprintf (stderr. " Не может читать, errno = % d\n ", errno);
exit (-2);
} else if (retc)
fprintf (stderr, " Частичное чтение\n ");
return (retc);
int countchar (buffer)
char * buffer;
{
int cnt;
char *p;
p = buffer;
While (*p)
{
if ((*p) == '\n ') cnt ++;
p ++;
} return (cnt);
}
main ()
{
int fd;
int cnt = 0;
char inbuf [1024];
fd = openbuffer (" /usr/lib/libc. a");
while (readbuffer (fd, inbuf, 1024)) cnt + = countchar (inbuf);
printf ("Странный знаковый счетчик= % d\n ", cnt);
Далее следует
ввод, сопровождаемый выходом:
$ gdb badprog core
GDB - свободное программное обеспечение и Вы свободно можете получить копии
его при некоторых условиях; напечатайте " show copying " чтобы видеть условия.
Нет абсолютно никакой гарантии для GDB; напечатайте " show warranty " для
ознакомления с деталями. GDB 4.7 (NtXT 3.1). Авторское право 1992 Свободная
Основа Программного обеспечения. Inc ... Чтение символов от /home/badprog ...
выполненный. Чтение символов от /usr/shiib/libsys_s. B.shiib ... done.
Чтение в символах для badprog.c ... done.
Ox3dda in countchar (буфер = 0x3fff89c " Karch > \n __. SYMDEF
SORTED747000476 0
45
if ((*p) == t\n ') cnt ++;
При входе в GDB с программой, или основным файлом, GDB сообщает сразу же,
где программа потерпела крах:
( Gdb) where * 0 Ox3dda in countchar (буфер = 0x3fff89c " Karch > \n __. SYMDEF
SORTED747000476.
* 1 Ox3e40 un main () at badprog. C: 58
где команда показывает стек, так что сразу понятно, что ошибка в countchar,
функции. Она вызвалась MAIN. < digits > в начале строки в стека вывода структура(фрейм). Вывод командного фрейма 0 переключит контекст и даст контекст
функции countchar. Аналогично, вывод фрейма 1 дал бы контекст главной функции:
( Gdb) frame 0
^ 0 Ox3dda in countchar (buffer = 0x3fff89c "! <arch > \n __, SYMDEF
SORTED747000476
45 , if ((*p) == '\n ') cnt ++;
(Gdb) list 40,50
40 char *p;
41
42 p = buffer;
43, while (p)
44 {
45, if ((*p) == '\n ") cnt ++;
46 p++;
47}
48 return (cnt);
49}
50
(Gdb) print buffer
$1 = Ox3ff (f89c "! < arch > \n __. SYMDEF SORTED747000476 0 1
100644 58652
Здесь, мы можем видеть, что воспроизведена ошибка программы.
Заканчивающееся условие находится на указателе вместо нахождения его на знаке,
указатель указывает на:
(Gdb) print p
$1 = Ox4000000 < Address 0x4000000 out of bounds >
( Gdb) print *p.
$2 = Cannot access memory:, address 0x4000000 out of bounds.
Попытка доступа к памяти вне того, где есть доступ, вызвал проблему:
(Gdb) frame 1
^ 1 Ox3e40 in main() at badprog. c-. 58
58, while (.readbuffer (fd, 1nbuf. l024)) cnt + = countchar (1nbuf);
Давайте взглянем на буфер, в то время как программа находилась в MAIN:
( Gdb) print -inbuf
$3 = {" '. < arch > \ri_. SYMDEF SORTED747000476 0 1 100644 58652
\n\
( Gdb) q. '.
Мы можем видеть, что можно действительно двигаться от одной функции до
другой, и таким образом можно исследовать каждое промежуточное звено, вызывающее
функцию от main к функции, которая потерпела крах.
GDB имеет намного более мощные возможности. Далее следуют наиболее важные из
них.
Опции командной строки
GDB может брать опции командной строки, показанные ниже.
-s
-symbols < file >
Заставят GDB читать символы из другого файла.
-c
-core <file>
Делает использование GDB <file> как файл ядра.
-x
-command <file>
GDB читает и выполнять команды GDB из файла < f11e>.
-d
-directory <dir>
Добавляет <dir> к списку справочников, чтобы искать исходные
файлы.
-nx
заставляет GDB не выполнять любые команды от .gdbinit файла, содержит
GDB команды, которые выполняются каждый раз когда GDB стартует.
-batch Заставляет GDB работать в пакете. Обычно этот режим используется с
командными опциями (из -cjmmand). GDB прекращает выполнение после всех указанных
в командном файле команд.
-quiet
Не будет печатать информацию лицензирования, когда GDB запущен.
GDB команды
GDB содержит большой словарь. Ниже указан список некоторых ключевых сллов,
которые Вы можете использовать при отладке программы.
Shell <команда> Запускает оболочку и выполняет команду.
<command>quit Выход из GDB.
make < args > Звпускает утилиту MAKE и передает ей <args> для выполнения.
help
подсказка GDB относительно категорий команд.
help <category> Подсказка GDB относительно команд данной категории
help <команда> Заставит GDB показать полную помощь относительно команды
complete <str> Задает список GDB команд, которые начинаются с <str>.
run <args> Начинает вашу программу и передает ей <args>. <Args> может содержать
переназначение ввода-вывода.
attach <pid>, связывает GDB с работающим процессом с <pid>.
detach Отключает GDB от работающего процесса. Обратите внимание, что, если Вы
вышли из GDB или Вы используете команду, в то время как Вы связаны с работающим
процессом, Вы убьете процесс. Всегда используйте DETACH, когда действительно
хотите сохранить процесс работающим
GDB позволяет Вам делать отладку в режиме мультипрограммирования! Ниже
содержится набор команд, используемых для этой цели.
thread <номер> Задает номер текущего процесса.
info thread Выдает список всех thread, которые присутствуют в настоящее время
thread apply < list> <args >
применяет команду к списку thread. <list> может
быть список номеров thread или ключевое слово ALL, которые указывает, что
команда относится ко всем threads. <args> - это используемая команда.
Перед рассмотрением списка команд, мы должны определить два понятия, контрольную
точку и watchpoint. Контрольная точка - это указатель на место в коде, который
Вы можете устанавливать. Когда код, направленный на контрольную точку,
выполняется, программа останавливается. Watchpoint контролирует выражение, и
когда оно достигает некоторой величины, которую Вы указали, программа
остановится. Вы устанавливаете контрольные точки и watchpoints, используя
различные команды, но однажды установив, те же самые команды не будут
выполняться и будут удалены. Ниже приведен список команд для остановки
программы при некоторых условиях.
Break <функция> Устанавливает контрольную точку в начале <функции>.
break <line > Устанавливает контрольную точку линии номер<line >.
break <offset> Контрольная точка установлена в -h/ <offset > от точки, где ваша
программа была остановлена перед этим.
break < file >: <f> Устанавливает контрольную точку в начале функции <f> в
исходном файле <f>.
break <file >: <l> Установка контрольной точки в линии номер <l> в исходном
файле <file>.
Break
Устанавливает контрольную точку в следующей выполняемой инструкции
break <args>, if <cond> Устанавливает контрольную точку, в зависимости от
<args>, который является тем набором аргументов который break, может брать если
<cond>
оцениваются как true.
Tbreak <args> Устанавливает контрольную точку, которая будет удалена как только
программа остановится.
Info break Печатает список всех установленных контрольных точек и watchpoints
установлены.
watch <expr> Устанавливают watchpoint для выражения <expr >. Когда выражение
становится ИСТИННЫМ, программа останавливается.
dear Удаляет набор контрольных точек в следующей инструкции, которая
выполняется.
dear <f>
Удаляет набор контрольных точек в начале функции <f>.
dear < file >: <f> Удаляет набор контрольных точек в начале функции <f> в
исходном файле.
dear <l>
Удаляет набор контрольных точек на линии <l>.
dear <file >: <l> Удаляет набор контрольных точек строке <l> в исходном файле
<file>.
Delete
удаляет всe контрольные точки
Опции
реультат
Delete <args> Удаляет контрольные точки согласно < args>, где
< Args > - список номеров контрольных точек, заданных в команде info break
disable
снимает все контрольные точки
disable < args>
Снимает контрольные точки, указанные в <args>
enable
Устанавливает все контрольные точки
enable <args>
Устанавливает контрольные точки, указанные в <args >
enable once <args> Устанавливает контрольные точки, указанные в <args > так,
чтобы они были сняты снова как только программа остановится
enable delete <args>, Устанавливает контрольные точки, указанные в <args > так,
чтобы они были удалены как только программа остановится.
Имеются также команды, чтобы выполнить шаг программы,
CONTINUE
обеспечивает выполнение вашей программы, пока не была обнаружена
следующая контрольной точкой.
Если ваша программа остановилась в том местоположении из-за
прохода контрольной точки - bypassed.
continue <count> - тот же самое, что и сontinue. Необязательный <count> количество раз, которое контрольная точка в этом местоположении будет
игнорирована.
step Продолжает выполнение вашей программы до другой строки исходного файла .
step <индекс> Тот же самое, что и step. Необязательный аргумент <индекс>
заставит GDB идти вперед на количество шагов, определяемое в <индекс>.
Next
Продолжает выполнение вашей программы, пока с различной исходной линией
не сталкиваются в той же самом фрейме. Это означает, что запрос функции не
последует.
next <индекс>.
finish Продолжает выполнение программы до текущей функции
returns.
unfit Продолжает выполнение программы, пока новая исходная строка за текущей
строкой не достигнута. Этот выбор используется, чтобы избежать проходить цикл
больше чем однажды.
until остановит программу после завершения цикла.
until <loc>
Продолжает выполнение программы, пока местоположение <loc> не
достигнуто. Аргумент <loc> может быть любой из аргументов это
break ' команда содержит.
GDB позволяет Вам обращаться с сигналами, посланными вашим программам (см.
таблицы сигналов в следующих лабораторных работах). Очень полезно, если Вы
можете блокировать сигнал так, чтобы ваша программа, не видела его, или Вы
можете останавливать вашу программу на данном сигнале.
Info signals
Показывают сигналы, в настоящее время обработанные GDB и как их
обрабатывать.
handle <signal> <arg > Сообщает GDB обращаться с сигналом <signal> как
определено в <arg>, который может быть один или большее количество сигналов:
проходят,позволяет вашей программе видеть signal.
Nopass Не позволяет вашей программе видеть signal.
Stop
Останавливает вашу программу, когда этот сигнал получен.
Nostop
Позволяет вашей программе работать, хотя сигнал и получен.
print Печатает сообщение, когда этот сигнал получен.
Noprint Не печатает сообщение, когда сигнал получен.
signal <signal>
Продолжает выполнение вашей программы и посылает сигнал
<signal> сразу после этого.
Вы теперь имеете идею относительно того, насколько богатый набор команд
установлен в GDB. Вы можете использовать много других команд. GDB также
позволяет Вам делать удаленную отладку через сеть или TCP/IP связи. Это
означает, что Вы можете управлять GDB на одной машине, а делать отладку
программы, идущей на другой машине. Вы можете делать это используя расширение
GDB, называемое gdbserver.
Вы можете использовать GDB с Emacs, популярным редактором текста, который
многие используют как главную среду программирования. Emacs имеет GDB режим,
который делает его основным интерфейсом с GDB.
Два графических интерфейса к GDB также доступны. Они не поддержаны или
распространяются с Gnu, потому что они были развиты специально для работы с
GDB, они стоят упоминания. Первый называется gdbtk (ftp: //
ftp.cygnus.com/pub/gdb /), и второй называется xxgdb (ftp: // ftp.x.org/
contrib/utilities).
Gdbtk - большой пакет, который включает интерфейс непосредственно, tk и tcl. Они
обычно работают вместе и составляют новый способ делать X окна основанной
программы. Они содержат примитивы высокого уровня для создания GUI элементов.
Xxgdb - простой пакет, который не компилируется. Он работает с GDB 4. x, но он
должен также работать и со старыми версиями. Он поддерживает два способа
действия, отдельных и многократных окон.
Лабораторное задание
1). Перед выполнением лабораторного задания:
- ответьте на вопросы коллоквиума, заданные преподавателем;
- получите у администратора системы или преподавателя право на
использование терминала;
- зарегистрируйтесь в системе;
2). Создайте в Вашей домашней директории поддиректорию Mytherdir;
3). Получите у преподавателя Ваш вариант текста, занесите его в файл
mytext.1.txt с помощью редактора vi.
4). В редакторе vi в начале текста файла mytext.1.txt добавьте шифр
своей группы и свой номер по журналу группы. Результат сохратить в
файле.
5). В редакторе vi в конце текста файла mytext.1.txt добавьте текст
(что нибудь, типа "Your most humble obedient servant, .........,
student"), результат сохранить.
6). Выполните задания пунктов 4) и 5) в новых строках, создаваемых в
средней части текста файла mytext.1.txt.
7). По заданию предодавателя произведите поиск подстроки вперед/назад
по тексту файла mytext.1.txt с контекстной заменой на новую подстроку.
8). Скопируйте mytext.1.txt в mytext.2.txt.
9). В файле mytext.1.txt поменяйте местами первую и последнюю строки,
а в этих строках - первые и последние слова, сохраните результат.
10). В файле mytext.1.txt скопируйте первые 3 строки после последней
строки, сохраните результат.
11). Переместите 1-ю, 3-ю и последнюю строки из mytext.1.txt в
mytext.2.txt в качестве последней, первой и второй соответственно.
Сохраните результат.
12). Скопируйте половину строк mytext.2.txt в начало файла
mytext.1.txt, сохраните результат.
13). Установите режимы нумерации строк, автозаписи и новой ширины
сдвига, "заворачивания" длинной строки, автоотступа.
14). В режиме редактирования mytext.1.txt создайте файл mytext.3.txt,
содержащий текущую дату, список активных пользователей и процессов, а
текже текст файла mytext.2.txt, файл mytext.2.txt удалите, а текст
файла mytext.3.txt поместите в файл mytext.1.txt начиная со второй
строки, сохраните результат.
15) Выполните программирование функции в соответствии с номером
вашей бригады, Описание функций:
№ функции
Описание
8.
Слова в каждой записи переупорядочить так, чтобы первым было слово,
длина которого минимально отличается или равна длине первого слова
первой записи.
________________________________________________________________________________
16) Приготовьте двухмодульную программу, содержащую главный модуль, и
функцию, описанную в предыдущем пункте. Главная программа служит для
передачи параметров в функцию.
17) Откомпелируйте программу, устранив синтаксические и семантические ошибки.
18) Используя GDB, проведите отладку основного цикла функции.
19) Выполните всю программу полностью.
20). Завершите сеанс работы с ОС.
Download