Структура програми. Базові типи даних

1.3.1 Функція main() : з цього все починається
Усі програми, написані на мові Сі, повинні містити в собі хоча б одну функцію. Функція main() - вхідна точка будь-якої програмної системи, причому немає різниці, де її розміщувати. Але потрібно пам'ятати наступне: якщо вона буде відсутня, завантажувач не зможе зібрати програму, про що буде виведене відповідне попередження. Перший оператор програми повинен розміщуватися саме в цій функції.
Мінімальна програма на мові Сі має вигляд:
main()
{
return 0;
}
Функція починається з імені. В даному прикладі вона не має параметрів, тому за її ім'ям розташовуються порожні круглі дужки (). Далі обидві фігурні дужки {...} позначають блок або складений оператор, з яким ми працюватимемо, як з єдиним цілим. У Паскалі аналогічний зміст мають операторні дужки begin ... end.
Мінімальна програма має лише один оператор - оператор повернення значення return. Він завершує виконання програми та повертає в нашому випадку деяке ціле значення (ненульове значення свідчить про помилки в програмі, нульове про успішне її завершення). Виконання навіть цієї найпростішої програми, як і решти багатьох, проходить у декілька етапів (рис 1.1.).

код запуску функція main() код завершення

Рис. 1.1.Етапи виконання програми на мові Сі

1.3.2 Базові типи даних
Будь-яка програма передбачає виконання певних операцій з даними. Від їх типу залежить, яким чином будуть проводитися ці операції, зрештою, буде визначено, як реалізовуватиметься алгоритм.
Що таке тип даних? Сформулювати це поняття можна так : множина значень плюс перелік дій або операцій, які можна виконати над кожною змінною даного типу. Вважається, що змінна або вираз належить до конкретного типу, якщо його значення лежить в області допустимих значень цього типу.
Арифметичні типи даних об'єднують цілі та дійсні, цілі у свою чергу - декілька різновидів цілих та символьних типів даних. Скалярні типи включають в себе арифметичні типи, покажчики та перелічувані типи. Агрегатні або структуровані типи містять в собі масиви, структури та файли. Нарешті функції представляють дещо особливий клас, який слід розглядати окремо.
Базові типи даних Сі можна перерахувати у наступній послідовності:
1. char - символ
Тип може використовуватися для зберігання літери, цифри або іншого символу з множини символів ASCII. Значенням об'єкта типу char є код символу. Тип char інтерпретується як однобайтове ціле з областю значень від -128 до 127.
2. int - ціле
Цілі числа у діапазоні від -32768 до 32767. В операційних середовищах Windows та Windows NT використовуються 32-розрядні цілі, що дозволяє розширити діапазон їх значень від -2147483648 до 2147483647. Як різновиди цілих чисел, у деяких версіях компіляторів існують short - коротке ціле (слово) та long (4 байти) - довге ціле. Хоча синтаксис мови не залежить від ОС, розмірність цих типів може коливатися від конкретної реалізації. Гарантовано лише, що співвідношення розмірності є наступним: short <= int <=long.
3. float - число з плаваючою комою одинарної точності
Тип призначений для зберігання дійсних чисел. Може представляти числа як у фіксованому форматі (наприклад число пі - 3.14159), так і в експоненціальній формі - 3.4Е+8.
4. double - число з плаваючою комою подвійної точності
Має значно більший діапазон значень, порівняно з типом float: ±(1.7 10- 308 ... 1.7 10308).
У мові Сі, на відміну від Паскаля, використовується префіксний запис оголошення. При цьому на початку вказується тип змінної, а потім її ім'я. Змінні повинні бути описаними до того моменту, як вони будуть використовуватися у програмі. Ніяких додаткових ключових слів при цьому не пишуть. Наприклад:
int name;
float var, var1;
double temp;
char ch;
long height;
Змінні можна ініціалізувати (присвоювати їм початкові значення) безпосередньо у місці їх опису:
int height = 33 ;
float income = 2834.12 ;
char val = 12 ;
Для виведення інформації на екран використаємо функцію printf() (детально про операції введення-виведення значень змінних йтиметься у розділі 1.3.4. "Функції введення та виведення"):
printf("Вік Олега-%d.Його прибуток %.2f",age,income);
Крім того, цілі типи char, short, int, long можуть використовуватися з модифікаторами signed (із знаком) та unsigned (без знаку). Цілі без знаку (unsigned) не можуть набувати від'ємних значень, на відміну від знакових цілих (signed). За рахунок цього дещо розширюється діапазон можливих додатних значень типу (таблиця 1.2.).

Таблиця 1.2.Діапазони значень простих типів даних

 

Тип Діапазон значень Розмір (байт)
char -128 … 127
short  
int -32768 ... 32767 2 або 4
long -2,147,483,648 ... 2,147,483,647
unsigned char 0 ... 255
unsigned short 0 … 65535
unsigned   2 або 4
unsigned long 0 ... 4,294,967,295
float ±(3.4 10-38 ... 3.4 1038)
double ±(1.7 10-308 ... 1.7 10308)
long double ±(3.4 10-4932 ... 3.4 104932)

