Вы находитесь на странице: 1из 40

 Операционная система Линукс. Особенности загрузки.

Структура
файловой системы.
Файловая система состоит из четырех основных компонентов: 
Пространство имен – методы именования объектов и организации в
виде единой иерархии 
API – набор системных вызовов для перемещения между объектами и
управления ими 
Методы безопасности – схема защиты, сокрытия и совместного
использования объектов 
Реализация – программный код, который связывает логические модели
с дисковой подсистемой 
Файловая система – это единая иерархическая структура, которая
начинается с каталога / и разветвляется, охватывая произвольное число
каталогов. 
Каталог верхнего уровня называется корневым. Это
моноиерархическая система отличается от используемой в Windows,
где применяется понятие пространства имен, основанное на принципе
деления диска на разделы. 
Цепочка имен каталогов, через которые необходимо пройти для
доступа к заданному файлу, вместе с именем этого файла образуют
путь к файлу. Путь может быть абсолютным (например, /temp/foo) или
относительным (например, book4/filesystem). Последние
интерпретируются начиная с текущего каталога. Стоит отметить, что
текущий каталог есть у каждого процесса (большинство процессов
никогда не изменяют свои рабочие каталоги, и поэтому просто
наследуют текущий каталог процесса, который их запустил). 
Существует ограничение на длину имени файла – не более 255
символов. В имя нельзя включать символ косой черты и нулевые
символы. Также есть ограничение на длину пути, который передается
ядру в качестве аргумента системного вызова – 4095 байт.

2.Общие принципы построения вычислительных систем. 


Вычислительная система - это совокупность взаимосвязанных и
взаимодействующих процессоров или ЭВМ, периферийного
оборудования и программного обеспечения, предназначенную для
подготовки и решения задач пользователей. 
ЭВМ (под словом ЭВМ обычно понимают цифровые электронные
машины, предназначенные для автоматизации процесса обработки
информации). 
Принципы построения вычислительных систем (ВС). Выделим
“модульность” и “близкодействие” как главные принципы. 
Модульность – принцип, предопределяющий формирование
вычислительной системы из унифицированных элементов (называемых
модулями), которые функционально и конструктивно закончены,
имеют средства сопряжения с другими элементами и разнообразие
которых составляет полный набор. Функциональные и конструктивные
возможности модулей, разнообразие их типов определяются исходя из
требований, предъявляемых к вычислительным системам, и,
безусловно, из возможностей микроэлектронной базы. 
Модульность вычислительной системы обеспечивает: 
· Возможность использования любого модуля заданного типа для
выполнения любого соответствующего ему задания пользователя; 
· Простоту замены одного модуля на другой однотипный; 
· Масштабируемость, т.е. возможность увеличения или уменьшения
количества модулей без коренной реконфигурации связей между
остальными модулями; 
· Открытость системы для модернизации, исключающую ее моральное
старение. 
При конструировании вычислительных систем достаточно
ограничиться единственным модулем–вычислителем, который бы
обладал вычислительной и соединительной полнотой. Следовательно,
модуль должен иметь средства автономного управления, располагать
арифметико-логическим устройством и памятью и содержать
локальный коммутатор – схему для связи с другими модулями. На
практике принято такой модуль–вычислитель называть либо
элементарным процессором (ЭП), либо элементарной машиной (ЭМ).
При этом считается, что ЭП это композиция из процессора и
локального коммутатора. Разрядность таких ЭП в различных
вычислительных системах колеблется от 1 до 64. Под элементарной
машиной понимается архитектурно более развитая композиция из ЭВМ
и локального коммутатора. 
Близкодействие – принцип построения вычислительных систем,
обусловливающий такую организацию информационных
взаимодействий между модулями–вычислителями, при которой
каждый из них может непосредственно (без “посредников”)
обмениваться информацией с весьма ограниченной частью модулей-
вычислителей. Следовательно, структура ВС позволяет осуществлять
информационные взаимодействия между удаленными вершинами-
вычислителями лишь с помощью промежуточных вершин-
вычислителей, передающих информацию от “точки к точке”.
Удаленными считаются все те вершины в структуре ВС, расстояние
между которыми более 1 (число ребер между которыми более
единицы). 
Принцип близкодействия допускает реализацию механизма управления
ВС (организации функционирования коллектива вычислителей как
единого целого), не зависящий от числа составляющих ее
вычислителей. Данный принцип, в частности, выражается в том, что
поведение каждого вычислителя зависит от поведения только
ограниченного подмножества других вычислителей системы.

3.Командная строка системы Linux. Пользователи и права


доступа. 
Командный интерпретатор bash – это один из нескольких
интерпретаторов, доступных в Linux. Bash позволяет интерактивно
взаимодействовать с компьютером, вводя некоторые команды и
получая на них соответствующий отклик. Также этот командный
процессор позволяет исполнять скрипты (команды из файла), может
производить автодополнение названий файлов и директорий,
благоволит использование переменных, операторов ветвления и цикла. 
Команды для службы с файлами и каталогами: 
pwd – отобразить путь текущего каталога 
cd – перейти в указанный каталог 
ls – показать список файлов каталога, с ключом -l показывает
дополнительные сведения о файлах. 
cp – копирование файлов/папок 
mv – смещение файлов/папок 
mkdir – создать папку 
rm – удалить файлы/папки, с ключом -r устраняет и все вложенные
папки, с ключом -f — удаляет открытые файлы или каталоги 
rmdir – удаление порожний папки 
chmod – изменить права доступа к файлу 
chown — сменить владельца файла или каталога 
find — отыскать файл. Задается исходный путь для поиска и шаблон
поиска, find / -name .X* — разыскивать от корневого каталога файлы,
содержащие в имени символы .X 
which — отобразить полный путь выполняемого файла, доступного в
данной оболочке, например which ifconfig 
touch — изменить преходящие отметки файла. Удобно использовать
для создания пустых файлов – touch myfile основывает пустой файл
myfile . 
Системные команды 
Эти команды обычно используется от имени суперпользователя ( с
бригадой sudo ). 
reboot – перезагрузка системы 
poweroff – выключение компьютера 
reset – очищает окно терминала 
passwd – переменить свой пароль, а суперпользователю — поменять
пароль любого пользователя 
users — отобразить перечень пользователей, вошедших в систему. 
yum — установка, удаление или обновление программного
обеспечения в дистрибутивах Fedora, RedHat и т.п. 
dpkg — аппарат, удаление или обновление программного обеспечения
в дистрибутивах Debian, Ubuntu и т.п. 
apt-get — установка, устранение или обновление программного
обеспечения в дистрибутивах на основе Debian (Ubuntu, Mint и т.п.) 
Управление действиями 
ps – отобразить список текущих активных процессов 
lsof — отобразить список открытых файлов процесса или юзера 
strace — отобразить список системных вызовов 
last — отобразить историю перезагрузок и регистраций юзеров в
системе 
kill – послать сигнал процессу, обычно используется для
принудительного завершения службы процесса. 
killall – завершение работы всех процессов, имена которых заданы
метеопараметром командной строки 
top – отображение списка текущих процессов и интерактивное
управление ими. 
В случае, если требуется выполнить какую-либо команду, которая
обычному пользователю недоступна, можно использовать команду: 
sudo команда 
Эта команда позволяет выполнять операции от имени root’a. 
ПРИМЕЧАНИЕ: Команда sudo будет доступна пользователю только в
том случае, если он входит в группу, которая указана в файле
/etc/sudoers. Например, по умолчанию, это может быть группа ”admin”. 
Второй вариант – это использование команды: 
su имя_пользователя 
При помощи этой команды можно переключиться на оболочку другого
пользователя и выполнять команды из-под него. Недостаток этой
команды заключается в том, что пользователи должны знать пароль
пользователя, в оболочку которого они хотят переключиться 
Для того, чтобы пользователь мог выполнять какие-либо действия с
файлом/папкой, необходимо, чтобы он имел на это полномочия. В
данном случае, это возможно, если данный пользователь является
владельцем редактируемого файла/папки, либо входит в группу,
которой открыты данные права. 
Изменить права к файлу можно командой: 
chmod [права] имя-файла 
Наиболее распространенным вариантом задания прав для файла
является цифровой. В данном случае, право на чтение (r) кодируется
цифрой 4, право на запись (w)— цифрой 2, а право на запуск (x) —
цифрой 1. Таким образом, если Вы хотите, чтобы владелец файла имел
все права на управление файлом, группа могла читать и выполенять, а
остальные пользователи могли его только читать, то это
 можно осуществить следующей командой: 
