Математика 2

Коммерциялық емес акционерлік қоғамы
АЛМАТЫ ЭНЕРГЕТИКА ЖӘНЕ БАЙЛАНЫС УНИВЕРСИТЕТІ
Инженерлік кибернетика кафедрасы

 

АЛГОРИТМДЕУ ЖӘНЕ ПРОГРАММАЛАУ

5В074600- Ғарыштық техника және технологиялар мамандығының студенттеріне арналған дәрістер жинағы

 

Алматы 2013

ҚҰРАСТЫРУШЫ: Сауанова К.Т. «Алгоритмдеу және программалау» 5В074600- Ғарыштық техника және технологиялар мамандығы студенттеріне арналған  дәрістер жинағы.- Алматы: АЭжБУ, 2013- 60 бет.

 

Дәрістер жинағы «Алгоритмдеу және программалау» пәні бойынша 5В074600- Ғарыштық техника және технологиялар мамандығы студенттеріне құрастырылған. Жинақ 15 тақырыптан тұрады. Теориялық материал С/С++ тілін меңгеруге мүмкіндік беріп, зертханалық жұмыстарды орындауға көмек ретінде негізделген.

Безенд.-6, кест.-1, әдеб.көрсеткіші-10 атау.

Пікір беруші: доцент К.О. Гали

«Алматы энергетика және байланыс университеті» коммерциялық емес акционерлік қоғамының 2013 ж. қосымша жоспары бойынша басылады.

©”Алматы энергетика және байланыс университеті”  ҚЕАҚ, 2013 ж.

Кіріспе

Бағдарлама - компьютерде әртүрлi әрекеттердi орындауға сілтейтін нұсқауларды теру болып табылад: кейде орындайтын әрекет осы әрекетке дейінгі әрекет нәтижесіне тәуелді болады. Бұл бөлiмде, сiзге, инструкция немесе «әмір» (команда) беретін екi негiзгi тәсiлдерге шолу сипатталынады. Бiр тәсiлде интерпретатор қолданылса, ал басқасында  компилятор тәсілін  пайдаланамыз. Әмiр (команда), әдетте, әдейі  өңделіп жасалған программалау  тiлдерiне  жазылады.

Интерпретаторды пайдаланған жағдайда,  тiл –  біз командаларды теретін болсақ, ал оларды  орындайтын қабық болып саналады. Күрделi программалар үшін файлдан командаларды алып, интерпретаторға оны жүктеп, содан сон, оны соның ішінде орындаймыз. Егер  қателіктер табылса, интерпретатор басқаруды  баптаушыға жібередi.

Бұл тәсілдің басымдылығы - қателерді сол мезгізде түзетіп, нәтижесін тез көру мүмкіндігі.

Өнiмдiлiк көзғарасынан (қолданылатын жады өлшемі бойынша), интерпретаторларды компиляторлармен салыстырғанда, компиляторлар жұмысы тиімдірек. Компиляторлар интерпретаторлардан едәуір күштi ерекшеленедi. Алдымен, сіз өзіңіздің кодыңызды редакторды пайдалана отырып  файлға  (немесе файлдарға) жазасыз. Содан соң компиляторды іске қосып, сіздің бағдарламаңызды қабылдаған немесе қабылдамағанын байқайсыз. Егер ол құрастырылмаса, сiз редакциялауға қайта ораласыз ,ал егер код құрастырылса және бағдарламаны берсе, жұмыс қабілеттілігінің дұрыстығын тексере аласыз. Компиляторда операциялық жүйемен тікелей байланысқан кодты жаза аласыз.

Компиляция тiлдерiне жататындар:  Pascal, C  және С++ тілдері  кiредi. «Түзету, компиляция, іске қосу, баптаушы»  циклын  бөлек бағдарламаларды қолдану кезінде қолайсыз болғандықтан, көптеген коммерциялық  компилятор өндірушілері  интегралданған орталарын ұсынады (IDE- Integrated Development Environment).

С++ тілі С тілінен дамыған, яғни С++ тілі С тілінің ұлғайған түрі болып табылады. Ал С тілі болса Деннис Риччи тілінен дамыған болып саналады. Алғашында, С тілі Unix басқару жүйесіне әзірленген тіл болып жарияланған. Қазіргі таңда, барлық жаңа  операциялық жүйелер С немесе С++ тілдерінде жазылған, оларға Windows ОЖ-сі де жатады, мұнда С++ тілі қолданылған. С++ тілі–  С тілінің ұлғайған түрі,оның құрастырушысы  Бьерн Страуструп. Обьектілер - бұл, шынайы өмірдің обьекттілерін модельдейтін, программада бірнеше рет қолдануға болатын  компоненттер. 

С++ тілі  С тілін қалыпқа келтіріп түзейтін қасиеттері бар, сонымен қатар,  С тілінде де, обьектіні бағытталған С++ тілінде де  және осы екі тілде де жазылған программаны қолдайтын  гибритті тіл.

1 дәріс. СИ/CИ++ тіліне шолу

Дәріс мақсаты: алғашқы программаны жазу үшін Си тілінің элементтері мен негізін беру.

1.1 СИ тіліндегі программа

Бұл тақырыпта айнымалылар мен тұрақтылар, арифметика, есептеу тізбектерін басқару, функциялар мен қарапайым енгізу-шығару жайында айтылады.

Кез келген программаны жазу үшін: программаның мәтінін құру керек, оны сәтті құрастыра білу, жүктеу, есептеуге жіберу және нәтиже қайда жіберілетіні туралы білу қажет. С++ - те кез келген процедуралық бірлік функция деп аталады, басқа тілдерде процедуралар, әдістер, функциялар, ішкі программалар мұнда функциялар деп аталады. С++ тілінде жазылған программа бұл стандартты main атаулы  функцияны кескіндеу  Программаны іске қосқаннан соң орындаушы жүйе осы функцияны автоматты түрде шақырады. “Сәлем!” сөзін басып шығару үшін Си  программасы келесі түрде болады:

#include <stdio.h>

main ()

         {printf (“Сәлем!\n”);}

С++ программасы келесі түрде болады:

#include <iostream.h>

main ()

{ cout<<“Сәлем!\n”;

return 0; } //бұл  main()  функциясына шақырушы ортаға 0 мәнін қайтару нұсқауы.

Программаны орындауға жіберген соң бірінші орындалатын оператор болып main() функциясынын бірінші операторы қабылданады. Программаны іске қосқан соң басқару үнемі main() функциясына беріледі. Бұл функция программада болмаса, программаны орындауға жібергеннен кейін қате тұралы хабарлама шығарылады. Кодты терген соң File мәзірінен Save as командасын тандап файлды    first.cpp атымен сақтаңыз. Орындаушы машиналық кодты генерациялау үшін Compile мәзірінің Make (немесе F9) командасын таңдау керек. Экранның төменгі бөлігінде компиляция процесінің қалай өткенін rhide терезесінде көруге болады. Егер қате болмаса Creating Conversion.exe хабардан соң no error сөзі шығады. GNU C++ бастапқы орнатудағы қателер:Bad  command or file MS DOS gcc.exe (GNU C++ компиляторы) файлын тауып алмағаны.Бұл қатені жою жолы: gcc.exe файлы орналасқан каталогқа дұрыс жолды көрсету. No such file or directory файлдың ұзын атын қабылдамай тұрғаны. Программаның кодындағы қателерді компилятор көрсетеді, бірақ түзетпейді. 

“#include<…>”процессор алдындағы директивасы, яғни файлды компиляциялау алдында орындалатын команда. “#”символынан басталатын жол процессор алдындағы директиваларды хабарлайды. Процессор алдындағы директивасы компиляторға кодты компиляциялау алдын ала директиваларды өңдеу қажет екеніне нұсқағыш деп те түсінуге болады. #include көмегімен іске қосылған файл тақырыпша файл деп аталады. #include <iostream.h> директивасы компиляторға кодты компиляциялау алдында бастапқы мәтінге iostream.h мазмұнын қосуға нұсқау. Мұнда iostream.h мазмұнында “cout” айнымалымен және  “<<” операциясымен жұмыс жасау үшін қажет сипаттамалары көрсетілген. Егер бұл тақырыпша файлдың мазмұнын көргілерің келсе оны unclude каталогынан көруге болады.  

Әдетте тақырыпша файлдарда функциялардың прототипі жазылады, яғни аргументтері мен қайтарылатын мәнінің типі көрсетілген функциялар тізімі.

“\n” белгілеу басқарушы немесе escape-тізім мысалы, біздің жағдайда келесі мәтінді шығару жаңа жолдан басталатынын көрсетеді (эквивалентті манипулятор ehdl).

Функцияның алдында жазылған int сөзі функция бүтін сан қайтаратынын көрсетеді. Функция атынан соң жазылатын дөнгелек жақшалар компиляторға айнымалының атын функция атымен шатастырмау үшін жазылады.Одан соң фигуралық жақша ашылып функцияны сипаттау жазылады, соңында фигуралық жақша жабылады.

Программаның бірінші жолы сәйкес стандартты кітапхана туралы ақпаратты қосу керек. Мысалы, #include <stdio.h> (StanDart Input/Output Library) инструкциясы компиляторға, ол программаның мәтініне стандартты библиотеканың енгізу-шығаруы туралы ақпаратты қосу керек екендігін хабарлайды (printf функциясының прототипін шақыру), “h”-“header” яғни тақырыпша файл. Тақырыпша файлдарда соларға сәйкес кітапхана кандай функцияларды мүмкіндік беретіні сипатталған.

Функциялардың арасындағы мәліметтерді беру сыртқы айнымалылар, аргументтер арқылы немесе қайтарылатын мәндер арқылы жүзеге асырылады.

Си-де басқа функциялардың ішіндегі функцияларды анықтауға тыйым салынатындықтан, функцияның өзі әрқашан сыртқы объектілер болып табылады. 

Идентификатор деп программада анықталған өзге элементтің немесе айнымалының аты аталады. Идентификатор әріптен немесе төменгі сызықтан басталуы мүмкін және төменгі сызық, цифрлар мен әріптерден басқа белгілерден тұрмайды. Дұрыс идентификаторлар:

x, x1, _abc, big sale; frame, control.

Дұрыс емес идентификаторлар:

9Loop, %popcorn, c.pp.

Идентификатор ретінде жоғарғы, төменгі регистрдегі әріптер және 1-ден 9-ға деінгі сандар пайдаланылуы тиіс. Алдын ала анықталған main, cout, cin идентификаторлар тек төменгі регистрде жазылады. Айнымалылардың аты ретінде пайдалануға болмайтын кілттік немесе қосалқылау сөздері идентификаторлардың бір ерекше бөлігін құрайды.

Си++ - те кез келген айнымалыны қолданудан бұрын, орындалатын нұсқауға дейін сипатталуы қажет. Компилятор жоғарғы, төмеңгі регистрдің әріптерін айырып таниды. Trojan trojan TROJAN үш идентификаторы, үш әртүрлі айнымалылардың аты ретінде пайдалануы мүмкін.  Мәліметтер үшін базалық типтер: char, short, long, double, int, float.

Айнымалыларды хабарлаудағы инициализация:

типтің_атау 1_айнымалы_атауы=1_өрнек,

типтің_атауы 2_айнымалы_атауы=2_өрнек,…..

Мысалы:

int count=0, limit=15, factor=2;//инициялизацияланған айнымалылар

Мәнін есептеуге әкелетін айнымалылар, тұрақтылар және операциялар комбинациясы өрнек деп аталады.

Мысалы: alpha-37;

printf стандартты функциясы – форматты енгізу-шығарудың әмбебап функциясы. Оның бірінші аргументі, әрбір % таңбасы оның келесі аргуметтерінің біреуіне сәйкес келетін стринг болатынын, ал % таңбасынан кейін орналасқан ақпарат осы аргументтердің әрбірі шығарылу түрін көрсетеді.

Мысалы, %d бүтін типтегі аргументтің шығуын көрсетеді. Спецификация %3.0f  printf-тегі бөлшек бөлік пен ондық нүктесіз үш позициядан артық емес өлшемді аймақтағы, жылжымалы нүктелі float типті санды баспаға шығаруды анықтайды. Спецификация %6.1f ондық нүктеден кейінгі бір цифрлы алты позициядан тұратын, аймақтағы санды басуды сипаттайды. Спецификатор %.0f бөлшек бөлігінсіз және ондық нүктесіз басуды білдіреді.   

Ені мен дәлдігін бермей-ақ қоюға болады: %6f дегеніміз – сан алты позициядан артық орын алмайтындығын білдіреді, %.2f – сан ондық нүктеден кейін екі цифрға ие, бірақ ені шектелмеген, %f жылжымалы нүктелі санды басуды ғана көрсетеді, %6d – алты позициялы аймақтағы бүтін ондықты баспаға шығарады.

Бұдан басқа, printf келесі спецификаторларды қолданады: %0 – сегіздік сан үшін, %X – он алтылық сан үшін, %С – литерді басу үшін, %S – стрингті басу үшін, литер %% -  %-тің өзі үшін.

printf (“%4x %6o”, x,y); х және у бүтін сандарды сәйкесінше оналтылық жүйеде 4 цифрді, сегіздік жүйеде алты цифрді  түрде шығарады.

 

1.2 Атаулы тұрақтылар

 

Программалауда қолданылатын мәліметтерге нақты көрініс беру өте маңызды. Мүмкіндіктердің бірі – санға мағыналы ат беру. # таңбасынан басталатын жол макроқоюды, шартты компиляцияны аталған файлдарды қосуды орындайтын препроцессормен байланысты орнатады. Мұндай жолдың синтаксисі Си тілінің басқа бөліктерінен тәуелсіз; мұндай жол программа мәтінінде қай жерде болсын пайда бола алады және трансляцияланатын компоненттердің соңына дейін (әрекет ету аймағына тәуелсіз) әсер етеді.

#define жолы символдық атауды немесе берілген стринг литері үшін аталған тұрақтыны анықтайды.

#define макрос_аты мәні

# define pi 3,1415926   // pi идентификатордын, компиляциялаудан кейін, барлық кездескен жерде 3,1415926 мәніне ауыстырылады.      

const float pi=3,14159F; // нақ тұрақтыны хабарлау

const  кілттік сөзі айнымалыны сипаттау алдында жазылады, ол бұл тұрақтының мәнін программа орындалу барысында өзгертуге болмайды дегені.

Программада айнымалыға меншіктеген мән, атаулы тұрақтыға берілген мән немесе өрнекте пайдаланған мән литералды тұрақты немесе литерал деп аталады. Әр мәліметтер типі үшін программада литералдарды жазудың қатаң ережелері бар. Қандай да болса сандық литералдарда: бүтін немесе жылжымалы үтірлі - жазылуында үтір болмауы тиіс. Бүтін типті литералдарда нүкте болмауы тиіс. double типті литерал екі түрде жазылуы мүмкін. Бірінші түрі бұл әдеттегідей, тек бөлшек бөлігі бүтін бөлігінен нүктемен ажыратылады. Ал екінші түрі бұл ғылыми жазылу формасы. Мысалы: 3,65∙1011, яғни бұл 365000000000.0.,  литерал түрі: 3,65е11. сhar типті литералдар: char symbol=’X’.

Символдар жолын кескіндейтін литерал: сонымен ‘Г’ және “Т” литералдардың мәндері әртүрлі. Біріншісі char типті айнымалыға меншіктеуге болатын char типті жалғыз символ. Екіншісі бір символдан тұратын символдар жолы, char типіне жатпайтын. bool  типті мәліметтердің мәні екі литералмен кескінденген: true және false.

 

2 дәріс. Типтер, операторлар және өрнектер

 

Дәріс мақсаты: мәліметтердің негізгі типтерімен, операторларымен танысу.

 

2.1  Мәліметтердің базалық типі

 

Айнымалылар мен тұрақтылар - программа жұмыс жасайтын негізгі объектілер болып табылады.

Объектінің типі осы объекті қабылдай алатын мәндер жиынын және оған орындалатын операцияларды анықтайды.

Барлық айнымалыларға олардың белгіленуіне сәйкес мағыналы аттарды берген дұрыс. Қысқа аттар локальды айнымалыларға арналған, әсіресе цикл есептеуіштері үшін және ұзындырақтары сыртқы айнымалылар үшін тағайындалған. Әдетте Си программаларында кіші әріппен айнымалыларды, ал үлкен (бас) – аталған тұрақтыларды тереді.

Си-де тек бірнеше базалық типтер бар: char, int, float, double. Сондай-ақ көрсетілген базалық типтермен бірге қолдануға болатын квалификаторлардың бірнешеуі бар: short, long – бүтінге қолданылатын және signed, unsigned – char типіне қолданылатын және кез келген бүтін типке қолданылатын. long double типі жоғарғы дәлдікті жылжымалы нүктелі арифметика үшін арналған. Айнымалыларды сиппатау барысында префикстік жоба қолданылады   (бірінші типі содан соң аты жазылады):

float red;

int green;

Айнымалыларды пайдаланған жерде де инициализациялауға болады.

float income=24,356;

Бүтін тұрақтылар ондық, екілік, сегіздік, оналтылық есептеу жүйлерінде берілуі мүмкін. Ондық бүтін тұрақтылар цифрлерден тұрады, нөлден басталмауы тиіс. Сегіздік бүтін сандар нөлден басталады, одан соң бірде немесе бірнеше 0-ден 7-ге деін цифрлар тұрады. Оналтылық бүтін сандар 0-ден және x немесе X басталады, одан соң бір немесе бірнеше оналтылық сандар тұрады.