1.3.3 Перетворення типу
Згадаємо, що компілятор Паскаля виконує автоматичне перетворення типів даних, особливо в математичних виразах, коли найчастіше цілочисельний тип перетворюється у тип з плаваючою комою. Цей стиль підтримує і Сі, причому значення типу char та int в арифметичних виразах змішуються: кожний з таких символів автоматично перетворюється в ціле. Взагалі, якщо операнди мають різні типи, перед тим, як виконати операцію, молодший тип "підтягується" до старшого. Результат - старшого типу. Отже,
• char та short перетворюються в int;
• float перетворюється в double;
• якщо один з операндів long double, то і другий перетворюється в long double;
• якщо один з операндів long, тоді другий перетворюється відповідно до того ж типу, і результат буде long;
• якщо один з операндів unsigned, тоді другий перетворюється відповідно до того ж типу, і результат буде unsigned.
Приклад:
double ft, sd;
unsigned char ch;
unsigned long in;
int i;
/* ... */
sd = ft*(i+ch/in);
При виконанні оператора присвоювання в даному прикладі правила перетворення типів будуть використані наступним чином. Операнд ch перетворюється до unsigned int. Після цього він перетворюється до типу unsigned long. За цим же принципом і перетворюється до unsigned long і результат операції, що розміщена в круглих дужках буде мати тип unsigned long. Потім він перетворюється до типу double і результат всього виразу буде мати тип double.
Взагалі, тип результату кожної арифметичної операції виразу є тип того операнду, який має у відповідності більш високий тип приведення.
Але, окрім цього в Сі, з'являється можливість і примусового перетворення типу, щоб дозволити явно конвертувати (перетворювати) значення одного типу даних в інший. Загальний синтаксис перетворення типу має два варіанти :
1). (новий_тип) вираз ;
2). новий_тип (вираз) ;
Обидва варіанти перетворення виглядають так:
сhar letter = 'a';
int nasc = int (letter);
long iasc = (long) letter;

1.3.4 Функції введення та виведення
Що б там не було, але реальні програми важко уявити без використання операцій введення та виведення.
В мові Сі на стандартні потоки введення-виведення (в більшості випадків - клавіатура та монітор) завжди вказують імена stdin та stdout. Обробку цих потоків здійснюють функції, визначені в заголовочному файлі stdio.h.
Розглянемо основні функції введення-виведення.
Функція getchar() зчитує і повертає черговий символ з послідовності символів вхідного потоку. Якщо цю послідовність вичерпано, то функція getchar() повертає значення -1 (цьому значенню відповідає константа EOF).
Функція putchar(аргумент), де аргументом є вираз цілого типу, виводить у стандартний вихідний потік значення аргументу, перетворене до типу char.
Приклад :
#include<stdio.h>
void main()
{
char ch;
ch=getchar();
putchar(ch);
}
Для введення та виведення більш складної інформації використовуються функції scanf() та printf().
Функція printf() призначена для виведення інформації за заданим форматом. Синтаксис функції printf():
printf("Рядок формату"[, аргумент1[, аргумент2, [...]]]);
Першим параметром даної функції є "рядок формату", який задає форму виведення інформації. Далі можуть розташовуватися вирази арифметичних типів або рядки (в списку аргументів вони відокремлюються комами). Функція printf() перетворює значення аргументів до вигляду, поданого у рядку формату, "збирає" перетворені значення в цей рядок і виводить одержану послідовність символів у стандартний потік виведення.
Рядок формату складається з об'єктів двох типів : звичайних символів, які з рядка копіюються в потік виведення, та специфікацій перетворення. Кількість специфікацій у рядку формату повинна дорівнювати кількості аргументів.
Приклад :
#include<stdio.h>
void main()
{
int a=10,b=20,c=30;
printf(" a==%d \n b==%d \n c==%d \n",a,b,c);
}
Специфікації перетворення для функції printf():
%d - десяткове ціле;
%i - десяткове ціле;
%o - вісімкове ціле без знаку;
%u - десяткове ціле без знаку (unsigned)
%x - шістнадцяткове ціле без знаку;
%f - представлення величин float та double з фіксованою точкою;
%e або %Е - експоненціальний формат представлення дійсних величин;
%g - представлення дійсних величин як f або Е в залежності від значень;
%c - один символ (char);
%s - рядок символів;
%p - покажчик
%n - покажчик
%ld - long (в десятковому вигляді);
%lo - long (у вісімковому вигляді);
%p - виведення покажчика в шістнадцятковій формі;
%lu - unsigned long.
Можна дещо розширити основне визначення специфікації перетворення, помістивши модифікатори між знаком % і символами, які визначають тип перетворення (таблиця 1.3.).

Таблиця 1.3. Значення основних модифікаторів рядка формату

 