chmod 754 имя_файла 
Заметьте, что последовательность указания прав неизменна. То есть,
первая цифра (7) указывает права для владельца, вторая (5) – для
группы, и третья (4) – для всех остальных пользователей в системе. 
Для того, чтобы изменить владельца файла или папки, используется
команда: 
chown имя_пользователя имя_файла

 4.Компиляция, трансляция, линковка, интерпретация. Makefile. 


Трансляция программы — преобразование программы, представленной
на одном из языков программирования, в программу на другом языке
и, в определённом смысле, равносильную первой. При трансляции
выполняется перевод программы, понятной человеку, на язык,
понятный компьютеру. Выполняется специальными программными
средствами (транслятором). 
Трансляторы реализуются в виде компиляторов или интерпретаторов. 
Компиляция — преобразование программой-компилятором исходного
текста программы, написанного на языке высокого уровня в машинный
язык, в язык, близкий к машинному, или в объектный модуль.
Результатом компиляции является объектный файл с необходимыми
внешними ссылками для компоновщика. 
Компилятор читает всю программу целиком, делает ее перевод и
создает законченный вариант программы на машинном языке, который
затем и выполняется. 
Интерпретация — процесс непосредственного покомандного
выполнения программы без предварительной компиляции, «на лету»; в
большинстве случаев интерпретация намного медленнее работы уже
скомпилированной программы, но не требует затрат на компиляцию,
что в случае небольших программ может повышать общую
производительность. 
Линкование (компоновка) - это процесс, при котором все
"недокомпилированные" части программы доводятся до конца и
связываются между собой в исполняемый файл (или файлы) формата,
понятного данной операционной системе. В итоге, мы получаем
исполняемую программу. 
Makefile — это набор инструкций для программы make, которая
помогает собирать программный проект буквально в одно касание. 
То есть, правило make это ответы на три вопроса: 

{Из чего делаем? (реквизиты)} —-> [Как делаем? (команды)] —-> {Что
делаем? (цели)} 
Несложно заметить что процессы трансляции и компиляции очень
красиво ложатся на эту схему: 

{исходные файлы} —-> [трансляция] —-> {объектные файлы} 


{объектные файлы} —-> [линковка] —-> {исполнимые файлы} 
Предположим, что у нас имеется программа, состоящая из 2 файлов: 
main.c и hello.c 
Makefile, выполняющий компиляцию этой программы может
выглядеть так: 

hello: main.c hello.c 


gcc -o hello main.c hello.c 
Для компиляции достаточно дать команду make в рабочем каталоге: 
$ make <цель> 
На самом деле, в качестве make целей могут выступать не только
реальные файлы. 
Командой make производят компиляцию программы, командой make
install — установку. 
all — является стандартной целью по умолчанию. При вызове make ее
можно явно не указывать. 
clean — очистить каталог от всех файлов полученных в результате
компиляции. 
install — произвести инсталляцию 
uninstall — и деинсталляцию соответственно.

5.Отладка программ с использованием GDB. 


Для использования GDB необходимо откомпилировать программу с
ключом -g, например, -g program.c. После этого необходимо запустить
gdb, передав ему в качестве параметра имя отлаживаемого
исполняемого файла: gdb ./a.out. 
После приглашения (gdb) можно вводить различные команды. Можно
использовать клавишу <Tab> для завершения имени команды,
переменной или функции исходного года. Простое нажатие клавиши
<Enter> без указания команды повторяет последнюю ранее введенную
команду. Для завершения работы GDB введите команду quit
(сокращается до q). 
Для получения справки небходимо ввести команду help (h). 
Исполнение программы 
run (r) начинает исполнение программы до первой точки остановки или
возникновения ошибки вроде Floating point exception или Segmentation
fault. Для перенаправления ввода-вывода команды можно использовать
команду run <input_file >output_file. 
continue (c) продолжить выполнение программы до следующей точки
остановки или ошибки. 
step (s) Продолжить выполнение программы пока управление не
достигнет следующей строки исходного текста. При этот происходит
вход в функцию, то есть при вызове каждой функции программа также
останавливается. 
next (n) Работает аналогично команде step, но при этом функции
вызываются без остановки. 
finish Продолжить выполнение программы до возврата из текущей
функции, напечатать возвращаемое значение. 
Командам step и next можно передать числовой параметр, в этом
случае это будет означать выполнение этой команды заданное число
раз. 
Исследование данных 
print expr (p) Отобразить значение выражения expr. 
Если имеется динамический массив (например, int * p=new int[10]), то
при выводе его на экран (print p) будет напечатано значение указателя,
то есть адрес в памяти компьютера. 
Точки останова 
В произвольном месте программы можно поставить точку останова,
при достижении которой команда остановится. При этом можно
исследовать значения переменных, продолжить выполнение
программы при помощи команд c, s, n или выполнить другие команды
GDB. 
break n (b) Установить точку останова на строке исходного кода с
номером n. Все точки останова имеют свои номера, определяемые при
их установке. 
break function Установить точку останова на функции function.
Исполнение программы остановится при вызове этой функции.
Распространенный пример: break main для того, чтобы можно было
пошагово исполнять программу с самого начала. 
tbreak ... Установка разовой точки останова. Работает аналогично break,
но точка останова автоматически удаляется при достижении ее. 
break ... if cond Установка условной точки останова. Выполнение
останавливается на данной строке или функции, если выполнено
условие cond. Пример: break 120 if a==15. 
clear function или clear n Удалить точку останова на функции function
или в строке с номером n. delete диапазон (d) Удалить точки останова с
номерами из указанного диапазона. Например, delete 1-5 или delete 4.
Если диапазон не задан, удаляются все точки останова. 
disable диапазон Отключить точки из заданного диапазона. 
enable диапазон Включить точки из заданного диапазона. 
enable once диапазон Точки из заданного диапазона включаются до
первого срабатывания, после чего отключаются. 
enable delete диапазон Точки из заданного диапазона включаются до
первого срабатывания, после чего удаляются. 
condition n cond Превращение точки останова с номером n в условную с
условием cond. 
condition n Превращение точки останова с номером n в безусловную. 
info break Вывести информацию обо всех имеющихся точках останова. 
Если непонятно, в каком месте программы находится ошибка, то
возможно в отладке помогут точки наблюдения. Точка наблюдения
позволяет остановить программу в любом месте, когда изменяется
заданное значение. 
watch expr Установить точку наблюдения на выражении expr.
Исполнение программы останавливается, когда значение expr
сохраняется программой и его величина изменяется. Пример: watch a. 
rwatch expr Установить точку наблюдения, которая остановит
програму, когда выражение expr считывается программой. 
awatch expr Установить точку наблюдения,
 которая остановит программу, если выражение expr считывается или
