Базова обробка командного рядка

Типи даних

Тип даних визначає необхідне місце в пам’яті для запису цього даного, формат його представлення, а також множину операцій, які можуть виконуватись над даним.

Мова С строго типізована. Це означає, що:

· кожна змінна має тип і кожен тип строго визначений;

· всі привласнення, явні або виконувані при передаачі параметрів у процесі виклику функцій , перевіряються на відповідність типів.

Є 5 базових типів даних: символьний, цілочисельний, речовинний з одинарною точністю, речовинний з подвійною точністю і void. Значення типу char використовуються для зберігання ASCII-кодів або будь-яких 8-бітових величин. Змінні типу int використовуються для зберігання цілих величин. Змінні типу float і double містять дійсні числа.

Тип void має три призначення. Перше - вказівка про неповернення значення функцією. Друге - вказівка про неотримання параметрів функцією. Третє - створення покажчиків, що нетипізуються.

С підтримує дещо інших типів, включаючи структури, об'єднання, бітові поля, переліки і типи, визначувані користувачем. Їх призначення розкриватиметься у міру вивчення матеріалу.

2.3 Змінні

Дані, використовувані програмою, можуть бути незмінними (константи) або такими, що змінюються в процесі виконання програми (змінні). Кожне дане має бути віднесене до певного типу. Якщо тип константи можна встановити за її значенням, то кожна змінна вимагає опису.

Опис змінних

Опис змінної має відповідати такому правилу:

Тип ідентифікатор [=значення], ідентифікатор[=значення]…];

Тут квадратні дужки є метасимволами. Конструкція, узята в квадратні дужки, не повинна бути обов’язково присутньою у визначенні.

З синтаксису опису виходить, що можна одночасно описати декілька змінних одного типу, а також при бажанні їх проініціалізувати. Нижче наведені приклади описів:

char ch,ch1=’*’;

int d=5,e,f=-1;

float fl;

double d1=0.5,d2;

Для описів в програмі не має певного місця. Єдина вимога – опис змінної має передувати її використовуванню.

Область видимості змінних

Кожна змінна оголошується у середині блока, який і визначає її область видимості. Блок починається з відкриваючої фігурної дужки і закінчується закриваючою фігурною дужкою. Якщо змінна є формальним параметром функції, то вона належить блоку, що є тілом методу.

Розрізняють локальні, глобальні, регістрові, зовнішні і статичні змінні.

Локальні змінні. Змінні, оголошені усередині блоку, локальні по відношенню до цього блоку, тобто вони видіми в цьому блоці. Локальні змінні запам'ятовуються в стеку разом адресами повернення з функції і стек динамічно то росте, то зменшується у міру того, як оператори викликають функції і відбувається повернення з них. Оскільки область дії локальних змінних обмежена областю функції, в яких вони оголошені, то дві функції можуть безконфліктно оголошувати дві змінні з однаковими іменами.

Будь-яка функція на початку роботи виділяє пам'ять в стеку для запам'ятовування своїх змінних. Ця пам'ять існує до тих пір, поки ця функція активна. Після свого повернення функція звільняє пам'ять і дані, що все зберігаються там, втрачаються.

Глобальні змінні існують протягом всього життєвого циклу програми і видимі в усіх точках програми. Вони запам'ятовуються в сегменті даних програми і займають пам'ять незалежно від те, потрібні вони чи ні.

Регістрові змінні запам'ятовуються безпосередньо в регістрі процесора і обробляються інструкціями в машинних кодах. Використання таких змінних може значно підвищити швидкодію програми.

Для оголошення регістрових змінних використовується специфікатор класу пам'яті register:

register int c;

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

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

Змінні, оголошені поза модулем, в якому вони використовуються, називаються зовнішніми.

Для оголошення зовнішніх змінних використовується специфікатор класу пам'яті extern.

Статичні змінні. Якщо локальні змінні після завершення роботи блоку втрачаються, а їх значення потрібно зберегти, то використовуються змінні типу static.

При оголошенні такої змінної її потрібно ініціалізувати:

static int c=5;

 

2.4 Операції мови С

Якщо вираз містить декілька операцій, то порядок виконання дій визначається пріоритетом цих операцій. Крім пріоритету операції характерізуються кількістю лперандів (одномісні, двомісні, тримісні), а також асоциативністю – порядкои виконання при однакових пріоритетах (зліва направо або справа наліво). У навеленій нижче таблиці представлені всі операції мови С в порядку убування пріоритету.

Таблиця 1

Пріо-рітет Знак операції Число опер андів Асо ціат ивні сть Назва Приклади

 

[]   Індексні дужки arr1 [ і ], агг2 [j+1 ]
( )   Виклик функції sin ( х), а * ( b + з )
! Логічне НЕ !(а>b)
~ Побітове НЕ ~ а
++ -- Приріст, зменшення на 1 і ++, j - -
( )   Приведення типів b = ( bуtе )
+ - Зміна знака а=-b
* Множення а*b
/ Ділення а/b
% Остача а % b ( а і b - цілі)
+ Додавання а + b
- Віднімання а-b
<<>> Зрушення вліво, управо а >> n, а << n (а і n - цілі)
>>>   Зрушення із заповненням 0 а >>> n (а і n - цілі)
< <= > >= Операції відношення а < b, а > b, а >= b, а <= b
== != Операшї відношення а == b, а!=b
& Побітове І а & b ( а і b - цілі)
^ Побітове, що виключає, АБО а ^ b ( а і b - цілі)
  | Побітове АБО а | b ( а і b - цілі)
&& Логічне І а&&b
|| Логічне АБО а||b
?: Операція «?» х > у ? 5 : 6
= Привласнення а = b
+= _= *= /= %= != ^= <<= >>= >>>= Виконання бінарної операції і привласнення а += b (аналогічно а = а + b ), а /= b ( аналогічно а = а / b )

 

2.5 Структура простої С програми

Програма складається з сукупності однієї або більш функцій, одна з яких повинна називатися main(). Заголовок містить операторів (директиви) препроцесора, наприклад, #include і імяфункциі. Ім'я функції можна дізнатися по круглих дужках, які можуть бути порожніми. Тіло функції поміщене у фігурні дужки і складається з ряду операторів, кожний з яких закінчується крапкою з комою:

Заголовок

-----------------------------------------------

#include<stdio.h>

int main(void)

-----------------------------------------------

Тіло

-----------------------------------------------

{

int q;

q=1;

printf(“q=%d \n”,q);

return 0;

}

------------------------------------------------

В даному прикладі тіло програми містить оператора оголошення, що оголошує ім'я і тип використовуваної змінної. Крім того, присутній оператор привласнення, що привласнює значення змінної. Потім слідує оператор друку, який викликає функцію printf(). Функція main() закінчується оператором повернення.