Модифікатор Значення
- Аргумент буде друкуватися починаючи з лівої позиції поля заданої ширини. Звичайно друк аргументу закінчується в самій правій позиції поля. Приклад : %-10d
Рядок цифр Задає мінімальну ширину поля. Поле буде автоматично збільшуватися, якщо число або рядок не буде вміщуватися у полі. Приклад : %4d
Цифри.цифри Визначає точність : для типів даних з плаваючою комою - число символів, що друкуються зліва від десяткової коми; для символьних рядків - максимальну кількість символів, що можуть бути надруковані. Приклад : %4.2f

Розглянемо декілька прикладів:
Приклад 1 :
#include <stdio.h>
main()
{
printf("/%d/\n",336);
printf("/%2d/\n",336);
printf("/%10d/\n",336);
printf("/%-10d/\n",336);
};
Результат виконання програми буде виглядати так :
/336/
/336/
/ 336/
/336 /
Приклад 2 :
#include <stdio.h>
main()
{
printf("/%f/\n",1234.56);
printf("/%e/\n",1234.56);
printf("/%4.2f/\n",1234.56);
printf("/%3.1f/\n",1234.56);
printf("/%10.3f/\n",1234.56);
printf("/%10.3e/\n",1234.56);
}
На цей раз результат виконання програми буде виглядати так :
/1234.560000/
/1.234560e+03/
/1234.56/
/1234.6/
/ 1234.560/
/ 1.235e+03/
Для введення інформації зі стандартного потоку введення використовується функція scanf().
Синтаксис :
scanf("Рядок формату",&аргумент1[,&аргрумент2[, ...]]);
Так, як і для функції printf(), для функції scanf() вказується рядок формату і список аргументів. Суттєва відмінність у синтаксисі цих двох функцій полягає в особливостях даного списку аргументів. Функція printf() використовує імена змінних, констант та вирази, в той час, як для функції scanf () вказується тільки покажчики на змінні.
Поширеною помилкою використання scanf() у початківців є звертання: scanf("%d",n) замість scanf("%d",&n). Параметри цієї функції обов'язково повинні бути покажчиками!
Функція scanf() використовує практично той же набір символів специфікації, що і функція printf().
#include <stdio.h>
main()
{
int a,b,c;
printf("A=");
scanf("%d",&a);
printf("B=");
scanf("%d",&b);
c=a+b;
printf("A+B=%d",c);
}
Більшість реалізацій мови Сі дозволяють пов'язувати імена stdin та stdout не тільки з клавіатурою та екраном, а й із зовнішніми файлами. Для цього в рядку виклику Сі програми необхідно вказати імена цих файлів. Якщо перед ім'ям файла введення поставити знак <, то даний файл буде пов'язаний з потоком введення.
prog < file.in
В даному прикладі інформація читається з файла file.in поточного каталогу, а не з клавіатури, тобто цей файл стає стандартним файлом введення, на який вказує stdin.
prog > file.out
А при такому виклику програми інформація виводиться не на екран, а у файл file.out.
Якщо необхідно читати інформацію з одного файла, а результати записувати у інший одразу, виклик програми буде мати вигляд :
prog < file.in > file.out

1.3.5 Директиви включення
У багатьох програмах ми зустрічаємо використання так званих директив включення файлів. Синтаксис використання їх у програмі наступний :
#include <file_1>
#include <file_2>
...
#include <file_n>
По-перше, слід звернути увагу на те, що на відміну від більшості операторів, ця директива не завершується крапкою з комою. Використання таких директив призводить до того, що препроцесор підставляє на місце цих директив тексти файлів у відповідності з тими, що перелічені у дужках < ... > . Якщо ім'я файла міститься у таких дужках, то пошук файлу буде проводитися у спеціальному каталозі файлів для включення (як, правило, каталог INCLUDE, усі файли з розширенням *.h - header-файли). Якщо даний файл у цьому каталозі буде відсутнім, то препроцесор видасть відповідне повідомлення про помилку, яка є досить типовою для початківців при роботі в інтегрованому середовищі:
< Unable to open include file 'file.h'>
<Неможливо відкрити файл включення ' file.h'>
У цьому випадку достатньо перевірити не тільки наявність header-файлу у відповідній директорії, але й впевнитися у тому, що опція Options\Directories дійсно відповідає правильному диску та спеціальному каталогу, де розташовані файли включення.
Існує і другий спосіб - вказівка імені файла у подвійних лапках - "file_n.txt ", так найчастіше підключають програмісти власностворені файли включення. Тоді пошук файлу ведеться у поточній директорії активного диску, якщо ж пошук буде невдалим, система закінчує його у спеціальному каталозі для header-файлів, як і у загальному випадку. Найбільш частим у початківців є включення файлу "stdio.h":
#include <stdio.h>
main()
{
printf("Hello ! ...\n");
return 0;
}
Слід зауважити, що файли включення іноді можуть вміщувати в собі командні рядки включення інших файлів, причому без ніяких обмежень у глибину вкладеності. Цей прийом широко застосовується при розробці великих програмних проектів.