сохраняется. 
info watch Вывести информацию о всех точках наблюдения. 
Просмотр исходного кода и стека программы 
Для вывода исходного текста программы используется команда list (l) в
различных вариантах: 
list n Вывести строки исходного кода, вокруг строки с номером n. 
list function Вывести начало кода функции function. 
list n,m Вывести строки с номерами от n до m. 
list n, Вывесть строки, начиная с n. 
list ,m Вывести строки до номера m. 
list Вывести еще немного строк. По умолчанию выводится 10
следующих строк, после ранее выведенных. Это значение можно
изменить командой set listsize число. 
Когда программа остановила свою работу, прежде всего необходимо
узнать, где это произошло и как она туда попала. Особенно это полезно
при отладке run-time ошибок. 
Всякий раз, когда вызывается функция, информация о вызове
сохраняется в стеке. Информация сохраняется в блоке данных,
называемом кадром стека. 
where Вывести содержимое стека. 
frame n Переключиться в кадр с номером n. При этом можно
просматривать значения локальных переменных в контексте этого
кадра. 
up n Переместиться по стеку вверх на n кадров. По умолчанию n равно
1. 
down n Переместиться по стеку вниз на n кадров. 
info frame Вывести информацию о текущем кадре. 
info args Вывести информацию об аргументах текущего кадра. 
info locals Вывести информацию о всех локальных переменных
текущего кадра. 

6.Язык программирования C. Типы данных. Стандартные типы
данных. 
Программы оперируют с различными данными, которые могут быть
простыми и структурированными. Простые данные - это целые и
вещественные числа, символы и указатели (адреса объектов в памяти).
Целые числа не имеют, а вещественные имеют дробную часть.
Структурированные данные - это массивы и структуры; они будут
рассмотрены ниже. 
В языке различают понятия "тип данных" и "модификатор типа". Тип
данных - это, например, целый, а модификатор - со знаком или без
знака. Целое со знаком будет иметь как положительные, так и
отрицательные значения, а целое без знака - только положительные
значения. В языке Си можно выделить пять базовых типов, которые
задаются следующими ключевыми словами: 
char - символьный, единичный байт, который может содержать один
символ из допустимого символьного набора; 
int - целый, целое, обычно отображающее естественное представление
целых в машине; 
float - вещественный, число с плавающей точкой одинарной точности; 
double - вещественный двойной точности, число с плавающей точкой
двойной точности; 
void - не имеющий значения. 
Дадим им краткую характеристику: 
Переменная типа char имеет размер 1 байт, ее значениями являются
различные символы из кодовой таблицы, например: 'ф', ':', 'j' (при
записи в программе они заключаются в одинарные кавычки). 

Размер переменной типа int в стандарте языка Си не определен. В


большинстве систем программирования размер переменной типа int
соответствует размеру целого машинного слова. Например, в
компиляторах для 16-разрядных процессоров переменная типа int
имеет размер 2 байта. В этом случае знаковые значения этой
переменной могут лежать в диапазоне от -32768 до 32767. 
Ключевое слово float позволяет определить переменные вещественного
типа. Их значения имеют дробную часть, отделяемую точкой,
например: -5.6, 31.28 и т.п. Вещественные числа могут быть записаны
также в форме с плавающей точкой, например: -1.09e+4. Число перед
символом "е" называется мантиссой, а после "е" - порядком.
Переменная типа float занимает в памяти 32 бита. Она может
принимать значения в диапазоне от 3.4е-38 до 3.4e+38. 
Ключевое слово double позволяет определить вещественную
переменную двойной точности. Она занимает в памяти в два раза
больше места, чем переменная типа float (т.е. ее размер 64 бита).
Переменная типа double может принимать значения в диапазоне от
1.7e-308 до 1.7e+308. 
Ключевое слово void (не имеющий значения) используется для
нейтрализации значения объекта, например, для объявления функции,
не возвращающей никаких значений. 
Объект некоторого базового типа может быть модифицирован. С этой
целью
 используются специальные ключевые слова, называемые
модификаторами. В стандарте ANSI языка Си имеются следующие
модификаторы типа: 
unsigned 
signed 
short 
long 
Модификаторы записываются перед спецификаторами типа, например:
unsigned char. Если после модификатора опущен спецификатор, то
компилятор предполагает, что этим спецификатором является int.
Таким образом, следующие строки: 
long а; long int а; 
являются идентичными и определяют объект а как длинный целый.
Табл. 1 иллюстрирует возможные сочетания модификаторов (unsigned,
signed, short, long) со спецификаторами (char, int, float и double), а также
показывает размер и диапазон значений объекта (для 16-разрядных
компиляторов). 
7.Язык программирования С. Структура программы. 
Программа на языке Си состоит из одной или более подпрограмм,
называемых функциями. 

Язык Си является блочно-структурированным. Каждый блок


заключается в фигурные скобки {}. 

Основным блоком в программе консольного приложения на языке Си


является главная функция, имеющая имя main(). 

Каждое действие в языке Си заканчивается символом «точка с запятой»


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

Имя функции — это коллективное имя группы описаний и операторов, 


заключенных в блок (фигурные скобки). За именем функции в круглых
скобках указываются параметры функции. 
Директивы препроцессора 
Составной частью компилятора является программа, называемая
препроцессором. Препроцессор работает до трансляции программы с
языка высокого уровня на машинный язык, выполняя её
предварительное преобразование. Каждая директива препроцессора
начинается с символа # (номер) и занимает всю строку. Директивы,
которые не помещаются в одной строке, могут быть продолжены в
следующей строке. Признаком продолжения строки является символ
обратной косой черты ( \ ) в продолжаемой строке. 
Наиболее часто используется директива включения в программу файла 
#include <name> 
где name – имя файла, включаемого в текст программы. 
Эту директиву называют директивой подстановки. Она предписывает
компилятору поместить на её место файл name. Файл name называется
заголовочным. Он содержит объявления данных и функций,
используемых в программе. Например, включение в программу
директивы 
#include <math.h> 
позволит использовать в программе стандартные математические
функции, такие как sin x, cos x, ln x и т.д. Список стандартных
математических функций будет приведён ниже. 
Директива 
#include <stdio.h> 
даёт возможность использования в программе стандартных функций
ввода-вывода. 
Другой часто используемой директивой является директива
определения 
#define S1 S2 
где S1, S2 – строки символов. 
Препроцессор отыскивает в тексте программы строку символов S1 и
заменяет её строкой S2. Например, включение в программу директивы 
#define P printf 
позволит набирать на клавиатуре букву P вместо слова printf. 
Эта замена не выполняется внутри текстовых строк (литералов),
символьных констант и комментариев, т.е. действие
директивы #define не распространяется на тексты, ограниченные
кавычками, апострофами и находящимися внутри комментариев. 
Главная функция 
Каждая программа на языке Си должна содержать объявление функции
main(), которая называется главной функцией. Как правило, эта
функция не имеет параметров и не возвращает никакого значения. Для
указания этого факта используется слово void. Таким образом, строка с
именем главной функции обычно имеет вид: 
void main(void) 
или 
void main( ) 
Комментарии 
В языке Си для комментариев используются символы: 

/* — начало комментария; 
*/ — конец комментария. 

Вся последовательность, заключенная между этими символами,


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

Язык программирования С. Управляющие элементы программы,