Коротко кажучи, проста програма ANSI C повинна мати наступний формат:

#include<stdio.h>

int main(void)

{

Операторы

return 0;

}

 

2.6 Організація введення-виведення

Мова С має могутню бібліотеку функцій введення-висновку (есліне наймогутнішу серед всіх мов програмування). Розглянемо дві з них - printf() і scanf().

Функція printf()

Формат функції:

printf(“Управляюча строка”, список аргументів);

· Управляюча строка - рядок символів, що показує, як повинні бути надруковані параметри. У рядку, що управляє, міститься інформація двох різних видів:символы, печатаемые текстуально;

· ідентифікатори даних, звані також специфікаторами перетворення.

· Список аргументів - це перераховані через кому друковані параметри, які можуть бути:

· змінними;

· константами;

· виразами, що обчислюються тут же при виведенні на друк.

Кожному аргументу із списку повинна відповідати одна специфікація перетворення, відповідна типу даного:

%d – для цілих чисел;

%f – для дійсних чисел;

%c – для виведення символа;

%s – для виведення рядка.

Наприклад:

int i=5;

char symbol=’A’;

float pi=3.14159;

char *str=”Hello!”;

printf(“%d %c %f %s”,i,symbol,pi,str);

 

Функція scanf()

Функція scanf() приймає значення з консолі (клавіатури) до натиснення клавіші Enter, перетворить одержані символи і значення записує за адресою змінної.

Формат функції:

scanf(%специфікатор_типу”,&им’я_змінної>)

Якщо вам потрібно ввести деяке значення і привласнити його змінною одного з базових типів, то перед ім'ям змінної потрібно ставити &. Якщо ви хочете ввести значення строкової змінної, використати & не потрібно.

Наприклад:

int a;

float b;

char s1,s2[10];

printf(“Enter a: “);

scanf(“%d”,&a);

printf(“Enter b: “);

scanf(“%f”,&b);

printf(“Enter symbol: “);

scanf(“%c”,&s1);

printf(“Enter string: “);

scanf(“%s”,s2);

2.7 Компіляція в системі Linux

Хай, наприклад, за допомогою текстового редактора біл підготовлений наступний код програми :

#include<stdio.h>

int main()

{

printf(“This is my first C-program!”);

return 0;

}

 

Файли, що містять код програми, повинні мати розширення .с. Збережемо нашу програму під ім'ям myprg.c.

Щоб перевести код програми в машинний код, необхідно виконати компіляцію програми. В результаті буде одержаний виконуваний файл.

Наприклад, щоб скомпілювати програму myprg, необхідно ввести команду:

gcc myprg.c

Якщо в програмі немає синтаксичних помилок, то в результаті виконання цієї команди буде одержаний файл а.out. Це виконуваний файл, що містить результат компіляції даної програми. Для запуску на виконання цього файлу введіть команду:

a.out

В результаті роботи програми на екрані з'явиться:

This is my first C-program!

Якщо необхідно зберегти исполняемій файл (а.out), перейменуйте його. Інакше, після виконання наступної компіляції якої-небудь програми, він буде замінений новим файлом а.out.