Мысалы: int a=3452, int a=0342, int a=0xC127. 

Барлық өлшемдер үшін машинаның басқа мінездемелерімен бірге және компиляторда аталған тұрақтылар стандартты басты <limits.h>  және  <float.h> файлдарда сақталады.

Бүтін тип (тізбекті типті қоса алғанда) және жылжымалы тип жиында арифметикалық типті құрайды.

void типі (бос) белгілеуге ие. Функцияны жариялауда void типінің спецификациясын көрсету, функция мәнді қайтармайтынын білдіреді.

Функцияны жариялауда аргументтерді жариялау тізіміндегі void типінің көрсетілуі, функция аргументтерді қабылдамайтынын білдіреді.

Көрсеткіштің void типіне жариялануы, ол кез келгеніне көрсете алады, яғни арнайы тип емес екендігін білдіреді.

void типі типті келтіру операциясында көрсетілуі мүмкін. void типті айнымалыны жариялауға болмайды.

Мәндер аймағы – бұл ең аз мәннен ең үлкен мәнге дейінгі берілген тип айнымалысында көрсетіле алатын интервал.

Барлық айнымалылар қолданудан бұрын декларациялануы (сипатталуы) қажет. Декларация типтің өзгешелігін анықтайды және осы типтің айнымалылар тізімінен тұрады. Өз декларацияларында айнымалылар инициализациялана алады.

Автоматты емес айнымалының инициализациясы программаның жұмысының басталуының алдында бір рет қана орындалады (инициализатор – тұрақты өрнек). Локальды айнымалы функцияға немесе сұлбаға әр кіру кезінде бастапқы мәнін алады (инициализатор – кез келген өрнек). Сыртқы және статистикалық айнымалылар үнсіздікпен нөлдік мәнді қабылдайды.

Декларацияда кез келген айнымалыға const  квалификаторы, оның мәні келешекте өзгермейтіндігін көрсету үшін қолданылуы мүмкін.

const айнымалының _ типі айнымалының_аты=тұрақты;

Мысалы. const int max_size=40;

const double min_size=3,14;

Массивке қолданылатын const квалификаторы, оның элементтерінің бірде-біреуі өзгермейтіндігін көрсетеді. const көрсеткішін аргумент-массивке, бұл массивті функция өзгертпейтіндігін хабарлау үшін қолдануға болады.

Егер операторлардың операндалары әртүрлі типке жатса, онда олар үлкен мәндегі диапазонды, кейбір ортақ типке келтіріледі. Жалпы жағдайда, оператор әртүрлі типтегі операндаларға ие болса, операция орындалмас бұрын “кіші” тип  “үлкен” типке тартылады. Нәтиже үлкен типке ие болады. Үлкен типтерге олардың приоритеттерінің кему тәртібімен жатады: long double, double, float, int, long.

char типінің мәні – бұл бар жоғы кіші бүтін және оларды  литерлермен барлық мүмкін болатын манипуляциялауды барынша жеңілдететін арифметикалық өрнектерде еркін қолдануға болады. Мысал ретінде цифрлар тізбегін оның сандық эквивалентіне түрлендіретін atoi функциясының қарапайым жүзеге асуын келтірейік:

// atoi:  s стрингісін санға түрлендіру

 int atoi (char s[])

             { int i,n=0;

               for (i=0; s[i]>=’0’ s[i]<=’9’; i + +)

               n =10*n+(s[i]-‘0’);

               return n;

              }

Түрлендіру меншіктеу кезінде де орынға ие: меншіктеудің оң жақ бөлігінің мәні, нәтиженің типі болып табылатын сол жақ типіне келтіріледі.

Функция шақыруындағы аргумент өрнек болғандықтан, оны функцияға беру кезінде типті түрлендіруі мүмкін. Функцияның прототипінің болмауы кезінде char және short типіндегі аргументтер int-ке, aл floatdouble-ге түрлендіріледі. Егер аргументтер функцияның прототиптерінде сипатталған жағдайда, функцияны шақыру қажетті түрлендіруде автоматты түрде қосылуы керек.

 

2.2 Операторлар

 

Барлық операторлар мыналардың біріне жатады:

-      меншіктеу;

-      функцияны шақыру;

-      тармақталу;

-      циклдар.

Оператор түсінігі мен операция түсінігі байланысты.

Операциялардың келесі топтарын айыруға болады:

-      арифметикалық операциялар;

-      қарым- қатынас операциялары;

-      меншіктеу операциялары;

-      логикалық операциялар;

-      биттер бойынша операциялар;

-      өлшемді есептеу операциялары (sizeof);

-      жалғастыру операциясы (үтір).

Меншіктеу операторлары: “=”, “+=”, “-=”, “*=”, “/=”, “++”, “--”.

Мысалы: c=a+b;

Қысқаша синтаксисі     Толық синтаксисі

count+=2                         count=count+2

bonus*=2                        bonus=bonus*2

change %=10                  change=change % 10

   Мысалы:

int n=1, m=7;

n++;

cout<<n<<”\n”;

cout<<m<<”\n”;

нәтижесі:

2

6

 

Қысқартумен қатар меншіктеу операторлары адам қалай ойлайды, соған сәйкес келетін қасиетке ие. Күрделі өрнектерде жазба түсіну үшін жеңіл болып келеді. Меншіктеу операторы компиляторға тиімді код құрастыруына көмектеседі.

Меншіктеуде кез келген өрнектің мәні мен типі болып, оның меншіктеуден кейінгі сол жақ операндасының мәні және типі саналады.

Си-де айнымалыларды өсіру және кеміту үшін арналған екі ерекше операторлар бар: ”+ +” - өз операндасына 1 қосатын инкременттік оператор, және  “- -“ -  өз операндасынын мәнін 1-ге кемітетін декременттік оператор.

Өз кезегінде, бұл екі оператор префикстік (операндт алдында оларды орналастыра отырып, мысалы: + + n), сондай-ақ постфикстік (оларды операндт соңына орналастыра отырып, мысалы: n++) ретінде қолданыла алады. Екі жағдайда да n-нің мәні 1-ге артады. Бірақ “++n”  өрнегі n-ді оның мәні өрнекте қолданылғанға дейін өсіреді, ал “n+ +” қолданылғаннан кейін өсіреді. Бұл операторлардың өрнектен тысқары айнымалыларға қолданылуы, олардың орналасу орындары айнымалыға қатысты тәуелсіз. Сондықтан жоғарыда келтірілген мысалда, nline айнымалысымен өсу (“ + +  nline” немесе “nline + +” )  бірдей нәтижеге ие.

Айнымалы өрнекке кіруінің өзі басқаша жағдай. Бұл жағдайда айнымалыларға қарасты инкрементті (декрементті) операторларды орналастыру өрнектің мәнін есептеу кезінде маңызды.

Арифметикалық операциялар: “+”, “-”, “*”, “/”, “%”.

 

 

2.1 кесте- Салыстыру  операторлары

Математикалық символ

Оператор атауы

С++ -те белгілеуі

С++ мысал

Мысалдың математикалық эквиваленті

=

тең

==

x+7==2*y

x+7=2y

тең емес

!=

ans !=’n’

ans≠’n’

кем

cout<m+3

cout<m+3

кем немесе тең

<=

time<=limit

time≤limit

үлкен

time>limit

time>limit

үлкен немесе тең

>=

age>=2

age≥2

 

Салыстыру екі өрнегінің нәтижесін С-де “&&” операторымен кескінденетін логикалық “ЖӘНЕ” операция көмегімен біріктіруге болады. Мысалы: х 2-ден үлкен және 7-ден кем болса ғана логикалық өрнектің мәні ақиқат есебі: (2<x)&&(x<7). Өрнек тек екі шартта ақиқат жағдайда ақиқат болады.         

Синтаксисі:

 (1_шарт)&&(2_шарт);

Логикалық “Немесе” Си/Си++-те “||” операция көмегімен жүзеге сырылады. Осы операция арқылы біріктірілген екі шарттың бірі ақиқат болса өрнекте ақиқат болады.

Cинтаксисі:

 (1_шарт)||(2_шарт);

Логикалық өрнектің мәнін терістеу үшін “!” қолданылады.

Логикалық өрнектер программадағы тармақталу және циклдарды басқару үшін қолданылады.

Логикалық өрнектерде жақшалары толық қойылмаған жағдайда операторлар келесі тәртіппен орындалады: бірінші «!», содан соң салыстыру операторлары, содан соң “&&”, соңғы орындалатын “||”. Бинарлық операциялар сол жақтан оң жаққа орындалады. Мысалы: х+y+z  (x+y)+z жазумен тең.

Си-де биттермен манипуляция жасау үшін алты операторлар бар. Оларды char, short, int және long операндтарына ғана, таңбалы және таңбасыздарына қолдануға болады.

Кейбір биттік операцияларды суреттеу үшін getbits (x,p,n) функциясын қарастырайық, ол өрісті n биттен, х-тен кесіп алынған, р позициядан бастап, оны оң жаққа жабыстыра құрастырады. Мысалы, getbits (x,4,3) х мәнінің 4,3 және 2-ші биттерін, оларды оң жаққа жабыстыра отырып нәтиже ретінде қайтарады. Бұл функция:

//getbits:  р-ші позициядан бастап n бит алады

unsigned get bits (unsigned x, int p , int n)

{  return (x>>(p+1-n)) & ~(~0<<n); }

Бинарлы операторлардың көбісіне, яғни сол және оң операндтары бар операторларға “op=” меншіктеу операторы сәйкес келеді, мұндағы ор “–“,“ +”, ” –“, ”*”, “/”, “%”, “<<”, “>>”, “&”, “^”, “¦” операторларының бірі .

sizeof операциясының орындалуы:

printf(“\nБүтін санға жады көлемі %d”, sizeof(int));

printf(“\nСимволға  жады көлемі %d”, sizeof(char));

 

3 дәріс. Басқару

 

Дәріс мақсаты: басқару нұсқауларға жататындармен танысу.

 

3.1 Басқару нұсқауы

 

Есептеу орындалу ережесі басқару нұсқауымен анықталады.

Кез келген өрнек, егерде оның соңына нүктелі үтір қойсақ нұсқау болады, мысалы: х=0, i++, printf (…).

Си-де нүктелі үтір Паскаль тіліндегі сияқты бөлуші емес, нұсқаудың аяқтаушы литері болып табылады.

Құрама нұсқау немесе сұлба – бұл фигуралық жақша көмегімен сипаттаманы және нұсқауды біріктіру. Синтаксистік тұрғыдан, мұндай сұлба бір нұсқау ретінде қабылданады. Ол “;” аяқталмау тиіс себебі фигуралық жақша жабылғаны құрама нұсқаудын аяқталуын білдіреді.

Нұсқауды біріктірудің басқа мысалы – бұл if, else, while немесе for-дан кейінгі қойылатын дөңгелек жақша.

Сұлба соңында оң жабушы фигуралы жақшадан кейін нүктелі үтір қойылмайды.

Басқару нұсқауларға жататындар:

if- else – шешім қабылдау үшін;

else -if– көпбаспалдақты шешім қабылдау үшін;

switch – көп жолдардың бірін таңдау үшін қосу (Паскальда case операторына пара-пар);

while және for циклдері;

цикл do- while;

break және continue нұсқаулары;

goto және  белгілер нұсқаулары.

 

3.2 Шартты тармақталу операторы (if- else)

 

If - else операторы шартты оператор немесе шартты тармақталу операторы деп аталады. Бұл оператор логикалық өрнектің мәніне қарап екі альтернативтік оператордың бірін таңдайды.

1.    Синтаксисі: (әр альтернативі үшін бір оператор)

if (логикалық өрнек)

оператор1

else

оператор2

2.    Синтаксисі (әр альтернативі үшін бірнеше операторлар)

if (логикалық өрнек)

{оператор_1

оператор_2

…}

else

{оператор1

оператор2

… }

 

if(a>b) z=a;

else z=b;


нұсқаулары екі а және b мәндерінің ең үлкенінің бірін z-ке жібереді.

2 сурет - if…else операторынын орындалу сұлбасы

1 сурет - if операторының орындалу сұлбасы

Си-де тернарлық оператор “?:” бар, ол осы және оған ұқсас конструкцияларға жазбаның басқа тәсілін көрсетеді:

z=(a>b) ? a:b;        // z=max(a,b)

3 сурет – Тернарлық оператор жұмысының сұлбасы

Шартты өрнек  шынымен де өрнек болып табылады және оны өрнек жіберілетін кез келген жерде қолдануға болады.

3.3   Цикл операторлары

 

for операторының синтаксисі:

for (инициализациялау_өрнегі; логикалық_өрнек; өзгерту_өрнегі)

Цикл_операторының_денесі

Инициализациялау өрнегі

Мысалы:

 

for (number=30;number>=10; number++)

 

cout<<number;// бір оператордан тұратын цикл денесі

while синтаксисі:

while (логикалық өрнек)

{1_оператор

2_оператор

…соңғы_оператор}

while операторының мысалы:

number=30;

5 сурет- while циклі орындалуының блок-сызбасы

while (number>=10)

{cout<<number;

number--;}

do … while  синтаксисі:

do

1_оператор

2_оператор

cоңғы_оператор}

6 сурет - do… while циклі орындалуының блок-сызбасы

while (логикалық өрнек):

 

Пайдаланушы пернетақтадан енгізіп жатқан символдық жолда сөздер және символдар санын есептейтін программаны құруда getche() кітапханалық функцияны пайдаланамыз, және if…else тармақталу операторын while циклі үшін де қолданылғанын талдайық.

#include <iostream.h>

#include <conio.h>        //getche үшін

int main()

{

int chcani=0;       // бос орын емес символдар саны

int probelcani=1;// бос орын саны

char ch=’a’;     

cout<<”Жолды енгіз:”;

while(ch!=’\r’)    //Enter басылғанша

{ch=getche();    //символды алу

If(ch==’ ‘)       // егер символ бос орын  болса

probelcani++ //сөздер санын инкременттеу

else

chcani++;     //символдар санын инкременттеу

}

cout<<”\nСөздер саны:”<<probelcani<<endl;

<<”Әріптер саны:”<<chcani-1<<endl;

return 0; }

 

3.4   swich, case, break операторлары

 

switch операторы көп тармақталуды қамтамасыз ететін таңдау операторы.Тандау switch операторынан кейін жазылатын басқарушы өрнек мәні негізінде орындалады. Басқарушы өрнек bool типті мән, enum типті тұрақтыны, бүтін типті мән немесе символ қайтару тиіс. switch операторын орындау барысында компьютер басқарушы өрнекті есептейді. Басқарушы өрнектің мәніне сәйкес тұрақтыны кездесіп, ол сол тұрақтыға сәйкеc case тармақтың кодын орындайды. Мұнда екі бір-біріне тең тұрақтылар бар екі case тармақтары болмауы тиіс.

Синтаксисі:

switch (басқарушы өрнек)

{case 1_тұрақты;

1_операторлар_тізбегі;

break;

case 2_тұрақты;

2_операторлар_тізбегі;

break;

default;

үнсіз_жағдайда_берілген_операторлар тізбегі;

}

Мысал:

#include <iostream.h>

int main()

{

int cosmos;              //космостық кемелер нәсілдері

cout<<”\n1,2 немесе 3 санын енгізіңіз”;

cin>>cosmoc;         // нәсіл нөмірін енгізу

switch(cosmos)      // енгізілген нәсіл нөміріне байланысты әрекеттер

{

case 1:               //  пайдаланушы 1 енгізген жағдайда

cout<<”«Восток» космостық кемелерінін бірінші нәсілі\n”;

break;

case 2:             //пайдаланушы 2 енгізген жағдайда

cout<<”«Восход» космостық кемелерінін екінші нәсілі\n”;

break;

case 1:           // пайдаланушы 3 енгізген жағдайда

cout<<”«Союз» космостық кемелерінін үшінші нәсілі\n”;

break;

}

return 0;

}

break операторы әр case операторының тармағында болуы міндет емес.

Берілген нұсқауларға бірнеше түсініктемелер келтірейік:

-      бір-біріне қойылған if – конструкция else – бөлімінің біреуінің болмауы жазбаны бір мәнсіз талдауға әкелуі мүмкін. Бұл мәнсіздікті, else әрқашан жақын тұрған else-сі жоқ if-пен байланыстыру арқылы шешіледі;

-      if және else нұсқаулары күрделілігінің дұрыс интерпретациясы фигуралы жақшаны, сол сияқты мәтінде шегіністерді көрсету көмегімен дұрыс қоюға көмектеседі;

-      switch  қосқышының әр тармағы (соның ішінде және default-тан кейін) break – қосқыштан шығу нұсқауымен аяқталуы керек;

-      грамматика тұрғысынан for циклінің үш компоненті (өрн1; өрн2; өрн3) кездейсоқ өрнекті көрсетеді, өрн1 және өрн3 – бұл функцияны меншіктеу немесе шақыру, ал өрн2 – қатынас өрнегі. Осы үш өрнектердің кез келгені болмауы мүмкін, бірақ нүктелі үтірді қалдыруға болмайды. өрн1 және өрн3-тің болмауы, олар циклдің нұсқауында жоқ деп есептеледі; өрн2-нің болмауы, оның мәні әрқашан ақиқат екендігін білдіреді;