ветвление, циклы.
Итак, программа всегда представляет собой последовательность операторов,
определяющих необходимые вычисления. Существует два типа
операторов: операторы преобразования данных и операторы
управления работой программы.
Операторы преобразования данных – это операторы присваивания и
произвольные выражения, завершенные символом «;».
*символ «;» - указатель на завершение
о оператора, помогает в определении структуры
п программы.
Итак, составной оператор – это последовательность операторов, заключенная
в фигурные скобки. Если в теле составного оператора присутствуют
операторы определения переменных, то такой составной оператор
называется блоком. Наиболее часто блок используется в качестве тела
функции.
Порядок, в котором осуществляются вычисления в программе, определяют
операторы управления.
Операторы управления работой программы называют управляющими
конструкциями программы.
К ним относятся:
-составные операторы;
-операторы выбора;
-операторы перехода;
-операторы циклов.
- операторы ветвления.

Операторы ветвления.
Простейший из них – условный оператор, имеет сокращенную и полную
формы. Сокращенная форма синтаксически определяется так:
if (выражение_условие) оператор;
Здесь в качестве элемента выражение_условие могут использоваться:
- арифметическое выражение;
- отношение;
- логическое выражение.
Оператор, указанный в условном, выполняется только тогда, когда
выражение_условие оказывается истинным (т.е. при его ненулевом
значении). Если выражение_условие ложно (равно нулю) – будет
выполняться оператор, следующий за условным.
Полная форма условного оператора:
if (выражение_условие)
оператор_1;
else
оператор_2;
Здесь в случае истинности (ненулевое значение) условия выполняется только
оператор 1, при нулевом условии выполняется только оператор 2. 

Другим оператором языка С, предназначенным для реализации


множественного выбора, является переключатель.
Синтаксически переключатель определяется следующей формой:
switch (выражение_условие)
{
case константа_1: операторы_1
case константа_2: операторы_2
...
case константа_n: операторы_n
default: операторы_n+1
}

Здесь следует ввести еще один существующий в языке С оператор


прерывания, обозначаемый через ключевое слово break.
Заметим, константное выражение не может включать переменных или
вызовы функций.
Пример.
Case 3+4: правильно
Case X+Y: неправильно
Оператор default не обязательно должен быть последним.
Поставленный в конце отдельной ветви case, оператор break прерывает
последовательность выполнения записанных операторов и управление
передается на оператор, следующий за закрывающей фигурной скобкой,
ограничивающей «тело» оператора switch.
Оператор break прекращает выполнение ближайшего вложенного
оператора цикла или ветвления. Управление передается оператору,
следующему за заканчиваемым. Одно из назначений этого оператора –
закончить выполнение цикла при присваивании некоторой переменной
определенного значения.
Операторы циклов.
При программировании очень часто возникает потребность многократного
повторения одних и тех же действий. Для упрощения программирования в
этом случае в язык вводятся специальные операторы, называемые
операторами цикла. В языке С существует 3 вида операторов цикла:
-параметрический for;
-с предусловием while;
-с постусловием do.
1. for (выражение_1; выражение_условие; выражение_3)
тело цикла
В качестве тела цикла может выступать простой или составной оператор или
программный блок.
выражение_1 задает начальные условия цикла. Обычно это присвоение
некоторых начальных значений некоторым используемым в теле цикла
переменным.
выражение_условие – это логическое или арифметическое условие,
определяющее окончание цикла (ложь) или его продолжение (истина). В
случае истинного значения вычисляется выражение_3.
выражение_3 задает действия, связанные с изменением значений любых
переменных тела цикла, необходимых для выполнения очередной итерации
Цикл повторяется до тех пор, пока указанное в операторе
выражение_условие дает при вычислении значение истина(не 0).

a) while b) do - while


c) for
Язык программирования С. Указатели и массивы. Адресная
арифметика.
Массивы.
Кроме одиночных переменных, представляющих в программе отдельное
число или символ, в языке С определено понятие массива. Массив – это
совокупность (последовательность) значений одного типа. Для объявления
массива в программе используется следующий синтаксис:
int Array[10]; // Массив из десяти целых чисел
Для доступа к отдельному элементу массива введено понятие индекса – т.е.
номер элемента в массиве, причем индекс массива, состоящего из N
элементов, меняется от 0 до N – 1. Так массив int Arr[3];
содержит элементы Arr[0], Arr[1], Arr[2].
Синтаксическая форма
<тип> <имя> <размер N> … <размер 2><размер 1>;
Как и в предыдущем случае, массив представляется одним фрагментом
памяти, в котором элементы располагаются друг за другом:
int matr [2][3];
matr[0][0] : matr[0][1] : matr[0][2] : matr[1][0] : matr[1][1] : matr[1][2]

При объявлении массивов их можно инициализировать, т.е. присвоить


элементам конкретные значения.
Инициализация – это объединение определения объекта с одновременным
присваиванием ему конкретного значения. Она позволяет изменить формат
определения массива. Например, можно явно не указывать количество
элементов одномерного массива, а только перечислить их начальные
значения в списке инициализации:
double seq [] = {1.0, 2.1, 3.2, 4.3, 5.0};
В данном примере будет определен одномерный массив из пяти элементов,
причем
seq[0]=1.0 seq[1]=2.1 seq[2]=3.2 seq[3]=4.3 seq[4]=5.0
Если в определении массива явно задан его размер, то количество начальных
значений не может быть больше количества элементов в массиве (в таком
случае компилятор выдаст ошибку типа «too many initializers»).

Массивы символов. Массивы символов, содержащие строки, допускают


удобную инициализацию в виде:
char <имя_массива> [размер] = “строка”;
char str [6] = “hello”;

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

Для компьютера все, что находится в памяти, является просто данными –


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

Без помощи указателей невозможно организовать передачу данных между


программами.
Указатель – это переменная, которая содержит адрес (в памяти) другой
переменной, точнее адрес первого байта занимаемого этой переменной
фрагмента памяти.
Указатель может указывать на что угодно в памяти. Более того, с его
содержимым (адресом) можно производить арифметические операции, в
результате чего он будет указывать на другую область памяти, содержащую
другую переменную или другую часть переменной-массива.
Указатель является просто переменной, поэтому прежде чем работать с ним,
его следует объявить. Как и у любой переменной, у указателя имеется имя и
тип данных. Единственное отличие в объявлении указателя состоит в
прибавлении знака операции косвенной ссылки «*» (еще говорят «косвенной
адресации» или «разыменования» или «раскрытия ссылки» или «обращения
по адресу»), говорящего компилятору, что создается указатель.
double *pVal, Value;
int *pNum0, *pNum1;
В первом из примеров создаются две переменные: pVal является указателем
на переменную типа double, а Value – обыкновенной переменной этого типа;
во втором примере объявляются два указателя на целое.
Операция разыменования (*) применяется не только для объявления. Ее
операндом всегда является указатель, а результатом – тот объект, который
адресует указатель операнд. Таким образом, *pNum0 обозначает объект типа
целое (целая переменная), на который указывает pNum0.

int x=100, y = 99, *pNum0; // pNum0 - указатель,


pNum0 = &x; // pNum0 = 0x0012fec3 - адрес в памяти
y = *pNum0; // y = 100 - поменяло значение
В приведенном примере «&» является операцией взятия адреса и в словесной
форме вторая строка примера может быть описана как «указателю pNum0
присвоить значение адреса переменной x».
Следует обратить особое внимание на то, что указатель может ссылаться
только на объект того типа, который задан в его определении. Исключением
являются указатели, в определении которых использован тип void –
отсутствие значения. Такие указатели могут ссылаться на объекты любого
типа, однако к ним нельзя применять операцию разыменования (для void
*pAdr объект по адресу pAdr не определен).
Как и любые другие переменные, объявленный указатель
можно инициализировать. Это действие реализуется операцией присваивания
следующего вида:
double *pVal, Value;
...
pVal = &Value;
Поскольку различные типы данных имеют различную длину, то
существенным является соответствие типов объявленного указателя и
присваиваемого ему значения.