Компілятор gcc також створює файл об'єктного коду, що має те ж саме основне ім'я, що і початковий файл, і розширення .о ( у нашому випадку файл об'єктного коду називатиметься myprg.o). Але компонувальник після створення виконуваної програми цей файл видаляє. Проте, якщо початкова програма складається з декількох файлів початкового коду, файли об'єктного коду зберігаються. Трохи пізніше ми детально розглянемо роботу з програмами, що складаються з декількох файлів.

Ви можете зіткнутися з проблемою, що полягає в тому, що вікно, в якому виконується програма, зникає після закінчення роботи програми. Якщо таке відбувається, ве можете зробити припинення виконання програми, що припиняється при натисненні клавіші Enter. Для цього в кінці програми перед оператором return додайте виклик функції getch(). Ця функція считиваєткод натиснутої клавіші, тому програма робить паузу до тих пір, поки ви не натиснете Enter.

 

2.8 Організація розгалужень в програмі

Усі дії в програмі виконують оператори. Найпростіший оператор – оператор присвоювання ми вже розглядали раніше у зв’язку з описом змінних.

Якщо оператори завжди виконуютьмя в одній і тій же послідовності, то говорять, що програма або дялянка програми лінійна. Реальні програми мають нелінійну організацію. Для створення таких програм використовуються оператори розгалужень і цикли. Найчастіше для розгалужень використовують оператори if и switch.

Оператор if

Синтаксис оператора if визначається таким чином

if(вираз)

оператор1;

[else

оператор2]

де обчислене значення виразу має бути логічного типу, оператор1 і оператор2 можуть бути будь-якими операторами мови С.

Порядок обчислення оператора такий. Спочатку обчислюється значення виразу. Потім, якщо набуте значення істинно, то виконується оператор1, інакше, якщо в операторі if присутня гілка else, то виконується оператор2, якщо гілки else немає, то не виконується нічого.

Наступний фрагмент програми ілюструє обчислення Y за формулами:

Y=a-x, якщо x<a; Y=a,якщо x=a; Y=a+x,якщо x>a.

if(x<a)

Y=a-x;

else

if(x==a)

Y=a;

else

Y=a+x;

Ми розглянули приклад використовування оператора if, коли при виконанні умови і при невиконанні вибирався тільки один внутрішній оператор. Проте часто вибір варіанта передбачає деякий ланцюжок дій. Припустимо, необхідно при значенні X>0 надати нові значення дл яY і Z за відповідними формулами. У цьому випадку потріьно буде замінити внутрішнього оператора на блок. Блок – це послідовність операторів (і описів), узята у фігурні дужки. Нижче наведений оператор if, що містить блок.

if(X>0)

{

Y=2;

Z=X-1;

}

 

Оператор switch

Оператор switch використовують для організації в програмі множини гілок обчислень. Якщо умова вибору варіанта може бути представлена константою, то оператор switch представляє розгалуження в програмі значно простіше, ніж це можна реалізувати ланцюжком операторів if – else – if.

Синтаксис оператора має такий вигляд.

switch(вираз)

{

case константа1 : Послідовність операторів

case константа1 : Послідовність операторів

...

case константаN : Послідовність операторів

[default: Послідовність операторів]

}

Обчислене значення виразу може бути будь-якого простого типу. Константи повинні мати тип, сумісний з типом виразу.

Робота оператора відбуваєтьмя таким чином.

Обчислене значення виразу послідовно порівнюється зі всіма константами. Якщо знайдений збіг значень в деякій гілці, то відповідна послідовність операторів виконується. У цьому випадку оператор switch не передбачає вихід за свої межі, тому виконуватимуться всі послідовності операторів, розташовані нижче. Якщо такий алгоритм не відповідає розв’язуваній задачі, є можливість в кінці послідовностей операторів записати оператор break (розглянуто пізніше), який завершує роботу оператора switch.

Якщо значення виразу не збіглося ні з однією константою, то управління передається гілці default, а при її відсутності оператор switch не виконує ніяких дій.

Наступний приклад дозволяє вибрати один з п’яи варіантів обчислення Y залежно від значення деякої змінної P.

switch(P)

{

case 1 : Y=x+a;break;

case 2 : Y=x+a+b;break;

case 3 : Y=x+a+b+c;break;

case 4 : Y=x+a+b+c+d;break;

default : Y=0;

}

 

2.9 Організація циклів

Ділянки програми, які можуть багато разів повторюватися в процесі одного запуску програми, називаються циклами. У кожному циклі має бути умова його повторення. Якщо ця умова перевіряється до входу в цикл, говорять, що це цикл з передумовою, а якщо умова перевіряється в кінці циклу, то такий цикл називається циклом з постумовою. Цикл з передумовою може жодного разу не виконатися, тоді як цикл з постумовою виконується як мінімум один раз.

Оператор while

Оператор while є циклом з передумовою. Нижче наведений синтаксис оператора:

while(умова)

оператор

Умовою має бути булевий вираз. Внутрішній оператор циклу (тіло циклу) виконується до тих пір, поки значенням умова буде істина. Звичайно цикл охоплює декілька операторів, тоді їх потрібно об’єднати в блок.

Наприклад, наведений далі фрагмент програми дозволяє визначити суму всіх парних чисел від 4 до 56.

int S=0,i=4;

while(i<=56)

{

S+=i;

i+=2;

}

 

Оператор do-while

Цикл do-while є циклом з постумовою. Нижче наведений синтаксис цього оператора.

Do

оператор

while(умова)

Умова має бути булевого типу. Одне виконання оператора do-while завжди забезпечене. Подальші виконання визначаються значенням умови. Якщо воно істинне, то цикл виконується. Інакше виконання циклу завершується. Аналогічно іншим операторам управління як внутрішній оператор можна використовувати блок.

Для виконання попередньої задачі відповідний фрагмент програми з застосуванням оператора do-while має такий вигляд.

int S=0,i=4;

do

{

S+=i;

i+=2;

}

while(i<=56)

 

Оператор for

Цикли while і do-while можуть реалізувати всі запити користувача по організації циклічних обчислень. Проте в тих випадках, коли відома кількість повтореньциклу зручно віикористовувати цикл for. З циклом for звичайно зв’язане поняття управляючої змінної циклу, яка набуває початкового значення при вході в цикл, змінюється згідно із законом, визначеним у заголовку циклу після кожного його виконання і порівнюється з деяким межовим значенням для вироблення рішення про продовження обчислень або їх завершення. Нижче наведений синтаксис циклу.

for(ініціалізація_управляючої_змінної;

перевірка_умови_закінчення_циклу;

зміна_значення_управляючої_змінної)

оператор

Як управляюча змінна використовуються змінні будь-яких числових типів.

Наприклад, наведений далі фрагмент програми дозволяє визначити суму всіх парних чисел від 2 до 38:

int i, S=0;

for(i=2; i<=38;i+=2)

S+=i;

 

Оператор break

Оператор break використовується у таких випадках:

· для виходу з оператора switch;

· для виходу з циклу;

Перший варіант використовування оператора break вже розглянуто у підрозд. 2.8.

Для виходу з циклу оператор break розтошовують у циклі як внутрішній оператор умовного оператора if, як це зроблено у наступному прикладі, де оператор break дозволяє запобігти ділення на 0.

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

{

if(b==i)

break;

Y=a/(b-i);

}

 

Оператор continue

Оператор continue використовується тільки в циклах. Він перериває виконання операторів, що складають тіло циклу і повертає управляння заголовку циклу. При цьому заголовок працює так, ніби чергова ітерація завершилася. Конкретно в циклах while і do-while управляння передається умові, а в циклі for – третьому елементу заголовка, змічуючому управляючу змінну. У наступному прикладі, якщо b==i, обчислення Y не відбудеться, але виконання циклу буде продовжено.

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

{

if(b==i)

continue;

Y=a/(b-i);

}

 

Масиви

Масив це структурований тип даних, що являє собою сукупність однотипних даних, елементи якої розрізняються індексами.

Масиви прийнято підрозділяти на одновимірні (відповідають векторам в математиці) і багатовимірні (відповідають матрицям).

Елементи масива завжди займають послідовно розташовані комірки пам’яті. А ім’я масиву має значення адреси першого елемента масиву.

Одновимірні масиви

Використовування масиву передбачає його оголошення, виділення місця в пам’яті під масив і привласнення його елементам конкретних значень.

Оголошення масиву виглядає так:

int A[15];

При оголошенні масиву можлива його ініціалізація:

int B[5]={8,4,-1,3,5};

Доступ до елементів масиву здійснюється по індексу. Нумерація елементів масиву в мові починається з нуля.

int C=B[2]; //C=-1;

Нижче наведений приклад введення-виведення одновимірного масиву.

int A[15],i,n;

printf(“Введіть кількість елементів масива: “);

scanf(“%d”,&n);

printf(“Введіть %d елементів масива: \n“);

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

scanf(“%d”,&A[i]);

printf(“Був прийнятий масив: \n“);

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

printf(“%d “,A[i]);

 

Багатовимірні масиви

Мова С підтримує роботу з багатомірними масивами (розміром до 7 вимірів).

Оголошення масиву виглядає так:

int A[5][7];

де:

5 - число рядків;

7 - число стовпців.

Сортування масивів

Наведений далі фрагмент програми дозволяє виконати сортування одновимірного масиву так званим методом мінімума і максимума.

int A[15];

int ,n,i,j,temp;

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

{

temp=A[i];

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

{

if(A[i]>A[j])

{

A[i]=A[j];

A[j]=temp;

temp=A[i];

}

}

}

Ось приклад сортування одновимірного масиву методом «бульбашки».

int A[15];

int ,n,i,j,temp;

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

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

if(A[j]>A[j+1])

{

temp=A[j];

A[j]=A[j+1];

A[j+1]=temp;

}

 

2.12 Функції

Для того, щоб написати складну програму на мові C, застосовують метод низхідного програмування, в основі якого лежить наступне:

· необхідно розділити головне завдання на дрібніші;

· написати для кожного дрібного завдання функцію;

· з'єднати ці функції в одну програму.

Функція - це логічно самостійна іменована частина програми, якій можуть передаватися параметри і яка може повертати якесь значення. До функції можна звертатися скільки завгодно часто з будь-якої точки програми. Будь-яку функцію, яку ми використовуємо в програмі, необхідно заздалегідь описати (визначити).

Формат визначення функції:

[тип данных] имя_функции([список аргументов])

{

Определение переменных;

Операторы;

[return];

}

Сукупність пропозицій у фігурних дужках називають тілом функції. Поле тип даних задає тип повертаного функцією значення. Якщо це поле відсутнє, то функція повертає за умовчанням тип int. Якщо функція нічого не повертає, то поле тип даних містить ключове слово void.

Поле имя_функции - це особливий вид покажчика, званий покажчиком на функцію. Його значенням є точка входу на функцію.

Поле список_аргументов визначає аргументи (параметри), передавані у функцію і містить будь-яку комбінацію типів і імен. Це поле у визначенні функції називають списком формальних параметрів. Поле список_аргументов - необов'язково. Якщо у функцію не передаються ніякі параметри, то це поле порожнє або містить ключове слово void. Якщо тип повертаного значення відмінний від void, то функція повинна обов'язково містити оператора return і навпаки..

Наприклад, хай потрібно обчислити значення виразу
y=(2b-z)/k;

і оформити обчислення у вигляді функції

.

#include<stdio.h>

float f_y(float, float, float);

void main(void)

{

float y,b,z,k;

printf(“\n Enter b: “);

scanf(%f”,&b);

printf(“\n Enter z: “);

scanf(%f”,&z);

printf(“\n Enter k<>0: “);

scanf(%f”,&k);

y=f_y(b,z,k);

printf(“\n y=%f”,y);

}

float f_y(float b1, float z1, float k1)

{

float a;

a=(2*b1-z1)/k1;

return a;

}

2.13 Рекурсивні функції

Рекурсія - це такий спосіб організації обчислювального процесу, при якому підпрограма в ході виконання складових її операторів звертається сама до себе.

Розглянемо класичний приклад - обчислення факторіалу: програма запрошує з клавіатури ціле число n і обчислює його факторіал.

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

int factr(int n)

{

int answer;

if(n==1)

return (1);

answer=factr(n-1)*n;

return (answer);

}

 

Основною перевагою рекурсивних функцій є використання їх для простішого створення версії деяких алгоритмів в порівнянні з ітеративними еквівалентами. Наприклад, сортуючий алгоритм Quick sort достатньо важко реалізувати ітеративним способом. Деякі проблеми, особливо пов'язані з штучним інтелектом, також використовують рекурсивні алгоритми. Нарешті, деяким людям здається, що думати рекурсивно набагато легше, ніж ітеративно.

При написанні рекурсивних функцій слід мати оператора if, щоб примусити функцію повернутися без рекурсивного виклику. Якщо цього не зробити, то, одного разу викликавши функцію, вийти з неї буде неможливо. Це найбільш типова помилка, пов'язана з написанням рекурсивних функцій.

 

2.14 Читання і запис текстових файлів

Для тривалого зберігання даних, введених користувачем, можна використовувати два підходи: зберігати дані у файлах даних або використовувати для цих цілей бази даних. Предметом нашого розгляду буде читання і запис текстових файлів.

Перед роботою з файлом його потрібно відкрити. Це можна зробити за допомогою функції fopen():

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

Функція fopen() відкриває існуючий файл або створює новий. У разі успіху вона повертає покажчик потоку, який повинен бути оголошений до цього, запам'ятав в змінній для використання іншими функціями. У разі помилки функція повертає нуль. Розглянемо параметри цієї функції:

const char *filename - покажчик на рядок імені файлу. Може містити повний шлях до файлу.

const char *mode - покажчик на один з можливих режимів, розглянутих нижче.

Таблица 2

Режим Опис
rt Відкриває файл для читання
wt Створює новий файл. У випадку, якщо файл з таким ім'ям вже існує, то він буде перезаписаний  
at Відриває файл в режимі додавання інформації
r+t Відкриває існуючий файл для читання і запису
w+t Створює новий файл для читання і запису. Якщо файл з таким ім'ям вже існує, то він буде перезаписаний
a+t Відкриває існуючий файл або створює новий в режимі додавання інформації

 

Параметр, задаючий режим, при виклику функції fopen() потрібно брати в подвійні лапки.

Нижче приведений фрагмент коду для створення файлу dat1.txt:

FILE *fp;

fp=fopen(“dat1.txt”,”wt”);

if(fp==NULL)

{

printf(“Неможливо створити файл”);

exit(1);

}

Розглянута в даному прикладі функція exit() використовується для припинення роботи програми при виникненні помилки.

Якщо до exit() звернулися з функції, викликаною головною програмою, то припиняє роботу вся програма, а не тільки ця функція. Приємна особливість цієї функції полягає в тому, що вона закриває будь-які файли, відкриті функцією fopen().

Аргументом функції exit() є номер коду помилки. У некоторsх системах він може передавться іншій програмі, якщо початкова припинила роботу.

Існує угода, що 0 - нормальне завершення, тоді як будь-яке інше значення свідчить про помилку.

Розглянемо деякі основні функції для роботи з файлами.

Fgetc()

Функція fgetc() призначена для посимвольного читання з файлу.У разі успіху повертає лічений символ. Досягши кінця файлу або у разі помилки повертає EOF.

Далі приведений приклад програми, яка прочитує і відображає на екран вміст текстового файлу.

#include<stdio.h>

#include<stdlib.h>

#include<conio.h>

void main(void0

{

FILE *fp;

char з;

fp=fopen(“dat1.txt”,”rt”);

if(fp==NULL)

{

printf(“Неможливо відкрити файл”);

exit(1);

}

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

printf(“%c”,c);

fclose(fp);

getch();

}

 

Fgets()

Формат функції:

char *fgets(char *str,int num,FILE *stream)

Функція fgets() прочитує до num-1 символів з файлу stream і поміщає їх в масив символів, на який указує str. Символи прочитуються до тих пір, поки не зустрінеться символ “новий рядок”, EOF. У разі успіху fgets() повертає рядок, при невдачі повертається NULL.

Приведений нижче фрагмент коду по строках прочитує вміст текстового файлу.FILE *fp;

char str[256];

fp=fopen(“dat1.txt”,”rt”);

if(fp==NULL)

{

printf(“Невозможно открыть файл”);

exit(1);

}

while(fgets(str,256,fp)!=NULL)

puts(str);

fclose(fp);

 

Fputc()

Функція fputc() здійснює запис одного символу у відкритий файл в позицію, відповідну поточному положенню покажчика. Потім дає приріст покажчику. Повертає значення записаного символу. У разі помилки повертає EOF. Наприклад:

FILE *fp;

char c=’A’;

fputc(с,fp);

 

Fputs()

Функція fputs() записує рядок, заданий першим аргументом у файл. У разі помилки повертає EOF

 

Структури даних

Структури - це один з складених типів даних. Структурні змінні, або просто структури, це об'єднання однієї або більш змінних, можливо різних типів, в одну область пам'яті, що має для простоти одне ім'я. Окремі складові частини структурної змінної називаються полями.

Поняття структури в мові З відповідає двом різним поняттям. По-перше, це позначення місця в пам'яті, де розташовується інформація. Це місце називається структурною змінною. По-друге, це правило формування структурної змінної, використовуване для виділення їй місця в пам'яті і організації доступу до її полів. Далі таке правило називатимемо шаблоном структури.

Опис структурної змінної складається з двох кроків:

· завдання шаблону структури;

· опис структурної змінної.

Кожен шаблон має унікальне ім'я, яке підкоряється правилам формування імен змінних в мові С.

Синтаксис шаблону:

struct name {

type1 fieldname1;

type2 fieldname2;

typen fieldnamen;

};

де:

name - ім'я шаблону;

typen - будь-які типи змінних;

fieldname - імена полів.

Імена полів в одному шаблоні повинні бути унікальними, в різних - можливі співпадаючі імена.

Приклад шаблону приведений нижче:

struct student {

char name[32];

char lastname[32];

int kurs;

int years;

float stypendiya;

};

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

struct student first_student, second_student;

Існує ще один варіант опису структурної змінної - оголошення її безпосередньо після оператора опису шаблону, наприклад:

struct student {

char name[32];

char lastname[32];

int kurs;

int years;

float stypendiya;

} first_student, second_student;

Існує два способи звернення до структурної змінної:

· з використанням оператора .

· з використанням оператора ->

Оператор . формує посилання на потрібне поле з імені структурної змінної і імені поля, наприклад:

first_student.stypendiya=500;

Якщо дані прочитуються з клавіатури, то це можна зробити так6

scanf(“%f”,&first student);

При описі структурної змінної дозволяється виконувати ініціалізацію полів змінних, наприклад:

struct student first_student{

“Іванов”,

“Іван”,

2,

19,

500,

};

За заданим шаблоном структури можна описувати масиви структур і покажчики на структури. Наприклад:

struct student gr1[40];

Доступ до елементів масиву може виконуватися через індекс елементу або через покажчик-константу, яким є ім'я масиву:

gr1[i].stypendiya=500;

*(gr1+i).stypendiya=500;

(gr1+i)->stypendiya=500;

Як приклад використання даних типу структура розглянемо програму, в якій у файлі даних car.dat (передбачається, що файл існує і зберігає дані типу структура car) шукається автомобіль за номером oi8413t.

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

void main(void)

{

struct car {

char nomer[15];

char type[15];

char mark[15];

} car_current;

char nomer[ ]=”oi8413t”;

char work[80];

unsigned int flag=0;

FILE *fp;

if((fp=fopen(“car.dat”,”rt”))==NULL)

{

printf((“n Cannot open file”);

exit(1);

}

while(fgets(work,80,fp)!=NULL)

{

sscanf(work,”%s %s %s”, car_current.nomer, car_current.type, car_current.mark);

if(strstr(car_current.nomer,nomer)==0)

{

continue;

}

else

{

printf(“%s”,work);

flag=1;

break;

}

if(flag==0)

{

printf(“ Автомобіль не знайдений”);

}

fclose(fp);

}

Тут файл car.dat відкривається на читання. На кожному кроці циклу з файлу в буфер (масив work) прочитується рядок і порівнюється з шуканого за допомогою функції пошуку підрядка в рядку strstr(). Функція strstr() у разі невдачі повертає 0, інакше повертає 1.

Функція sscanf() прочитує дані з work і розносить їх по рядку, формату, по трьох структурованих змінних.

 

Контрольні запитання і завдання

1. Які оператори дозволяють організовувати розгалуження в програмі?

2. Яких операторів циклів ви знаєте? Сформулюйте рекомендації щодо їх використовування.

3. Яке призначення оператора break?

4. Яке призначення оператора continue?

5. Яке призначення оператора return?

6. Що є описом масиву?

7. Як можна описати і проініціалізувати масив?

8. Складіть фрагмент програми для обчислення коренів квадратного рівняння, що враховує різні значення і співвідношення значень коефіцієнтів.

9. Складіть фрагмент програми для сортування елементів двовимірного масиву дійсних чисел за збільшенням величин елементів.

10. складіть фрагмент програми пошуку числа у відсортованому одновимірному масиві цілих чисел. Треба використовувати факт сортування для прискорення процесу пошуку.


3. Операційні системи

3.1. Поняття операційної системи

Причиною появи операційних систем була необхідність створення зручних у використанні комп’ютерних систем (під комп’ютерною системою будемо розуміти сукупність апаратного і програмного забезпечення комп’ютера). Комп’ютерні системи від самого початку розроблялися для розв’язання практичних задач користувачів. Оскільки робити це за допомогою лише апаратного забезпечення виявилося складно, були створені прикладні програми. Для таких програм знадобилися загальні операції керування апаратним забезпеченням, розподілу апаратнихз ресурсів тощо. Ці операції згрупували в рамках окремого рівня програмного забезпечення, який і стали називати операційною системою.

Далі можливості операційних систем вийшли далеко за межі базовогонабору операцій, необхідних прикладним програмам, але проміжне становище таких систем між прикладнимим програмами й апаратним забезпеченням залишилося незмінним.

Таким чином, операційна система (ОС) – це програмне забезпечення, що реалізує зв’язок між прикладнимим програмами й апаратнимим засобами комп’ютера.

3.1.1 Призначення операційної системи

ОС реалізує безліч функцій, зокрема:

визначає так званий інтерфейс користувача;

забезпечує розподіл апаратних ресурсів між користувачами;

дає можливість працювати з общимімі даними в режимі колективного користування;

планує доступ користувачів до загальних ресурсів;

забезпечує ефективне виконання операцій введення/виведення;

здійснює оновлення інформації про обчислювальний процес у разі помилок.

Операційні системи забезпечують, поперше, зручність використання комп’ютерної системи, по-друге, ефективність і надійність її роботи.

Перша функція властива ОС як розширеній машині, друга – ОС як розподілювач апаратних ресурсів.

3.1.2 Операційна система як розширена машина

За допомогою операційної системи у прикладного програміста (а через його програми і в користувача) має створюватися враження, що він працює з розширеною машиною.

Апаратне забезпечення недостатньо пристосоване для безпосереднього використання у програмах. Наприклад, якщо розглянути роботу із пристроями введення-виведення на рівні команд відповідних контролерів, то можна побачити, що набір таких команд обмежений, а для багатьох пристроїв – примітивний. Операційна система приховує такий інтерфейс апаратного забезпечення, замість нього програмістові пропонують інтерфейс прикладного програмування, що використовує поняття вищого рівня (їх називають абстракціями).

Наприклад, при роботі з диском типовою абстракцією є файл. працювати з файлами простіше, ніж безпосередньо з контролером диска (не потрібно враховувати переміщення головок дисковода, запускати і зупиняти мотор тощо), внаслідок цього програміст може зосередитися на суті свого прикладного завдання. За взаємодію з контролером диска відповідає операційна система.

Виділення абстракцій дає змогу досягти того, що код ОС і прикладних програм не потребує зміни при переході на нове апаратне забезпечення. Наприклад, якщо втсановити на комп’ютері дисковий пристрій нового типу (за умови, що він підтримувається ОС), всі його особливості будуть враховані на рівні ОС, а прикладні програми продовживатимуть використовувати файли, як і раніше. Така характеристика системи називається апаратною незалежністю. Можна сказати, що ОС надають апаратно-незалежне середовище для виконання прикладних програм.

 


Абстракції

 

 

Деталі доступу

 

Рис. 1Взаємодія ОС із апаратним забезпеченням і застосуваннями

3.1.3 Операційна система як розподілювач ресурсів

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

Розрізняють два основні види розподілу ресурсів. У разі просторового розподілу ресурс доступен декільком споживачам одночасно, при цьому кожен із них може користуватися частиною ресурсу (так розподіляється пам’ять). У разі часового розподілу система ставить споживачів у чергу і згідно з нею надає їм змогу користуватися всім ресурсом обмежений час (так розподіляється процесор в однопрцесорних системах).

При розподілі ресурсів ОС розв’язує можливі конфлікти, запобігає несанкціонованому доступу програм до тих ресурсів, на яуі вони не мають прав, забезпечує ефективну роботу комп’ютерної системи.

3.2 Класифікація сучасних операційних систем

Розглянемо класифікацію сучасних операційних систем залежно від області їхнього застосування.

Насамперед відзначимо ОС великих ЕОМ (мейнфреймів). Основною характеристикою апаратного забезпечення, для якого їх розробляють, є продуктивність введення-виведення: великі ЕОМ оснащують значною кількістю периферійних пристроїв (дисків, принтерів, терміналів тощо). Такі комп’ютерні системи використовують для надійної обробки значних обсягів даних, прицьому ОС має ефективно підтримувати цю обробку (в пакетному режимі або в режимі розподілу часу). Прикладом ОС такого класу може бути OS/390 фірми IBM.

До наступної категорії можна віднести серверні ОС. Головна характеристика таких ОС – здатність обслуговувати велику кількість запитів користувачів до спільно використованих ресурсів. Важливу роль для них відіграє мережна підтримка. Є спеціалізовані серверні ОС, з яких виключені елементи, не пов’язані з виконанням їхніх основних функцій (наприклад, підтримка застосувань користувача). Нині для реалізації серверів часіше застосувують універсальні ОС (UNIX або системи лінії Windows XP).

Наймасовіша категорія – персональні ОС. Деякі ОС цієї категорії розробляли з розрахунком на непрфесійного користувача (лінія
Windows 95/98/Me фірми Microsoft, яку далі називатимемо
Consumer Windows), інші є спрощеними версіями універсальних ОС. Особлива увага в персональних ОС приділяється підтримці графічного інтерфейсу користувача і мультимедіа-технологій.

Виділяють також ОС реального часу. У такій системі кожна операція має бути гарантовано виконана в заданому часовому діапазоні. ОС реального часу можуть керувати польотом космічного корадля, технологічним процесом або демонстрацією відеороликів. Існують спеціалізовані ОС реального часу, такі як QNX і VxWorks.

Ще однією категорією є вбудовані ОС. До них належать керуючи програми для різноманітних мікропроцесорних систем, які використовують у військовій техниці, системах побутової електроники, смарт-картах та інших пристроях. До таких систем ставлять особливі вимоги: розміщення в малому обсязі пам’яті, підтримка спеціалізованих пристроїв введення-виведення, можливість прошивання в постійному запам’ятовувальному пристрої. Часто вбудовані ОС розробляють під конкретний пристрій; до універсальних систем належать Embedded Linux і Windows CE.

Контрольні запитання

1. Які основні функції операційної системи?

2. Наведіть кілька прикладів просторового і часового розподілу ресурсів комп’ютера. Від чого залежить вибір того чи іншого методу розподілу?

3. У чому полягає основна відмінність багатозадачних пакетних систем від систем з розподілом часу?


4 Архітектура операційних систем

Операційну систему можна розглядати як сукупність компонентів, кожен з яких відповідає за певні функції. Набір таких компонентів і порядок їхньої взаємодії один з одним та із зовнішнім середовищем визначається архітектурою операційної системи.

Розглянемо основні поняття архітектури операційних систем, підходи до їх, особливості взаємодії ОС із зовнішнім середовищем. Реалізацію архітектури розглянемо на прикладі Linux.

4.1 Базові поняття архітектури операційних систем

4.1.1 Ядро системи. Привілейований режим і режим користувача

Базові компоненти ОС, які відповідають за найважливіши її функції, зазвичай перебувають у пам’яті постійно і виконуються у привілейованому режимі, називають ядром операційної системи (operating system kernel).

Існуючі на сьогодні підходи до проектування архітектури ОС по-різному визначають функціональність ядра. До найважливіших функцій ОС, викладання яких, звичайно, покладають на ядро, належать обробка переривань, керування пам’яттю, керування введенням-виведенням. До надійності та продуктивності ядра висувають підвищені вимоги.

Основною характерною ознакою ядра є те, що воно виконується у привілейованому режимі. Розглянемо особливості цього режиму.

Для забезпечення ефективного керування ресурсами комп’ютера ОС повинна мати певні привілеї щодо прикладних програм. Треба, щоб прикладні програми не втручалися в роботу ОС, і в одночас ОС повинна мати можливість втрутитися в роботубудь-якої програми, наприклад для перемикання процесора або розв’язання конфлікту в боротьбі за ресурси.

Для реалізації таких привілеїв потрібна апаратна підтримка: процесор має підтримувати принаймні два режими роботи – привілейований (захищений режим, режим ядра, kernel mode) і режим користувача (user mode). У режимі користувача недопустимі команди, які є критичними для роботи системи (перемикання задач, звертання до пам’яті за заданими межами, доступ до пристроїв введення-виведення тощо).

Розглянемо, яким чином використовуються різні режими процесора під час взаємодії між ядром і застосуваннями.

Після завантаження ядро перемикає процесор у привілейований режим і отримує цілковитий контроль над комп’ютером. Кожне застосування запускається і виконується в режимі користувача, де воно не має доступу до ресурсів ядра й інших програм. Коли потрібно виконати дію, реалізовану в ядрі, застосування робить системний виклик (system call). Ядро перехоплює його, перемикає процесор у привілейований режим, виконує дію, перемикає процесор назад у режим користувача і поветрає результат застосування.

Системний виклик виконується повільніше за виклик функції, реалізованої в режимі користувача, через те що процесор двічі перемикається між режимами. Для підвищення продуктивності в деяких ОС частина функціональності реалізована в режимі користувача, тому для доступу до неї системні виклики використовувати не потрібно.

4.2 Реалізація архітектури операційних систем

Розглянемо кілька підходів до реалізації архітектури операційних систем. У реальних ОС звичайно використовують деяку комбінацію цих підходів.

4.2.1 Монолітні системи

ОС, у яких усі базові функції сконцентровані в ядрі, називають монолітними системами. У разі реалізації монолітного ядра ОС стає продуктивнішою (процесор не перемикається між режимами під час взаємодії між її компонентами), але менш надійною (весь їїкод виконується у привілейованому режимі, і помилка в кожному з компонентів є критичною).

Монолітність ядра не означає, що всі його компоненти мають постійно перебувати у пам’яті. Сучасні ОС дають можливість динамічно розміщувати в адресному просторі ядра фрагменти коду (модулі ядра). Реалізація модулів ядра дає можливість також досягти його розширюваності (для додання нової функціональності досить розробити і завантажити у пам’ять відповідний модуль).

4.2.2 Багаторівневі системи

Компоненти багаторівневих ОС утворюють ієрархію рівнів (шарів, layers), кожен з яких спирається на функції попереднього рівня. Найнижчий рівень безпосередньо взаємодіє з апаратним забезпеченням, на найвищому рівніреалізуються системні виклики.

У традіційних багаторівневих ОС передача керування з верхнього рівня на нижчий реалізується як системний виклик. Верхній рівень повинен мати права на виконання цього виклику, перевірка ціх прав виконується за підтримки апаратного забезпечення. Прикладом такої системи є ОС Multics, розроблена у 60-роки. Практичне застосування цього підходу сьогодні обмежене через низьку продуктивність.

Рівні можуть виділятися й у монолітному ядрі; у такому разі вони підтримуються програмно і спричиняють спрщення реалізації системи. У монолітному ядрі визначають рівні:

засоби абстрактування від устаткування, які взаємодіють із апаратним забезпеченням безпосередньо, звільняючи від реалізації такої взаємодії інші компоненти системи;

базові засоби ядра, які відповідають за найфундаментальніші, найпростіші дії ядра, такі як запис блоку даних на диск. За допомогою цих засобів виконуються вказівки верхніх рівнів, пов’язані з керуванням ресурсами;

засоби керування ресурсами (або менеджери ресурсів), що реалізують основні функції ОС (керування процесами, пам’яттю, введення—виведенням тощо). На цьому рівні приймаються найважливіші рішення з керування ресурсами, які виконуються з використанням базових засобів ядра;

інтерфейс системних викликів, який служить для реалізації зв’язку із системним і прикладним програмним забезпеченням.

4.3 Системи з мікроядром

Одним із напрямів сучасних ОС полягає в тому, що у првілейованому режимі реалізована невелика частка функцій ядра, які і є мікроядром (microkernel). Інші функції ОС виконуються процесами режиму користувача (серверними процесами, серверами). Сервери можуть відповідати за підтримку файлової системи, за роботу із процесами, пам’яттю тощо.

Мікроядро здійснює зв’язок між компонентами системи і виконує базовий розподіл ресурсів. Щоб виконати системний виклик, процес (клієнтський процес, клієнт) звертається до мікроядра. Мікроядро посилає серверу запит, сервер виконує роботу і пересилає відповідь назад. А мікроядро переправляє його клієнтові. Клієнтами можуть бути не лише процеси користувача, а і інші модулі ОС.

Перевагами мікроядрового підходу є:

невеликі розміри мікроядра, що спрощує його розробку й налагодження;

висока надійність системи, внаслідок того що сервети працюють в режимі користувача й у них немає прямого доступу до апаратного забезпечення;

більша гнучкість і розширюваність системи (непотрібні компоненти не займають місця в пам’яті, розширення функціональності системи зводиться до додавання в неї нового сервера);

можливість адаптації до умов мережі (спосіб обміну даними між клєнтом і сервером не залежить від того, зв’язані вони мережою чи перебувають на одному комп’ютері).

Головним недоліком мікроядрового підходу є зниження продуктивності. Замість двох перемикань режиму процесора у разі системного виклику відбувається чотири (два - під час обміну між клієнтом і мікроядром, два – сервером і мікроядром).

Зазначений недолік є, швидше, теоретичним, на практиці продуктивність і надійність мікроядра залежить насамперед від якості його реалізації. Так, в ОС QNX мікроядро займає кілька кілобфйтів пам’яті й забезпечує мінімальний набір функцій, при цьому система за продуктивністю відповідає ОС реального часу.

4.4 Концепція віртуальних машин

У системах віртуальних машин програмним шляхом створюють копії апаратногозабезпечення (відбувається його емуляція). Ці копії (віртуальні машини) працюють паралельно, на кожній із них функціонує програмне забезпечення, з яким взаємодіють прикладні програми і користувачі.

Контрольні запитання

1. Перелічіть причини, за якими ядро ОС має виконуватися в привілейованому режимі процесора.

2. Чи може процесор переходити у привілейований режим під час виконання програми користувача? Чи може така програма виконуватися виключно в привілейованому режимі?


5. Підтримка програмування в OC UNIX.

5.1. Вивчення передачі інформації

Як відомо, ядро операційної системи – це програма, яка виконує основні функції операційної системи. Воно взаємодіє з апаратними пристроями, виділяє пам'ять і інші ресурси, дозволяє декільком програмам працювати одночасно, управляє файловими системами і т.д.

Ядро саме по собі не має в своєму розпорядженні засобів взаємодії з користувачами. Воно не може навіть видати простий рядок з запрошенням на введення команд. Ядро не дозволяє користувачам редагувати файли, взаємодіяти з іншим комп'ютером або писати програми. Для вирішення всіх цих задач задач потрібне велике число інших програм, включаючи интерпретатори команд, редактори і компілятори. Багато хто з цих програм користується бібліотеками функцій загального призначення, не включеними в ядро.

У системах GNU/UNIX більшість таких програм розроблена в рамках проекту GNU (GNU – це рекурсивний акроним, який розшифровується як GNU’s Not UNIX (GNU – не це UNIX)). Багато хто з них був написаний раніше, ніж з'явилося ядро UNIX. Мета проекту GNU – «створення| повноцінної операційної системи на зразок UNIX, оснащеної безкоштовним програмним забезпеченням».

Ядро UNIX і GNU-програми складають дуже могутню комбінацію, яку найчастіше називають просто «UNIX». Але без GNU – програм система не працюватиме, як і без ядра. Тому у багатьох випадках ми говоримо GNU/UNIX.

 


Компілятори GCC

Компілятор перетворює вихідний вихідний текст програми, зрозумілий людині, в об'єктний код, що виконується комп'ютером. Компілятори, доступні в UNIX-системах, являються являються частиною колекції GNU-компіляторів, відомої як GCC (GNU Compiler Collection). У неї входять компілятори мов C, C++, Java, Objective-C, Fortran і Chill. Нас цікавитиме компілятор з мови С.

Компіляція вихідного вихідного файлу.

Компілятор мови С називається gcc. При компіляції вихідного вихідного файлу потрібно вказувати вказувати опцію –c.

От як, наприклад, в режимі командного рядка компілюється файл prog.c:

$gcc –c prog.c

Одержаний об'єктний файл називатиметься prog.o.

Компоновка об'єктних файлів

Для отримання виконуваного файлу потрібно викликати gcc зопцією –o.

$gcc prog.c –o hello

Створення простої програми prog.c:

#include<stdio.h>

main()

{

printf(«Hello, World!»);

}

Створили текст програми і зберегли її під ім'ям prog.c.

 

Відкомпілювали програму, використовуючи компілятор gcc.

$ gcc –c prog.c

Одержимо виконуваний файл з ім'ям hello, скориставшись опцією –o.

$ gcc prog.c –o hello

Перевіримо працездатність і правильність виконання програми:

$ ./hello

В результаті успішної роботи програми на екрані повинне з'явитися вітання Hello, world!

Базова обробка командного рядка

Програма на С дістає доступ до своїх аргументів через параметри argc і argv. Аргументи командного рядка - це інформація, слідуюча слідуюча за ім'ям програми в командному рядку операційної системи.

Є два звичайніі засоби визначення функції main() - головної функції програми на мові С:

int main(int argc, char *argv[])

і

int main(int argc, char **argv)

Параметр argc єявляється цілим числом, вказуючим кількість наявних аргументів, включаючи ім'я команди.

Параметр argv - це покажчик на масив символьних покажчиків (інакше можна сказати так: argv єявляється масивом покажчиків на символи).

Між двома цими оголошеннями немає різниці, хоча перше концептуально зрозуміліше, а друге технічно коректніше:

 

char ** char *

«cat»

«file1»

«file2»

 

 

Тут cat, file1, file2 – рядки мови С, що завершуються символом кінця рядка ‘\0’.

За угодою, argv[0] (у С індекси відлічуються з нуля) є являється ім'ям програми. Подальші елементи являються являються аргументами командного рядка. Останнім елементом масиву є являється покажчик NULL. Всі аргументи командного рядка - це рядки. Всі числа конвертуються програмою у внутрішній формат. Аргументи командного рядка повинні відокремлюватися відокремлюватися пропусками. Коми, точки точки з комами і їм подібні символи не розглядаються розглядуються як роздільники. Якщо необхідно передати рядок, що містить пропуски або табуляції, у вигляді одного аргументу, слід укласти його в подвійні лапки.

Приклад: Програма виводить Hello, а потім ім'я користувача, якщо його набрати прямо за ім'ям програми:

 

#include<stdio.h>

int main(int argc, char *argv[])

{

if (argc!=2)

{

printf(«You forgot to type your name!\n»);

return 1;

}

printf(«Hello, %s!», argv[1]);

return 0;

}

Якщо назвати дану програму name, а як ім'я вказати Sergey, то в результаті виконання програми на екрані буде

Hello, Sergey!

У випадку, якщо програма буде запущена без вказівки аргументу, на екран буде виведено повідомлення

You forgot to type your name!

Контрольні завдання

Завдання 1:Написати програму, яка:

Виводить на екран ім'я програми;

Перевіряє, чи є хоч би один аргумент і якщо так, то скільки і які.

 

#include<stdio.h>

int main(int argc, char *argv[])

{

int i;

printf("The program name is %s\n", argv[0]);

if(argc>1)

{

printf("The program has %d params.\n",argc-1);

for( i=1; i<argc;i++)

printf("%d param is: %s\n",i,argv[i]);

}

else

printf("The program has not parametrs!");

return 0;}

Завдання 2:Написати програму, в якій перевіряється можливість відкриття на читання файлу(написаного на мові С).

Рекомендації до виконання:

Перед роботою з файлом його потрібно відкрити. Це можна зробити за допомогою функції fopen():

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

Функція fopen() відкриває існуючий файл або створює новий.

У разі успіху вона повертає покажчик потоку, інакше повертає NULL.

Параметри функції:

сonst char filename – покажчик на рядок імені файлу. Може містити в собі інформацію про повний шлях до файлу;

const char mode – покажчик на один з можливих режимів роботи з файлом. Ось деякі з них:

r – читання;

w – запис;

а – додавання.

 

#include<stdio.h>

main(int argc, char *argv[])

{

FILE *fp;

 

if(argc>1)

{

if((fp=fopen(argv[1],"r"))==NULL)

{

printf("Cannot opened file %s", argv[1]);

exit(1);

}

else

printf("The file %s is opened",argv[1]);

}

else

printf("Enter file name.");}

 

Завдання 3:Модифікувати програму з завдання 1 так, щоб для виведення інформації на екран використовувався стандартний потік виведення stdout.

Рекомендації до виконання:

Для роботи з файлами можна використовувати функцію fprintf(), яка працює практично як printf(), але їй потрібен аргумент для посилання на файл.

Наприклад:

int a=87;

fprintf(fp,»a=%d»,a);

де fp - покажчик на файл.

 

#include<stdio.h>

int main(int argc, char *argv[])

{

int i;

printf("The program name is %s\n", argv[0]);

if(argc>1)

{

fprintf(stdout,"The program has %d params.\n",argc-1);