-      Си-де for циклдің индексі және оның шектік мәні циклдің ішінде өзгеруі мүмкін және циклдің индексінің мәні циклден шықаннан кейін әрқашан белгілі болады;

-      “үтір” операторымен бөлінген өрнектер жұбы солдан оңға қарай есептеледі. Нәтиженің типі мен мәні for нұсқауында әр үш компонент бірнеше өрнекке ие болатындай, оң жақ өрнектің мәні мен типі болып табылады, мысалы екі индексті параллель енгізу (функция аргументтерін, сипаттаудағы айнымалыларды және т.б. бөлетін үтірлерден басқа);

-       do – while циклінде  шарт ақиқат болғанда қайталау жүзеге асырылады;

-       break және continue нұсқаулары циклдің дұрыс аяқталуының альтернативі болып табылады: break нұсқауы оның ең ішкі циклдері мен қосқыштарынан тез арада шығуды қамтамасыз етеді; continue нұсқауы жақын оның циклін (for, while немесе do – while) итерацияның келесі қадамын бастауға әкеп тірейді.

-      continue циклдің келесі итерациясына көшу операторы, циклдің қалған операторларын орындамай басқаруды келесі итерацияға береді.

 

4 дәріс. Функциялар

 

Дәріс мақсаты: программа құрылымындағы функцияларды хабарлау, сипаттау, пайдалану тәсілдермен, кітапханалық функциялардмен танысу.

 

4.1 Функциялар және программа құрылымы

 

Функция - бұл атаулы біріктірілген операторлар тобы. Бұл біріктірулер программаның басқа бөлімдерінен шақырылады. Программаны функцияларға бөлу- бұл құрылымдық программалаудың негізгі принципі. Функцияларды пайдалану негізі программа кодының өлшемін азарту. Программада бірнеше рет кездесетін операторлар тізімі, функцияға шығару арқылы программа өлшемін азайтады. Программа орындалу барысында функция бірнеше рет шақырылса да, оның коды жадынын тек бір ғана аймағында сақталып тұрады.  

Функцияны хабарлау:

нәтижесінің_типі функцияның_аты ();

Мысалы:  void  start();

Функцияны хабарлау  программа мазмұнында осы функцияның коды болатынын ескертеді. void кілттік сөзі функция ешқандай мәнді қайтармайтынын, ал бос жақшалар берілетін аргументтері жоқ екенін көрсетеді. Функцияны хабарлауды фунция прототипі деп те атайды, себебі ол функцияның жалпы бейнесі немесе сипаттамасы болып табылады. Прототип компиляторға хабарланған атрибуттарға ие функцияның коды кештеу кездесетініне және функцияны, коды әлі кездеспесе де, программада шақыра беруге болады деп жеткізеді. Хабарламадағы функция туралы ақпаратты функцияның сигнатурасы деп те атайды. Хабарлама main функциясы алдында тұруы керек (функцияны анықтаудың өзі main функциясынан кейін болуы керек).

Функцияны шақыру мысалы: start();

Шақырудын прототиптен айырмашылығы, функцияны шақыруда функция қайтар атын мәні көрсетілмейді.

Кез келген функцияны анықтау келесі түрде болады:

нәтижесінің_типі функцияның_аты (формальды_параметрлер_тізімі)

             {инструкция }

Мысалы:

void start()       //функциянын тақырыпшасы

{

for int j=0; j<10;j++)     //функциянын денесі

              cout<<’!’;

cout<<endl;

}      

Егер функцияны анықтау немесе шақыру өзінің прототипіне сәйкес келмесе қателік туындайды.

Функция шақырылғаннан соң программа басқаруды функцияның бірінші операторына береді, содан соң жабық фигуралық жақша кездескенге дейін функция денесіндегі барлық операторлар орындалады, Фигуралық жақшаны кездескеннен соң басқару кері функцияны шақырған программаға қайтарылады.

void fun()

{

1-ші оператор;

2-ші оператор;

......

n-ші оператор;

}

Кейбір жағдайларда функцияны анықтау оны алғашқы шақырылуына дейін қойылады, бұл жағдайда функция прототипі қолданылмайды.

Программаның функцияға беретін мәліметтер бірлігі аргумент деп аталады.Функцияға айнымалылардың мәні де берілуі мүмкін. Функцияның аргументтері ретінде қолданылатын айнымалылар типтері, функцияны хабарлауда және анықтауда көрсетілген типтермен сәйкес болуы тиіс.

Функциямен есептелінетін мән return нұсқауының көмегімен main-ға оралады. return сөзінен кейін кез келген өрнек жазылуы мүмкін:

return өрнек;

Қажет болса, өрнек нәтиже типіне келтіріледі. 

Функция қандай-да бір мәнді міндетті түрде қайтармайды. return нұсқауы өрнексіз, ешқандай нәтижесіз мәнді бермей-ақ, оны шақыратын программаға тек қана басқаруды береді.

Функциялар үлкен есептеулердегі есептерді ұсақтарға бөледі және құрылған программаны қайтып «нөлден» бастамай, басқа жасаушылармен жасалғандарды пайдалануға мүмкіндік береді. Осы түрде таңдалған функцияларда «жасырынған» (ипкапсуляцияланған), олардың функционалдануының бөлшектері программаның басқа бөлімдері үшін болмайды, бұл программаны жалпы түсініктірек қылып оған өзгерістерді енгізуді жеңілдетеді.

Есепті кіші бөліктерге бөлу функционалдық декомпозиция арқылы шешіледі, оның нәтижесі болып олардың арасындағы қатынастар, басты бір функциялардан (main) тұратын құрамы табылады. Осы тұрғыдан кез келген программа  айнымалылар мен функциялар анықтамаларының жай және жалпы түрі бола келеді. Функциялар арасындағы байланыс мәнді және сыртқы айнымалыны қайтаратын аргументтер арқылы жүзеге асады. Функцияны бастапқы файлдарда кез келген тәртіпте орналастыруға рұқсат етіледі: бастапқы программаны функциялардың бірде-біреуі үзіліп қалмайтындай етіп файлдың кез келген санына бөлуге болады.

Функциялар екі топқа бөлінеді: мән қайтаратын (main) және қайтармайтын (void).

Егер функцияның нәтижесінің типі өткізіліп алынған болса, онда ол int типіндегі мәнді қайтарады деп шамалайды.

Бүтін емес мәндерді қайтаратын функциялар, біріншіден осы жөнінде қайтаратын мәннің типі арқылы декларациялануы қажет. Сонымен қатар, шақырушы программа шақырылатын функция бүтін емес мәнді қайтаратынын білу маңызды. Бұны қамтамасыз ететін тәсілдердің бірі – шақыратын функцияларда шақырылатынды оның сипаттауы керек.

Егер өрнекте алдын ала еш жерде сипатталмаған және одан кейін ашық жақша болатын ат кездессе, онда int типінің нәтижесін қабылдайтын мұндай ат, контекст бойынша функция аты деп есептеледі, бұдан оның аргументтеріне қатысты ешнәрсе шамаланбайды.

 

4.2 Әрекет ету аймағы және жады класы

 

Айнымалылар мен функциялар өзара байланысы туралы айтар болсақ, мұнда екі мәселеге көңіл аударамыз: бірінші мәселе бұл айнымалыға программаның қай жерінен қол жеткізуге болатыны, ал екінші - жады класы,  бұл компьютердін жадысында айнымалылардын өмір сүру уақыты. Көбісіне айнымалыға қол жеткізу аймағының екі типі қолданылады: әрекет ету, қол жеткізу аймағы шектелген (локалды) мүмкіндігі, файлдан қол жеткізу мүмкіндігі. Локалды айнымалыларға қол жеткізу аймағы олар қай блокта анықталды, тек сол жерде  мүмкін. Файлдық қол жеткізу аймағы бар айнымалылар қай файлда өздері анықталды сол файлдың кез келген жерінен оларға қол жеткізуге болады.

Жадының екі класы болады: automatic (автоматты) және static (статикалық). Алатын жады класы automatic айнымалы өмір сүру уақыты өздері қай функцияда анықталды сол функциянын өмір сүру уақытына тең. Алатын жады класы static айнымалылардың өмір сүру уақыты программанын өмір сүру уақытына тең.

Функциялардың ішінде анықталған айнымалылар локалды айнымалылар деп аталады. Оларға қол жеткізу аймағы функция аймағымен жектеледі. Оларды алатын жады класына қарай автоматты айнымалар деп те атайды. Сыртқы немесе жергілікті деп функциялардан тыс анықталған айнмалыларды атайды. Оларды айнымалыларға бірнеше функциялардан қол жеткізу қажет жағдайларда пайдаланады. Статикалық локалды айнымалы деп өмір сүру уақыты оларды бірінші рет пайдаланудан басталып, программа аяқталғанша дейін күшіндегі айнымалыларды атайды. Локалды айнымалылар және функцияның аргументтері стекте сақталады, ал жергілікті және статикалық айнымалылар динамикалық жадыда орналасады.

Кез-келген функция сыртқы (external) айнымалыға оның аты бойынша қатынас жасайды, егер бұл атауы ойдағыдай сипатталса.

Бастапқы программадан тұратын файлдың барлық жиындарына, әрбір сыртқы айнымалы үшін бір ғана анықтама болуы қажет; басқа файлдар, сыртқы айнымалыға қатынас құру үшін extern декларациясынан тұруы керек.

Сыртқы айнымалыда немесе функцияда қолданылған static нұсқауы объектінің әрекет ету аймағын файл соңымен шектейді. Бұл -  басқа файлдардан аттарды жасыру тәсілі. Статистикалық жады қарапайым сипаттаудың алдында араласатын static сөзімен спецификацияланады. val және sp айнымалыларының декларацияларының алдындағы static  нұсқауы, бұнымен тек қана push және pop жұмыс істейтін, оларды қалған функциялардан жасырады.

Әдетте функцияның аттары бастапқыда және программаның кез келген жерінен көрінеді. Егер де функция static сөзімен араласқан болса, онда оның аты ол анықталатын файлдан тысқары көрінбейтін болады. static декларациясын ішкі айнымалылар үшін де қолдануға болады.

 

5 дәріс. Массивтер

 

Дәріс мақсаты: бір, екі өлшемді массивтерді хабарлау, сипаттау, масcив көмегімен программалауды үйрету.

 

5.1 Бір өлшемді массив

 

Бір типті ақпаратты өндеу және сақтауға арналған мәліметтер құрылымы массивтер деп аталады.

Массивті құрайтын айнымалылар индекстанған айнымалылар немесе массивтін элементтері деп аталады. Квадратты жақша ішінде жазылған массив элементінің номері индексі деп аталады. Массив элементтері 0-ден басталып индекстеледі. Массив элементтерінің санын массивтің өлшемі деп түсінеді. Массивті хабарлауда массив өлшемі атынан кейін квадратты жақша ішінде жазылады.

int array[10] декларациясы аты –array, int типті 10 элементтен тұратын массиві ретінде анықталады.

double score[3] double типті үш айнымалыны хабарлағанмен тең: double score[0], score[1], score[2]. Си-де массивтің элементтері әрқашан нөлден басталып нөмерленеді. Индекс -  бүтін айнымалы мен бүтін тұрақтылардан құралған, кез келген бүтін өрнек болуы мүмкін.

Массивтерді айнымалылармен бірге сипаттауға болады:

int pop, massiv[5], min;// int типті екі айнымалы pop және min хабарланған және бір int типті, 5 элементтен тұратын  массив massiv.

int n=5;

score[n-1]=88;// score[4] массив элементіне 88 мәні меншіктелген;

Бір өлшемді массивті сиппатау синтаксисі:

типі массив_аты [өлшемі];

Массив элементтеріне қол жеткізу индекс көмегімен орындалады. Индекс элементтің массив ішіндегі орнын көрсетеді.

Mas массивіне 0-ден 9-ға дейін сандарды енгізетін программаны қарастырайық:

# include <oistream.h>

int main()  

{int Mas[10];

int t;

for(t=0; t<10; t++) Mas[t]=t;

for(t=0; t<10; t++)

cout<<”Бұл массивтің [”<<t<<”] ші элементі:”<<Mas[t]<<”\n”;

return 0;

}

Нәтижесі:

Бұл массивтің [0] ші элементі:0;

Бұл массивтің [1] ші элементі:1;

Бұл массивтің [3] ші элементі:3;

….

Массив өлшемін программада анықталған тұрақтылар көмегімен берген жөн:

const int number_student=5;

Жоғарыда көрсетілген программадағы массивті хабарлау жолы былай жазылады:

int i; score[number_student],max

Массивті инициализациялау оны хабарлауда орындалу мүмкін:

int children[3]={2,1,5};

Егер тізімдегі мәндер саны хабарланған массив элементері санынан аз болса, берілген мәндер массивтің бірінші элементтеріне меншіктеліп, қалған элементтері нөлге теңестіріледі.

children[6]={5,4.6};

 

5.2 Екі өлшемді массивтер

 

char page[3][10];//          аты page массивті хабарлайды.

Онда екі индексі бар, біріншінің мәндері 0...2, екінші индекс мәндері 0...9 аралығында.

Массив элементтеріне қол жеткізу үшін екі индексті көрсету керек: page [0][1], page[1][8]…          Бірінші индекс жолды анықтайды, ал екінші бағанды.

int massiv[2].[3]={{1,1},{2,3},{1,0}};

Массивті аргумент ретінде қабылдайтың және оның мазмұнын экранға шығаратын мысалды қарастырайық:

void display (const char p[][10], int size)

{

for (int index1=0; index1<size; index1++)

{for (int index2=0; index2<10;index2++)

cout<<p[index1][index2];

cout<<endl;

}

}

 

5.3 Массивтер және функциялар

 

Функция аргументтері ретінде массивтерді және оның бөлек элементтерін пайдалануға болады.

Мысалы:

double i,n, bin[5];

Егер MyFunction функциясы double типті бір аргументті қабылдатын болса, оны MyFunction(n); деп шақыруға болады. Массив элементтері double типті болған соң:

myFunction(bin[3]); деп шақыру да ретті.

Массив типті параметрлі функция мысалы:

void FillUp(int bill[], int size);

{

cout<<”enter”<<size<<”number:\n”;

for (int i=0;i<size;i==)

cin>>bill[i];

cout<<”The last array index used is”<<(size-1)<<endl;

}

 

5.4 Жолдар

 

Бір өлшемді массивтер көбісіне символдардан тұратын жолдарды құру үшін пайдаланады. Жолдардың бірінші типі бұл символдардан тұратын нөл мен аяқталатын(‘\0’) массив. Екінші тип бұл string класы объектілерін пайдалану. Символдардан тұратын массивті хабарлауда ол нөлмен аяқталатынын еске ала отырып, массив элементері санның жол элементері санынан 1-ге асыру көрсету тиіс.

Мысалы: 10 символдан тұратын массивті хабарлау:

char Mas[11];

Символдардан тұратын массивті пернетақтадан енгізу:

#include <iostream.h>

int main()

{char RETRO[50];

cout<<”Символдарды енгіз:-->”;

cin>>RETRO;

cout<<”енгізген символдар-->”;

cout>>RETRO;

return 0;

}

Егер енгізілген символдардың арасында пробел, табуляция, жаңа жолға көшу символдары болса “>>” операторы (cin) одан кейін орналасқан символдарды оқымай тастайды. Бұл проблеманы шешу үшін кітапханалық gets() функциясын пайдалану керек. Форматы: gets(массив_аты) (тақырыпша файл:cstdio).

Мысал:

#include <iostream.h>

#include <cstdio.h>

int main()

{char RETRO[50];

cout<<”Символдарды енгіз:-->”;

gets(RETRO);

cout<<”енгізген символдар-->”;

cout>>RETRO;

return 0;

}

Кітапханалық функциялар:

Форматтары:

strcpy(to,from);// from мазмұнын to-ге көшіреді.

strcat(s1,s2);// s2-ні s1-дің соңына қосады.

strlen(s);// s жолының ұзындығын қайтарады.

strcmp(s1,s2);//s1 s2 лексикалық салыстыру, егер s1,s2 тең болса 0 қайтарады, егер s1>s2 он сан қайтарады, егер s1<s2  теріс сан қайтарады.

Тақырыпша файл string.h (cstring). ANSI/ISO стандарты ішкі жолдық айнымалыларды өндеу және сақтау үшін string класын анықтаған.  С++ тілінің стандартты. string класы STL стандартты кітапхана шаблондарына ұксас шаблондарды пайдаланады. string класымен жұмыс жасау үшін программаға төменде көрсетілген директиваны қосу қажет:

#include <string.h> // <string>.

string типті айнымалыны инициализациялау “=” операторымен, ал екі айнымалы үшін конкатенация “+” операторымен орындалады:

str=”Hello”;

str3=str1+str2; 

Төменде көрсетілген инициализациялар бір- біріне тең:

char str[5]=(“Салем”);

char str[5]=(‘С’,’а’,’л’,’е’,’м’,’\0’);

Инициализациялау мысалдары:

int down[6]=(1,2,3,6,23,0);

int down[4][2]=(

1,1,

2,4,

3,9,

4,16);

 

5.4 Массивтер көмегімен программалау

 

Таңдау әдісі бойынша массивтерді сұрыптау: Алгоритм ең кіші болып саналатын элементті таңдаудан басталады. Содан соң ол элемент массивтің екінші элементімен салыстырылады, егер екінші элемент таңдалған элементтен кіші болса олар орын ауыстырады, осылай n-1 рет.