Следует отметить, что операция взятия адреса «&» применима только к


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

Указатель можно инициализировать специальной константой NULL, которая


определена в файле stdio.h, и соответствует заведомо не равному никакому
адресу значению, принимаемому за нулевой адрес.

Язык программирования С. Функции. Передача аргументов в функцию.


Возвращаемое значение.
В С определены три производных типа:
массив
указатель
функция.
Функцию следует рассматривать с двух точек зрения:
функция – это один из производных типов данных,
функция – это минимальный исполняемый модуль программы на языке С.
Все функции в языке С имеют следующий формат определения:
<тип> <имя_функции> (<спецификация параметров>)
<тело функции>
Здесь
<тип> -
 void (для функций, не возвращающих значения),
 обозначение типа возвращаемого функцией значения.
<имя_функции> -
 main для основной (главной) функции программы,
 произвольно выбираемое программистом имя (идентификатор).
<спецификация параметров> -
 это либо пусто,
 либо список формальных параметров, каждый элемент которого имеет
вид:
<обозначение_типа> <имя_параметра>

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


параметров, существует возможность завершения списка параметров запятой
с последующим многоточием «, …». Это означает, что при обращении к
функции может быть указано большее число фактических параметров. Так,
известные функции printf и scanf имеют прототипы:
int printf (const char *format, …);
int scanf (const char *format, …);
Указанные функции позволяют применять теоретически неограниченное
количество фактических параметров. Обязательным является только
параметр char *format – «форматная строка», внутри которой с помощью
спецификаций преобразования определяется реальное количество
параметров, участвующих в обменах.
<тело функции> - это часть определения функции, представляющая
программный блок, т.е. ограниченный фигурными скобками фрагмент текста
на языке С, размещенный непосредственно вслед за заголовком функции.
Особенность С состоит в невозможности внутри тела функции определить
другую функцию: определения функций не могут быть вложенными.
Для завершения (прерывания) работы функции служит оператор return.
Существует две формы этого оператора:
 return; // Возврат без передачи результата
 return <выражение>; // Возврат значения
Первая форма используется для функций, перед именем которой в
определении указан тип void. Для функций этого типа
оператор return можно в тексте программы не писать вовсе – компилятор
встроит его автоматически перед закрывающей фигурной скобкой тела
функции.
<выражение> во второй форме оператора должно иметь тип, совпадающий с
типом функции или допускающий автоматическое преобразование к типу
возвращаемого функцией значения.
Описание функции:
Для корректного обращения к функции сведения о ней должны быть
известны компилятору, т.е. до вызова функции в том же
файле рекомендуется помещать ее описание – прототип:
<тип> <имя_функции>(<спецификация параметров>);
В отличие от заголовка функции в ее прототипе могут не указываться имена
формальных параметров, например эквивалентны
double Func(int n, double x);
double Func(int, double);

Вызов функции:
Вызов функции реализуется выражением с операцией «круглые скобки». При
этом используется следующий синтаксис:
<обозначение_функции> (<список фактических параметров>);
Обычно в качестве <обозначение_функции> выступает ее имя. Кроме того,
функцию можно обозначить, разыменовав указатель на нее. Этот способ
будет рассмотрен в дальнейшем.
<список фактических параметров>, называемых по аналогии с
математикой аргументами – это список выражений, количество которых
равно числу формальных параметров функции (исключение составляют
функции с переменным количеством параметров). Соответствие между
формальными и фактическими параметрами устанавливается по их
взаимному расположению в списках.
Между формальными и фактическими параметрами должно быть
соответствие по типам. Если типы не совпадают – включается механизм
преобразования типов (если проведение такого преобразования допустимо).

Передача параметров:
Синтаксис языка С предусматривает только один способ передачи
параметров – передачу по значениям. Это означает, что формальные
параметры функции локализованы в ней и не доступны вне ее определения,
никакие операции над формальными параметрами в теле функции не
изменяют значений фактических параметров.
Передача параметров по значению предусматривает следующие шаги:
1. При вызове функции выделяются участки памяти для ее формальных
параметров. Если параметром является массив, то формируется указатель на
начало этого массива и он служит представлением массива-параметра в теле
функции.
2. Вычисляются значения выражений, использованных в качестве
фактических параметров при вызове функции.
3. Вычисленные значения заносятся в участки памяти, выделенные для
формальных параметров функции.
4. В теле функции выполняется обработка с использованием значений
внутренних объектов-параметров, и результат передается в точку вызова
функции как возвращаемое ею значение.
5. После выхода из функции освобождается память, выделенная для ее
формальных параметров.

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


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

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


объявления типов.
Из базовых типов данных можно формировать производные типы, к
которым относятся указатели, массивы, функции, структуры и объединения.
Данные базовых типов (int, float, …) считаются скалярными данными.
Массивы и структуры являются агрегирующими типами данных в отличие от
объединений и скалярных данных, которые относятся
к неагрегирующим типам. Т.е. агрегирующий тип включает несколько
компонентов, например, массив в общем случае состоит из совокупности
элементов. Для агрегирующего типа данных выделяется такое количество
памяти, чтобы разместить одновременно значения всех его элементов. Кроме
массивов, состоящих из однородных элементов, в языке С определен такой
агрегирующий тип данных как структура.
Структура – это совокупность переменных, объединенных одним именем,
представляющая общепринятый способ хранения информации.
Структуры помогают в организации сложных данных (особенно в больших
программах) поскольку позволяют группу связанных между собой элементов
трактовать не как множество отдельных элементов, а как единое целое.
Например, состав зачетной ведомости группы может быть представлен
следующими данными:
- номер в списке (int);
- фамилия имя отчество студента (char[]);
- оценка (int).
Структуры могут копироваться, над ними могут выполняться операции
присваивания, их можно передавать функциям в качестве аргументов, а
функции могут возвращать их в качестве результатов.
Для определения таких данных в программе и используется структурный тип
данных, для описания которого используется ключевое слово struct:
struct InspectSheet {
int Number;
char Names[40];
int Mark;
};
1. В общем случае формат определения структурного типа таков:
struct <имя_структурного_типа>
{<определения_элементов>};
struct – спецификатор структурного типа.
<имя_структурного_типа> - идентификатор, произвольно выбираемый
программистом.
<определения_элементов> - совокупность одного или более описаний
объектов, каждый из которых служит прототипом для элементов вводимого
структурного типа.
Следует обратить внимание на то, что <имя_структурного_типа> в
некотором смысле эквивалентно типу (int, double и т.п.), а не имени
переменной.
2. Еще одним способом определения структур является использование
препроцессорной директивы typedef:
typedef struct {<определения_элементов>}
<обозначение_структурного_типа>;
В этом случае используется так называемый безымянный структурный тип.
При определении структурного типа при помощи typedef можно указывать
еще и имя типа:
typedef struct COMPLEX
{
double real;
double imag;
} complex;
Это позволяет впоследствии использовать как имя структурного типа, так и
его обозначение. Правильно будет:
complex first; // Определяем переменную типа complex
struct COMPLEX first; // Определяем переменную типа complex
Теперь рассмотрим правила определения элементов в структурах. Однако
имеется и существенное отличие: при определении типа не создается
соответствующий объект в памяти, а это значит - элементы структуры нельзя
инициализировать. Только после определения типа можно определять
соответствующую программную переменную. Однако допустимо
определение структур одновременно с их описанием:
struct List {//Определяем тип данных – обычно в заголовочном файле
int n;
char Name[40];
} Var1, Var2, Var3;
Размер памяти, выделяемый для размещения объектов структурного типа
можно получать при помощи операции
sizeof(<имя структуры>)
sizeof(<имя структурного типа>)
………………………………………………………………………….
Cтруктуры - это составной объект, в который входят элементы любых типов,
за исключением функций. В отличие от массива, который является
однородным объектом, структура может быть неоднородной. Тип структуры
определяется записью вида:
struct { список определений }
В структуре обязательно должен быть указан хотя бы один компонент.
Определение структур имеет следующий вид:
тип-данных описатель;
где тип-данных указывает тип структуры для объектов, определяемых в
описателях. В простейшей форме описатели представляют собой
идентификаторы или массивы.
Пример:
struct { double x,y; } s1, s2, sm[9];
struct { int year;
char moth, day; } date1, date2;
Переменные s1, s2 определяются как структуры, каждая из которых состоит
из двух компонент х и у. Переменная sm определяется как массив из девяти
структур. Каждая из двух переменных date1, date2 состоит из трех
компонентов year, moth, day. >p>Существует и другой способ
ассоциирования имени с типом структуры, он основан на использовании тега
структуры. Тег структуры аналогичен тегу перечислимого типа. Тег
структуры определяется следующим образом:
struct тег { список описаний; };
где тег является идентификатором.
В приведенном ниже примере идентификатор student описывается как тег
структуры:
struct student { char name[25];
int id, age;
char prp; };
Тег структуры используется для последующего объявления структур данного
вида в форме:
struct тег список-идентификаторов;
Пример:
struct studeut st1,st2;
Использование тегов структуры необходимо для описания рекурсивных
структур. Ниже рассматривается использование рекурсивных тегов
структуры.
struct node { int data;
struct node * next; } st1_node;
Тег структуры node действительно является рекурсивным, так как он
используется в своем собственном описании, т.е. в формализации указателя
next. Структуры не могут быть прямо рекурсивными, т.е. структура node не
может содержать компоненту, являющуюся структурой node, но любая
структура может иметь компоненту, являющуюся указателем на свой тип,
как и сделано в приведенном примере.
Доступ к компонентам структуры осуществляется с помощью указания
имени структуры и следующего через точку имени выделенного компонента,
например:
st1.name="Иванов";
st2.id=st1.id;
st1_node.data=st1.age;
………………………………………………………………………………