include <iostream.h>

int main (){

const int n=20;

int b[n];

int i;

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

cin>>b[i];

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

int min=i;

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

if (b[i]<b[min]) min=j;

int a=b[i];

b[i]=b[min];

b[min]=a;}

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

cout<<b[i]<<’ ‘;

return 0;

}

Си-дегі ең көп таралған массив түрі – литерлі массив. Литерлі массивті қолдануды мәтіндік жолды оқумен, олардың арасындағы ең ұзынынын баспаға шығаратын программамен сипаттаймыз.

Программалау технологиясы жобалаудың міндетті кезеңі болып табылады. Си тілі құрылымдық программалау тіліне жататындықтан, функционалды декомпозицияны қолданамыз, сол сияқты негізгі басқару конструкцияны қолданумен блок-сұлба түрінде жеке функцияның логикасын көрсетеді.

Біздің  мысалымыздың  программасының сұлбасы өте қарапайым:

ӘЗІРШЕ-ЦИКЛІ (тағы жол бар ма?)

    Егер (берілген жол алдыңғы жолдың ең ұзынынан да ұзынырақ болса)

ОНДА

          Оны есте сақтау

          Оның ұзындығын есте сақтау

ЕГЕР -БАРЛЫҒЫ

          Ең ұзын жолды басып шығару

БАРЛЫҚ- ЦИКЛ

Сұлбадан программа әдеттегідей бөліктерге бөлінетіндігі көрінеді. Олардың біреуі жаңа жолды алады, басқасы оны тексереді, үшіншісі есте сақтайды, ал қалғандары есептеу процесін басқарады. Ереже бойынша процесс нақты бөліктерге бөлінеді, оны Си-ге аударуға болады.

# include <stdio.h>

# define MAXLINE 1000        // енгізілген жолдың ең үлкен өлшемі

int getline (char line[], int maxline);   // функцияның прототипі getline ( ) 

void copy (char to[], char from[]);      // прототип 

                 

 // ең ұзын жолды басу

main ( )

{           int len ;              //  ағымдық жолдың ұзындығы 

  int max;                        // қарастырылған жолдың  максималды ұзындығы

  char line [MAXLINE];             // ағымдық жол

  char longest [MAXLINE];        // ең ұзын жол

  max =0;

 while ((len=getline (line, MAXLINE )) >0)

  if(len > max)

     {    max=len;

            copy (longest, line);

      }

  if (max > 0)          //  ең болмағанда бір жол болды ма?

         printf(“%S”,longest);

return 0;

}

// getline: s –те жолды оқиды, ұзындықты қайтарады

int getline (char s [], int lim)

{    int c,i;

       for (i=0; i<lim-1 (c=getchar( ))!= EOF c!=’\ n’; i++)

       s[i] =c;

       if(c= =’\n’)

              {s[i]=c;

                ++ i;

                          }

     s[i]=’\0’; // литер стрингісінің соңы

                   return i ;

             }

// copy: ‘from ’-нан ‘to’-ға көшіреміз

void copy (char to [], char from [])

{    int i=0;

       while ((to [i]=from[i])!=’\0’)

            + + i; }

main и getline функциялары аргументтер жұбы арқылы және қайтарылатын мән арқылы өзара әрекеттеседі. Бір функциялар нәтижелі мәнді қайтарады, басқасы (copy сияқты) ешқандай мәнді бермей, қандай-да бір әрекетті орындау үшін қажет. Нәтиже типі copy-дің орнында void тұрады. Бұл берілген функция ешқандай мәнді қайтармайтынын анық көрсетіп тұр.

Функцияны анықтауда массивтің өлшемін беру жадыны резервке қоюды мақсат етеді. getline-нің өзінде s массивінің ұзындығын берудің қажеттілігі жоқ, өйткені оның өлшемі main-да көрсетілген..

Құрылатын массивтің соңында литер міндетті түрде ‘\0’ литеріне (нөлдік байтпен кодталған, null – литері), литерді стринг соңына орналастыру үшін орналастырылады.

 

6 дәріс. Көрсеткіштер

 

Дәріс мақсаты: көрсеткіштер түсінігем, оларды инициализациялау әдістерімен, функцияларда пайдалануымен танысу.

 

6.1 Көрсееткіштер және адрестер

 

Көрсеткіш - бұл айнымалы адресінен тұратын айнымалы. Көрсеткіштер Си-де кеңінен пайдаланылады, бір жағынан кейбір жағдайларда да оларсыз мүмкін емес, ал бір жағынан программалар олармен қысқа және тиімді құрылады. Белгілі тәртіпті көрсеткіштердің көмегімен сақтау кезінде түсініктілік пен қарапайымдылыққа жетуге болады.

Келесі декларациялар болсын:

int x=1, y=2, z[10];

int *ip;        //ip –int-ке көрсеткіш

Унарлы оператор &(амперсанд) объектінің адресін береді. Унарлы оператор “*”- сілтемені ашу операторы. Бұл оператор көрсеткішке ғана қолданылады және көрсеткіш жіберетін объектінің мазмұнын береді.

Келесі бірнеше жолдар “*” және “&” операторлары қалай қолданылатынын көрсетеді.

ip=&x; // көрсеткішке х айнымалысының адресі меншіктелген ip - х-ті көрсетеді

y=*ip;                   // y енді 2-ге емес, 1-ге тең

*ip=0;                   // х енді 0-ге тең

ip=z;            // ip - z (бастапқы) массивінің 0 –ші элементін көрсетеді.

ip = &z [5];           // ip - енді z массивтің 5 элементін көрсетеді.

Бұдан көрсеткіш айнымалысы декларацияның синтаксисі, бұл айнымалы кездесуі мүмкін болатын “синтаксис” өрнегіне бейімделеді. Көрсетілген принципті функцияны сипаттау қатынасында да қолданамыз. Мысалы, double *dp, atof(char*) жазбасы *dp және atof (s) өрнегі double типінен екендігін, ал atof функциясының аргументі char-дың көрсеткіші (нақты түрде, стрингті көрсету) екенін білдіреді.

Көрсеткішке берілген типтің объектісіне ғана жіберілуіне рұқсат етілген.

void-тың көрсеткіші кез келген типті объектіге сілтенуі мүмкін, бірақ мұндай көрсеткішке сілтемені ашу операторын қолдануға болмайды.

Көрсеткіштердің өзі айнымалылар болғандықтан, мәтінде олар сілтемені ашу операторынсыз да  кездесуі мүмкін.

int  n  = 6;   // Объявление переменной int типті n айнымалысын хабарлау және оған 6 мәнің меншіктеу.

int *pn = malloc( sizeof ( int ) ); //  pn көрсеткішті хабарлау және оған қажет жадыны резервтеу.

*pn = 5;    // көрсеткіш  мәні 5-ке тең.

 n = *pn;   // pn  көрсетіп тұрған мәнді (5), n-ге меншіктеу

free(pn);  //  көрсеткіш алып тұрған жадыны тазарту.

pn = &n; // pn-ге  n айнымалының адресі меншіктелді (көреткіш n-ге сілтеп тұр).

n = 7;     // *pn 7 тең

 

6.2 Функцияның аргументтері мен көрсеткіштері

 

Си-де функциялар өзінің аргументтері ретінде параметрлер мәнін қабылдайтын болғандықтан, шақырған программада параметр ретінде берілген айнымалының мәнін өзгертуге болмайды. Айнымалыға қол жеткізу оның сақталған жады ауданы адресі бойынша орындалады. Жады ауданы адресін сақтау үшін программист арнаулы айнымалыларды анықтауы мүмкін. Көрсеткіштер бөлек типке жатпайды, олар үнемі анықталған бір типпен байланысты.

Қажетті нәтижені алу үшін, шақырылатын программада өзгертілуі тиісті мәнге көрсеткіштерді беру қажет. Бұл жағдайда шақырылатын функция формальды параметрлердің  көрсеткіштер ретінде сипатталуы қажет, бұдан параметрлердің мәніне қатынасу олар арқылы жанама түрде жүргізіледі. Формальды параметрлер – шақырылатын функцияда оның программасын шақыратын объектіге қатынасуды жүзеге асыратын көрсеткіштер және осы объектілерді өзгертуге мүмкіндік береді.

Синтаксисі:

типі (*аты)(аргументтер тізімі);

Мысалы:

int (*rop) (double,double);

int  типті мән қайтаратын, екі double типті аргументтері бар аты «rop» функцияға көрсеткіш. Программада сұрыптауды екі реттелмеген элементтердің орнын ауыстыратын RET функциясы арқылы орындалатынын қарастырайық.

return операторы көмегімен шақырған функцияға тек бір мәнді ғана қайтаруға болады. Ал шақырған программаға бір емес бірнеше мәндерді қайтару қажет болса көрсеткіш типті айнымалыларды пайдаланған жөң.

Мысалы:  х және y айнымалылардың мәндерін алмастыру үшін біз мұны RET функциясы арқылы орындайтын болсақ онда бұл функцияның аргументі ретінде оған екі мән беріледі және нәтижесі ретінде екі мән қайтарылуы тиіс:

RET (u,v)

int u,v;

{ int temp;

temp=u;

u=v;

v=temp;}

Егер оны  RET (x,y) деп шақырсақ, онда u мәні х мәніне, ал v – у мәніне тең болады, функция u және v мәндерін бір-бірімен ауыстырады, бірақ оларға функцияны шақырған программадан қол жеткізу мүмкін емес, себебі олар локалды, функциядан тыс жерден қол жеткізу мүмкін емес. RET (x,y) нәтижесінде х және у мәндері өзгермейді.

Көрсеткіштерді пайдалана отырып функциядан бірнеше міндерді қайтару мысалын қарастырайық:

Функцияны анықтау:

RET (int *u, int *v) /* u және v көрсеткіштер */

{ int temp;

temp = *u; /* temp мәні u * көрсетіп тұрған мәнге теңестірілді */

*u = *v;

*v = temp;

}

Онда шақырылуы:

int x,y;

RET(&x,&y); /* адрестерін беру */

 

6.3 Көрсеткіштерді инициализациялау

 

Көрсеткіштерді анықтау барысында оған бастапқы мән беру қажет (инициализациялау). Инициализатор көрсеткіштің атынан соң дөңгелек жақшада немесе теңдеу белгісінен соң жазылады:

1.   Көрсеткішке объектінің адресін меншіктеу:

int a=5;      //бүтін айнымалы

int*p=&a; //көрсеткішке а адресі жазылады

int*p(&a) //алдыңғы операцияның басқаша орындалуы

int*t=p

массив немесе функция аттары көмегімен:

int v[5];   //массив

int*v=b; //массив басының адресін меншіктеу

 

void f(int a){/*_*/};функция анықтамасы

void(*pt)(int);  //функцияға көрсеткіш

pt=f; //функцияның адресін меншіктеу

2.   Жады ауданы адресін тура меншіктеу:

char* vip=(char*)0Xb8000000;

3.   int* zero=NULL;

int* ret=0;

4.   Динамикалық жады бөлімін ерекшелеп және адресін көрсеткішке меншіктеу:

new

int*M=new int;//new операциясы int типті айнымалы орналасуға жеткілікті динамикалық жадыны ерекшелеп, бастапқы адресті N-ге жазады.

int*N=new int(10);//жоғарыдай қосымша динамикалық жады 10 мәнімен инициализацияланады

int*Q=new int [10];//int типті 10 элементке динамикалық жады бөлініп (10 элементті массив) адрестің басын Q айнымалыға жазып, осы айнымалыны массив аты ретінде пайдалануға мүмкіндік беру.

malloc

int* y=(int *)malloc(sizeof(int));//1 оператор сияқты malloc функцияны пайдаланып.

Жадыны босату:

delete n; delete m; delete [] Q; free(u;)

 

7 дәріс. Көрсеткіштермен орындалатын арифметика

 

Дәріс мақсаты: көрсеткіштермен орындалатын операциялармен танысу, көрсеткіштер массивін қолдану.

 

7.1 Адрестік арифметика

 

Көрсеткіштермен келесі операцияларды орындауға болады: адресті алып тастау, меншіктеу, тұрақтымен қосу, азайту, инкремент, декремент, салыстыру, типтерді келтіру, адресті алу операциясы- &.

short *p=new short [7];

p++;//р мәнін екіге көбейту.

Егер р массивттің кейбір элементтеріне көрсеткіш болса, онда “р+ +” р-ны келесі элементке сілтейтіндей жылжытады, онда  “р+=I” оны бұрын көрсеткен i-ші элементке сілтейтіндей арттырады. Бұл және осыған ұқсас конструкциялар  адрестік арифметика деп аталатын, көрсеткіштермен жасалатын арифметиканың өте қарапайым мысалдары.       

Си тілінің бір өзінде көрсеткіштерді, массивтерді және адрестік арифметиканы біріктіру - оның ең мықты жақтарының бірі.

Көрсеткішті кез келген басқа айнымалы сияқты сәйкес типті мәліметтердің бұрын анықталған адресіне келтіретін нөл немесе өрнек сияқты инициализациялауға болады.

static  char –декларациясы p-ны  char –ға көрсеткіш ретінде анықтайды және оны ar  массивінің адресімен инициализациялайды. Көрсетілген декларация мынадай түрге ие болар еді:

static  char *p=&ar[0]

массив аты оның нөлдік элементінің адресі болып табылатындықтан қасиеттерді атап өтейік:

 -  кейбір ережелерді сақтай отырып көрсеткіштерді салыстыруға болады. Егер p және q бір массивтің элементтеріне көрсетсе, онда оларға қатынас операторын “=”,  “!=”, “<”, “<=”, “>” және т.б. қолдануға болады. Мысалы, егер  р  q-ден бұрынғы массив элементіне сілтеме жасаса, p<q  түріндегі қатынас шындық болады;

-      кез-келген көрсеткішті әрқашанда нөлмен тең болуын және тең болмауын салыстыруға болады;

-      әртүрлі массивтер элементтеріне сілтеме жасайтын көрсеткіштер үшін арифметикалық операциялар немесе салыстырулар нәтижесі анықталмаған;

-      көрсеткіштерді және бүтіндерді қосуға және кемітуге болады. p+n жазбасы р көрсететін объектілерден кейінгі  n-ші  орынды алатын объектінің адресін білдіреді. Бұл р сілтемесін объектінің типіне қатыссыз шындық; n автоматты түрде объектінің өлшеміне (типіне) сәйкес келетін коэффициентке көбейеді. Өлшем жөніндегі ақпарат р-ның сипаттамасында берілген. Егер, мысалға int төрт байттан тұрса, онда көбейту коэффициенті төртке тең болады;

-      сондай-ақ көрсеткіштерді кемітуге боады. Мысалы, егер p және q бір массивтің элементтеріне сілтеме жасаса және р<q, онда q-p+1 дегеніміз  р-дан q-ға дейін элементтер саны болады;

-      көрсеткішті арифметика объектінің типін ескереді: егер ол char-ға қарағанда көп жадыдан орын алатын float мәнімен жұмыс жасаса және  р-көрсеткіш float-қа, онда  “р++”  р-ны float-тың келесі мәніне жылжытады.

“I am a string”түрінде жазылған, стрингтік тұрақты литер массиві болып табылады. Ішкі көріністе бұл массив программа стринг соңын таба алатын ‘\0’, “бос” литермен аяқталады.

Стрингтік тұрақтыға қатынас оның бірінші элементіне көрсеткіш арқылы жүзеге асады. 

Әдетте стрингілік тұрақтылар функцияның аргументі ретінде қолданылады, мысалы printf-та. Келесі сипаттаманы қарастырайық

char *pmessage;

pmessage=”now is the time” меншіктеуі; Осы айнымалыға сілтемені литерлік массивке орналастырамыз, бұдан стрингтің өзі көшпейді, тек оған көрсеткіші ғана көшіріледі.

Си-де стрингпен жұмыс істеу операциясы түгелдей қарастырылмаған.

Литерлі массивті стрингілік тұрақтыға сілтемемен инициализацияланған, литер тізбегінің ‘\0’ массиві ретінде және көрсеткіштің көмегімен екі тәсілмен ұйымдастыруға болады, яғни;

char armessage [ ] = “now is the time”;

char *pmessage = “now is the time”;

сәйкес келеді.

Осы екі анықтамалардың арасында маңызды айырмашылық бар, көрсеткіш бұл айнымалы, ал массив аты – тұрақты көрсеткіш.

 

7.2  Көрсеткіштер массиві

 

Кез келген басқа айнымалылар сияқты, көрсеткіштерді массивке топтастыруға болады. Ақпараттық жүйелерде іздеу алгоритмдері және әртүрлі табиғаттағы мәліметтерді сұрыптау, жеке жағдайда кез келген ұзындықтағы мәтіндік жолдар кеңінен қолданылады. Іздеу алгоритмдері мен сұрыптаудың тиімділігі көп жағдайда мәліметтердің көрінісін таңдаумен байланысты. Осындай ыңғайлы және тиімді көріністің бірі кез келген ұзындықтағы мәтіндік жолдың басындағы көрсеткіштер массиві болып табылады.

Жадыда жолдар бір-бірімен тығыз орналасқандықтан, әр жеке жолға көрсеткіш арқылы оның бірініші литеріне қатынас жасау жүзеге асады.

Көрсеткіштердің өзін массив түрінде ұйымдастыруға болады. Мүмкіндіктердің бірі: екі жолдарды салыстыру арқылы – олардың strcmp функцияларына көрсеткіштерді беру. Жолдардың орындарын ауыстыру үшін, массивте олардың көрсеткіштерінің орнын ауыстыру жеткілікті (жолдардың өзін емес).

Бұдан екі мәселе бірдей шешіледі: біреуі – жадыны басқару күрделілігімен, ал екіншісі – жолдардың өзін ауыстыруда жинақталған үлкен шығындармен байланысты.

Сұрыптау процесі үш кезеңге бөлінеді:

-      енгізу кезінде барлық жолдарды оқу;

-      енгізілген жолдарды сұрыптау;

-      оларды рет-ретімен басып шығару.

Енгізу программасы барлық жолдардағы литерлерді оқу және есте сақтау керек, сол сияқты жолдардың көрсеткіштер массивін тұрғызу керек. Бұл функция, сол сияқты, енгізілген жолдардың санын есептеу керек – бұл ақпарат сұрыптау және баспаға шығару үшін қажет болады.

Шығару программасы жолдарды басып шығарумен ғана айналысады  және ол көрсеткіштер массивінде оларға сілтеме жасайтындай ретпен орындалуы керек.

Сұрыптау алгоритмі реінде 1962 жылы К.А. Хоор ұсынған тез сұрыптауды пайдалану ұсынылған.

Си-де тікбұрышты көпөлшемді массивтерді және жеке жағдайда екі өлшемді массивтерді беру мүмкіндігі бар. Си-де екі өлшемді массивтің ерекшелігі жазбаның формасында ғана, ал қалған жағдайда оны басқа тілдердегідей айтуға болады. Элементтер жолдармен есте сақталады, сәйкесінше, олар жадыда қалай орналасқан сияқты ретпен таңдағанда, көбінесе ең  бірінші индексі өзгереді.

Массив фигуралы жақшамен жабылған бастапқы мәндердің тізімімен инициализацияланады; екі өлшемді массивтің әр жолы сәйкес ішкі тізімдермен инициализацияланады.

Егер екі өлшемді массив функцияға аргумент ретінде берілсе, онда оның параметріне сәйкес декларация бағандар санынан тұруы керек; осы жағдайда, әдеттегідей функцияға массив жолдарының сілтемесі берілетіндіктен, жолдар саны болмайды.

Екі өлшемді массив декларациясын қарастырайық;

int ar[5] [10];

Егер ar массиві f –тің кейбір функциясына берілсе, онда бұл функцияны келесі түрде анықтауға болады:

f(int array[5][10]) {…};

Бұның орнына төмендегіні жазуға болады:

f( int array [][10]) {…}

мұнда жолдар санының маңызы жоқ болғандықтан немесе

f( int (*array )[10]) {…}

соңғы жазбада параметр int типті 10 мәннен тұратын массивтің көрсеткіші екенін сипаттайды. Тік [] жақшаның *-ға қарағанда приоритеті жоғарырақ болғандықтан, мұнда жақша қажет болады. Жақшасыз (дөңгелек) декларация int * array[10]   int-тің 10 көрсеткішінен тұратын массивті анықтайды. Жалпы жағдайда тек бірінші өлшемді (бірінші индекске сәйкес келетін) бермеуге болады, қалған басқаларының өзгешеліктерін анықтау қажет.

Көрсеткіштер массивтің инициализациясының механизмін n-12 айдың атынан тұратын, стринг литеріне сілтеме жасауды қайтаратын, month-name (n) функциясының мысалында көрсетейік. Бұл функция статистикалық массивтерді қолдануды көрсету үшін өте жақсы. Функцияның стринг массивтері бар, олардың біреуіне ол сілтеме жасауды қайтарады.

month_name: char * month _ name (int n) – n-12 айдың атын қайтарамыз

{ static char * name [] { “Дұрыс емес ат”,

“Қаңтар”, “Ақпан”, “Наурыз”, “Сәуір”, “Мамыр”, “Маусым”, “Шілде”, “Тамыз”, “Қыркүйек”, “Қазан”, “Қараша”, “Желтоқсан”,};

return (n<1 ¦¦ n>12)? name [0]: name [n];

Инициализатор ретінде стрингтер тізімі қызмет атқарады, олардың әрқайсысына массивте белгілі бір орын сәйкес келеді. і-ші стрингтің литері жадыда орналасқан және оларға көрсеткіш name [і]-де есте сақталады. name массив өлшемінің өзгешеліктері анықталмағандықтан, компилятор оны берілген бастапқы мәннің мөлшері бойынша есептейді (инициализацияланатын өрнек).

Екі өлшемді массив пен көрсеткіш массивтерінің арасындағы айырмашылық қандай? Келесі анықтамалар үшін:

int ar [10][20];

int *x[10];

ar[3][4] жазбасы және x[3][4]-те int типінің кейбір мәндеріне сілтеме синтаксистік жағынан дұрыс болады. Бірақта ar ғана расында да екі өлшемді массив болып табылады: int типінің екі жүз элементі үшін жады бөлінеді, ал ar [жол, баған] элементін жылжытып қою массивтің басынан бастап, оның тікбұрышты табиғатын ескере отырып, 20 * жол + баған, формуласы бойынша есептеу жүргізіледі.

х массиві үшін 10 ғана көрсеткіш анықталады және ол инициализациясыз. Инициализация анық түрде – статистикалық немесе есептеу процесінде берілуі тиіс.

Егер әрбір x элементі бес элементті массивке сілтеме жасаса, онда нәтижесінде жадыда int типті 50 мәнді орналастыру үшін және көрсеткіштердің 10 ұяшықты үшін кеңістік бөлінеді.

Көрсеткіштердің маңызды артықшылығы мұндай массивтің жолдары әртүрлі ұзындыққа ие бола алатындығында.

Си-ді қолдауды қамтамасыз ететін операциялық жүйеде, командалық жолдың көмегімен жіберілетеін программадағы параметрлер немесе аргументтерді беру мүмкіншілігі бар. Біріншіден, әдетте argc (argument count) деп аталатын, командалық жолда беріген аргументтер саны тұрады. Екінші argv (argument vector) аргументтердің өзі тұратын литерлік стрингтер массивіне сілтеме жасау болып табылады. Бұл стрингтермен жұмыс жасау үшін әдетте бірнеше деңгейлердің көрсеткіштері қолданылады. Бір жолдағы командалық жады аргументтерін бір-бірінен бос орынмен бөле отырып басып шығаратын, echo (эхо) деп аталатын қарапайым программа. Мысалы. Сонымен команда;

Echo Здравствуй, мир! – ді басып шығарады.

# include <stdio.h>

main (int argc, char*arg [])

{int i;

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

printf (“% s %  s”, argv [i], (i<argc-1)? ” ”: ” ”);

printf (“\n”);

return  0 ;}

argv [0] келісімі бойынша шығарылатын программалық аты бар, сондықтан argc-нің мәні әрқашанда 1-ден кем болмайды. Егер argc=1, онда командалық жадыда программаның атынан кейін ешқандай да аргументтер болмайды. Біздің мысалымызда argc 3-ке тең, және сәйкесінше argv[0]=“echo”; argv [1]= “Здравствуй”, argv [2]= “мир!” болады. Стандарт argv [argc] әрқашанда бос көрсеткіш болғанын талап етеді.

argv көрсеткіштер массивіне көрсеткіш болғандықтан, біз онымен индекстелінетін массив ретінде емес, көрсеткіш ретінде жұмыс жасай аламыз. echo, программаның екінші версиясын келтіреміз, мұнда көрсеткішке көрсеткіш, сондай-ақ профикстік операторлар ұғымдарын қолданылады.

# include <stdio.h>

main (int argc, char * argv [])

{while (--argc >0)

printf (“% s % s”,*++ argv,(argc>1)?’’:’’);

printf (“\n”);

return  0 ;}

 

8 дәріс. Функцияның көрсеткіштері

 

Дәріс мақсаты: функцияға көрсеткіштерді анықтаумен, олармен айнымалылармен сияқты жұмыс жасауды үйрету.

 

8.1  Функцияның көрсеткіштері

 

Си-де функцияның өзі айнымалы болып табылмайды, бірақ функцияға көрсеткішті анықтауға болады және олармен әдеттегі айнымалы ретінде жұмыс жасауға болады: меншіктеуге, массивте орналастыруға, функцияның параметрі ретінде беруге, функциядан нәтиже ретінде қайтаруға т.б.

Бұл мүмкіншіліктерді суреттеу үшін  жолдарды лексикографикалық ретпен емес, n-міндетті емес аргументті беру кезінде жолдарды олардың сандық мәні бойынша реттейтін сұрыптау программасын қолданайық.

Сұрыптау үш бөлімнен тұрады:

-      салыстыруға, жұп объектілердің ретін анықтайтын;

-      алмастыруға, жұп объектілердің ретін керісіншеге алмастыратын;

-      сұрыптаушы алгоритм, барлық объектілер реттелмейінше салыстыру мен алмастыруды жүзеге асырады.

Атап айтсақ, сұрыптау алгоритмі салыстыру және алмастыру операцияларынан тәуелсіз, сондықтан оған салыстыру мен алмастырудың әртүрлі функцияларын параметрлер ретінде бере отырып, алгоритмді сұрыптаудың әртүрлі критерилеріне бағыттауға болады (сызықтық таңдау, стандартты алмасу, Шелл сұрыптауы, қайықтық (челнок) сұрыптауы және т.б.).

#include <stdio.h>

#include <string.h>

#define MAXLINES 500              // жолдардың максималды саны

char *lineptr [MAXLINES];      // жолдарға көрсеткіштер массив    

int readlines (char *lineptr[ ], int nlines);    

void writelines (char *lineptr[ ], int nlines);

void qsort (void* lineptr[ ], int left, int right, int (*comp)(void*, void*));

int numcmp(char*,char*); //жолдарды сұрыптау

main (int argc, char*argv[ ])

       int nlines;        // оқылған жолдар саны

int numeric=0;         // 1, егер сұрыптау сандық мәні бойынша

       if (argc>1 && strcmp(argv[1],”-n”)= =0)

          numeric=1;

if(( nlines=readlines(lineptr,MAXLINES))>=0)

          {qsort((void**) lineptr, 0, nlines-1,

          (int (*)(void*,void*))(numeric?numcmp:strcmp));

   writelines(lineptr,nlines);

return 0;}

          else

          {printf(“өте көп \n жолы енгізілген”);

                    return 1;}

           }

qsort, strcmp және numcmp функциясына көңіл аударғанда олардың  аттары осы функциялардың адрестері ретінде айтылады. Сондықтан & операторы массивтің атының алдына қажет болмағандықтан, олардың алдарында да керек емес (массив аты мен функция аты – бұл көрсеткіштер).

qsort функциясы тек стрингтерді ғана емес, кез-келген типтегі берілгендерді өңдейді. Прототиптен көрінгендей qsort функциясы өз аргументтері ретінде күтеді: сілтемелер массивін екі бүтін мәнді және функцияны екі аргумент – көрсеткіш ретінде. Аргументтер көрсеткіштері ретінде void* типіндегі жалпыланған көрсеткіштер берілген. Кез келген көрсеткішті void* типіне және керісінше ақпаратты жоғалтусыз қайта қалпына келтіруге болады. Сондықтан void*аргументтерін алдын ала түрлендіріп барып қана, біз qsort-пен жұмыс жасаймыз. Салыстыру функциясының ішінде оның аргументтері оған қажетті типке келтіріледі. Шындығында да бұл алмастыруға ешқандай да әсерін аргументтерге тигізбейді, олар тек компилятор үшін типтердің келісімділігін қамтамасыз етеді.

// qsort – сұрыптайды v[left]…v[right] өз ретімен

void qsort (void*v[ ],int left,int right, int (*comp)(void*, void *))