Язык программирования С. Указатели на функции и структуры


данных.

Указатели на функцию.
Рассмотрим теперь вопрос о том, почему в языке С функция введена как один
из производных типов. Необходимость в таком типе связана с задачами, в
которых функция (или ее адрес) должна выступать в качестве параметра
другой функции или в качестве значения, возвращаемого другой функцией. В
этом случае используется «указатель на функцию».
Самый употребительный «указатель на функцию» – это ее имя
(идентификатор). Идентификатор <имя_функции> в ее определении и в ее
прототипе подобен имени массива и является указателем-константой. Он
навсегда связан с определяемой функцией и не может быть «настроен» на
что-либо иное, чем ее адрес.
«Указатель на функцию» (как переменная) вводится отдельно от определения
и прототипа какой-либо функции. Для этих целей используется конструкция:
<тип> (*<имя_указателя>)(<спецификация_параметров>);
где <тип> - определяет тип возвращаемого функцией значения;
<имя_указателя> - идентификатор, произвольно выбранный программистом;
<спецификация_параметров> - определяет состав и типы параметров
функции.
В отличие от имени функции указатель func0 является переменной, т.е. ему
можно присваивать значения других указателей, определяющих адреса
функций программы. Важно, что тип указателя-переменной должен
полностью соответствовать типу функции, адрес которой ему присваивается.

Указатели-переменные могут группироваться в массивы, при этом


используется следующая форма их определения:
<тип> (*<имя_массива>[<размер>])(<спецификация_параметров>);
где <тип> - определяет тип возвращаемых функциями значений;
<имя_массива> - идентификатор, произвольно выбранный программистом;
<размер> - количество элементов в массиве;
<спецификация_параметров> - определяет состав и типы параметров
функций.
Пример:
int (*farray[4]) (int);
где farray - массив из 4-x указателей на функции, каждому из которых можно
присвоить адрес функции с прототипом <int имя_функции (int)>.