{         int i, last;

void (swap (void *v[ ], int, int);

if (left>=right)  // егер массивтік екі элементтен кем болмас, ештеме жасалынбайды.

return;

swap (v, left,(left+right)/2);

last=left;

for (i=left+1; i<=right; i++)

if ((*comp)(v[i],v[left])<0)

   swap(v, ++last, i);

    swap (v, left, last);

qsort (v, left, last-1, comp);

qsort (v,last+1, right, comp);}

qsort функциясының төртінші параметріне көңіл аударыңыз:

int (*comp)(void*, void*)

Бұл параметр comp  функцияға көрсеткіш және екі аргументі бар көрсеткіш және int типінің нәтижесін хабарлайды. Бұдан, comp функцияға, көрсеткіш болғандықтан *comp – функция болады, онда (*comp) (v[i], v[left]) өрнегі – оған көңіл бөлу (шақыру).

Екі стрингті салыстыратын numcmp функциясының мәтінін оларды сан ретінде қарастырып отырып келтірейік: алдын ала олар atof функциясының сандық мәніне аударылады.

#include <stdlib.h>

\\ numcmp: s1 мен s2-ні сан ретінде салыстырады

int numcmp (char *s1, char *s2)

{         double v1, v2;

v1=atof(s1); v2=atof(s2);

if (v1<v2) return –1;

else if (v1>v2) return 1;

else return 0;}

 

8.2  Күрделі декларациялар

 

Кейде Си декларация синтаксисін, әсіресе, функцияның көрсеткіштері барларды сөз қылады. Қарапайым жағдайда бұл синтаксис жақсы, бірақ күрделі жағдайларда декларация жақшаларымен аса қаныққан және оларды солдан оңға қарай оқу мүмкін емес болғандықтан, ол қиыншылық тудырады.

Мәселені келесі екі декларацияның айырмашылығымен мысал келтіре отырып жазайық: 

int * f();       // f int-қа көрсеткішті қайтарады.

int ( * pf)(); // pf – int-ті қайтаратын функция көрсеткіш.

* префикстік операторлардың приоритеті () операторының приоритетіне қарағанда төмен, сондықтан екінші жағдайда жақшаның болуы міндетті емес.

Келесі декларацияны және олардың сөздік сипаттамасын қарастырайық:

char ** argv÷ argv - көрсеткіштен көрсеткішке char-ға;

int (*array) [13] ÷ array- int-тен массив [13]-ке көрсеткіш;

void* comp ()   ÷ comp - void -қа қайтаратын көрсеткіш функциясы;

void ( * comp) ( ) ÷ comp - void-қа қайтарушы көрсеткіш функциясы;

char ( *(*f())[]()÷ f - char-ды қайтарушы функцияға көрсеткіштерден массивке [] қайтарушы көрсеткіштердің функциясы;

char ( *(*ar[3])()) [5] ÷ ar- char  типінен тұратын [5] массивіне көрсеткішті қайтаратын функцияға [3] көрсеткіштер массиві.

Си тілінде типтерді түрлендіру кейде анық емес түрде жүргізіледі, мысалы үнсіздік бойынша түрлендіруде немесе меншіктеу процесінде, типтерді келтіру операцияларын орындау жолымен кейде анық түрде жүргізіледі. Типтерді түрлендіру функциясының аргументі ретінде берілген, мәндер түрленгенде орындалады. Көрсеткіштерді түрлендіру жағдайын қарастырайық.

Бір типтің мәніне көрсеткіш басқа типтің мәнінің көрсеткішіне алмастырыла алады. Бірақ нәтиже әртүрлі типтегі объектілердің түзетілуіне байланысты талаптардағы өзгерістерге және типтердің жадыда алатын өлшемдеріне байланысты анықталмаған болуы мүмкін.

 Жариялануы кезінде көрсеткіш үнемі кейбір типпен байланыса алады. Жеке жағдайда бұл void типі болуы мүмкін. void-қа көрсеткішті кез келген көрсеткішке алмастыруға болады және керісінше.

Кез келген тип көрсеткіштері функция көрсеткіштеріне алмасуы мүмкін және керісінше.

Арнайы кілттік near, far, huge сөздері программалауға көрсеткіштердің форматы мен өлшемдерін модификациялауға мүмкіндік береді. Компилятор таңдалған моделдің жадысында қабылданған көрсеткіштің өлшемін ескереді және адрестік мәндердің түрлендіруінің сәйкестілігін анық емес түрде жүргізуі мүмкін. Сонымен, көрсеткіштің функция аргументі ретінде берілуі, оның өлшемінің келесі екі мәндердің үлкеніне түрленуінің анық емес болуына әкеліп соғуы мүмкін:

-      жадының әрекет етуші моделі үшін көрсеткіштің қабылданған көрсеткішіне;

-      аргументтің тип өлшеміне.

Егер көрсеткіштің – аргумент типі анық көрсетілген болса, сондай-ақ near, far, huge, модификаторларымен бірге берілген функцияның прототипі (функцияның алдын ала жариялануы) болса, онда дәл осы типке түрлендіру жүргізіледі.

Егер функцияға көрсеткіш көрсетілсе, онда оның типтік түрлендіруде аргументтердің басқа типтерін беруге болады. Мысалы:

int (*p) (long);       // long типінің мәнін тосатын және int типін

                            // қайтаратын функцияға көрсеткіш жариялау

(*(int(*)(int))p)(0);    - көрсеткіш бойынша функцияны шақыру.

 

9 дәріс. Құрылымдар

 

Дәріс мақсаты:  құрылымдарды Си тілінде хабарлау, сипаттау, олармен жұмыс жасау, пайдалануды тәсілдерімен үйрету.

 

9.1 Негізгі мәліметтер

Құрылым – бұл араларында логикалық өзара байланысы бар, жұмыс істеу ыңғайлы болу үшін бір атауға топтастырылған бір немесе бірнеше айнымалылыр (әртүрлі болуы мүмкін). Құрылымдар күрделі мәліметтерді (әсіресе үлкен программаларда) ұйымдастыруға көмектеседі және өзара байланысқан айнымалылар тобын жиынның жеке элементіндей емес, біртұтас ретінде түсіндіреді.

Құрлымдар мысалдары: төлемдік ведомостардың жолдары (қызметкерлердің толық аты, адресі, социалды сақтандыру карточкаларының нөмері, еңбек ақысы және т.б. мәліметтер), сыныпқұрылымы (оқушының аты-жөні, сынып әрпі, орташа балы), футбол командасы құрылымы (тренері, команда аты, турнирдік кестедегі орны). Құрылымдағы мәліметтер әртүрлі типті болуы мүмкін. Паскаль тілінде құрылымдарды “жазбалар” деп атайды.

Құрылымдар көшірілуі мүмкін, олармен меншіктеу операциялары орындалуы мүмкін, оларды функцияға аргументтер ретінде беруге болады және ол функцияны олардың нәтижелері ретінде қайтаруға болады. Автоматты құрылым мен массивтер үшін сол сияқты инициализация рұқсат етіледі.

«Нүкте» құрлымының декларациясы келесі түрде болады:

          struct point

          {        int x;    // декларация тізімі фигуралы жақшада

                    int y;  };

struct klass

          {        char name[20];     //  декларация тізімі фигуралы жақшада

                    char klass_name;

float bal;                };

Кез келген құрылым “struct” кілттік сөзінен басталуы тиіс. point, klass бұл құрылым тегі (аты) (tag – ярлык, этикетка).

Тег берілген түрдегі құрлымның атын береді және әрі қарай фигуралы жақшамен аяқталған декларацияның қысқаша белгіленуі ретінде қызмет атқарады.

Құрылымда тізілген айнымалылар мүшелер деп аталады. Тегтер мен мүшелердің аттары программаның кәдімгі айнымалыларының аттармен сәйкес келуі мүмкін. Мүшелердің аттары әртүрлі құрлымдарда кездесуі мүмкін. .

Құрылым декларациясы – бұл тип.

struct {…}x,y,z;

жазбасы құрлымдық типтің үш айнымалыларының сипаттамасын білдіреді.

struct klass a, b[5],*c;

Мұнда біз struct klass типті a айнымалыны,  struct klass типті 5 элементтен тұратын b массивін және  struct klass айнымалыға нұсқағышты сиппатадық.

Құрылымды инициализациялау: struck klass a=(“Andrey”,”B”, 4.7);

Айнымалылар тізімінен тұрмайтын құрылым декларациясы жадыны резервке алмайды: ол тек шаблонды немесе құрылым үлгісін сипаттайды. Егер құрылымның тегі болмаса, онда осы тегтерді объектілерді анықтауда қолдануға болады. Мысалы, struct point pt; декларациясы struct point типті pt құрылымдық айнымалыны анықтайды.

Құрылымдық айнымалыны оны анықтауда инициализациялауға болады:

struct point maxpt = {320, 200};

Автоматты құрылымдарды (auto жады класының құрлымы) сәйкес келетін типтегі құрылым түрінде нәтижені қайтаратын меншіктеумен  немесе функцияға айналдырумен инициализациялауға болады.

Құрлымның жеке мүшесіне қатынасу келесі түрдегі конструкциямен жүзеге асады:

құрылым мүшесінің_аты

Мысалы, pt координаттар нүктесін келесі түрде басып шығаруды жүзеге асыруға болады:

printf (“%d,%d”, pt.x, pt.y);

Құрылымдар бір-біріне қойылуы мүмкін.

 

9.2  Құрылымдар мен функциялар

Құрылымдарға орындалатын операциялар – бұл оларды көшіру, меншіктеу, & операторының көмегімен аталатын адрес және олардың мүшелеріне қатысудың жүзеге асырылуы. Құрылымды функцияға аргумент ретінде беру және оларды нәтиже түрінде функциялардан қайтару. Сондай-ақ көшіру және меншіктеу операцияларына жатады. Құрылымдарды салыстыруға болмайды.

Құрылымдық объектілерді функцияға берудің үш тәсілі бар:

-      компоненттерді (мүшелерді) жеке беру;

-      барлық құрылымдарды түгелдей беру;

-      көрсеткішті құрылымға беру.

Егер функцияға, оны түгелдей көшіретін үлкен құрылым берілсе, көрсеткішті оған беру тиімдірек.

struct point *pp; декларациясы рр - struct point типті структураға көрсеткіш екенін хабарлайды.

Егер рр – point құрылымына жіберілсе (одан болса), онда *рр-тің өзі құрылым, ал (*рр).х және (*рр).у – point құрылымының мүшелері болады. рр көрсеткішін қолдана отырып:

struct point origin, *pp;

pp=&origin;

printf (“origin:(%d,%d)\n”,(*pp).x,(*pp).y);

жазуға болады.

* операторының приоритетіне, операторының приоритеті жоғары болғандықтан, (*рр).х-дағы жақша қажет.

“Құрылымдағы көрсеткіш” түсінігімен байланысты мүшелерге қатынас жасау үшін жазбаның қысқаша формасы енгізіледі. Егер р – құрылымға көрсеткіш болса, онда p->құрылым_мүшесі – оның жеке мүшесі болады. Сондықтан printf-ты келесі түрде жазуға болады.

printf(“origin:(%d,%d)\n”,pp->x,pp->y);

Екі және -> операторларда солдан оңға қарай орындалады.

Құрылым мүшелеріне қатынас жасау. және -> операторлары функцияны шақыру ( ) және массивті индекстеу [ ] операторларымен бірге приоритеттер иерархиясында ең жоғарғы орынға ие және басқа кез келген операторлардан бұрын орындалады.

Операциялардың орындалу тәртібін өзгерту үшін анық жақшалар керек. (++р) -> len-де len-нің мәнін алмас бұрын, программа р көрсеткішін жылжытады.

Си программасының мәніне кіретін әр кілттік сөздердің санын анықтайтын программаны құрастырайық. Бұл үшін біз кілттік сөздерді стрингтің массиві түрінде, сондай-ақ кілттік сөздерді есептеуді бүтін массивтер түрінде сақтай білуіміз керек болатын, варианттардың біреуі – бұл екі параллель массив болу керек.

char *keyword [NKEYS];

int keycount [ NKEYS];

Бірақта олардың параллельдігі, сақтаудың басқаша - құрылым массиві арқылы ұйымдастыруды ойға салады. Әр кілттік сөзді келесі сипаттамалар түрімен

char *word;

int count;  жазуға болады

Мұндай жұптар массивті құрайды.

struct key

          {        char *word;

                    int count;

          }keytab[NKEYS];

декларациясы key типті құрылымды сипаттайды және keytab массивін анықтайды және бір жерде жады бөлінетін және осы типтің әр элементі құрылым болады. Мұны басқаша жазуға да болады:

struct key

          {        char *word;

                    int count;

          };

struct key keytab[NKEYS];

keytab массиві тұрақты аттардың жиынынан тұратындықтан, оны сыртқы массив ретінде жасаған жеңіл және анықтау кезінде бір рет инициализациялаған дұрыс:

struct key     {

                    char *word;

                    int count;

                    } keytab[ ]={“auto”,0,”break”,0,/*…*/”while”,0};

keytab массивтің элементтерінің саны инициализаторлардың мөлшері бойынша есептелінеді.

Си-де компиляция кезінде жұмыс жасайтын sizeof, унарлы оператор бар. Оны кез-келген объектінің өлшемін есептеу үшін қолдануға болады.

sizeof объект  және        sizeof (тип аты)

өрнегі көрсетілген объектінің өлшеміне немесе байттың типіне тең бүтін мәндерді береді. Объект айналымы, массив немесе құрылым болуы мүмкін. Тип аты ретінде базалық типтің аты немесе туындылы типтің аты, мысалы құрылымның немесе көрсеткіштің қолданылуы мүмкін.

 

 

10 дәріс. Құрылымның көрсеткіштері

 

Дәріс мақсаты: бинарлы ағаш мәліметтер құрылымында көрсеткіштерді пайдалануды үйрену.

 

10.1  Құрылымның көрсеткіштері

 

Құрылымның көрсеткіштері мен құрылым массивтеріне тиісті кейбір моменттерге мысал келтіру үшін, массивтің элементтерін алу үшін көрсеткіштерді қолдана отырып, Си программасының мәтініндегі кілттік сөздерді есептеу программасын көрсетейік.

#include <stdio.h>

#include <ctype.h>

#include <string.h>

#define MAXWORD 100

int getword (char*, int);

struct key * binsearch (char*, struct key *, int);

// Си-де кілттік сөздерді есептеу

main ( )

{         char word [MAXWORD];

struct key *p;

while (getword (word, MAXWORD)!=EOF)

if (isalpha(word[0]))

if ((p=binsearch(word, keytab, NKEYS))!=NULL)

          p->count++;

for (p=keytab; p<keytab+NKEYS; p++)

if (p->count > 0)

printf (“%4d%s\n”,p->count, p->word);

return 0;

}

// binsearch: tab[0]…tab[n-1-тағы сөзді табу]

   struct key * binsearch (char *word, struct key *tab, int n)

{         int cond;

struct key *low=&tab[0];

struct key * high = &tab[n];

struct key *mid;

while (low<high);

{        mid=low+(high – low)/2;

          if ((cond=strcmp (word,mid->word))<0)

                              high=mid;

          else if (cond > 0)

                              low=mid+1;

          else

                              return mid;

}

return NULL;}

Егер binsearch функциясы кілттік сөзді тапса, онда ол көрсеткішті оған береді, кері жағдайда ол NULL-ді қайтарады. keytab массивінің элементіне қатысу көрсеткіш арқылы жүзеге асады. low және high үшін инициализаторлар ретінде басына және массивтің соңынан кейінгі орынға көрсету қызмет атқарады. mid = (low +high)/2 теңдеуі арқылы орташа элементті есептеуге болмайды, өйткені көрсеткіштерді қосуға болмайды. Бірақта оларға алу амалын қолдануға болады, high-low элементтер саны болғандықтан,

mid = low + ( high – low )/2

меншіктеуді low және high-тің ортасында жатқан mid-тің элементінің көрсеткішін орнатады.

for циклінде p++ операторы р-ны массивтің келесі құрылымдық элементіне өсіреді. Құрылым өлшемі оның мүшелерінің өлшемдерінің қосындысына тең болады деп ұйғаруға болмайды. Sizeof операторы дұрыс мәнді қайтарады. Getword функциясы кіру ағынының келесі сөзін немесе литерін қабылдайды. Сөз ретінде әріптен немесе жеке бос орынсыз литерлерден басталатын цифр - әріптер шынжыры деген түсінік алынады. Файлдың соңы бойынша функция EOF-ты береді, ал қалған жағдайларда сөздің бірінші литерінің коды немесе жеке литер коды, егер ол әріп болмаса, оның мәні болып табылады.

// getword: келесі сөзді немесе литерді қабылдайды

int getword (char * word, int lim)

{int c, getch (void);

void ungetch (int);

char * w =word;

while (isspace (c=getch( ));

if (c!=EOF) *w++=c;

if (!isalpha(c))

{        *w=’\0’;

return c;}

for (; --lim >0; w++)

if (!isalnum (*w=getch( )))

{        ungetch (*w);

break;}

*w=’\0’;

return word [0];    }

ungetch-ті пайдалану кіру ағынындағы артық литерді қайтаруға мүмкіндік береді. getword-та бос орынды литерді жіберу үшін  - isspace, әріпті идентификациялау (ұқсастыру) үшін - isalpha және әріп цифрдың бейнесін тану үшін isalnum қолданылады. Олардың барлығы <ctype.h> стандартты бас файлда сипатталады. getch және ungetch функциялары қалай жұмыс істейді? Көп жағдайда, программа артықтарын оқып болғанша қажеттілердің барлығын оқыды ма, әлде жоқ па соны “түсінбейді”. Бұдан программа керектісіне қарағанда, бір литерге артық оқығанын, әсіресе санаққа кірмейтін литерді оқығанын білдіреді. Содықтан артық литерді қайтаратын операция қажет. Керісінше, литерді жіберу механизмі енгізу кезінде getch кезекті литерді қоятын, ал кіру ағынынан ungetch литерді кері жіберетін, бір-бірімен келісілетін функциялар жұбының көмегімен оңай моделденеді.

 

10.2  Құрылымдардың өзіне сілтеме жасауы

 

Біз жалпылама есепті шешкіміз келсін делік – кіру ағынының кез келген сөздері үшін кездесетін жиілікті есептейтін программа жазайық. Сөздер тізімі алдын ала белгісіз болғандықтан біз оларды алдын ала реттестіре және қолдана алмаймыз, мысалы бинарлы іздеу, сұрыптау мақсаты – реттелген жиында элементтерді іздеу жеңілдетіледі. Әр алынған сөзді, бұрын ол кездестіме, әлде жоқпа екенін анықтау үшін сызықты іздеуді қолдану орынсыз болар еді, осы жағдайда программа өте жай жұмыс істер еді. Сондықтан әртүрлі сөздердің тізімдерімен дұрыс жұмыс істеу үшін, мәліметтерді қалай ұйымдастыруға болады деген сұрақ туындайды.

Тәсілдердің бірі – реті бұзылмайтындай, орынға әр жаңа сөзді орналастыра отырып, алынған сөздердің ретін әрдайым сақтау. Бұл үшін бинарлы ағаш деп аталатын мәлеметтер құрылымы қажет.

Ағашта әр жеке сөзге “түйін” қарастырылған, ол:

-      сөздің мәтініне көрсеткіш;

-      кездесетіндердің санын есептеу;

-      сол жақ балалық түйінге көрсеткіш;

-      оң жақ балалық түйінге көрсеткіш.

Әр түйіннің бір немесе екі баласы бар, немесе түйінің типті баласы жоқ. Мұрагері жоқ түйінді жапырақ деп атайды.

Ағашта түйіндер сол жақ ағаштың кез келген түйініне қатысты берілген түйіннің сөзіне қарағанда лексикографикалық жағынан аз сөздерден, ал оң жағы – одан артығырақ сөздерден тұратындай орналасады. Кез келген түйінді іздеу өзінің балалық түйіндерінің бірінен іздеу нәтижелерін қолданатындықтан, ағашта іздеу процессі мәні бойынша рекурсивті. Осыған сәйкес түйінді қосу мен ағашты басып шығару үшін мұнда әрине рекурсивті функцияларды қолданған тиімді.

Бинарлы ағаштың түйінін сипаттауды төрт компонентті құрылым түрінде көрсетейік:

struct tnode //ағаш түйіні

{         char *word;          //мәтінге көрсеткіш

int count;               //кіру саны

struct tnode*left;   //сол жақ бала

struct tnode*right; // оң жақ бала

};

Құрылым өз-өзін қоса алмайды, бірақ:

struct tnode*left;

tnode түйініне left-ті көрсеткіш ретінде анықтайды, ал tnode өзі анықтамайды.

Кейде өзара жіберілетін құрылымдар қажеттілігі туындайды: бір-біріне жіберілетін екі құрылымдар. Осындай мәселені шеше алатын тәсіл келесі фрагментті ұйымдастырады:

struct t

{         …

struct s *p;  // p s-ті көрсетеді

};

struct s

{         …

struct t *q;  // q t-ны көрсетеді

};

Біздің есебіміздің басты программасы getword функциясының көмегімен

cөзді оқиды және оларды addtree функциясы арқылы ағашқа қояды.

#include <stdio.h>

#include <ctype.h>

#include <string.h>

#define MAXWORD 100

struct tnode * addtree (struct tnode *, char *);

void treeprint (struct tnode *);

int getword ( char * , int );

// кездесетін сөздің жиілігін есептеу

main ()

{         struct tnode * root;                  // түпкі түйінге көрсеткіш

char word [MAXWORD];

root = NULL;

while ( getword(word, MAXWORD)!=EOF)

if ( isalpha (word[0]))

          root = addtree(root,word);

treeprint(root);

return 0 ;}

addtree функциясы рекурсивті. Әр қайтадан түскен сөздер үшін оның көшірмесі ізделінеді және саны 1-ге артады, немесе ол үшін жаңа түйін енгізіледі, егер мұндай сөз ағашта болмаған жағдайда жаңа түйіннің құрылуы, түйіннің ата-анасына қойылатындай, оған көрсеткішті addtree қайтарумен қосақтала жүреді.

struct tnode * talloc ( void);

char * strdup ( char *);

// addtree: w сөзімен p-ға немесе одан төменге түйін қосылады.

struct tnode * addtree ( struct tnode *p, char * w)

{         int cond;

if ( p= = NULL)              //сөз бірінші рет кездесіп тұр

{ p=talloc();                       // жаңа түйін құрылады

 pword = strdup (w);

 pcount =1;

 pleft = p right = NULL;  }

else if (( cond = strcmp( w,pword))= = 0)

pcount + + ;                  // бұл сөз кездескен

else if ( cond<0)                             // түбір < сол жақ ағаштың

 pleft=addtree( pleft,w);

else

 pright=addtree( pright,w);

return p;}

Жаңа түйіннің жадысы ағаштың бір түйінін сақтауға жеткілікті, жадының бос кеңістігіне көрсеткішті қайтаратын, talloc программасының көмегімен сұратылады, ал жаңа сөзді жадының жеке орнына көшіру strdup.

# include <stdlib.h>

// talloc : tnode-ті құрайды

struct tnode * talloc ( void)

{return ( struct tnode * ) malloc ( sizeof (struct tnode));}

// strdup: аргументте көрсетілген стрингті

//malloc көмегімен алынған орынға көшіреді.

char * strdup ( char*s)

{char *p;

p=(char*) malloc ( strlen (s) + 1);              // +1  -  ‘\0’ үшін

if (p!=NULL)                                                // егер жады бөлінсе

 strcpy (p,s);

return p;}   

treeprint – функциясы ағашты лексикографикалық ретпен басып шығарады; әр түйін ол оның сол жақ ағашын (барлық сөздер берілген түйіннің сөзінен аз), содан кейін сөзді және соңында оң жақ ағашты (барлық сөздер берілген түйіннің сөздерінен үлкен) басын шығарады.

// treeprint: p ағашын ретімен басып шығару

void treeprint (struct tnode * p)

{if (p!=NULL     //ағашты ең болмағанда бір түйін бар

{ treeprint ( pleft);

 printf ( “%4d%s\n”, pcount, pword);

treeprint (pright)         }}

 

10.3  Кестені көру

 

Құрылымдарды қолдануда жаңа аспектілердің мысалын келтіру, элементтерді кестеге қою мен оларды кестелерден іздеуді жүзеге асыратын программаның ядро пакетіне әкелеміз. Бұл пакет – кез келген макропроцессорда немесе компиляторда кесте аттарымен жұмыс істейтін әдеттегі программа жиынтығы. Мысалы, #define нұсқауын қарастырайық.

#define IN 1 түрінде жол кездескенде, оның 1 мәтінінің орнын басатын және IN  аты кестеде сақталынуы керек. Егер одан кейін бұл IN аты нұсқауда кездессе, мысалы, state=IN-де, ол бірге ауыстырылуы тиіс.

Аттармен манипуляциалайтын және мәтіндерімен орын ауыстыратын екі программа бар. Бұл – s атын жазатын install (s,t) және оның t мәтінінің орнын кестеге (s және t – стрингтер) ауыстыратын және кестеде s іздеуін жүзеге асыратын lookup(s) және s аты табылатын көрсеткішті қайтарушы орын, немесе NULL – егер s кестеде болмаған жағдайда.

Алгоритм «хэширлеу»-ге (функцияларды орналастыру) негізделген: түскен аттар көрсеткіштер массивінің индексі ретінде кейін қолданылатын теріс емес санға (хэш-код) жинақталады. Осы массивтің әр элементі хэш-кодтың мәліметтерінің атын сипаттайтын блок тізімдерінің бастапқы сілтемелерімен байланысқан көрсеткіштері болып табылады. Егер массив элементі NULL-ден тұрса, яғни бұл хэш-кодымен аттардың арасында бірде-біреуі сәйкес келмегендігін білдіреді.

Тізімнің блогы – бұл аттың көрсеткіштерінен, мәтіндер орнын ауыстырудан және тізімдегі келесі блоктан тұратын құрылым; NULL мәні көрсеткіште келесі блок тізімінің соңы екенін білдіреді.      

struct nlist                          // кесте элементі

{struct nlist * next;            // келесі тізім блогының көрсеткіші

char * name;                  // анықталған ат

char * defn;                   // мәтіннің орнын басады

};

Ал көрсеткіштер массивін анықтау былайша жазылады:

#define HASHSIZE 101

static struct nlist *hashtab[HASHSIZE];      // көрсеткіштер кестесі

// hash: s стрингісі бойынша хэш-код алынады.

 unsigned hash (char * s)

{unsigned hashval;

for (hashval=0; *s!=’\0’; s++)

hashval = *s+31*hashval;

return hashval %HASHSIZE;             }

// lookup: s-дан элементті іздеу

struct nlist * lookup (char * s)

{struct nlist *np;

for (np = hashtab [hash(s)]; np!=NULL; np=np->next)

if (strcmp (s,np->name)= =0)

return np;              // қойылған стрингті тапты

return NULL;                  // таппады

          }

install функциясы қойылған стрингтің бар екенін анықтау үшін lookup-қа қатынас жасайды. Егер бұл осылай болса, онда ескі анықтама жаңамен ауыстырылады, кері жағдайда жаңа элемент құрылады.

struct nlist * lookup (char *);

char * strdup (char *);

// install: (name, defn) кестеге кіргізу

struct nlist * install ( char * name, char * defn)

{        struct nlist * np;

          unsigned hashval;

          if (( np=(lookup (name))= =NULL)           // табылмады

          {        np =(struct nlist*) malloc(sizeof(*np));

if (np = =NULL);   (np -> name =strdup(name))= =NULL)

          return NULL;

          hashval = hash (name);

          np -> next = hashtab [hashval];

          hashtab [hashval] = np; }

          else                                              // бар

          free ((void *) np-> defn);          // бұрынғы defn-ті босатамыз

          if (( np -> defn = strdup (defn)) = =NULL)

          return NULL;

          return np;              }

 

11 дәріс. Біріктірулер

 

Дәріс мақсаты: біріктірулер түсінігімен, олардың мақсатымен, атқаратын ролімен танысу, инициализациялауды, пайдалануды үйрену.

 

Біріктіру – бұл әртүрлі типтегі және өлшемдегі объектілерден (әр уақыт кезеңдерінде) тұратын айнымалы. Өлшемдер мен тегістеуге қатысты барлық талаптарды компилятор орындайды. Біріктірулер программаға машиналы – тәуелді ақпараттарды қоспай, әртүрлі мәліметтерді жадының бір немесе басқа аймақтарында сақтауға мүмкіндік береді. Бұл құралдар Паскальдің вариантты жазбаларымен ұқсас.

Программаға біріктірулерді енгізудің мақсаты – заңды негізде бірнеше типтердің мәнін өзіне сақтайтын айнымалылардың бар болуы. Біріктіру синтаксисі құрылым синтаксисіне ұқсас. Біріктірулер мысалын келтірейік.

union u_tag

{        int ival;

float fval;

char * sval; }r;

r айнымалысы көрсетілген үш айнымалылардың  кез-келген оған сиятындай орналастыру жеткілікті үлкен болады; r айнымалысының дәл өлшемі орындалуға байланысты тәуелді. Осы үш типтердің біреуінің мәніне r айнымалысы меншіктелуі мүмкін және егер ол заңды болса, яғни оған алынған мәннің типі соңғы оған меншіктелген мәннің типтерімен сәйкес келсе, әрі қарай өрнектерде қолданылады. Осы талаптарды әр ағымдық моментте орындау программистің еркінде. Нәтиженің “Келіспеушілік” типтері жағдайы іске асырудан тәуелді. 

Біріктіру мүшелеріне қатынасу синтаксисі келесіндей:

мүшелерді біріктіру_аты

немесе                  

біріктіру -> мүшелеріне_көрсеткіш

яғни нақтылы құрылымдағыдай.

Біріктірулер құрылымдарға және массивтерге кіруі мүмкін және керісінше. Мысалы, құрылым массивінде:

struct

{        char *name;

          int flags;

          union

{        int ival;

          float fval;

          char * sval; } r;}

symtab [NSYM];

ival-ға келесі түрде жіберіледі

          symtab[i].r.ival

ал sval стрингтің бірінші литеріне келесі екі тәсілдің кез келгенімен қатынасуға болады:

* symtab [i].r.ival

symtab [i].r.sval[o].

Нақтылы біріктіру – бұл барлық мүшелері, оның базалық адресінің өлшеміне қатысты нөлдік жылжытуы бар және онда ең үлкен мүшенің орналасуына және біріктірулердің барлық типін тегістеуді қанағаттандыруға мүмкіндік беретін құрылым.

Құрылымға қолданылатын операциялар біріктірулер үшін де орындалады.

Біріктірулерді инициализациялауды оның бірінші мүшесінің типі бар мәнге ғана жасауға болады.

 

12 дәріс. Енгізу-шығару интерфейсі

 

Дәріс мақсаты: енгізу-шығару мүмкіндіктерімен танысу.

 

12.1 Стандартты енгізу-шығару

Енгізу-шығару мүмкіндіктері Си тілінің негізгі бөлігі емес. Нақтылы программалар біз сөз қылғандарға қарағанда, өзін қоршағандармен өте күрделі тәсілдермен өзара әрекеттеседі.

Библиотекалық енгізу-шығару функциясы ANSI стандартымен дәл анықталады, сондықтан Си-де қолдау болатын кез келген қондырғымен байланысты. Осы функциялар енгізу-шығару мәтінінің қарапайым моделін іске асырады. Мәтіндік ағын жолдар тізбегінен тұрады; әр жол жаңа литер жолымен аяқталады.

Енгізудің қарапайым механизмі – ағынның келесі литерін қайтаратын немесе егер файлдың  EOF  соңы табылса getchar функциясының бір литерін оқу:

int getchar ( void)

әдетте EOF = -1 мәніне тең.

int putchar (int) функциясы шығару үшін қолданылады: putchar (c) с литерін стандартты шығаруға (экран) жібереміз. putchar фукнкциясы нәтиже ретінде жіберілген литерді немесе қате болған жағдайда EOF-ты қайтарады.

рrintf функциясымен орындалатын шығару, сондай-ақ, стандартты шығу, ағынына жіберіледі putchar және  printf-ды қалауымыз бойынша кезектесіп шығаруға болады, бұдан шығару осы функциялар шақыратын жүйелілікпен қалыптасады.

Мысал ретінде енгізуді төменгі регистрге ауыстыратын lower программасын қарастырайық:

#include<stdio.h>

#include<ctype.h>

main( )

{         int c;

while (( c= getchar( )) ! = EOF)

          putchar(tolower(c));

return 0;}

tolower функциясы <ctype.h>-та анықталады. Ол жоғарғы регистрдің әріптерін төменгі регистр әріптеріне ауыстырады, ал қалған литерлерді өзгеріссіз қайтарады.

Форматты шығару:

printf функциясы ішкі мәндерді мәтінге ауыстырады.

int printf ( char*format, arg1, arg2,…)

printf функциясы форматты басқару арқылы (форматты стринг) стандартты шығаруда өзінің аргументтерін түрлендіреді, форматтайды және басып шығарады. Ол басып шығарылған литерлердің санын қайтарады.

Форматты стринг объектілерінің кәдімгі литерлерден шығу ағынына тура көшірілетін кәдімгі литерлерден және әрқайсысы түрлендірілетін және printf-тің кезекті аргументін басып шығаратын түрлендіру спецификациясынан тұрады. Түрлендіру кез келген спецификациясы % белгісімен басталып, спецификатор литерімен аяқталады. % мен спецификатор литерінің арасында минус таңбасы, нүкте, әріп элементтері орналасуы мүмкін. Еген %-ден кейін спецификатор литері орналаспаса, printf функциясының бағыты анықталмайды.

sprintf функциясы printf-тегі түрлендіруді орындайды, бірақ шығару int sprintf ( char*string, char*format, arg1,arg2,…) стрингте есте сақталады.

 

 

 

 

12.2  Форматты енгізу

 

Ұзындық айнымалысы аргументтерінің тізімдерімен және де олар жұмыс істейтін функцияны қалай жазуға болатынын көрсетейік. Бұл үшін minprintf деп аталатын printf функциясының қысқартылған версиясын құрайық. Бұл функция форматы берілген стрингпен және аргументтермен жұмыс істейді, формат түрлендіру жайында қозғасақ, олар стандартты printf көмегімен іске асырылады.

printf стандартты функциясының декларациясы төмендегідей болады.

int printf (char*fmt,…),

мұндағы көп нүкте аргументтердің типтері мен сандары өзгеретіндігін білдіреді. Біздің minpritnf функциямыз былайша сипатталады:

void minprintf ( char * fmt,…)

және ол литер санын бермейді.

Барлық қиындық minprintf – аргумент тізімінің бойымен қалай жылжитындығында, өйткені осы тізімнің атауы да жоқ. Стандартты бос <stdarg.h> файл аргументтер тізімі бойынша қалай жүру керектігін анықтайтын макроанықтамалар (va_start, va_arg, va_end) жиынынан тұрады.

Тізілген құралдар printf қысқартылған версияның негізін құрайды.

#include <stdarg.h>

// minprintf: printf версиясы аргументтердің айнымалы санымен

void minprintf ( char * fmt,…)

{         va_list ap;                  //көрсеткіш типі - va_list кезекті атаусыз аргументіне көрсеткіш

char *p, *sval;

int ival;

double dval;

va_start(ap,fmt);          // макрос аргументы бірінші  атаусыз аргументпен инициализациалайды

for(p=fmt; *p; p+ +)

        {    if (*p!=’%’)

        {  putchar (*p);

          continue;               }

          switch (*+ +p)

{case ‘d’: ival= va_arg( ap,int); // макрос кезекті аргументті береді,

                                                          // ал ар келесіге жылжытады

printf ( “ %d”, ival);

break;

case‘f’:

dval=va_arg(ap,double);
         printf(“%f”,dval);
         break;

case‘s’:
         for ( sval=va_arg( ap,char*);
*sval; sval+ +)

putchar( * sval) ;

                    break;

                    default: putchar (*p);

                    break;          }        }
      va_end(ap);
                                     // барлығы жазылғанда тазалау

}

Форматты енгізу (scanf) функциясының декларациясы келесі түрде болады:

int scanf ( char * format, …);

scanf функциясы литерлерді стандартты кіру ағынынан оқиды, оларды format стринг спецификациясымен келісе отыр түсіндіріп береді және нәтижелерді әрқайсысы көрсеткіш болатындай, өзінің қалған аргументтеріне жібереді. scanf нәтижесі ретінде мәліметтердің табысты енгізілген элементтер санын қайтарады.

Сондай-ақ стрингтен оқитын (стандартты енгізуден емес) sscanf функциясы бар.

int sscanf ( char*string, char*format, arg1,arg2,…)

sscanf функциясы format  форматымен келісе отырып string- ті қарастырады және алынған мәндерді  arg1, arg2,және т.б-ға жібереді. Соңғылары көрсеткіштер болуы керек.

Енгізу форматын түрлендіруді басқару үшін, әдетте, бос орын, кәдімгі литерлер, түрлендіру спецификациясы, * белгісі, сан сияқты спецификациялардан тұрады.

“1 сен. 2004” түрдегі мәліметтерден тұратын енгізу жолдарын бізге оқу керек делік.

scanf-қа назар аудару келесі түрде болады:

int day, year;

char monthname [20];

          scanf ( “%d%s%d”, &day, monthname, &year);

& белгісі monthname-нің алдына керек емес, өйткені массив аты функцияның аргументі типіне сәйкес келетін көрсеткіш болады.

         

13 дәріс. Файлға қатынас жасау

 

Дәріс мақсаты: файлдарға қатынас жасау тәсілдерімен танысу.

 

13.1 Файлға қатынас жасау

 

Барлық алдыңғы көрсетілген мысалдарда, біз программа үшін нақтылы машиналардың операциялық жүйелері алдын ала автоматты түрде анықталған, стандартты енгізу мен стандартты шығарумен жұмыс істедік.

Келесі қадам – алдын ала программаға қосылған, файлға қатынас жасай алатындай программаны жазып үйрену.

Файлдан оқу немесе файлға жазу үшін ол әрі қарай файлға қатынас жасау үшін қолданылған көрсеткішті қайтаратын, кітапханалық fopen функциясының көмегімен алдын-ала ашылуы керек.

Файлдың көрсеткіші деп аталатын бұл көрсеткіш, файл туралы ақпараттан (буфер адресі, буфердегі ағымдық литердің жағдайы, файл оқуға немесе жазуға ашық па, файлмен жұмыс істеуде қате бар ма және файлдың соңы кездестіме) тұратын құрылымға жіберіледі. <stdio.h>-тан алынған анықтамалар FILE деп аталатын құрылымның сипаттамасын қосатын болғандықтан, қолданушыға толығымен білу керек емес. Тек қана файл көрсеткішін анықтау үшін талап етілетін – бұл келесі түрдегі декларацияны беру:

FILE * fp;

FILE * fopen(char*name, char* mode);

Осы жазбадан fp FILE-ға көрсеткіш екені шығады, ал fopen FILE-ға көрсеткішті қайтарады. FILE құрылым тегі емес, типтің аты екенін ескерген жөн.

Программада fopen-ге қатынас жасау, программада келесі түрде болады:

fp= fopen ( name, mode);

Бірінші аргумент – стринг файл атынан тұрады, екінші ереже туралы ақпаратты алып жүреді.

Бұл да стринг: онда қолданушы қандай түрде файлды қолдану керек екендігі көрсетіледі. Бұрын жазбада немесе қосуда болмаған файлды ашу, ол құрылатынын білдіреді. Жазбадағы бар файлды ашу оны тазалауға әкеп тірейді, ал қосылған файлды ашуда оның ескі мәндері сақталады.

Файлдан оқу мен файлға жазудың бірнеше тәсілдері бар. Ең қарапайымы getc және putc функцияларын қолдану.  int getc ( FILE*fp) функциясы с литерін *fp файылына жазады және қате болған жағдайда, жазылған литерді немесе EOF-ті қайтарады.

Си программасын жүктеуде операциялық жүйелер әрқашан үш файлды ашады және оларға үш файлдық сілтемені қамтамасыз етеді. Бұл файлдар: стандартты енгізу, стандартты шығару және қателердің стандартты файлы болып табылады; оларға сәйкес көрсеткіштер stdin, stdout және stderr деп аталады, олар < stdio.h>-та сипатталған.

Файлдардың форматты енгізу-шығаруын scanf және printf функцияларында тұрғызуға болады. Олар scanf және printf-тің, олардың бірінші аргументі файлға көрсеткіш болып табылатын айырмашылығымен ұқсас

 int fscanf( FILE*fp, char * format,…)

 int fprintf( FILE*fp, char * format,…)

stdio және stdout файлдық көрсеткіштер FILE* типті объектілерді көрсетеді. Бұл айнымалылар емес тұрақтылар, сәйкесінше оларға ешнәрсені меншіктеуге болмайды.

int fclose ( FILE*fp) функциясы – fopen-ге қатысты керісіншесі; ол файлдық көрсеткіш пен сыртқы атаулардың арасындағы байланысты үзеді.

Стандартты библиотекада fgets енгізу функциясы бар:

char * fgets (char*line, int maxline, FILE*fp),

ол maxline-ен 1 литерден артық оқи алмайды, line литер массивінде fp файлынан келесі енгізу жолдарын оқиды (литердің жаңа жолын қоса алғанда). Жазылған жол ‘\0’ литерімен толықтырылады.

fputs шығару функциясы стрингті файлға жазады (литердің жаңа жолымен аяқталмауыда мүмкін).

int fputs ( char*line, FILE*fp).

gets және puts библиотекалық функциялардың fgets және fputs-ден айырмашылығы, олар тек stdin және stdout стандартты файлдарын пайдаланады, сол сияқты gets соңғы ‘\n’ литерді алып тастайды, ал puts оны қосады.

 

14 дәріс. Препроцессор директивалары

 

Дәріс мақсаты: препроцессор директиваларымен танысу.

 

14.1 СИ тілінің препроцессоры

Препроцессор - бұл бастапқы кодты объектті кодқа трансляциялаудағы программаны әртүрлі тесттік өзгерістерге айналдыратың компилятордың бір бөлігі. Программа жазушы препроцессорға Си тіліне еңбейтін препроцессор директивалары деп аталатын командаларды беруі мүмкін. Си тілінің препроцессоры компиляцияны нөлінші фазасында алғашқы файлды өңдеу үшін қолданылатын макропроцессор болып есептелінеді. Препроцессор директиві Си-дегі программаның алғашқы мәтінінің нұсқауы болып табылады және әдетте алғашқы программаның модификациясын жеңілдету үшін және оларды Си тілінің компиляторынан және операциялық ортадан тәуелсіз болу үшін қолданылады. Препроцессор директиві программаның мәтінінде лексемдерді кейбір мәндерімен ауыстыруға мүмкіндік береді, соның ішінде алғашқы файлға басқа алғашқы файлдың мәндерін қояды, алғашқы файлдың кейбір бөліктерің компиляциясына тыйым салады және т.с.с.

Препроцессор директивалары:

#define       #error       #include

#if              #else         #elif

#endif         #ifdef       #ifndef

#undef        #line        #pragma

Си тілінің мынандай мүмкіндіктері жиірек қолданылады: компиляция кезінде файлды қосатын #include және бірдей мәтінді шынжырларды басқаға ауыстыратын # define.

#include құралы жеке жағдайда #define  және декларациясы құралдарымен оңай манипулияция жасауға  мүмкіндік береді.

#include “файл аты” немесе

# include <файл аты>

түрдегі кез келген жол файлдың файл аты атауымен ауыстырылады. Егер файл аты екі тырнақшаға алынса, онда ереже бойынша, файл программасының алғашқы файлдарының арасынан ізделінеді; егер мұндай табылмаса немесе файл аты <және> бұрыштық жақшаға алынса, онда іздеу жүйелік библиотекаларда жүзеге асырылады. Қосылған файлдың өзі  #include жолынан тұрады.

#include <oisstream.h> файлдық енгізу/шығару операцияларын жүзеге асыру үшін стандартты тақырыпша.

# include құралы –декларациямен үлкен программаларды жинақтаудың жақсы тәсілі. Ол әсіресе, жағымсыз қателерді неге байланыстыруға болмайтынын, барлық алғашқы файлдарды бір және сондай анықтамалармен және айнымалылардың сипаттамаларымен қолдануға болатынына кепілдік береді. Қосылған файлға өзгерісті енгізуде барлық одан тәуелді файлдар қайтадан құрастырылуы қажет.

Макроауыстырулар анықтамасы келесі түрде болады:

# define макроаты символдар_тізбегі

 Мысалы: -дін мәні ретінде  UP сөзі, ал 0-дін мәні ретінде  DOWN сөзі пайдаланатын болса:

#define UP 1

#define DOWN 0

Бұл директивалар программада UP пен DOWN  кездескен сайын компиляторды олардың орнына сәйкесінше орнына 1 мен 0 қоюға мәжбүрлейді.

cout<<UP<<’ ‘ <<DOWN<<’ ‘<<UP+UP;

 Нәтижесі:

1 0 2

Макроауыстырулар орын басатын мәтін берілген параметрлерден тәуелді түрленетін  аргументтерімен анықталады. Мысалы, max-ты анықтайық.

#define max (A,B)  ((A)>(B) ? (A):(B))

max-қа қатынасу мәтіндік ауыстыруларды ғана шақырады. Әр формальды параметр сәйкес аргуменпен ауыстырылады.

x=max (p+q, r+s) жолы x=((p+q) > (r+s) ? ( p+q) : ( r+s)) жолымен ауыстырылады.

Аргументтерге кез келген ауыстыруларды жасауға болатындықтан, көрсетілген max анықтамасы кез келген типтің мәліметтері үшін орынды!

Макроауыстырулар бұл идентификаторды сәйкес жолмен ауыстыру деп түсіну керек.

Егер #define GetFile “Файл аты еңгіз

Онда cout<<GetFile;     бұл          cout<<”Файл атын еңгіз”;

#error директивасы компилятордың жұмысын тоқтатады. Ол көбісіне өңдеу үшін пайдаланылады.Форматы: #error хабарлама

Атауларды  #undef  көмегімен препроцессордан “жасыруға” болады.

#undef getchar

int getchar ( void) {…}

Ереже бойынша, бұл макроанықтамаларды осы функциялардың сол атымен “қайта басу” керек. #define  директиві әдетте, #undef директиві жұбында қолданылады.

Макроанықтамалар синтаксис бойынша функцияның анықтамасын еске түсіреді. Функцияны шақыруды макрошақыру мен ауыстыру программаның орындалу жылдамдығын жоғарылатуы мүмкін және басқа жағынан, макроанықтамаларды қайта-қайта қолданып анағұрлым үлкен жадыны талап етеді.

 

14.2  Шартты компиляция

 

Шартты компилияция Си тілінде тікелей компилияцияны емес, препроцессорлық өңдеуді басқаратын шартты нұсқаулардың командаларының жиынымен қамтамасыз етеледі.

#if          тұрақтылы өрнек                            // шартты тексеретін

#ifdef     идентификатор

# ifndef  идентификатор

#else

#endif

#elif

Шартты нұсқаулар компилияция кезінде есептелетін шарттың мәніне тәуелді, сол немесе басқа программа мәтінін таңдап қосу үшін құрал болып есептеледі.

Бірінші үш командалар шарттың орындалуын тексереді, қалған екеуі – тексерілген шарттың әрекет ету диапазонын анықтауға мүмкіндік береді.

Шартты құрылымы төмендегідей:

#if1 мәтін

#else   2 мәтін

#endif

#else  мәтін 2  нұсқау міндетті түрде емес. Мәтін 1 құрастырылған мәтінге тексерілген шарттың тек ақиқат болғанда ғана қосылады, кері жағдайда  ( #else-нің барында) компилияцияға мәтін 2  беріледі. Егер #else директиві болмаса, онда  # if –тең  #endif-ке дейінгі барлық мәтін шарт жалған болғанда қалдырылып кетеді:

#if директивасына қарапайым мысал:

#include <iostream.h>

#define min 20

int main()

{ #if min>5

cout<<”қате\n”;

#endif

return 0;}

#ifdef және ifndef директивалары шартты компиляциялаудың екі нұсқасын («анықталған»/ «анықталмаған») ұсынады.

Жалпы форматы:

#ifdef  макроаты     // егер макроаты алдын- ала #define директивасымен                                         инструкциялар тізбегі     //анықталған болса, онда

#endif                     // инструкциялар тізбегі компиляцияланады.

 

#ifndef директивінде кері шарт тексеріледі – идентификатордың анықтамаларындағы ақиқат болып есептеледі, яғни идентификатор #define командасында қолданылса немесе анықтамасы #undef командасымен жойылса ақиқат болады.

Жалпы форматы:

#ifndef макроаты           //егер макроаты анықталмаған болса

инструкциялар тізбегі   //#ifdef және #endif арасындағы инструкциялар                     

#endif                             //компиляцияланады.        

 

#undef директивасына мысал:

#define TimeOut

#define Wait

//

#undef TimeOut

#undef Wait

Мұнда TimeOut, Wait атаулары сәйкес #undef директивасын кездескенше анықталған.

Шартты компилияцияны өңдеуде қосу немесе бақылау баспаларын шығару үшін қолданған ыңғайлы. Мысалы,

#define DE 1

#ifdef DE

 printf ( “өңделетін баспа”);

#endif

DE идентификаторының анықталуынан тәуелді программада пайда болатын мұндай баспалар бірнеше болуы мүмкін және  #define DE 1 директивін алып тастай отырып бірден барлық өңделетін баспаларды үзіп тастаймыз.

Программаның модулімен препроцессорды қосу үшін арналған файлдар әдетте, қайталанып қосылудан сақтанумен жабдықтайды. Мұндай қайталап қосылу, егер әрқайсысында бірдей және сондай файлдың препроцессоры қосылуы жоспарланған бірнеше модульдер, программаның жалпы мәтініне біріктірілетін болса, жүзеге асады. Мысалы, осындай құрылымдар мен қорғау стандартты библиотеканың барлық аты үшін алынған файл жабдықталады. Қайталап қосылудан қорғау сұлбасы мынадай болуы мүмкін:

// filename атымен берілген файл

#ifndef FILE_NAME

…    // filename файлын қосатын мәтін

#define FILE_NAME 1

#endif

Мұнда  FILE_NAME – программаның басқа мәтіндерінде кездеспеуге  тиісті filename файлы үшін кейінге сақтайтын процессорлары идентификатор.

Программаның алғашқы мәтінінің препроцессорымен өңдеу кезінде  мультитармақталуды ұйымдастыру үшін  # elif тұрақтылы өрнек директиві енгізілген.

Осы директивті қолдануды алғашқы мәтіннің құрылымы төмендегідей болады:

#if

          if­_үшін_ мәтін

#elif    өрнек 1

          мәтін 1

#elif    өрнек 2

мәтін 2

#else жағдайы_ үшін_мәтін

#endif

Препроцессор бастапқыда #if дериктивіндегі шартты тексереді, егер ол жалған болса, (0-ге тең), өрнек 1-ді есептейді, егер өрнек 1 0-ге тең болса - өрнек 2 және т.б. Егер барлық өрнектер жалған болса, онда құрастырылған мәтінге else_жағдайы_ үшін_ мәтін қосылады. Кері жағдайда, яғни ең болмағанда бір ақиқат өрнектің пайда болуында (#if-те немесе #elif-те), осы директивтен кейін тікелей орналасқан мәтіннің өңделуі басталады, ал барлық қалған дериктивтер қарастырылмайды.

Макроатаулар:

#line директивасы арнаулы идентификаторлар (жалған айнымалылар ) _ _LINE_ _   және _ _FILE_ _ мазмұндарын өзгерту үшін пайдаланылады. _ _LINE_ _   жалған айнымалы компиляциядан өткен жолдың нөмерін сақтайды, _ _FILE_ _-компиляциядан өтіп жатқан файлдың аты.

Форматы:

#line номер “файлдың аты”

_ _DATE_ _макрос ай/күн/жыл форматты жол

_ _TIME_ _сағат.минут.секунд

_ _STDC_ _егер бұл макрос анықталған болса, компилятор тек стандартты С/С++ кодты қабылдайды.

_ _cplusplus_ _макросы кем дегенде 6 санды мән.

 

 

 

 

 

 

15 дәріс. Объектілі –бағытталған программалау негіздері

 

Дәріс мақсаты: объектілі – бағытталған программалау негіздерін  меңгеру.

 

Программа бұл тізбекті орындалатын инструкциялар жиыны, ал объектілі-бағытталған программа- орындайтын әрекеттері мен қасиеттері ұқсас біріктірілген объекттер  түрінде қарастырылады. Объектілі-бағытталған тілдердін ішінде С++ ең кен таралған тілдердін бірі.

Объектілі-бағытталған тілдердің соңғы жетістіктер бірі Java тілінің өзі С++ тілімен салыстырғанда С++ тілінің басымдылықтары көп, соның ішінде көрсеткіштер, тұқым қуу, шаблондарды қолдану. Java тілінін синтаксисі С++ тілінің синтаксисына ұксастығынан С++ тілін меңгеруде алынған білім Java тілінде программалауда қолданылады.

Объектілі-бағытталған әдістеме “объект” түсінігіне негізделген, барлық стилдік жақсартуларды қосады. Әртүрлі объектілер өзінің қасиеттері және мінездемелерімен сипатталады. Объектілі-бағытталған әдістемені программалау ретінде, сол сияқты талдау және дизайн (программаларды жобалау) ретінде қолдануға болады.

Объектілі-бағытталған әдістеменің  (ОБӘ) дамуына процедуралық тілдер негізгі әсерін тигізді, соның ішінде әсіресе Си тілі.

Құрылымдық парадигма (сөздердің есептелуі, жіктелу үлгісі) машиналық тілдегі программалаудың парадигмасымен салыстырғанда үнемі дамып отырады, бірақ басқа жағынан алғанда кемшіліктері бар.

Программалау технологиясының дамуындағы келесі қадам модульдік программалау болып табылады..

ОБӘ модульге мұрагер және полиморфизмді қоса отырып, модульдік программалаудан кейіңгі келесі логикалық сатысынан орын алады. ОБӘ-ні  қолдана отырып, программист программаны, оны жоғары деңгейлік объектілер қатарына бөле отырып қарастырады. Әр объект шешілетін мәселенің анықталған аспектілерін модельдейді. Объектілі-бағытталған программалау программаның орындалу процесін басқару үшін процедураны шақыратын тізбектелген тізімді құруға программистің назарын аударады. Сонымен қатар, объектілер өзара бір-бірімен әрекеттеседі. Объектілі-бағытталған әдістеме көмегімен өңделген программа шешілген мәселенің әрекет етуші моделі болып есептеледі. Соңғы уақытқа дейін С++ тілі стандарттаудан тыс дамыған. ANSI/ISO ұжымының С++ тілін стандарттау бойынша комитет “Стандартты С++” деп танымалы құжат өндеген. Стандартты С++ -не көп мүмкіншіліктер кіргізілген, соның бірі шаблондардың стандартты кітапханасы (STL). С++ үшін ең әйгілі өңдеу орта Microsoft Windows операциялық жүйе басқаруымен жқмыс жасайтын, Microsoft және Borland компаниялардың бірігіп шығарылған Visual C++.

 

 

Әдебиеттер тізімі

 

1.               

Шилдт Г. Искусство программирования на С++. - СПб.: БХВ-Петербург, 2005. -496 с.

2.               

Павловская Т.А. С/С++. Программирование на языке высокого уровня.- СПб.: Питер, 2003. - 461 с.

3.               

Фридман А., Кландер Л., Михаэлис М., Шильдт Х.С/С++.Архив программ. - М.: ЗАО «Издательство БИНОМ», 2001. - 640 с.

4.               

Шилдт Г.С++.Руководство для начинающих. - М.: Издательский дом «Вильямс», 2005. - 672 с.

5.               

Подбельский В.В. Язык Си++. -М.: Финансы и статистика, 2003. - 560 с.

6.               

Бондарев В.М. Програмирование на С++.-Харьков.: Компания СМИТ, 2005.- 284 с.

7.               

Штерн В. Основы С++. Методы программной инженерий. -М.: Издательство «Лори»,  2003.- 860 с.

8.               

Страуструп Б. Язык программирования С++. -http://www/research.att.com/-bs.

9.               

Лафоре Р. Объектно-ориентированное программирование в С++. 4-ое изд.-СПб.: Питер, 2011.-928 с.

10.           

Савич У. С++ во всей полноте. – М.: Издательская группа BHV; СПб.: Интер, 2005.- 784 с.

 

 

Мазмұны

1     

СИ/С++ тіліне шолу

4

2     

Типтер, операторлар және өрнектер

7

3     

Басқару

12

4     

Функциялар

17

5     

Массивтер

20

6     

Көрсеткіштер

26

7     

Көрсеткіштермен орындалатын арифметика

29

8     

Функцияның көрсеткіштері

34

9     

Құрылымдар

37

10             

Құрылымның көрсеткіштері

41

11             

Біріктірулер

47

12             

Енгізу/шығару интерфейсі

48

13             

Файлға қатынас жасау

51

14             

Препроцессор директиві

53

15             

Объектілі бағытталған программалау

58

Әдебиеттер тізімі

59

Қосымша жоспары, 2013 ж.  реті  20