Указатели на структуры.
Указатели на структуры определяются точно так же, как и указатели на
другие типы данных.
struct COMPLEX *pC;
complex *pcmpl;
Можно вводить указатели и в качестве обозначений структур, т.е.
struct birth {
char Where[40];
struct date When;
} *pB1, *pB2;
- это для одновременного описания структуры и ее определения (создаются
указатели pB1 и pB2.
Возможно также:
typedef struct COMPLEX
{
double real;
double imag;
} complex, *ptr_comp;
и тогда для определения переменной типа указатель на структуру:
ptr_comp px [12];
complex * px [12];
- одинаково определяют массивы из 12-ти указателей на структуру complex.

Язык программирования С. Файловый ввод-вывод. Бинарные и


текстовые файлы.
Перед тем, как выполнять операции ввода и вывода в файловый поток,
нужно его открыть с помощью функции f open (). Эта функция имеет
следующий прототип:
FILE *fopen(const char *filename, const char *mode);
Она открывает файл с именем filename и связывает с ним поток. Функция f
open () возвращает указатель, используемый для идентификации потока в
последующих операциях. Параметр mode является строкой, задающей
режим, в котором открывается файл

R Файл открывается только для чтения.

W Файл создается для записи. Если файл с этим именем уже


существует, он будет перезаписан.

А Режим добавления записей (Append): файл открывается для записи


в конец (начиная с EOF) или создается для записи, если он еще не
существует.

r+ Существующий файл открывается для обновления (считывания и


записи).

w+ Создается новый файл для обновления (считывания и записи).


Если файл с этим именем уже существует, он будет перезаписан.
а+ Файл открывается для добавления (т.е. записывания, начиная с
EOF); если файл еще не существует, он создается.

Чтобы указать, что данный файл открывается или создается как текстовый,
добавьте символ t в строку режима открытия (например, rt, w+t и т.д.).
Аналогичным образом, чтобы сообщить, что файл открывается или создается
как бинарный, добавьте в строку режима открытия символ b (например, wb,
a+b и т.д.). Функция f open () также позволяет вставить символы t или b
между буквой я символом (+) в строке режима открытия (например, строка
rt+ эквивалентна строке r+t). Когда файл открывается для обновления, можно
вводить и выводить данные в результирующий поток. Однако вывод не
может осуществляться непосредственно после ввода, если ему не
предшествует вызов функции fseek () или rewind (). В случае успеха fopen()
возвращает указатель на открытый поток; в случае ошибки - указатель
NULL.
Например:
FILE* stream = fopen("Install.dat", "r");
Указатель на открытый файловый поток используется во всех последующих
функциях работы с потоком.
По завершении работы с потоком он должен быть закрыт. Это
осуществляется с помощью функции f close (), которая имеет следующий
прототип:
int f close (FILE *stream) ;
Все буферы, связанные с потоком, освобождаются перед закрытием потока.
В случае успеха fclose() возвращает 0; в случае ошибки - EOF. Если ваша
программа не закрывает поток с помощью явного вызова fclose (). то он
закрывается автоматически по ее завершению.
Рассмотрим теперь функции, осуществляющие ввод-вывод в файловый
поток.
Функция fgetc() имеет следующий прототип:
int fgetc(FILE *stream);
Она осуществляет ввод символа из файлового потока stream. В случае успеха
функция преобразует прочитанный символ в тип int без учета знака. Если
делается попытка прочесть конец файла иди произошла ошибка, функция
возвращает EOF. Как видим, эта функция во всем аналогична функции getc(),
за исключением того, что чтение осуществляется из файлового потока. Более
того, как мы уже отмечали, на самом деле getc () - это макрос, реализованный
с помощью f getc (). То же самое относится и к следующим функциям: все
они имеют уже рассмотренные нами аналоги.
Функция fputc() имеет следующий прототип:
int fputc(int с, FILE *stream);
Она осуществляет вывод символа в файловый поток и во всем аналогична
функции putc ().
Функция f gats () имеет следующий прототип:
char *fgets(char *s, int n, FILE *stream);
Она осуществляет чтение строки символов из файлового потока в строку s.
Функция прекращает чтение, если прочитано n - 1 символов или встретился
символ перехода на новую строку ' \n'. Если встретился символ перехода на
новую строку, он сохраняется в переменной в. В обоих случаях в
переменную s добавляется символ '\0 ', который является признаком
завершения строковой переменной. В случае успеха функция возвращает
строку, на которую указывает параметр s. Если делается попытка чтения
конца файла или произошла ошибка, она возвращает null.
Функция f puts () имеет следующий прототип:
int fputs(const char *s, FILE *stream);
Она осуществляет вывод строки в файловый поток. Символ перехода на
новую строку не добавляется, и завершающий строку нуль-символ в
файловый поток не копируется. В случае успеха fputs () возвращает
неотрицательное значение. В противном случае она возвращает EOF.
Функция fscanf () имеет следующий прототип;
int fscanf(FILE *stream,
const char *format[, address, ...]);
Она во всем аналогична функции scanf (), за исключением того, что
форматированный ввод осуществляется не со стандартного устройства ввода,
а из файлового потока.
Функция fprintf () имеет следующий прототип:
int fprintf(FILE *stream,
const char *format[, argument, ...]);
Она во всем аналогична функции printf (), но осуществлят форматированный
вывод не на стандартное устройство вывода, а в файловый поток.
Функция feof () является на самом деле макросом и позволяет осуществлять
проверку на достижение символа конца файла при операциях ввода-вывода.
Она имеет следующий прототип:
int feof(FILE *stream);
Она возвращает ненулевое значение, если был обнаружен конец файла при
последней операции ввода в поток stream и 0, если конец файла еще не
достигнут.
Рассмотрим пример файлового ввода и вывода:
#include <stdio.h>
int main ()
{
FILE *in, *out;
if ({in = fopen("C;\\AUTOEXEC.BAT", "rt")) = NULL)
{
fprintf(stderr, "Cannot open input file.\n");
return 1;
}
if ((out = fopen
("C:\\AUTOEXEC.BAK", "wt")) = NULL)
{
fprintf(stderr,
"Cannot open output file.\n");
return 1;
}
while (!feof{in))
fputc(fgetc(in), out); fclose(in); fclose(out); fprintf{stderr, "The file is copied
successfully.\n");
return 0;
}
Две следующие функции предназначены для осуществления
неформатированного ввода и вывода в файловые потоки. Функция f read ()
имеет следующий прототип:
size_t
fread(void *ptr, size_t size, size_t n, FILE *stream);
Эта функция считывает из потока stream в буфер, указанный параметром ptr.
n блоков данных, каждый из которых содержит size байтов. В случае успеха
функция возвращает число прочитанных блоков. Если прочитан конец файла
или произошла ошибка, она возвращает число полностью прочитанных
блоков или 0.
Функция f write () имеет следующий прототип:
size_t
fwrite(const void *ptr, size_t size, size_t n, FILE «stream);
Она записывает в выходной поток stream из буфера, указанного параметром
ptr, n блоков данных, каждый из которых содержит size байтов. В случае
успеха функция возвращает число записанных блоков. В случае ошибки, она
возвращает число полностью записанных блоков или 0.
Язык программирования С. Работа со строками.

Объявление строк в C
Строки реализуются посредством массивов символов. Поэтому объявление
ASCII строки имеет следующий синтаксис:
char имя[длина];
Объявление строки в С имеет тот же синтаксис, что и объявление
одномерного символьного массива. Длина строки должна представлять собой
целочисленное значение (в стандарте C89 – константа, в стандарте C99
может быть выражением). Длина строки указывается с учетом одного
символа на хранение завершающего нуля, поэтому максимальное количество
значащих символов в строке на единицу меньше ее длины. Например, строка
может содержать максимально двадцать символов, если объявлена
следующим образом:
char str[21]; Инициализация строки в С осуществляется при ее объявлении,
используя следующий синтаксис:
char str[длина] = строковый литерал;
Строковый литерал – строка ASCII символов заключенных в двойные
кавычки. Примеры объявления строк с инициализацией:
char str1[20] = "Введите значение: ", str2[20] = "";
Пример:
const char message[] = "Сообщение об ошибке!";
Работа со строками в С
Так как строки на языке С являются массивами символов, то к любому
символу строки можно обратиться по его индексу. Для этого используется
синтаксис обращения к элементу массива, поэтому первый символ в строке
имеет индекс ноль. Например, в следующем фрагменте программы в строке
str осуществляется замена всех символов 'a' на символы 'A' и наоборот.
for(int i = 0; str[i] != 0; i++)
{
    if (str[i] == 'a') str[i] = 'A';
    else if (str[i] == 'A') str[i] = 'a';
}
Массивы строк в С
Объявление массивов строк в языке С также возможно. Для этого
используются двумерные массивы символов, что имеет следующий
синтаксис:
char имя[количество][длина];
Первым размером матрицы указывается количество строк в массиве, а
вторым – максимальная (с учетом завершающего нуля) длина каждой строки.
Например, объявление массива из пяти строк максимальной длиной 30
значащих символов будет иметь вид:
char strs[5][31];
При объявлении массивов строк можно производить инициализацию: 
char имя[количество][длина] = {строковый литерал №1, ... строковый литерал
№N};
Число строковых литералов должно быть меньше или равно количеству
строк в массиве. Если число строковых литералов меньше размера массива,
то все остальные элементы инициализируются пустыми строками. Длина
каждого строкового литерала должна быть строго меньше значения длины
строки (для записи завершающего нуля).
Например:
char days[12][10] = {
    "Январь", "Февраль", "Март", ”Апрель", "Май",
    "Июнь", "Июль", "Август", "Сентябрь","Октябрь",
    "Ноябрь", "Декабрь"
};
При объявлении массивов строк с инициализацией допускается не указывать
количество строк в квадратных скобках. В таком случае, количество строк в
массиве будет определено автоматически по числу инициализирующих
строковых литералов.
Например, массив из семи строк:
char days[][12] = {
    "Понедельник", "Вторник", "Среда", "Четверг",
    "Пятница", "Суббота", "Воскресенье"
};
Функции для работы со строками в С
Все библиотечные функции, предназначенные для работы со строками,
можно разделить на три группы:
1. ввод и вывод строк;
2. преобразование строк;
3. обработка строк.
Ввод и вывод строк в С
Для ввода и вывода строковой информации можно использовать функции
форматированного ввода и вывода (printf и scanf). Для этого в строке
формата при вводе или выводе строковой переменной необходимо указать
спецификатор типа %s. Например, ввод и последующий вывод строковой
переменной будет иметь вид:
char str[31] = "";
printf("Введите строку: ");
scanf("%30s”,str);
printf("Вы ввели: %s”,str);
Недостатком функции scanf при вводе строковых данных является то, что
символами разделителями данной функции являются:
1. перевод строки,
2. табуляция;
3. пробел.
Поэтому, используя данную функцию невозможно ввести строку,
содержащую несколько слов, разделенных пробелами или табуляциями.
Например, если в предыдущей программе пользователь введет строку:
"Сообщение из нескольких слов", то на экране будет выведено только
"Сообщение".
Для ввода и вывода строк в библиотеке stdio.h содержатся
специализированные функции gets и puts.
Функция gets предназначена для ввода строк и имеет следующий заголовок:
char * gets(char *buffer);
Между тем использовать функцию gets категорически не рекомендуется,
ввиду того, что она не контролирует выход за границу строки, что может
произвести к ошибкам. Вместо нее используется функция fgets с тремя
параметрами:
char * fgets(char * buffer, int size, FILE * stream);
где buffer - строка для записи результата, size - максимальное количество
байт, которое запишет функция fgets, stream - файловый объект для чтения
данных, для чтения с клавиатуры нужно указать stdin. Эта функция читает
символы со стандартного ввода, пока не считает n - 1 символ или символ
конца строки, потом запишет считанные символы в строку и добавит нулевой
символ. При этом функция fgets записывает в том символ конца строки в
данную строку, что нужно учитывать.
Функция puts предназначена для вывода строк и имеет следующий
заголовок:
int puts(const char *string);
Простейшая программа: ввод и вывод строки с использованием функций
fgets и puts будет иметь вид:
char str[102] = "";
printf("Введите строку: ");
fgets(str, 102, stdin);
printf("Вы ввели: "); 
puts(str);
Для считывания одного символа можно использовать функцию fgetc(FILE *
stream). Она считывает один символ и возвращает значение этого символа,
преобразованное к типу int, если же считывание не удалось, то возвращается
специальная константа EOF, равная -1. Функция возвращает значение -1 для
того, чтобы можно было обрабатывать ситуацию конца файла, посимвольное
чтение до конца файла можно реализовать следующим образом:
int c;
while ((c = fgetc(stdin)) != EOF) {
    // Обработка символа
}
Для вывода одного символа можно использовать функцию  int fputc(int c,
FILE *stream);.
Помимо функций ввода и вывода в потоки в библиотеке stdio.h присутствуют
функции форматированного ввода и вывода в строки. Функция
форматированного ввода из строки имеет следующий заголовок:
int sscanf(const char * restrict buffer, const char * restrict string, [address] ...); 
Функции форматированного вывода в строку имеют следующие заголовки:
int sprintf(char * restrict buffer,
const char * restrict format, [argument] ...);
int snprintf(char * restrict buffer, size_t maxsize,
const char * restrict format, [argument] ...);

Язык программирования С. Директивы препроцессова. Интерфейс с


командной строкой UNIX.
Препроцессор — это специальная программа, являющаяся частью
компилятора языка Си. Она предназначена для предварительной обработки
текста программы. Препроцессор позволяет включать в текст программы
файлы и вводить макроопределения.
Работа препроцессора осуществляется с помощью специальных директив
(указаний). Они отмечаются знаком решетка #. По окончании строк,
обозначающих директивы в языке Си, точку с запятой можно не ставить.
Основные директивы препроцессора
#include — вставляет текст из указанного файла
#define — задаёт макроопределение (макрос) или символическую константу
#undef — отменяет предыдущее определение
#if — осуществляет условную компиляцию при истинности константного
выражения
#ifdef — осуществляет условную компиляцию при определённости
символической константы
#ifndef — осуществляет условную компиляцию при неопределённости
символической константы
#else — ветка условной компиляции при ложности выражения
#elif — ветка условной компиляции, образуемая слиянием else и if
#endif — конец ветки условной компиляции
#line — препроцессор изменяет номер текущей строки и имя
компилируемого файла
#error — выдача диагностического сообщения
#pragma — действие, зависящее от конкретной реализации компилятора.

UNIX
В операционной системе UNIX основными средствами взаимодействия
пользователя с системой являются клавиатура и экран монитора,
работающий в текстовом режиме. Вводимый пользователем текст
немедленно отображается на мониторе соответствующими знаками, однако
может и не отображаться (например, в случае ввода пароля). Для управления
вводом используются некоторые нетекстовые клавиши на
клавиатуре: Backspace (он же «Забой») — для удаления последнего
введенного символа или Enter — для передачи команды системе. Нажатие на
эти клавиши не приводит к отображению символа, вместо этого вводимый
текст обрабатывается системой тем или иным способом — эти клавиши и их
комбинации объединяют понятием управляющие символы.
Текстовый принцип работы с машиной позволяет отвлечься от конкретных
частей компьютера, вроде системной клавиатуры и видеокарты с монитором,
рассматривая единое конечное устройство, посредством которого
пользователь вводит текст и передает его системе, а система выводит
необходимые пользователю данные и сообщения. Такое устройство
называется терминалом. В общем случае терминал — это точка входа
пользователя в систему, обладающая способностью передавать текстовую
информацию. Терминалом может быть отдельное внешнее устройство,
подключаемое к компьютеру через порт последовательной передачи данных
(«COM-порт»). В роли терминала может работать (с некоторой поддержкой
со стороны системы) и программа (например, xterm или ssh).
Свойство терминала передавать только символьную информацию приводит к
тому, что некоторые из передаваемых символов должны восприниматься не
как текстовые, а как управляющие (например, символы, возвращаемые
клавишами Backspace и Enter). На самом деле управляющих символов
больше: часть из них предназначена для экстренной передачи команд
системе, часть — для редактирования вводимого текста. Многие из этих
символов не имеют специальной клавиши на клавиатуре, поэтому их
необходимо извлекать с помощью клавиатурного модификатора Ctrl.
Проблема в том, что на клавиатуре может быть так много разных
нетекстовых клавиш, что на них не хватает ограниченного количества разных
управляющих символов. Поэтому большинство нетекстовых клавиш
возвращают так называемую управляющую последовательность, которая
начинается управляющим символом, за которым следует строго
определенное число обычных символов.
Основная среда взаимодействия с UNIX — командная строка. Суть её в том,
что каждая строка, передаваемая пользователем системе, — это команда,
которую та должна выполнить. Пока не нажата клавиша Enter, строку можно
редактировать, затем она отсылается системе.
Команды интерпретируются и выполняются специальной программой —
командной оболочкой (или «shell», по-английски). Через командную
оболочку производится управление пользовательскими процессами — для
этого используются средства межпроцессного обмена, описанные ранее (см.
«Межпроцессное взаимодействие»).
Командная оболочка непосредственно связана с терминалом, через который
осуществляется передача управляющих последовательностей и текста. На
рисунке Рисунок 2.1, «Интерфейс командной строки» представлена общая
схема взаимодействия пользователя с системой при использовании
командной строки.

#include <stdio.h>
#include <malloc.h>

mass = (int**)malloc(n * sizeof(int));


for (i = 0; i < n; i++)
{
mass[i] = (int*)malloc(m * sizeof(int));
}

FILE* fp; 
fp = fopen("matr.txt", "r+"); 

if (fp == NULL) 

puts("error"); 
return; 

Int k= 0;
While (!feof(fp))
{
Fscanf (“%d”, n);
K++
}
Int n = 0;
While (!feof(fp))
{
S = fgets(fp);
N++;
}

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



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

fscanf(fp, "%f ", &mass[i][j]); 

printf("\nМатрица \n"); 

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



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

printf("%f\t", mass[i][j]); 

printf("\n"); 

fclose(fp);

Int sum = 0;
Int max = 0;
For (int j = 0; j< n; j++);
{
Sum = 0;
}
For (int I =0; I <n; i++)
{
Sum = sum+ mass [i][j];
if (max > mass[i][j])
{
max = mass [i][j];
}
}
FILE* fp; 
fp = fopen("matr.txt", "r+"); 

if (fp == NULL) 

puts("error");