242
UNIVERZITET SINGIDUNUM Računarska tehnika i informatika UVOD U PROGRAMSKI JEZIK C Predavanja i vežbe

Uvod u programski jezik C Univerzitet Singidunum

Embed Size (px)

Citation preview

Page 1: Uvod u programski jezik C Univerzitet Singidunum

UNIVERZITET SINGIDUNUM

Računarska tehnika i informatika

UVOD U PROGRAMSKI JEZIK C

Predavanja i vežbe

Page 2: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C (C programming Language)

Садржај Историја језика C ........................................................................................................................2 Писање програма ........................................................................................................................2 Отклањање грешака (debuging) .................................................................................................4 Формат C програма.....................................................................................................................5 Елементи језика C.......................................................................................................................6 Идентификатори......................................................................................................................6 Службене речи ........................................................................................................................8

Подаци, Променљиве и типови података .................................................................................8 Типови података ......................................................................................................................9

Page 3: Uvod u programski jezik C Univerzitet Singidunum

2

Историја језика C Програмски језик C је први пут развијен почетком 1970-тих у Беловим лабораторијама (Bell Labs), у САД као основа за развој оперативног система UNIX. UNIX је намењен за ефикасан рад на малим рачунарима и први је оперативни систем који је написан у вишем програмском језику. До тада су сви оперативни системи писани у асемблеру (assembly language).

Други виши програмски језици тог времена (COBOL, FORTRAN, PL/I, ALGOLl...) су били превише спори да би се на њима заснивао оперативни систем, па је донета одлука да се напише нови програмски језик. Као основа за програмски језик C су послужила два, у то време ефиксана, виша програмска језика: ALGOL и BCPL.

Писање програма Да би се рачунару задao низ узастопних наредби, наредбе се могу уносити ручно или могу претходно бити написане у неком програму за обраду и унос текста (text editor). Тext editor се користи за писање новог или измену претходнo написаног програма. Тext editor може бити било који од постојећих програма за унос текста (notepad, wordpad и слично) или може бити интегрисан у неко посебно развојно окружење за писање и превођење програма (Тurbo C, Borland C, Microsoft Visual Studio ...).

Скуп наредби (instructions, statements) написаних у неком програмском језику се назива кôд или изворни кôд (Code или Source code). Фајл који садржи кôд се назива Source file.

У C програмском језику се низ наредби групише у целине које се називају функције (function). У Функције се најчешће групише низ наредби које заједно представљају неку логичку целину (нпр. обрада улазних података, извршење неке сложеније математичке операције, ...). Програм се може састојати од једне или више функција. Најједноставнији програм се састоји из једне функције. Нешто сложенији из две, три...

Полазећи од тога да се програм може састојати из више функција долазимо до питања да ли све функције које чине један програм морају бити записане у истом фајлу? Не морају и веома често нису! Дакле, кôд једног програма може бити записан у више функција, а ове функције могу бити записане у једном или више фајлова. Имена и екстензије фајлова које садрже функције најчешће имају облик imeprog.c (main.c, povrsina.c, tx_modem.c, ...). Обзиром да један програм сачињавају све функције, без обзира да ли су написане у једном или више фајлова, неопходно је обезбедити податак о именима и путањама свих тих фајлова.

Посебан фајл у који се уписују имена свих фајлова који садрже жељене функције се назива пројекат (project file). Пројекат је, најчешће, обичан текстуални фајл у коме су записана имена (и путање) фајлова које садрже кôд за жељени програм.

Након што се напише кôд, да би се добио извршни програм (екстензија .exe), програмер мора позвати програм који преводи написани кôд на језик „разумљив” рачунару. Тај програм се назива преводилац (Compiler).

Три корака у генерисању извршног програма:

1. корак: Предобрада (Preprocessing) − Обавља се програмом који се назива preprocessor. − Модификује изворни кôд (у RAM-у) у складу сa наредбама за предобраду (preprocessor directives)

које је програмер написао у кôду. Ове наредбе ближе дефинишу начин превођења програма. Примењује се на све фајлове који су укључени у пројекат. Приметите да кôд може садржати наредбе за предобраду, о чему ће касније бити речи.

− Приликом ове обраде се занемарују коментари (comments) и празан простор (white space) из кôда.

Page 4: Uvod u programski jezik C Univerzitet Singidunum

3

− Кôд, који је програмер написао и претходно снимио на диск, се НЕ МЕЊА у процесу предобраде!

2. корак: Превођење (Compilation) − Постиже се програмом који се назива преводилац (compiler) − Његов задатак је да изворни кôд, који је претходно подвргнут предобради, преведе у објектни кôд

(object code). Објектни код се још назива и машински кôд (machine code). Проверава се синтакса унетих наредби и генерише извештај о присутним грешкама (errors) и

упозорењима (warnings), уколико постоје. Под синтаксом се подразумева да су унете наредбе из скупа наредби које преводилац познаје и да су наредбе и њихови параметри правилно написане,

Снима објектни кôд на диск (екстензија .obj). Ако се приликом превођења констатује грешка, НЕ ГЕНЕРИШЕ се објектни кôд. Објектни кôд се може генерисати у случају појаве упозорења (warnings).

− У процесу превођења се проверава да ли у написаном кôду постоје грешке. У складу са тим се може говорити о два типа грешака: Синтаксне грешке (Syntax error): Односе се на нарушавање правила програмирања, односно

на случајеве када унете наредбе нису из скупа наредби које преводилац познаје или када оне и/или њихови параметри нису правилно написани.

Логичке грешке (Logic error): Наредбе и њихови параметри су синтаксно исправни, али програмер није „желео” да напише оно што је написао. Нпр. написао је оператор множења уместо оператора дељења. У случају логичке грешке преводилац не може да пријави грешку! За њихово откривање је неопходно детаљно тестирање извршног програма

3. корак: Повезивање (Linking) − Уколико је кôд писан у више фајлова након превођења ће постојати више фајлова са објектним

кодом. На основу података из пројект фајла (имена и путање фајлова који чине пројекат), повезују се у једну целину како би се формирао извршни фајл (executable file, са екстензијом .еxe).

− Други објектни кодови могу настати позивањем готових функција из тзв. библиотека (Libraries, Run-Time Library) или објектних фајлова које је креирао програмер.

− Снима се извршни фајл на диск. У MS-DOS и Windows оперативном систему извршни фајлови имају екстензију EXE.

− Уколико се приликом линковања констатује грешка неће се формирати извршни фајл!

Page 5: Uvod u programski jezik C Univerzitet Singidunum

4

Повезивање

Програм за унос и обраду текста

filename1.c

filename2.c

...

filenameN.c

Предобрада

Предобрада

Предобрада

Предобрада

filename1.obj

...

filenameN.obj

filename2.obj

Објектни фајлови

Фајл након предобраде, памти се у RAM-у

Превођење

Превођење

Превођење

Превођење

filename1.objfilename1.obj ... filenameN.obj

project_filename.prj

Извршни програм.exe

Слика 1. Процес превођења

Програмирање захтева прецизност! Будите прецизни што више можете.

Отклањање грешака (debuging) Овај енглески термин deBUGing je има основу у речи буба (bug). Један од првих рачунара, који је био у власништву војске САД, једног дана је „одбио” да штампа податке. Након вишечасовног труда програмера да открију грешку у програму, програмер Grace Hopper је решила да провери штампач. Пронашла је једног мољца који је направио кратак спој између две жице у штампачу. Након што је извадила мољца, штампач је наставио да ради (са мољцем није био исти случај). Мада је Grace Hopper један од аутора програмског језика COBOL, а поред тога је имала и успешну војну каријеру (постала је адмирал) остала је најпознатија по томе што је грешке у рачунарском програму назвала „рачунарским бубама” (computer bugs). Зато је приликом тестирања програма често неопходно отклонити грешке (debug), односно одстранити синтаксне или логичке грешке (bug) како би програм радио управо оно што желимо.

Page 6: Uvod u programski jezik C Univerzitet Singidunum

5

Формат C програма

C се назива језиком слободне форме (free-form language) првенствено због тога што нема посебног ограничења о месту почетка писања наредби (нпр. у некој задатој колони као што је случај са језиком FORTRAN или COBOL). Поред тога дозвољен је готово неограничен унос празних линија или размака (white space). Управо ова особина нуди могућност да се програм напише прегледно. Размотримо то на наредним примерима:

а)

#include <stdio.h>

/* Moj prvi C program koji ispisuje: "Zdravo!" */

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

printf("Zdravo!\n");

return 0; }

б)

#include <stdio.h>/* Moj prvi C program koji ispisuje: "Zdravo!" */

int main (int argc, char *argv[]){printf("Zdravo!\n");return 0;}

в) #include <stdio.h> /* Moj prvi C koji ispisuje: "Zdravo!" */ int main (int argc , char *argv[] ){ printf( "Zdravo!\n" ) ; return 0 ; }

Поређењем ова три примера, очигледно је да пример а) делује најпрегледније. За C преводилац су сва три програма потпуно иста и њиховим превођењем се долази до потпуно истог резултата. Ипак, за оног ко треба да прочита програм, да га разуме и по потреби мења или дорађује, најчешћи избор је пример а).

„Док год програм ради и даје очекивани излаз, кога је брига за стил којим је написан?” Чак и у случају када програм треба да се извршава на релативно брзом рачунару са довољно меморије (што није увек случај у пракси) програмер мора водити рачуна о стилу писања програма. И под претпоставком да нико други неће погледати ваш кôд, вероватно ћете некад имати потребе да постојећи програм прилагодите за неку другу

Page 7: Uvod u programski jezik C Univerzitet Singidunum

6

намену. Што га „читкије” напишете лакше ћете то урадити. Не заборавите да можете бити у прилици да наставите рад неког другог програмера, односно да неко други настави ваш рад.

Приметите да у примеру а) све линије програма не почињу у првој линији. Ово је још један од начина да поједине линије програма (кôда) које чине целину учините уочљивијим.

Mада C дозвољава прилично велику слободу форме писања кôда (као у претходном примеру) постоје строга правила код писања знакова интерпункције ( . , : ; ). Та правила омогућавају преводиоцу да исправно (једнозначно) тумаче написани кôд, а програмеру с друге стране, потпуно слободно уређивање кôда ради боље прегледности и читљивости.

Напомена #include наредба (и оно што следи иза ње)се не сме раздвајати у више редова нити је дозвољено уметање знакова између знака < и stdio.h, односно између stdio.h и знака >.

Уколико наредбу која се налази између знакова навода желите да напишете у више редова, на месту преласка у нови ред треба уписати знак \ (backslash). пример: printf("Ovo je veoma, veoma, \ veoma, veoma,duga linija.");

Употреба великих и малих слова такође није насумична. Наиме, C програмски језик разликује мала и велика слова (case sensitive). Све службене речи C програмског језика се пишу малим словима, а програмер може кориситити и велика и мала слова али при томе мора водити рачуна да се различити интерпретирају.

Елементи језика C Да би написали рачунарски програм, потребно је написати кôд у неком програму за обраду текста. Приликом уноса кôда постоји ограничен скуп знакова (карактера) који се могу користити. Скуп знакова који се користи у језику C − Велика и мала слова енглеског алфабета

Aa Bb Cc Dd Ee Ff Gg Hh Ii Jj Kk Ll Mm Nn Oo Pp Qq Rr Ss Tt Uu Vv Ww Xx Yy Zz − Децималне цифре

0 1 2 3 4 5 6 7 8 9 − Знаци: : _ ! ? . , : ; ‘ = + - / * # % & \ | ( ) [ ] { } < >

Поред тога што је скуп знакова за писање кôда ограничен, кôд се састоји од скупа знакова записаних по одговарајућим правилима. Недељиви низови знакова се називају Лексички симболи. У језику C се лексички симболи могу поделити на: − Идентификаторе − Службене речи − Константе − Операторе − Сепараторе

Идентификатори Служе за означавање (именовање) свих врста елемената програма (података, симболичких константи, нових типова података, функција ...).

Page 8: Uvod u programski jezik C Univerzitet Singidunum

7

Правила за одређивање имена идентификатора су следећа:

1. Могу да се састоје од: − Слова (малих и великих) − Цифара − Знака подвучено ( _ )

2. Први знак у имену (идентификатора) мора да буде слово. Може да буде и знак подвучено али се то не препоручује! (Разлог је у томе што неке готове функције имају оваква имена).

3. Дужина имена идентификатора (број карактера, односно број слова, цифара и знакова _ ) је ограничена! Уопштено, дужина имена може зависити од више ствари али се не препоручује да буде дужа од 31 карактера, чак и када преводилац то дозвољава. Основни разлози за овакво ограничење су прегледност кôда и рационално коришћење меморијских ресурса.

Пример: JednaPromenljivaKojaImaKarakteraJakoPuno = 1 JednaPromenljivaKojaImaKarakteraSamoJedanVise = 3 У претходном примеру преводилац ће сматрати да је реч о једном идентификатору са именом JednaPromenljivaKojaImaKarakte. Ово је један од разлога настајања логичких грешака у програму.

4. C разликује мала и велика слова (Rezultat и rezultat су два различита идентификатора)

5. Имена идентификатора не могу бити из скупа службених речи.

Програмер може дефинисати име идентификатора које је дуже од 31 карактера али је само првих 31 карактера значајно (зависи од преводиоца). Примери: a) исправни идентификатори

suma NovoIme c5_4 novo_ime JEDAN_BROJ Broj_studenata dugoimesamnogokaraktera Broj_Studenata TRUE TX_protocol _split_name UporediRezultat_sabiranja б) неисправни идентификатори

70fd име не може почињати бројем x-name знак - није дозвољен ime sa razmakom размак није дозвољен int резервисана реч axx& знак & није дозвољен Površina*kruga знак * није дозвољен jedan,dva знак , није дозвољен

Page 9: Uvod u programski jezik C Univerzitet Singidunum

8

Службене речи Мада постоји поприлично велика слобода у избору имена идентификатора, постоји група речи која се не може користити за имена идентификатора. То су идентификатори којима је унапред додељено одговарајуће значење и називају се резервисане или службене речи (укупно их има 32):

auto double int struct

break else long switch

case enum register typedef

char extern return union

const float short unsigned

continue for signed void

default goto sizeof volatile

do if static while могу се груписати и по намени: Контрола гранања (6) – if, else, return, switch, case, default Петље (5) – for, do, while, break, continue Топови променљивих (5 + 7) – int, float, double, char, void – extern, signed, unsigned, long, short, static, const Рад са структурама (3) – struct, typedef, union Рачунање и одређивање величине (2) – enum, sizeof Скокови (не треба кориситити!) (1) – goto Додатне (3) – auto, register, volatile

Подаци, Променљиве и типови података Подаци су предмет обраде у програмима. Сваки податак има одређене особине, а скуп свих особина јеног податка одређују тип податка. Тип податка је одређен: − скупом могућих вредности које податак може да узме и − скупом могућих операција које је могуће извести над податком.

Подаци у програму могу да се представе помоћу: − вредности или − идентификатора.

Уколико подаци се представе помоћу вредности онда они (подаци) не могу да мењају своју вредност у току извршења програма и називају се константе.

С друге стране, уколико се подаци представе помоћу идентификатора, њихова вредност може да се мења у току извршења програма, та вредност се смешта у меморију рачунара и заузима одређени меморијски простор. Подаци представљени преко идентификатора се називају променљиве или варијабле (variable).

Посебан случај представљају вредности података представљених помоћу идентификатора за које програмер не жели да им се вредност мења у току извршења програма. Оне се називају симболичке константе. Симболичке константе нису променљиве (варијабле) и њихова вредност не заузима меморију.

Пример симболичких константи:

#define PI=3.14;

#define MIN_RASTOJANJE=2;

#define Broj_Semestara=8; /*ne preporučuje se upotreba malih slova za simbolicke konstante*/

Page 10: Uvod u programski jezik C Univerzitet Singidunum

9

Напомена:

1. #define представља предпроцесорску наредбу и биће касније детаљно објашњена

2. Препорука је да се имена симболичких константи пишу великим словима, како би програмеру у току писања (анализирања) програма било уочљиво да се њихова вредност не мења. Обзиром да је ово само препорука, неће бити пријављена грешка или упозорење уколико се препоруке не придржавате (као у трећем примеру).

Шта су променљиве у програмском језику?

Речено је да су подаци предмет обраде у програму. Слично као и у математици (алгебри), променљиве су имена података (идентификатори) којима може бити додељена вредност (нпр. x=a+b). Једна од битних разлика је та да је име променљиве у алгебри најчешће представљено једним словом, док се име променљиве у C-у најчешће одређује помоћу више карактера, придржавајући се поменутих правила за именовање идентификатора.

Поред тога пожељно је придржавати се следећих препорука

− Трудите се да имена променљивих буду смислена, нпр. уколико променљива треба да садржи вредност рачуна дајте јој име: racun или bill а не xyz, r и сл.

− Уколико се име променљивих састоји од више делова користите један од два начина обележавања али их немојте мешати, будите доследни. На пример, променљиву која означава почетну цену назовите PocetnaCena или pocetna_cena, али немојте користити оба начина означавања истовремено (Pocetna_Cena) или још горе, да у истом програму имате две променљиве од којих се једна зове PocetnaCena а друга pocetna_cena.

− Избегавајте да променљиве именујете сличним именима, нпр. broj_kola и broj_koka.

Занемаривање ових препорука веома лако води ка грешкама у програмирању.

Вредности које садрже подаци се чувају у меморији рачунара. Због тога се свакој променљивој додељује меморијска локација у коју се смешта (записује) тренутна вредност промељнљиве. Може се рећи да је име променљиве адреса, односно замена за адресу меморијске локације на којој је записана тренутна вредност променљиве. Другим речима, меморија је подељена на одговарајуће целине различитих величина (у бајтовима) и свака та целина има своју адресу. Адреса представља број који једнозначно одређује позицију било ког податка у меморији рачунара. Када програм треба са прочита или упише вредност на неку меморијску локацију он мора знати адресу (место у меморији) на којој се тај податак налази. Како би било веома непрактично (а често и немогуће) за програмера да одреди меморијску адресу сваког податка, податку се додељује јединствено име које представља замену за стварну адресу.

Типови података Тип неког податка (променљиве) је представљен скупом вредности који тај податак може да има као и дефинисаним скупом операција над њим.

Програмски језик C познаје само нумеричке типове података, а међу њима се посебно разликују целобројни и реални нумерички типови.

Целобројни типови података су:

int

тип int је основни целобројни тип, означен је (signed), што значи да може имати позитивне и негативне вредности. Службена реч signed се подразумева па није потребно писати signed int већ је довољно да се тип означи са int. Тип податка int се састоји од знака + (плус) или – (минус) и једне или више цифара. (пример: 2, -30211, 0, +28). Знак + се не мора писати испред цифара он се подразумева.

Опсег целобројних вредности које може садржати променљива типа int је различита и зависи од рачунара на ком се извршава програм. Величина типа int је једнака величини рачунарске речи (word size) и не може

Page 11: Uvod u programski jezik C Univerzitet Singidunum

10

бити мања од 16-бита (2 бајта), а може бити и 32 бита (4 бајта). У случају да је резервисано 16 бита опсег вредности које се могу записати у променљиву типа int је од -32 767 до 32 768 што укупно износи (укључујући нулу) 65 536 целобројних вредности. Ове вредности је у бинарном облику могуће описати са 16 бита (2 бајта) па је меморијски простор које се резервише за променљиву типа int једнак 2 бајта.

На сличан начин је могуће одредити опсег вредности променљиве типа int за случај да је на располагању 32 бита меморије (4 бајта): од − 2 147 483 648 до 2 147 483 647.

Уколико покушате да променљивој типа int доделите вредност ван дефинисаног опсега (нпр. 6 632 343 000) направићете грешку: прекорачење дозвољеног опсега (overflow error).

Опсег целобројних вредности се може променити употребом службених речи signed, unsigned, short и long. На тај начин долазимо до типова: (signed) short int, unsigned short int, (signed) long int, unsigned long int,

Податак типа short int представља означени целобројни податак који заузима меморијску локацију која по величини не сме бити већа (може бити мања и једнака) од величине коју заузима податак типа int.

Податак типа long int представља означени целобројни податак који заузима меморијску локацију која по величини не сме бити мања (може бити већа и једнака) од величине коју заузима податак типа int.

Из овога следи да на неком рачунару сва три типа података: short int, int и long int могу да имају исти опсег вредности. У пракси обично постоји два типа целобројних података (short и long), а int се поклапа са једним од њих.

Неозначене (само позитивне) варијанте претходно поменута три целобројна типа настају додавањем службене речи unsigned испред наведених назива. Тако се долази до типова unsigned short int unsigned int и unsigned long int.

char

Тип char означава мали означени целобројни податак (мада му име потиче од речи character, што означава знак или слово) величине 8 бита (1 бајт). Генерално је намењен за манипулацију карактерима, али може послужити и за рад са малим бројевима са вредностима у распону од од −128 до 128. Употребом службене речи unsigned долази се до неозначеног типа unsigned char који може имати вредности од 0 до 255. (напомена: приметите да ASCII табела има 256 знакова).

Како се на неку меморијску локацију, резервисану за податак типа char, уписује, на пример, знак ‘A’? На ту локацију се уписује вредност 65 (или у бинарном облику 1000 0001) што представља ASCII кôд за тај занак.

Реални типови података

Програмски језик C познаје три врсте реалних бројева: float, double long double

Tип float означава реални податак у једнострукој тачности, тип double реални податак у двострукој тачности, а тип long double реални податак у вишеструкој тачности. Слично типу int, тип double не сме да буде мање тачан од типа float нити long double сме да буде мање тачан од типа double.

Page 12: Uvod u programski jezik C Univerzitet Singidunum

11

Нумеричке константе

Сваком наведеном типу податка је могуће доделити вредност одговарајуће константе. Константе могу бити

− целобројне

− реалне

Целобројне константе

У програмском језику C целобројне константе могу да се напишу у децималном, хексадецималном (основе 16) и окталном (основе 8) бројевном систему.

Константа чија је прва цифра нула припада окталном бројевном систему

пример:

0123 ово је октални број чија децимална вредност износи 83

Хексадецималне цифре почињу знаком 0x или 0X

0x123 ово је хексадецимални број чија децимална вредност износи 291

У свим случајевима, без обзира на бројевни систем који се користи, тип константе је int осим ако је вредност константе сувише велика за тај тип. Тада је типа long int. Додавањем суфикса l или L иза константе добија се тип long int.

пример:

974 константа типа int

0x2fc константа типа int

974l константа типа long int

01234L константа типа long int

Децимални тип константе је позитиван уколико се не наведе предзнак + (плус). За друга два типа бројевних система предзнак константе се одређује на основу вредности првог бита (након конверзије у бинарни бројни систем). Нула означава позитивне бројеве.

Додавањем суфикса u или U иза константе добија се тип unsigned int.

0xffff константа има негативну вредност

0xffffu константа има позитивну вредност

0723 константа има негативну вредност

0723u константа има позитивну вредност

Реалне константе

Пишу се само у децималном бројевном систему. За константе које имају веома малу или веома велику вредност корисит се експоненцијални запис облика mEe где је m мантиса а e вредност експонента

пример:

2.54Е-34 представља вредност 2.54 10-34

Подразумевани тип реалних константи је double, а може се поставити на тип float додавањем суфикса f или F.

Page 13: Uvod u programski jezik C Univerzitet Singidunum

12

Примери реалних константи

1.23

125. уколико се не наведе, подразумева се нула иза децималне тачке

.456 уколико се не наведе, подразумева се нула испред децималне тачке

3е5

2.1Е-45

22.3F

Page 14: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C (C programming Language)

Лекција 2

Садржај Дефинисање података.................................................................................................................................. 2 Дефинисање непроменљивих података ................................................................................................. 3

Читање и писање података .......................................................................................................................... 3 Функција printf().............................................................................................................................. 4 Функција scanf() ................................................................................................................................. 6

Оператори ..................................................................................................................................................... 8 Аритметички оператори .......................................................................................................................... 8 Оператор доделе вредности .................................................................................................................... 9 Релацијски оператори (оператори поређења) ..................................................................................... 10 Логички оператори ................................................................................................................................ 10 Оператори по битовима......................................................................................................................... 11 Величина податка................................................................................................................................... 11 Условни израз......................................................................................................................................... 12

Конверзија типа.......................................................................................................................................... 12 Редослед извршавања оператора .......................................................................................................... 13

Page 15: Uvod u programski jezik C Univerzitet Singidunum

2

Дефинисање података Сваки податак (променљива) који се користи у програму мора да се дефинише пре употребе!

Општи облик наредбе за дефинисање променљиве је следећи:

tip_podatka ime_podatka;

или

tip_podatka ime_podatka = početna_vrednost;

где су:

tip_podatka: било који од претходно поменутих типова података (int, long int, float, char,...). Може бити и неки ново-дефинисани или сложени тип о чему ће касније бити речи.

ime_podatka: је идентификатор односно име променљиве који одређује програмер. При избору имена треба водити рачуна од ограничењима (Лекција 1: Идентификатори)

početna_vrednost почетна или иницијална вредност променљиве.

Почетна вредност променљиве не мора бити одређена приликом дефинисања променљиве. Могуће је почетну вредност доделити промељивој на било ком месту у програму, наравно пре прве употребе променљиве.

Напомена: Део наредбе који се може, али не мора написати обично се пише унутар угластих заграда. Претходне две наредбе је могуће објединити на следећи начин:

tip_podatka ime_podatka [ = početna_vrednost] ;

Примери:

short int broj1;

short int broj2=34;

char slovo;

float a=5.43,

float b=12.65;

int visina=34/2;

float povrsina=a*b;

У примеру int visina=34/2; почетна вредност је додељена преко израза (операција дељења). У последњем примеру променљивој povrsina је додељена почетна вредност преко израза (множење две вредности), а при томе су вредности које се користе у изразу (a и b), почетне вредности две претходно дефинисане променљиве.

Могуће је дефинисати више података истог типа једном наредбом, с тим да се подаци међусобно одвајају зарезом:

tip_podatka ime_podatka[=početna_vrednost],...,ime_podatka [= početna_vrednost];

Пример:

int broj=2, prisutni=0, godiste=2004, br_casova;

Процес додељивања почетне вредности се назива иницијализација.

Page 16: Uvod u programski jezik C Univerzitet Singidunum

3

Дефинисање непроменљивих података Непроменљиви подаци су именовани подаци чија се вредност не мења.

Дефинишу се на сличан начин као и променљиве стим да:

– дефинисање почиње са кључном речи const

– мора им бити додељена почетна вредност

Општи облик дефинисања непроменљивих података

const tip_podatka ime_podatka = početna_vrednost;

Пример:

const double e=2.71828182845905;

const int br_radnih_dana=26;

const float radijus=14;

const float cena=14.2, porez=0.22; /* i porez je konstanta*/

const float obim=2*radijus*3.14;

Читање и писање података Основна намена сваког програма је обрада података. До података који се обрађују, може се доћи на различите начине, а један од честих је уносом са тастауре рачунара. Након обраде унетих података добијени резултати се најчешће приказују на монитору. Ово су уобичајене улазнa (тастатура) и излазнa (монитор) јединицa. Треба рећи да постоје и друге улаазное и излазне јединице (улазни и излазни подаци се могу налазити у фајловима и сл) о којима ће касније бити више речи.

Сви подаци који се уносе са тастатуре, укључујући и бројеве, се прихватају као знакови (character). Са друге стране, рачунар може да обрађује и чува искључиво бројеве па је неопходно извршити одговарајућу улазну конверзију.

Улазна конверзија за „читање” података унетих преко тастатуре се у програмском језику C обавља преко

уграђене (системске) готове функције scanf()

Приликом исписа података на монитор потребно је да се обави излазна конверзија, тј. да се подаци који су у општем случају представљени у бинарном облику прикажу у жељеном облику. Ова конверзија се обавља применом готове функције printf().

Информације потребне преводиоцу за програмски језик C у току превођења програма који користи уграђене

функције (називају се још и библиотечке функције) scanf() и printf() налазе се у фајлу stdio.h. Име stdio потиче од standard input output a екстензија .h од header (заглавље), па се овакви фајлови називају заглављима. Због тога је у свим програмима који користе наредбе scanf() и/или printf() потребно „рећи” преводиоцу да у програм укључи информације које се налазе у stdio.h фајлу. То чинимо помоћу предпроцесорске наредбе #include <stdio.h>.

Напомена: Име фајла stdio.h се налази унутар заграда < > што преводиоцу значи да се тај фајл налази на неком унапред дефинисаном месту на диску рачунара. Обзиром да програмер може сам дефинисати неки нови фајл заглавље, и снимити га на било ком месту на диску онда се претпроцесорска наредба нешто разликује и има облик #include ″c:\nekoime.h″. Уместо заграда < > се користе занкови навода ″ ″, а поред имена заглавља је потребно навести и потпуну путању.

Page 17: Uvod u programski jezik C Univerzitet Singidunum

4

Функција printf() Најједноставнији облик функције printf() је испис текста на монитор:

printf("Ovo je jednostavna poruka")

Резултат ове наредбе је испис на монитору

Ovo je jednostavna poruka

Сав текст који се исписује мора бити написан унутар знакова навода! Овом функцијом се може постићи много сложеније дефинисање изгледа исписа (отуда слово f у наредби од formated).

Општи облик наредбе је следећи:

printf(format,izraz,...,izraz)

при томе izraz може представљати име променљиве, вредност константе или неки аритметички израз

а format има облик:

%[Sirina][.Prec]Tip_kon

Tip_kon означава тип податка које се исписује и може да има вредности:

Tip_kon Тип у који се конвертује Излазни формат ----------------------------------------------------------------------------------------------------- i int oзначени децимални цео број d int oзначени децимални цео број u int неозначени децимални цео број o int неозначени октални број x int неозначени хексадецимални број применом малих слова „abcdef” X int неозначени хексадецимални, велика слова „ABCDEF” c int карактер дефинисан једним бајтом (ASCII) e float реални број једноструке тачносити, експоненцијални облик f float реални број једноструке тачносити уобичајеног облика g float реални број једноструке тачносити облика f или e у зависности од

тога који је погодниј за приказ

За сваку вредност која треба да се испише мора бити дефинисан по један Tip_kon.

Примери: printf(″%c″, ’A’); резултат је: А printf(″%c %d″, ’A’, 35); резултат је: А 35 приметите размак између c и %d. printf(″%c%d″ ’A’, 35); резултат је: А35 ovде нема размака. printf(″%c %d %f″, ’A’, 35, 4.5); резултат је: А 35 4.50000 printf(″%c %d %е″, ’A’, 35, 42.123); резултат је: А 35 4.212300е+001

Page 18: Uvod u programski jezik C Univerzitet Singidunum

5

.Prec представља прецизност исписа и има значаја само за реалне бројеве (тип конверзије: e, f и g)

За тип конверзије е и f одређује број цифара које ће бити исписане након децималне тачке.

– Уколико се прецизност не наведе подразумевана вредност је 6.

– Уколико је наведена прецизност мања од броја децимала исписује се онолико цифара колико прецизност дефинише а последња цифра се заокржује.

За тип конверзије g одређује укупан број цифара који ће се исписати. Нема заокруживања.

Пример: printf(″%е″ 42.126); резултат је: 4.212600е+001 Подразумевана вредност је 6 децимала printf(″4е″ 42.126); резултат је: 4.2126е+001 Исписује онолико децимала колико је наведено printf(″3е″ 42.126); резултат је: 4.213е+001 Заокруживање последње децимале printf(″%f″, 42.126); резултат је: 42.126000 Подразумевана вредност је 6 децимала printf(″7f″ 42.12345678); резултат је: 42.1234568 Заокруживање printf(″%.2f″, 42.126); резултат је: 42.13 Заокруживање printf(″%g″, 42.126); резултат је: 42.126 printf(″%.7g″, 42.12345678); резултат је: 42.12345 укупно седам цифара. „одсечена” последња цифра printf(″%.2g″, 42.126); резултат је: 42 printf(″%.2g″, 142.126); резултат је: 1.4е+002

Sirina Одређује минимални број карактера који се резервише за испис неког броја. То је позитивна вредност. Уколико је за испис потребно мање карактера од броја који је одређен параметром Sirina испис се допуњава бленковима (размацима) и поравнава се у десно.

Уколико се испред параметра Sirina наведе 0 бленкови се, уколико постоје, замењују нулама.

Page 19: Uvod u programski jezik C Univerzitet Singidunum

6

Пример:

("%2c %3d %10f\n", 'A', 35, 42.126);

printf("%02c %03d %010.3f\n", 'A', 35, 42.126);

printf("%c %d %2f\n", 'A', 35, 242.127456789);

резултат:

А 35 42.126000

А 35 42.1260000 празно место испред А, празно место испред 35, за последњи број је резервисано 10 места: шест је подразмевано за децимална места, један за децималну тачку, у целом делу броја постоје две цифре и додаје се једно празно место испред броја

0А 035 00042.126 Убацивање водећих нула

А 35 242.127456 Нема одбацивања у целом делу броја

Поред наведеног, формат исписа могуће је додатно дефинисати тзв, escape карактерима:

\a – звучни сигнал \\ - испис знака \ \? – испис знака ? \’ – испис знака ’ \” – испис знака ” \b – померај за један карактер у лево (backspace) \f – понерање на крај реда (formfeed) \n – почетак новог реда \r – нови ред (carriage return) \t – хоризонтални табулатор (tab) \v – вертикални табулатор (tab)

Пример: printf("%c\n\t%d\n%f\n", 'A', 35, 42.126);

резултат је: А /* \n je znak za novi red*/ 35 /* \t je znak tab, 35 je pomeren u desno*/ 42.126000

Функција scanf() Општи облик наредбе је следећи:

scanf(format,&podatak,...,&podatak)

при томе podatak може представљати име променљиве, или у општем случају идентификатор, у који ће се сместити резултат конверзије улазне вредности.

Oператор & испред имена променљиве означава да се функцији scanf() додељује меморијска адреса променљиве (податка) на коју се уписује резултат конверзије.

а format има исти облик као и код функције printf().

Page 20: Uvod u programski jezik C Univerzitet Singidunum

7

Примери: #include <stdio.h> main() { char cVar; int dVar; float fVar; scanf("%c %d %f", &cVar,&dVar, &fVar); printf("\n%c \n%d \n%f\n",cVar, dVar, fVar); }

У овом примеру се од корисника очекује да са тастатуре унесе: један карактер (нпр. слово F) , један цео број и један (нпр. 32) и један рационалан број (нпр. -67.5092).

Након уноса се мора притиснути тастер ENTER да би унос био прихваћен!

Aко у једној линији није унето довољно података они се очекују у наредној линији.

Након уноса се те исте вредности исписују на монитор по дефинисаном формату. Приметите да се „бленкови” који се уносе пре и после бројчаних вредности занемарују.

У наредном примеру се од корисника очекује да унесе једну целобројну вредност један карактер и једну реалну вредност.

Корисник је грешком унео једну вредност више и она остаје у меморији и придружује се првој наредној наредби за читање. Корисник о томе није обавештен. #include <stdio.h> main() { int x; char c; float y; scanf("%d %c %f", &x, &c, &y); printf("\n%d \n%c \n%f\n",x, c, y); scanf("%d", &x); printf("Da li sam cekao za drugi unos?\n"); printf("\n%d\n",x); }

унете вредности

излазне вредности

Page 21: Uvod u programski jezik C Univerzitet Singidunum

8

Оператори

Аритметички оператори Служе за обављање уобичајених аритметичких операција

Бинарни оператори Из имена је лако закључити да захтевају два операнда. Бинарни оператиори су:

+ сабирање - одузимање * множење / дељење % остатак после дељења два броја

Код употребе оператора дељења мора се водити рачуна да ако су оба операнда цели бројеви након дељења се као резултат добија цео број а децимални део се одбацује! Примери:

#include <stdio.h> main() { int x=5; int y=2; float z; z=x/y; } променљивој z ће бити додељена вредност 2. остатак при дељењу или модуо: 10%2 даје резултат 0 јер је 10/2=5 а остатак је 0 15%7 даје резултат 1 јер је 15/7=2 а остатак је 1

Унарни оператори: +, -

Представљају предзнак операнда (константе променљиве, израза...) и имају значење уобичајено у математици. Предзнак + се подразумева и није га неопходно писати за позитивне вредности.

Page 22: Uvod u programski jezik C Univerzitet Singidunum

9

Унарни оператори: ++, −− – Оператор ++ увећава вредност операнда за 1 (increment). – Оператор −− умањује вредност операнда за 1 (decrement). Оне служе као замена за веома честе наредбе облика А=А+1; А=А-1; Уместо тога може се писати: А++; /* odgovara naredbi А=А+1; */ ++А; /* odgovara naredbi А=А+1; */ А--; /* odgovara naredbi А=А-1; */ --А; /* odgovara naredbi А=А-1; */ – Оператори увећања и умањења вредности операнда за један могу да се навeду пре операнда или после

операнда али им се значење у тим случајевима може разликовати. У претходно наведеном примеру позиција операнда није имала никаквог утицаја на резултат. Међутим, уколико се оператори (++ и −− ) наведу пре операнда (као префикс) онда се прво врши операција инкрементирања или декрементирања а затим ће бити обављене друге операције са том променљивом. Ако се оператор пише после операнда онда ће се прво извршити операција над промељивом а потом ће њена вредност бити увећана, односно умањена за један.

Примери: int broj_a, broj_b, broj_c, broj_d; broj_a=3; broj_b=6; broj_c= broj_b++; /* broj_c je jednak 6 a nakon toga se povećava b na 7*/ broj_c= ++broj_a; /* prvo se broj_a poveća na 4 a potom je broj_c jednak 4*/ broj_d= broj_b; /* broj_d je jednak 7*/

Оператор доделе вредности Додела вредности се у C-у сматра операцијом.

Оператор доделе вредности је = , али може бити и: +=, - =, *=, /= и %=

Општи облик наредбе за додељивање вредности је:

ime_promenljive [op]= izraz; /* gde je op jedan od +, - , *, / ili % */

Променљивој чије име је написано на левој страни се додењује вредност израза (може и друге променњиве или константе) који је написан на десној страни.

broj_a=3; broj_b=6; broj_c= broj_b + broj_b a=а+1; a+=b; /* isto je sto i a=a+b */ a-=b; /* isto je sto i a=a-b */

Page 23: Uvod u programski jezik C Univerzitet Singidunum

10

a*=b; /* isto je sto i a=a*b */ a/=b; /* isto je sto i a=a/b */ a%=b; /* isto je sto i a=a%b */ a=b=c=27;

У последњем примеру све три променљиве добијају вредност 27. Прво се променљивој c додели вредност 27, потом се променљивој b додели вредност променљиве c и на крају се промељивој a додели вредност променљиве b.

Релацијски оператори (оператори поређења) Релацијски оператори успостављају релацију између два израза. Резултат је логичког типа: Истина/Неистина (True/False) зависно од тога да ли испитана релација вежи или не важи.

Релацијски оператори су: > веће од a > b < мање од a < b >= веће и једнако a >= b <= мање и једнако a <= b == једнако a == b != није једнако a != b

Резултати поређења су целобројног типа (int) и могу имати вредност 1 ако релација важи или вредност 0 уколико релација не важи.

3.1 != -3.1 /* rezultat 1 */ 5.2 >= 6 /* rezultat 0 */ 7<= 7 /* rezultat 1 */ 0.5 == 4.2 /* rezultat 0 */ a != 5 /* rezultat zavisi od vrednosti promenljive a */

Логички оператори У програмском језику C постоје три логичка оператора:

! негација

&& логичко и

|| логичко или

Оператор негације је унарни оператор док су преостала два ооператора бинарни.

при томе се треба придржавати следећих правила:

izraz1 !(izraz1) izraz1 izraz2 izraz2 && izraz2različit od 0 1 0 0 0

0 1 0 različit od 0 0

različit od 0 0 0

različit od 0 različit od 0 1

Page 24: Uvod u programski jezik C Univerzitet Singidunum

11

izraz1 izraz2 izraz2 && izraz2 0 0 0

0 različit od 0 1

različit od 0 0 1

različit od 0 različit od 0 1

Пример:

да ли се вредност променљиве налази између 10 и 100 (10<а) && (а<100) /* Ako je a>10 i a<100 vrednost izraza je 1, u suprotnom 0*/

да ли је апсолутна вредност променљиве је већа од 15 (а <-15) || (а>15) /* ako je a<-15 ili a>15 vrednost izraza je 1, u suprotnom 0 */

Оператори по битовима

~ потпуни комплемент

<< логичко попмерање у лево

>> логичко померање у десно

& логичко И на битском нивоу

| логичко ИЛИ на битском нивоу

^ логичко ексклузивно или (exor)

Примери:

0x1234 & 0x5678 /* 0001 0010 0011 0100 & 0101 00110 0111 1000 = 0x1230 */

0x1234 | 0x5678 /* 0001 0010 0011 0100 | 0101 00110 0111 1000 = 0x567c */

0x1234 ^ 0x5678 /* 0001 0010 0011 0100 ^ 0101 00110 0111 1000 = 0x444c */

~0x1234 /* ~ 0001 0010 0011 0100 = 0xedcb */

0x0001<<5 /* 0000 0000 0010 0000*/

Величина податка величина податка, односно величина меморијске локације коју заузима неки податак се може израчунати употребом оператора sizeof.

Примери: sizeof( short int); sizeof( long int); sizeof( broj_a); sizeof( broj_a*broj_b-3);

Page 25: Uvod u programski jezik C Univerzitet Singidunum

12

Условни израз Општи облик условног израза је

uslov ? izraz1 : izraz2

Прво се израчуна вредност логичог израза uslov.

Уколико је вредност логичог израза uslov различита од нуле израчунава се вредност операнда izraz1 и добијени резултат представља коначни резултат условног израза (вредност се у том случају izraz2 занемарује) .

Уколико је вредност логичог израза uslov једнака 0 израчунава се вредност операнда izraz2 и добијени резултат представља коначни резултат условног израза.

Пример:

Доделити променљивој c вредност променљиве a уколико је она већа од променљиве b, иначе променљивој c доделити вредност променљиве b

c= (a>b) ? a : b;

Доделити променљивој c вредност променљиве y уколико је она већа или једнака вредности променљиве x и мања или једнака од вредности променљиве z, иначе променљивој c доделити вредност израза (x+z)/2

c=(x<=y && y>=z) ? y : (x+z)/2

Конверзија типа У програмском језику C је дозвољено да се у нумеричким изразима користе операнди различитих типова. Како бинарни оператори захтевају да два оператора имају исти тип, у случајевима када то није испуњено долази до аутоматске конверзије типова.

Опште правило аутоматске конверзије типова је да се једноставнији тип претвара у сложенији тип.

5+6.0 /* isto sto i 5.0+6.0*/

5/4+3 /* 5/4 zbog celobrojnog deljenja je 1 pa je rezultat 4*/

3.0*5/4 /* (3.0*5)/4, zbog automatske konverzije (3.0*5) postaje (3.0*5.0)

pa sledi 15.0/4 sto zbog automatske konverzije

postaje 15.0/4.0. Rezultat je 3.75 */

Међутим, како су правила конверзије нешто сложенија проблем се може решити експлицитном наредбом за промену типа применом тзв. cast-оператора. Његов општи облик је:

(novi_tip) izraz

где је novi_tip један од могућих типова програмског језика C а izraz представља променљиву или неки сложени израз чији тип треба променити.

Пример: int a; ... (char)a; /* pretvara int promenljivu a u tip char*/ float x; double y; y=sin((double)x); /* parametar funkcije sinus mora biti tipa double */

Page 26: Uvod u programski jezik C Univerzitet Singidunum

13

Редослед извршавања оператора Редослед извршавања оператора је унапред дефинисан одговарајућим приоритетом.

Овај проблем није једноставан али је најлакше да га решите употребом заграда које имају највиши приоритет и да на тај начин будете сигурни да ће се израз израчунати на очекивани начин.

Page 27: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C (C programming Language)

Лекција 3

Садржај Наредбе ........................................................................................................................................................................2

Oсновна програмска наредба.................................................................................................................................2 Секвенца или блок наредби ...................................................................................................................................3 Досег или област важења идентификатора (scope).............................................................................................3 Наредбе гранања (селекције).................................................................................................................................7

Наредба if else ...............................................................................................................................................7 Наредба switch ...............................................................................................................................................12

Наредбе понављања (петље или циклуси).........................................................................................................15 while петља ....................................................................................................................................................15 for-петља..........................................................................................................................................................16 do while петља ...............................................................................................................................................18

Скокови ...................................................................................................................................................................19 Наредба break..................................................................................................................................................19 Наредба continue ...........................................................................................................................................20

Page 28: Uvod u programski jezik C Univerzitet Singidunum

2

Наредбе Сваки програм написан на неком програмском језику је састављен од низа појединачних наредби.

Редослед извршавањa наредби унутар програма је најчешће одозго на доле, по реду (секвенцијалан), али се измена редоследа може постићи помоћу управљачких наредби.

У управљачке наредбе спадају:

– наредбе гранања (селекције) у које спадају if-else и switch наредбе. Ове наредбе омогућавају извршење одређених наредби у зависности од испуњења, односно неиспуњења датих услова.

– нарeдбе понављања (петље, циклуси или итерације) у које спадају for и dо наредбе. Ове наредбе омогућавају да се одређени делови програма извршавају више пута.

Програмски језик C познаје следеће врсте програмских наредби:

1. основне наредбе

2. блок наредби

3. Наредбе гранања (селекције) у које спадају

– if else – switch

4. наредбе понављања (циклуси) у које спадају:

– while петља – for петља – do while петља

4. Наредбе контролисаног скока у које спадају

– break – continue

5. Наредба goto

6. Наредба return

Oсновна програмска наредба Ако се на крају једног израза дода знак „ ; ” добија се основна (елементарна или проста) програмска наредба језика C. Општи облик основне програмске наредбе је:

izraz;

Примери:

Visina=5;

printf("%d \n", Visina);

broj b--;

Посебан случај основне програмске наредбе је празна наредба. Она се састоји само од знака тачка-зарез „ ; ”.

Page 29: Uvod u programski jezik C Univerzitet Singidunum

3

Празна наредба нема никакав утицај на извршење програма и она се може појавити на сваком месту у програму где се може појавити и основна наредба. Она се корисити у случајевима када синтакса програмског језика, на неком месту у програму, захтева постојање наредбе а сам програм на том месту не треба да обави никакву програмску акцију.

Секвенца или блок наредби Блок наредби представља скуп програмских наредби које чине једну целину и које се извршавају једна за другом. У блоку наредби могу да се појаве како извршиве тако и декларативне наредбе. Блок наредби се пише унутар витичастих заграда { } и има општи облик:

{ izraz1; izraz2; ... izrazN;} или

{ izraz1; izraz2; ... izrazN; }

Блок наредби се користи у следеће сврхе:

– За груписање више наредби у једну целину

– Као тело неке функције

– За ограничавање улоге одређених описних наредби (нпр. дефинисање променљивих) на један локални део програма

На почетку сваке секвенце, пре него што се напише прва наредба, могу да се налазе декларативне наредбе! Другим речима, дефиниције променљивих, уколико их има, морају да се налазе на почетку секвенце, пре било које друге наредбе.

Досег или област важења идентификатора (scope) Област важења идентификатора у програмирању означава део програма у ком идентификатор може да се користи, тј. део програма у ком је нека променљива „видљива”.

Као што је речено, идентификатор мора бити дефинисан на почетку блока. Сходно томе, област важења идентификатора је од места где је дефинисан до места краја блока (почетак и крај блика су одређени витичастим заградама). Оваква област важења се назива блоковска област важења. Постоје и друге области важења о чему ће касније бити речи.

Пример блоковске области важења

main() { int prom1=1; double prom2=3.22; double prom3= prom2 + 5.92; prom1+=32; prom3= (double)prom1*54.76; printf("%d\n %f\n", prom1, prom3); } резултат: 33 1807.080000

Page 30: Uvod u programski jezik C Univerzitet Singidunum

4

У претходном примеру тело главне функције main(), које се налази између витичастих заграда, представља блок или секвенцу. На почетку тог блока су дефинисане три променљиве: prom1, prom2 и prom3. Њихова област важења је од места где су дефинисани до краја наведеног блока (до заграде „}”) односно у целој функцији main().

Обзиром да је област важења променљиве од места где је дефинисана до крај блока, било би погрешно писати:

main() { int prom1=1; double prom3= prom2 + 5.92; /* GRESKA */ /* error C2065: 'prom2': undeclared identifier */ double prom2=3.22; ... }

Не може се додељивати вредност променљиве prom2 променљивој prom3 пре него што је променљива prom2 дефинисана.

Поједине наредбе унутар блока и саме могу да буду блокови, називамо их унутрашњи или угнежђени блокови (nested blocks).

Пример:

main() { int prom1=1; double prom2=3.22; double prom3= prom2 + 5.92; prom1+=32; prom3= (double)prom1*54.76; ... { /* pocetak unutrasnjeg bloka*/ prom1 *=4; prom2= prom2*prom2; ... } /* kraj unutrasnjeg bloka*/ printf("%d %f\n", prom1, prom3); }

У унутрашњем блоку могу да се користе идентификатори (променљиве) дефинисани у спољашњем блоку. Каже се да су идентификатори спољашњег блока глобални за унутрашњи блок.

Идентификатори дефинисани у унутрашњем блоку не могу да се користе изван унутрашњег блока, ни испред ни иза унутрашњег блока. Област важења им је унутар њихових витичастих заграда! Зато се каже да су идентификатори који су дефинисани у унутрашњем блоку локални за тај блок.

Page 31: Uvod u programski jezik C Univerzitet Singidunum

5

Пример:

main() { int prom1=1; double prom2=3.22; double prom3= prom2 + 5.92; prom1+=32; prom3= (double)prom1*54.76; { /* pocetak unutrasnjeg bloka*/ int prom4; /* Lokalna promenljiva*/ prom4=76; prom1 *=4; prom2= prom2*prom2; } /* kraj unutrasnjeg bloka*/ printf("%d \n", prom4); /* Greska */ /*error C2065: 'prom4' : undeclared identifier */ }

У претходном примеру је показано да се може дефинисати променљива у унутрашњем блоку, али да се не може користити ван унутрашњег блока (као што је покушано у наредби printf() и што је проузроковало грешку).

Могуће је у унутрашњем блику дефинисати порменљиве које имају исто име као и променљиве у спољашњем блoку (тип може али не мора бити исти)! Овакве промењиве су потпуно независне једна од друге и свака од њих је потпуно непозната ван блока у ком је дефинисана.

Пример: /* Prikaz lokalnih promeljivih unutar bloka */ #include <stdio.h> main() { /* Definisanje promenljive lokalne za funkciju main(). */ int count = 5; printf("\nVrednost promenljive u spoljasnjem bloku, count = %d", count); /* Pocetak unutrasnjeg bloka */ { /* Definisanje promenljive lokalne za unutrašnji blok */ int count = 999; printf("\nVrednost promenljive u unutrasnjem \ bloku, count = %d",count); } printf("\nVrednost promenljive u spoljasnjem bloku, ponovo, \ count = %d\n", count); }

резултат је: Vrednost promenljive u spoljasnjem bloku, count = 5 Vrednost promenljive u unutrasnjem bloku, count = 999 Vrednost promenljive u spoljasnjem bloku, ponovo, count = 5

Из овог примера можете видети да је промељива count дефинисана у унутрашњем блоку независна од променљиве count дефинисане у спољашљем блоку. Прво је у спољашњем блоку дефинисана

Page 32: Uvod u programski jezik C Univerzitet Singidunum

6

променљива count типа int и додељена јој је вредност 5. Како је променљива count дефинисана на почетку блока функције main(), она се може користити у целој функцији main(). Применом наредбе printf показује се да је њена вредност 5. Након тога је у унутрашњем блоку дефинисана нова промељива count типа int и додељена јој је вредност 999. Применом наредбе printf показује се да је њена вредност 999. Након напуштања унутрашњег блока, наредаба printf приказује полазну вредност променљиве count дефинисане на почетку функције main().

#include <stdio.h> main() { /* Definisanje promenljive lokalne za funkciju main(). */ int count = 5; printf("\nVrednost promenljive u spoljasnjem bloku, count = %d", count); /* Pocetak unutrasnjeg bloka */ { /* Definisanje promenljive lokalne za unutrašnji blok */ double count = 99.888; printf("\nVrednost promenljive u unutrasnjem bloku, \ count = %f", count); } printf("\nVrednost promenljive u spoljasnjem bloku, ponovo, \ count = %d\n", count); }

резултат је: Vrednost promenljive u spoljasnjem bloku, count = 5 Vrednost promenljive u unutrasnjem bloku, count = 99.888000 Vrednost promenljive u spoljasnjem bloku, ponovo, count = 5

Овај пример се разликује од претходног само по томе што променљива дефинисана у унутрашњем блоку, није истог типа као и променљива дефинисана у спољашњем блоку.

Употреба оваквог начина декларисања локалних променљивих, наведених у претходна два примера, није тако честа у C програмском језику. Могуће је применити у покушају да се издвоји одговарајући проблем унутар програма. Можете привремено издвојити одређени део програма у витичасте заграде и дефинисати локалну променљиву коју лако можете да пратите. Други разлог је тај да можете да дефинишете и иницијализујете промељиву близу места на којој се примењује, што може допринети лакшем разумевању програма.

Page 33: Uvod u programski jezik C Univerzitet Singidunum

7

Наредбе гранања (селекције) Наредбе гранања омогућавају условно извршавање наредби односно доношење одлука. Оне у складу са неким постављеним условом извршавају једну од две или више наредби. Може се десити, у зависности од постављеног услова, да се ни једна од наведених наредби не изврши. У овом поглављу се разматра if наредба која омогућава програму да донесе одлуку засновану на анализи услова чија вредност може бити: тачно или нетачно.

Наредба if else Општи облик наредбе:

if(izraz1) { naredbe } else if(izraz2) { naredbe } else if(izraz3) { naredbe } ... else { naredbe }

a) Најједноставнији облик ове наредбе је if(izraz1){naredbа1; naredbа2; ... naredbаN; }

што је потпуно исто као и if(izraz1) { naredbа1; naredbа2; ... naredbаN; }

Наредба if се извршава тако што се прво израчуна вредност израза izraz1 у загради . У случају када је вредност израза различита од нуле (што одговара логичкој вредности „тачно") извршиће се naredbа1, naredbа2,..., naredbаN. У противном ће се наставити извршење програма без извршења наредби naredbа1, naredbа2,..., naredbаN.

Вредност izraz1, која представља услов у if наредби, се најчешће добија применом оператора поређења:

Page 34: Uvod u programski jezik C Univerzitet Singidunum

8

математички запис

програмски запис пример значење

= == x == y x jе једнако y

≠ != x != y x ниjе једнако y

> > x > y x jе веће од y

< < x < y x jе мање од y

≥ >= x >= y x jе веће или једнако од y

≤ <= x <= y x jе мање или једнако од y

Напомена: Уколико се између симбола који се састоје од два знака (==, !=, >= или <=) стави размак, компајлер ће пријавити грешку. Посебно треба обратити пажњу да се не меша оператор доделе (x = 5, oвде се променљивој x додељује вредност 5) и оператор поређења (x == 5, oвде се пореди да ли променљива x има вредност 5).

Пример: if(b>=3) { b=1; count = 45.32; slovo = 'A'; }

Ако постоји само једна наредба могу се изоставити витичасте заграде па је облик наредбе: if(izraz1)naredbа1;

што је потпуно исто као и if(izraz1) naredbа1;

Пример:

b=1; if(а>5)b=3;

Напомена: Ако је brој променљива онда:

if(brој != 0) је потпуно исто што и if(brој) if(brој == 0) је потпуно исто што и if(!brој)

У наредном примеру се пореде вредности два цела броја која уноси корисник применом шест if наредби. Ако се услов било које од тих шест if наредби испуни извршиће се printf наредба која прпада тој if наредби

Page 35: Uvod u programski jezik C Univerzitet Singidunum

9

Пример: int main()/* pocetak programa pocinje funkcijom main */ { int num1; /* prvi broj koji korisnik treba da unese */ int num2; /* drugi broj koji korisnik treba da unese */ printf( "Unesite dva cela broja i ja cu Vam reci\n" ); printf( "u kom su oni odnosu: " ); scanf( "%d%d", &num1, &num2 ); /* citaju se dva cela broja */ if(num1 == num2) { printf( "%d je jednak sa %d\n", num1, num2); }/* kraj if naredbe */ if(num1 != num2) { printf("%d nije jednak sa %d\n", num1, num2); } /* kraj if naredbe */ if(num1 < num2) { printf("%d je manji od %d\n", num1, num2); } /* kraj if naredbe */ if(num1 > num2) { printf("%d je veci od %d\n", num1, num2 ); } /* kraj if naredbe */ if(num1 <= num2) { printf("%d je manji ili jednak sa %d\n", num1, num2); } /* kraj if naredbe */ if(num1 >= num2) { printf("%d je veci ili jednak sa %d\n", num1, num2); }/* kraj if naredbe */ } /* kraj funkcije main */

Напомена: Приметите поравнање наредби унутар if наредбе. Након if наредбе се не ставља тачка-зарез (;).

б) Први сложенији облик if наредбе је if-else наредбa:

if(izraz1) { naredbe } else { naredbe }

Page 36: Uvod u programski jezik C Univerzitet Singidunum

10

Овај облик if наредбе (if else наредбa) омогућава програмеру да дефинише низ наредби које ће се извршит у случају да испитивани услов није испуњен.

Пример: if(brој_а == 0) printf(" broj a je jednak nuli\n"); else printf(" broj a je razlicit od nule\n");

Напомена: Приметите поравнање наредби унутар if else наредбе.

У наведеном примеру ће се извршити или прва или друга наредба у зависности од вредности променљиве а. Уколико је задовољен први услов, извршиће се прва наредба у супротном се извршава друга наредба.

Овде су изостављене витичасте заграде јер је било потребно извршити по једну наредбу. У случају да има више наредби које треба да се изврше по испуњењу услова, оне се морају ставити унутар витичастих заграда.

if(brој_а == 0) { brој_b=7; printf(" broj a je jednak nuli\n"); } else printf(" broj a je razlicit od nule\n");

Meђутим, постоје случајеви када је потребно ставити витичасте заграде и у случају да није потребно извршити више од једне наредбе. Обзиром да наредбе могу бити угнеждене, може доћи до недоумице:

if(x < 0) if(y < 0) broj_a=5; else broj_a=8;

У овом промеру, за случај када је x > 0 или x = 0 неће бити извршена наредба broj_a=8; Зашто? Без обзира што је наредба else написана тако да изгледа да припада првој if наредби, она припада другој if наредби.

Наредба else увек припада најближој наредби if која се налази испред (изнад) ње, а која нема наредбу else.

Зато би било правилније претходни пример написати као

if(x < 0) if(y < 0) broj_a=5; else broj_a=8;

што је исто као и

if(x < 0) { if(y < 0) broj_a=5; else broj_a=8; }

Page 37: Uvod u programski jezik C Univerzitet Singidunum

11

јер се у случају да је x > 0 или x = 0 неће извршити ни једна од наведених наредби.

Увлачење наредби нема никаквог утицаја на њихово извршење (мада има великог утицаја на разумљивост програма).

Желимо ли да у случају x > 0 или x = 0 буде извршена наредба broj_a=8; то можемо постићи на следеће начине

if(x < 0) if(y < 0) broj_a=5; else ; /* prazna naredba */ else broj_a=8;

или if(x < 0) { if(y < 0) broj_a=5; } else broj_a=8;

в) На крају долазимо до најсложенијег облика, од ког смо и пошли. if(izraz1) { naredbe } else if(izraz2) { naredbe } ... else { naredbe }

Пример:

if(а>90) ocena = 10; else if(a>80) ocena = 9; else if(a>70) ocena =8; else if(a>60) ocena =7; else if(a>50) ocena =6; else ocena = 5;

Почиње се испитивањем првог услова (if(а>90)). Уколико је он тачан извршава се наредба која следи (ocena = 10) и прекида се са испитивањем осталих услова. Уколико није испуњен први услов иде се на

Page 38: Uvod u programski jezik C Univerzitet Singidunum

12

испитивање следећег (if(a>80))... Важно је напоменути да ће се, уколико је задовољи неки од услова, извршити наредба (или више њих) која му следи и да без обзира да ли је испуњен неки наредни услов он неће бити испитиван па самим тим и не постоји начин да се наредбе које њему припадају изврше.

Пример: #include <stdio.h> main() { int a = 85; int ocena; if(a>90) ocena = 10; else if(a>80) ocena = 9; else if( (a%5)==0) /* Da li je a deljivo sa 5?*/ printf("\n a je deljivo sa 5\n"); else ocena = 5; printf("\nVrednost ocene je %d\n", ocena); }

резултат: Vrednost ocene je 9

Без обзира што је а дељиво са 5 (if((a%5)==0), је тачно), овај услов се не испитује јер је претходно испуњен услов да a>80.

Наредба switch Наредба switch представља уопштење наредбе if тј. она нуди могућност избора између више вредности а не само две, као код наредбе if.

Општи облик наредбе је дат са: switch(izraz) { case vrednost_1: niz_naredbi_1 case vrednost_2: niz_naredbi_2 ... case vrednost_N: niz_naredbi_N default: niz_naredbi }

У switch наредби се прво израчуна вредност израза izraz. Израчуната вредност се пореди са vrednost_1, vrednost_2,... vrednost_N, редом све док се не пронађе иста вредност или се установи да таква вредност не постоји ни у једном од понуђених case делова.

Уколико је иста вредност пронађена биће извршене СВЕ наредбе које се налазе између тог case дела и краја switch нардбе. Другим речима, преостали case делови се неће поредити са вредношћу датих израза (izraz), али ће њихове припадајуће наредбе бити извршене.

Page 39: Uvod u programski jezik C Univerzitet Singidunum

13

Пример: #include <stdio.h> main() { int a = 1; int b=2; switch(a+b) { case 1: printf("Zbir je 1\n"); case 2: printf("Zbir je 2\n"); case 3: printf("Zbir je 3\n"); case 4: printf("Zbir je 4\n"); default: printf("Zbir je veci od 4\n"); } }

резултат је: Zbir je 3 Zbir je 4 Zbir je veci od 4

У случају да треба извршити само низ наредби (niz_naredbi) из неког case дела, а не све одтале наредбе до краја switch нардбе, мора се користити наредба break. Сваком case и default делу је препоручљиво додати наредбу break. На тај начин ће, након поређења вредности izraz и проналажења еквивалентне вредности неког case дела (vrednost_i) бити извршене само наредбе из тог case дела и прекинуто даље извршавање switch наредбе.

Пример: #include <stdio.h> main() { int a = 1; int b=2; switch(a+b) { case 1: printf("Zbir je 1\n"); break; case 2: printf("Zbir je 2\n"); break; case 3: printf("Zbir je 3\n"); break; case 4: printf("Zbir je 4\n"); break; default: printf("Zbir je veci od 4\n"); break; } }

резултат је: Zbir je 3

Уколико еквивалентна вредност израза у case деловима није пронађена, програм ће наставити са извршавањем наредбе која се налази у default делу. У случају да default део не постоји (он је опциони тј. не мора се наводити) наредба switch се прескаче и програм наставља са извршењем прве наредбе која следи иза switch наредбе.

Page 40: Uvod u programski jezik C Univerzitet Singidunum

14

default део се може појавити највише једном у switch наредби, а место његовог појављивања је произвољно. Међутим, то може утицати на резултат.

Пример: #include <stdio.h> main() { int a = 4; int b=2; switch(a+b) { case 1: printf("Zbir je 1\n"); case 2: printf("Zbir je 2\n"); case 3: printf("Zbir je 3\n"); default: printf("Zbir je veci od 4\n"); case 4: printf("Zbir je 4\n"); } }

Резултат је: Zbir je veci od 4 Zbir je 4

Овај резултат је последица недостатка наредбе break, односно да након наиласка на еквивалентну вредност извршавају сви остали case делови без даље провере case вредности.

У switch наредби може бити произвољан број case службених речи (АNSI C дозољава максимално 257), али ce не дозвољава појава два case–а са истом вредношћу тј. погрешна је наредба облика:

switch(izraz) { case vrednost_1: niz_naredbi_1 case vrednost_1: niz_naredbi_2 /* Greska! dve pojave vrednost_1*/ case vrednost_2: niz_naredbi_N default: niz_naredbi }

Page 41: Uvod u programski jezik C Univerzitet Singidunum

15

Наредбе понављања (петље или циклуси) Петље или циклуси омогућавају вишеструко понављање једног дела програма, складу са задатим условом.

while петља Општи облик ове наредбе је:

while(izraz) naredba; или

while(izraz) {naredba_1; naredba_2;...; naredba_N;} или

while(izraz) { naredba_1; naredba_2; ... naredba_N; }

У while петљи се прво израчунава вредност израза izraz у загради. Уколико је израз логички тачан, тј. уколико је његова вредност различита од нуле, извршиће се наведене наредбе. Након извршења наредби цео поступак ће се понављати све док је вредност izraz-а различита од нуле.

То значи да вредност izraz у while петљи мора да промени своју логичку вредност да би се уопште могло доћи до напуштања петље.

Пример: #include <stdio.h> main() { float farenhajt, celzijus; int min, maks, korak; min = 0; maks = 120; korak = 20; farenhajt = min; while(farenhajt <= maks) { celzijus= (5.0/9.0) * (farenhajt-32.0); printf("%3.0f,%6.1f\n", farenhajt,celzijus); farenhajt += korak; } }

резултат је: 0, -17.8 20, -6.7 40, 4.4 60, 15.6 80, 26.7 100, 37.8 120, 48.9

У претходном примеру се проверава вредност промељиве farenhajt и све док је она мања од дефинисане вредности maks извршаваће се блок наредби унутар витичастих заграда.

Page 42: Uvod u programski jezik C Univerzitet Singidunum

16

Напомена: Уколико се у while петљи постави такав услов (izraz, у while(izraz)) да је увек логички тачан (различит од нуле) део кôда унутар тела while петље ће стално извршавати тј. добићете бесконачну петљу.

for-петља Општи облик for петље је дат са:

for(izraz_1; izraz_2; izraz_3) { naredbe }

– izraz_1 додељује почетну вредност (иницијализује) променљиве у петљи, (i=0, broj=2;)

– izraz_2 представља услов на основу ког се петља извршава или прекида (i<100;)

– izraz_3 служи за поновно додељивање вредности променљивама у петљи, најчешће мења вредност променљивих на основу чије вредности се одређује услов односно вредност izraz_2. (i++;)

Било који од израза (izraz_1, izraz_2 или izraz_3) може бити изостављен али се оба знака “;” морају написати.

У for петљи се:

1. прво израчуна вредност израза izraz_1, где се обично додељују почетне вредности промељивама које се користе у петљи.

2. После тога се израчуна вредност израза izraz_2. Ако је izraz_2 логички тачан, тј. ако је његова логичка вредност различита од нуле извршавају се наредбе у петљи а потом се израчуна вредност израза izraz_3.

3. Након овога се поново израчуна вредност израза izraz_2. Ако је izraz_2 логички тачан, извршавају се наредбе у петљи а потом се израчуна вредност израза izraz_3. У супротном се напушта петља.

Пример: #include <stdio.h> main() { int num; for(num = 1; num <= 10 ; num ++) { printf(" %d ", num); } printf("\n"); }

резултат је: 1 2 3 4 5 6 7 8 9 10

Page 43: Uvod u programski jezik C Univerzitet Singidunum

17

Пример: #include <stdio.h> void main() { int num; for(num = 10; num >= 0 ; num--) { printf(" %d ", num); } printf("\n"); }

резултат је: 10 9 8 7 6 5 4 3 2 1 0

Пример: #include <stdio.h> void main() { int num; for (num = 2; num <= 10 ; num += 2) { printf(" %d ", num); } printf("\n"); }

резултат је: 2 4 6 8 10

Page 44: Uvod u programski jezik C Univerzitet Singidunum

18

do while петља За разлику од for и while петљи, код do while се задовољење услова испитује тек на њеном крају.

То значи да ће свака do while петља бити извршена бар једанпут, што није случај за претходне две петље.

Општи облик је следећи do { naredbe } while( izraz );

do while петља се извршава све док је израз у загради тачан, тј. док је његова вредност различита од нуле.

Пример: #include <stdio.h> main() { float num, total; total=0;

do { printf("Please enter a number "); scanf("%f",&num); total += num; printf("The total is %f\n\n", total); }while(num!=0); }

резултат је:

Please enter a number 1 The total is 1.000000 Please enter a number 2 The total is 3.000000 Please enter a number 3 The total is 6.000000 Please enter a number 4 The total is 10.000000 Please enter a number 5 The total is 15.000000 Please enter a number 0 The total is 15.000000

Page 45: Uvod u programski jezik C Univerzitet Singidunum

19

Скокови

Као што је већ речено, у свакој петљи постоји услов, чија вредност одређује да ли ће се петља поново извршити. Међутим, понекад је пожељно да се прекине извршавање неке петље унутар неке наредбе петље, а не само на њеном почетку или крају. То се може постићи наредбама контролисаног скока:

– break – continue

Наредба break break наредба се користи за превремено прекидање итеративних петљи (while, for и do-while), као и наредбе switch. Извршењем наредбе break долази до прекида петље или switch наредбе а програм наставља да се извршава на првој наредби која следи после њих.

Уколико постоји више петљи, које су уметнуте једна у другу (угнежђене), наредба break прекида извршење само једне од њих и то оне у којој се налази.

Примери: ... for(i=0; ; ++i) /* nema uslova za prekid petlje! */ { ... x=i-3+12*i; if(x>=500) break; ... } y=33; ... У претходном примеру је дефинсана for петља која се извршава бесконачан (неограничен) број пута. То је последица непостојања другог израза у почетном делу петље ( for(i=0; ; ++i) ). Међутим, ова for петља ће се ипак извшти ограничен број пута. Извршаваће се све док променљива x буде имала вредност мању од 501, потом ће се прекинути извршавање for пртље и извшће се нареба y=33;

Пример: /* Prekid beskonacne petlje */ #include <stdio.h> main() { char ch; printf("Unesite karkter:\n(unesite x za izlaz)\n"); while (1) { scanf("%c",&ch); if(ch == 'x') break; } printf("Prekid beskonacne petlje. Cao!\n"); }

Page 46: Uvod u programski jezik C Univerzitet Singidunum

20

Наредба continue

Наредба continue, за разлику од наредбе break не прекида извршење целокупне наредбе итерације (while, for и do-while) већ прекида само једну њену (тренутну) итерацију.

Пример: #include <stdio.h> main() { int x; printf("Ispisujem samo parne brojeve od 1 do 10\n"); for( x = 1; x <= 10; x++ ) { if( x % 2 != 0 ) /* Ako broj nije paran ... */ continue; /* Idi na poсetak for petlje */ printf( "\n%d", x ); } }

резултат је: Ispisujem samo parne brojeve od 1 do 10 2 4 6 8 10

Page 47: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C (C programming Language)

Лекција 4

Садржај Низови ........................................................................................................................................................... 2 Дефинисање низова ................................................................................................................................. 2 Иницијализација низова .......................................................................................................................... 3 Приступање елементима низа................................................................................................................. 4

Page 48: Uvod u programski jezik C Univerzitet Singidunum

2

Низови Раније су разматрне особине основних типова података као што су променљиве и константе. У овом поглављу ће се разматрати нови, сложени, тип податка низ, који се још назива табела или поље (array).

Низови представљају сложену структуру података или скуп меморијских локација са заједничком особином да све те меморијске локације имају исто име и исти тип података.

Типичан пример низа представљају резултати неког мерења. Ови подаци могу да садрже неколико десетина или стотина вредности истог типа. Њиховим груписањем у један низ, могуће је у многоме поједноставити рад над њима (дефинисање иницијализацију...).

Низови могу бити једнодимензионални или вишедимензионални. Једнодимензионални се често називају вектори.

Дефинисање низова Дефинисање низа се обавља тако што се прво наведе име низа а потом унутар угластих заграда дужина низа. Угласте заграде су обавезне!

tip ime_niza [duzina] [duzina]... [duzina]

– tip било који тип који је до сада коришћен ( int float double....) – ime_niza у складу са правилима за идентификаторе (мора почињати словом...) – dužina представља број елемената низа и мора да буде константан израз, цео број

Примери:

float Plate[10];/* jednodimenzionalni niz*/ int А[10][5]; /* dvodimenzionalni niz, 10x5 elemenata*/*/

223.44

435.11

218.44

481.65

главна меморија

Plate[0]

322.76

712.34

490.09

318.66

348.33

395.27

Plate[4]

Plate[2]

Plate[3]

Plate[1]

Plate[5]

Plate[9]

Plate[7]

Plate[8]

Plate[6]

слика 4.1 Низ од 10 елемената

У првом примеру је дефинисан низ са именом Plate од 10 елемената.

Дужину низа (број унутар угластих заграда), или укупан број елемената низа, наводи се само при дефинисању низа

Сваки елемент унутар низа се идентификује помоћу имена низа и редног броја. Редни број се пише унутар угластих заграда [ ], и назива се индекс. Први елемент сваког низа се зове нулти елемент. Први елемент

Page 49: Uvod u programski jezik C Univerzitet Singidunum

3

у низу има индекс 0, други 1, а последњи индекс је за један мањи од дужине низа. У првом примеру је последњи индекс 9 (а не 10).

У другом примеру је дефинисан дводимензионални низ. Овде постоји пар индекса који креће од 0 0. Први индекс означава редни број врсте а други редни број колоне. На следећој слици су приказане вредности индекса низа А.

А00 А01 А02 А03 А04 А10 А11 А12 А13 А14 А20 А21 А22 А23 А24 ... А90 А91 А92 А93 А94

Иницијализација низова Као и код скаларних података и низовима могу да се наводе почетне вредности при дефинисању.

Синтакса: Tip Ime[Duzina] = { vrednоst0, vrednost1, …, vrednostDuzina-1 };

Почетне вредности су међусобно раздвојене зарезима, а све заједно су груписане витичастим заградама. • vrednost0 иницијализује Ime[0], vrednost1 иницијализује Ime[1], итд.

Почетне (иницијалне) вредности морају по типу да одговарају низу коме се додељују иначе ће доћи до аутоматске конверзије.

Примери: int tablica[4]={3, 5, 7, 9};

long a1[6]={3, 5, 7, 9, 0, 0};

int NumDays1[12] = { 31, 28, 31, 30,31, 30, 31, 31, 30, 31, 30, 31 }; /* Jan je 0, Feb je 1, ... */

int NumDays2[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,31 }; /* Jan je 1, Feb je 2, */

У првом примеру је иницијализован низ tablica од 4 елемента, и сваком елементу је додељена почетна вредност. Првом елементу (индекс 0) је додељена вредност 3, другом елементу (индекс 1) вредност 5 итд.

У тећем примеру је дефинсано поље од дванаест елемената и сваком елементу је додељена вредност која представља број дана у једном месецу. Тако, елемент са индексом 0 има вредност 31 (што је број дана у Јануару), елемент са индексом 1 има вредност 28 (што је број дана у фебруару)... Невоља је што индекс почиње од нуле, па се редни број месеца не поклапа са индексом. То је могуће решити, као у примеру 4, на тај начин што се дефинише низ од 13 елемената, први елемент низа (индекс нула) се не користи, другом елменту низа (индекс 1) се додели број дана у јануару, трећем елементу (индекс 2) се додели број дана у фебруару... Ако се наведе почетна вредност, Duzina може да се изостави приликом дефинисања низа. Број вредности одређује дужину низа

int tablica[ ]={3, 5, 7, 9};

је потпуно исто што и

int tablica[4]={3, 5, 7, 9}; Ако у дефиницији постоји дужина низа (duzina) , број наведених вредности за иницијализацију не сме да буде већи од броја елемената низа!

Page 50: Uvod u programski jezik C Univerzitet Singidunum

4

int tablica [3]={3, 5, 7, 9}; /*Greska! еrrоr too many initializers....*/

У претходном примеру је дефинисан низ oд 3 елемента (tablica [3]) а додељено му је 4 елемента. Ово је грешка, и пријавиће је преводилац. Ако је број вредности које се додељују мањи од димензија низа, оне се додељују елементима на почетку низа а остале ће имати вредност нула.

int tablica [5]={3, 5, 7, 0, 0}; је потпуно исто што и

int tablica [5]={3, 5, 7}; Код више-димензионалних низова појединачне вредности треба да су и саме низови одговарајућих димензија

int mat2[3][2]={ {1,2}, {3,4}, {5,6}

}; што је потпуно исто као и

int mat2[3][2]={{1,2},{3,4},{5,6}};

int NumDays2[2][13] = { {0,31,28,31,30,31,30,31,31,30,31, 30,31}, {0,31,29,31,30,31,30,31,31,30,31, 30,31}

};

short kvadratA[4][4]={ {0,1,2,0},{3,4,0,0},{5,6,7,8},{9,0,0,0} }; short kvadratB[4][4]={ {0,1,2},{3,4},{5,6,7,8},{9} };

Претходна два примера су потпуно иста јер оне вредности које нису иницујализоване имају вредност нула.

Међутим, то не мора увек бити тако. На пример, ако се унутрашње витичасте заграде не наведу (оне су опционе) иницијализација се врши редом, па ће ефекат наредне две наредбе бити идентичан

int kvA[4][4]={0,1,2,3,4,5,6,7,8,9}; int kvB[4][4]={ {0,1,2,3},{4,5,6,7},{8,9,0,0},{0,0,0,0} };

Приступање елементима низа Елементи низа се идентификују помоћу редног броја који се назива индекс. Поновимо да индекс може имати вредност од НУЛА до n-1, где је n дужина низа.

Индексирање се сматра бинарним оператором и обележава се угластим заградама [ ].

Пример:

Нека је дефинисана променљива i и низ tablica int tablica [3]={3, 5, 7, 9}; int i; ... i = tablica [1]; /* i dobija vrednost 5*/ tablica [0]=i+1; /* tablica [0]= 6/ ...

Page 51: Uvod u programski jezik C Univerzitet Singidunum

5

Честе грешке:

Нека је дефинисан низ float Sales[10];

Неправилно додељивање вредности: Sales = 17.50; /* nedostaje indeks */ Sales[-1] = 17.50; /* indeks izvan opsega */ Sales[10] = 17.50; /* indeks izvan opsega */ Sales[7.0] = 17.50; /* tip indeksa nije odgovarajuci */ Sales[′a′] = 17.50; /* tip indeksa nije odgovarajuci */ Sales[7] = ′A′; /* tip indeksa nije odgovarajuci */

Догодиће се аутоматска конверзија: Sales[7] = 17; /* 17 ce se konvertovati u float tip*/

Пример:

Додела вредности употребом for петље

#include<stdio.h> main() { int mat[10]; /* mat je niz od 10 elemenata*/ int i; /* brojac*/ /* inicijalizacija elemenata niza mat na 0*/ for(i = 0;i < 10;i++) { mat[i]=5; /* postavi element na lokaciji i na 0 */ }/* kraj for petlje*/ printf("\nElement\t Vrednost\n"); /* ispisi vrednost elemenata polja u obliku tabele*/ for(i = 0;i < 10;i++) { printf("\n%7d%10d\n",i,mat[i]); }/* kraj for petlje*/

}/* kraj funkcije main*/

Излаз:

Element Vrednost 0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 8 5 9 5

Press any key to continue

Page 52: Uvod u programski jezik C Univerzitet Singidunum

6

Nа овај начин се може на било ком месту у програму доделити вредност свим елементима у неком низу. У наведеном примеру ће сви елементи низа имати вредност 5. У наредном примеру ће елементи низа имати вредност 0, 2, 4,6, 8, ...18

#include<stdio.h> main() { int mat[10]; /* mat je niz od 10 elemenata*/ int i; /* brojac*/ /* inicijalizacija elemenata niza mat na 0*/ for(i = 0;i < 10;i++) { mat[i]=2*i; /* postavi element na lokaciji i na 2 puta i */ }/* kraj for petlje*/ printf("\nElement\t Vrednost\n"); /* ispisi vrednost elemenata polja u obliku tabele*/ for(i = 0;i < 10;i++) { printf("\n%7d%10d\n",i,mat[i]); }/* kraj for petlje*/

}/* kraj funkcije main*/

Излаз: Element Vrednost 0 0 1 2 2 4 3 6 4 8 5 10 6 12 7 14 8 16 9 18 Press any key to continue

Додела вредности дводимензионалном низу применом for петље

main() { int mat[4][3]; int i,j; for( i = 0; i < 4; i++ ) { for( j = 0; j < 3; j++ ) { mat[i][j]=5 ; } } }

Page 53: Uvod u programski jezik C Univerzitet Singidunum

7

Пример иницијализације низа при дефинисању типа, рачунања и исписа збира вредности свих елемената једног низа

#include<stdio.h> main() { #define MAXELEM 10 /* Nema ; iza pretprocesorske naredbe!*/ float plate[MAXELEM] = {117.0f, 129.95f, 276.22f, 201.10f,106.31f, 358.11f, 31.85f, 256.45f, 269.09f,197.81f}; float ukupno; int i; printf("\nElement\t Vrednost\n"); /* ispisi vrednost elemenata polja u obliku tabele*/ for(i = 0;i < MAXELEM;i++) { printf("\n%7d%10.2f\n",i,plate[i]); }/* kraj for petlje*/ /* Izracunaj zbir vrednosti elemenata niza */ ukupno = 0.0; for (i = 0; i < MAXELEM; i++) /* jedna naredba, moze i bez {} */ ukupno += plate[i]; /* moze i ukupno = ukupno + plate[i] */ printf("\n\nZbir svih elemenata niza: %.2f\n\n", ukupno); }/* kraj funkcije main */ Излаз је: Element Vrednost 0 117.00 1 129.95 2 276.22 3 201.10 4 106.31 5 358.11 6 31.85 7 256.45 8 269.09 9 197.81 Zbir svih elemenata niza: 1943.89 У овом примеру је приликом иницијализације иза сваког броја дописано f (float plate[MAXELEM] = {117.0f, 129.95f,...}). Разлог је у томе што је подразумевано да је реална константа типа double па би компајлер пријавио упозорење уколико се то не уради.

Пример: Употреба низа за анализу резултата анкете. Размотримо следећи проблем:

Четрдесет студената је одговарало на питање о квалитету хране у студентској мензи и давало оцене од 1 до 10 (1 недовољно, 10 изврсно). Направите фреквенцију појављивања одређених одговора (oцена).

Ово је типичан пример за употребу низова. Желимо да добијемо број одговора по свакој оцени (нпр. оцену 1 је дало 5 студената, оцену 2 је дало 3 студента, итд.). Низ odgovor ће имати 40 елемената, оцене које су

Page 54: Uvod u programski jezik C Univerzitet Singidunum

8

дали студенти. Користићемо низ frekvOcena од 11 елемената, да избројимо колико пута се нека оцена појавила у одговорима . Занемарићемо први члан низа тј. frekvOcena[0] јер желимо да: frekvOcena[1] садржи укупан број одговора са оценом 1, frekvOcena[2] садржи укупан број одговора са оценом 2, ... frekvOcena[10] садржи укупан број одговора са оценом 10.

#include <stdio.h> #define BROJ_STUDENATA 40 /* definisi velicinu nizova */ #define FREKV_VELICINA 11 /* function main pocetak izvrsavanja programa */ int main() { int i; /* brojac koji kruzi od 1. do 40. odgovora */ /* inicijaliacija niza sa brojem dobijenih ocena na 0 */ int frekvOcena[ FREKV_VELICINA ] = { 0 }; /* inicijalizacija niza odgovor studenata */ int odgovor[ BROJ_STUDENATA ] = { 1, 2, 6, 4, 8, 5, 9, 7, 8, 10, 1, 6, 3, 8, 6, 10, 3, 8, 2, 7, 6, 5, 7, 6, 8, 6, 7, 5, 6, 6, 5, 6, 7, 5, 6, 4, 8, 6, 8, 10 }; /* za svaki odgovor, odredite vrednost elementa niza odgovor i uzmite tu vrednost and kao indeks niza frekvOcena i da bi se pozicionirali na taj element niza frekvOcena i povecajte ga za 1 */ for ( i = 0; i < BROJ_STUDENATA; i++ ) { ++frekvOcena[ odgovor [ i ] ]; } /* kraj for petlje */ /* prikazi rezultate */ printf( "Ocena Broj glasova\n" ); /* Prikaz u tabelarnom formatu */ for ( i = 1; i < FREKV_VELICINA; i++ ) { printf( "%5d%14d\n", i, frekvOcena[i ] ); } /* end for */ } Излаз: Ocena Broj glasova 1 2 2 2 3 2 4 2 5 5 6 11 7 5 8 7 9 1 10 3 for петља (for ( i = 0; i < BROJ_STUDENATA; i++ ){++frekvOcena[odgovor[i]];}) узима редом одговор сваког студента из низа odgovor и то тако што за вредност i = 0 узима odgovor[0], за вредност i = 1 узима odgovor[1] ...У нашем примеру: вреност низа odgovor[0] је 1, вреност низа odgovor[1] је 2, вреност низа odgovor[3] је 6...

Page 55: Uvod u programski jezik C Univerzitet Singidunum

9

Вредност низа odgovor се потом узима као индекс низа frekvOcena и тај елемент низа frekvOcena се увећава за 1 (++frekvOcena[odgovor[i]];)

Корак по корак:

Улазимо у for петљу. Поставимо вредност бројача i = 0. Вредност odgovor[0] је 1 (видети иницијализацију у задатку). Према томе

frekvOcena[odgovor[i]]= frekvOcena[odgovor[0]]= frekvOcena[1]

Обзиром да је тренутна вредност другог елемента (индекс 1) низа frekvOcena једнака 0 (frekvOcena[1]=0) након наредбе ++frekvOcena[1] вредност овог елемента низа ће се повећати за један тј. frekvOcena[1]=1.

Враћамо се на почетак for петље, инкрементирамо вредност бројача за 1, (i = 1). Вредност odgovor[1] је 2 (видети иницијализацију у задатку). Према томе

frekvOcena[odgovor[i]]= frekvOcena[odgovor[1]]= frekvOcena[2]

Обзиром да је тренутна вредност трећег елемента (индекс 2) низа frekvOcena једнака 0 (frekvOcena[2]=0) након наредбе ++frekvOcena[2] вредност овог елемента низа ће се повећати за један тј. frekvOcena[2]=1.

Враћамо се на почетак for петље, инкрементирамо вредност бројача за 1, (i = 2). Вредност odgovor[2] је 6 (видети иницијализацију у задатку). Према томе

frekvOcena[odgovor[i]]= frekvOcena[odgovor[2]]= frekvOcena[6]

Обзиром да је тренутна вредност седмог елемента (индекс 6) низа frekvOcena једнака 0 (frekvOcena[6]=0) након наредбе ++frekvOcena[6] вредност овог елемента низа ће се повећати за један тј. frekvOcena[6]=1.

...

Пример:

Прављење хистограма:

#include <stdio.h> #define VELICINA 10 /* definisi velicinu niza */ /* function main pocetak izvrsavanja programa */ int main() { /* inicializacija niza n */ int n[ VELICINA ] = { 19, 3, 15, 7, 11, 9, 13, 5, 17, 1 }; int i; /* brojac za elemente polja */ int j; /* brojac zvezdica */ printf( "Element Vrednost Histogram\n" ); /* za svaki element polja ispisi index, vrednost i nacrtaj histogram */ for ( i = 0; i < VELICINA; i++ ) { /* ispis indeksa i vrednosti*/ printf( "%7d%10d ", i, n[ i ]) ; for ( j = 1; j <= n[ i ]; j++ ) { /* ispisi jednu zvezdicu */ printf( "%c", '*' ); } /* kraj unutrasnje petlje */ printf( "\n" ); /* kraj histigrama za jedan element niza */

Page 56: Uvod u programski jezik C Univerzitet Singidunum

10

} /* kraj spoljasnje petlje */ } /* kraj funkcijemain */

Излаз: Element Vrednost Histogram 0 19 ******************* 1 3 *** 2 15 *************** 3 7 ******* 4 11 *********** 5 9 ********* 6 13 ************* 7 5 ***** 8 17 ***************** 9 1 *

Пример Издвајање елемента који има највећу вредност у низу

#include<stdio.h> main() { #define MAXEMPS 10 double Sales[MAXEMPS] = {117.0, 129.95, 276.22, 201.10,106.31, 358.11, 31.85, 256.45, 269.09, 197.81}; double maxS; int i; maxS = Sales[0]; for (i = 1; i < MAXEMPS; i++) { if (Sales[i] > maxS) maxS = Sales[i]; /* Primetite da i pocinje od 1 */ } printf("maksimalna vrednost niza je %f\n", maxS); } Излаз: maksimalna vrednost niza je 358.110000

Page 57: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C (C programming Language)

Лекција 5

Садржај ППооккааззииввааччии..........................................................................................................................2 Дефинисање показивача .................................................................................................3 оператор &............................................................................................................................4 оператор *.............................................................................................................................5 Додела почетне вредности показивачу............................................................................11 Генерички показивачи .......................................................................................................11

Приказивање вредности показивача .........................................................................12 Рачунске операције над показивачима (Адресна аритметика) .........................12 Додела вредности једног показивача другом: .................................................................13 Додавање и одузимање целобројне вредности показивачу:..........................................13 Упоређивање вредности два показивача: .......................................................................14 Упоређивање вредности показивача са нулом: ..............................................................14

Показивачи и низови .......................................................................................................14 Динамичка додела меморије ........................................................................................16

Page 58: Uvod u programski jezik C Univerzitet Singidunum

2

Показивачи Показивач (pointer) је градивни део програмског језика C који нуди већу контролу над меморијом рачунара.

Показивач је променљива која садржи меморијску адресу друге променљиве.

Додатак:

Рачунар најчешће има два облика меморије: тзв. главну меморију (main memory) и додатну меморију (secundary memory). Додатна меморија је меморија која се користи за трајно чување података без обзира на то да ли је рачунар укључен или не (hard disk, CD-rom...). Главна меморија рачунара се често назива RAM (random access memory). Главна меморија се састоји од великог броја нумерисаних локација које се називају меморијске локације. Под појмом нумерисана локација се подразумева да је меморија подељена је на делове, а сваком од тих делова је додељен број који га једнозначно одређује. Број меморијских локација првенствено зависи од величине главне меморије. Садржај меморијских локација се може мењати и у њих се могу уписивати вредности неких података или се те вредности могу читати. Код већине рачунара су све основне меморијске локације међусобно једнаке величине и најчешће је једна основна меморијска локација величине 1 бајт (8 бита). Према томе, нумерисане меморијске локације можемо звати бајтовима. Број који једнозначно одређује положај тачно одређеног бајта унутар меморијске локације се назива адреса. Податак, као што је број или слово, може бити записан на неку од меморијских локација а адреса те меморијске локације се користи да би се приступило том податку (читање или мењање вредности) уколико је то потребно.

Уколико је потребно да се у меморију сместити податак који је превелик да би се описао једним бајтом (нпр. број 921) користи се неколико узастопних бајтова за смештање податка. У том случају тај део меморије (више бајтова) се назива меморијска локација. Адреса првог бајта у тој меморијској локацији се користи као адреса меморијске локације. Према томе, главна меморија рачунара се може посматрати као скуп меморијских локација различитих величина. Величина тих меморијских локација се изражава у бајтовима а адреса првог бајта се користи као адреса (име) меморијске локације.

У програмским језицима, променљиве (variable) представљају меморијске локације. Преводилац додељује меморијску локацију свакој промељивој која је дефинисана у програму. Вредност променљиве се чува („памти") на меморијској локацији која је додељена тој промељивој. Тако у наредном примеру, променљиве могу имати адресе 1220, 1222 и 1230. Тачна адреса зависи од рачунара, преводиоца и бројних других фактора. Ми незнамо, па чак и не водимо рачуна, које адресе ће преводилац (compiler) доделити променљивама. Уместо тога, жељеним меморијским локацијама приступамо преко имена променљивих, које представљају замену за меморијске адресе!

int main() { short int brojA; double tezina, zapremina; .... }

Раније је речено да тип податка представља скуп вредности који тај податак може да има (нпр. short int може да има вредности од -32768 до 32767) као и скуп операција које се могу примењивати над тим податком (сабирање, одузимање...). Сви досада поменути типови података су имали своје име (int, float, double...) .

Вредности које може имати податак типа показивач су адресе меморијских локација.

Тип податка показивач нема своје име.

Page 59: Uvod u programski jezik C Univerzitet Singidunum

3

Број бајтова које заузима овај тип податка је 2 или 4 бајта, што зависи од величине меморије (опсега адреса) а не од величине податка на које показивач указује. Наиме, први PC рачунари су укупно имали 64К меморије, и ако сматрамо да је минимална меморијска локација 1 бајт, онда се свака меморијска локација може описати са 16 бита (2 бајта) јер је 216= 65536, па у овом случају показивач заузима 2 бајта. Уколико се ради са већом меморијом неопходно је за показивач резервисати више меморије. Олакшавајућа околност је та да програмер о томе не мора посебно да води рачуна.

Дефинисање показивача Поновимо да показивач нема име за свој тип. Поред тога, променљива типа показивач може имати вредност неке меморијске адресе. Ако променљива типа показивач нема име типа, како је онда можемо дефинисати?

Вредност промељиве типа показивач је адреса. Када се дефинише променљива типа показивач, одређује се тип податка који ће се чувати на локацији на коју показивач указује.

Општа синтакса је: tip_podatka *ime_pokazivaсa; tip_podatka представља тип податка чију адресу показивач (треба да) садржи, или како се каже, тип податка на који показивач указује.

* обавезан знак, који се мора налазити између типа податка и имена показивача.

ime_pokazivača одређује се у складу са правилима за именовање идентификатора.

Размотримо наредни пример:

int *p;

float *ptr;

У овим наредбама p и ptr представљају променљиве типа показивач. Вредност (садржај) показивача p представља (када му буде додељена) адресу неке променљиве типа int, а вредност показивача ptr адресу неке променљиве типа float. Уобичајено је да се каже да је p показивач на тип int а ptr се назива показивач на тип float.

Пре него што наставимо даља разматрања, напоменимо следеће:

int *p;

је исто што и

int* p;

што је исто што и

int * p;

Због тога се знак * може поставити било где између типа податка и имена показивача.

Сада размотримо следећу наредбу:

int* p, q;

У овој наредби је само p променљива типа показивач, а q није. Овде је q промељива типа int. Да би се избегле овакве и сличне грешке препорука је да се знак * пише непосредно поред имена променљиве. Сходно томе, ако хоћете да постигнете исти ефекат као са претходном наредбом пишите

int *p, q;

а ако желите да обе променљиве буду показивачи пишите

int *p, *q;

Page 60: Uvod u programski jezik C Univerzitet Singidunum

4

Како је вредност показивача меморијска адреса, вредност (садржај) показивача може да буде само адреса меморијске локације назначеног типа. На пример, ако је p показивач типа int, p може да има вредност адресе само променљиве типа int.

C програмски језик има два оператора за променљиве типа показивач:

– оператор & – оператор *

оператор & Оператор & се користи за одређивање адресе променљиве на коју показивач указује и да се адреса те промељиве додели промељивој типа показивач.

Пример: main() { int x=25; int *p; ... p=&x; *p=48; ... }

Наредба p=&x; додељује адресу промељиве x променљивој p типа показивач.

Након наредби: int x=25; и int *p; имамо ситуацију приказану на слици 1. Дефинисане су две променљиве и преводилац им је доделио две меморијске локације: 1200 и 1800 (вредности адреса у овом примеру су одабране насумице). Како је променљивој x додељена вредност 25, та вредност је уписана на њену меморијску локацију. Променљивој типа показивач није додељена никаква почетна вредност тако да она може бити било која.

??

25

.

.

.

.

.

.

.

.

.

главна меморија

adresa:1200

adresa:1800

ime: p

ime: x

слика 1

Page 61: Uvod u programski jezik C Univerzitet Singidunum

5

Након наредбе p=&x; додељена је вредност променљивој типа показивач, а та вредност је адреса промељиве x. Та ситуација је приказана на слици 2.

1800

25

.

.

.

.

.

.

.

.

.

главна меморија

adresa:1200

adresa:1800

ime: p

ime: x

слика 2

оператор * Податак у меморији, на основу адресе податка, може да се „дохвати” применом оператора *. Дохватање података посредно, помоћу адресе, назива се и индиректно адресирање, па се и сам оператор * назива оператор индиректног адресирања.

Наредба *p=48; се може тумачити на следећи начин: На меморијску локацију на коју указује показивач p, упиши вредност 48. (Претходно је наредбом p=&x; постигнуто да показивач p указује на промељиву x). Резултат је приказан на слици 3.

1800

48

.

.

.

.

.

.

.

.

.

главна меморија

adresa:1200

adresa:1800

ime: p

ime: x

слика 3

Page 62: Uvod u programski jezik C Univerzitet Singidunum

6

Треба приметити следеће:

1. &p, p и *p имају три различита значења.

2. &p означава адресу променљиве (типа показивач), на слици 3 та адреса је 1200.

3. p означава садржај променљиве (типа показивач), на слици 3 та вредност је 1800

4. *p означава вредност (48, на слици 3) меморијске локације (1800, на слици 3) на коју указује променљива типа показивач (показивач се налази на локацији 1200, слика 3).

Пример:

Размотримо следеће наредбе int x; int *p;

Претпоставимо да су променљивама x и p додељене меморијске локације као на слици 4

.

.

.

.

.

.

.

.

.

главна меморија

adresa:1400

adresa:1750

ime: p

ime: x

слика 4.

Вредности &p, p, *p &x и x су следеће

Вредност

&p 1400 (адреса променљиве p)

p ?? (вредност променљиве p)

*p не постоји

&x 1750 (адреса променљиве x)

x ?? (вредност променљиве x)

Претпоставимо даље, да је извршена наредба

x=50;

Након тога имамо следећу ситуацију:

Page 63: Uvod u programski jezik C Univerzitet Singidunum

7

Вредност

&p 1400 (адреса променљиве p)

p ?? (вредност променљиве p)

*p не постоји

&x 1750 (адреса променљиве x)

x 50

Након наредбе

p=&x;

Вредност

&p 1400 (адреса променљиве p)

p 1750

*p 50

&x 1750 (адреса променљиве x)

x 50

Након наредбе

*p=38;

Вредност

&p 1400 (адреса променљиве p)

p 1750

*p 38

&x 1750 (адреса променљиве x)

x 38

Пример: main() { int *ptr1, *ptr2; int x, y; ptr1=&x; *ptr1=42; ptr2=ptr1; *ptr2=53; ptr1=&y; *ptr1=88;

} Након наредби int *ptr1, *ptr2; int x, y; имамо следећу ситуацију (адресе су додељене насумице):

Page 64: Uvod u programski jezik C Univerzitet Singidunum

8

Вредност

&ptr1 2350

ptr1 ??

*ptr1 ??

&ptr2 2600

ptr2 ??

*ptr2 ??

&x 1320

x ??

&y 1950

y ??

након извршења наредбе ptr1=&x;

Вредност

&ptr1 2350

ptr1 1320

*ptr1 ??

&ptr2 2600

ptr2 ??

*ptr2 ??

&x 1320

x ??

&y 1950

y ??

Page 65: Uvod u programski jezik C Univerzitet Singidunum

9

након извршења наредбе *ptr1=42;

Вредност

&ptr1 2350

ptr1 1320

*ptr1 42

&ptr2 2600

ptr2 ??

*ptr2 ??

&x 1320

x 42

&y 1950

y ?? након извршења наредбе ptr2=ptr1;

Вредност

&ptr1 2350

ptr1 1320

*ptr1 42

&ptr2 2600

ptr2 1320

*ptr2 42

&x 1320

x 42

&y 1950

y ?? Приметите да показивач ptr2 има сада исту вредност као и показивач ptr1, односно указује на променљиву x. Показивач ptr2 и даље има исту адресу (2600 у овом примеру) али његов садржај је адреса променљиве x (1320 у овом примеру).

Page 66: Uvod u programski jezik C Univerzitet Singidunum

10

Након извршења наредбе *ptr2=53;

Вредност

&ptr1 2350

ptr1 1320

*ptr1 53

&ptr2 2600

ptr2 1320

*ptr2 53

&x 1320

x 53

&y 1950

y ?? Након извршења наредбе ptr1=&y;

Вредност

&ptr1 2350

ptr1 1950

*ptr1 ??

&ptr2 2600

ptr2 1320

*ptr2 53

&x 1320

x 53

&y 1950

y ??

Page 67: Uvod u programski jezik C Univerzitet Singidunum

11

И коначно, након наредбе *ptr1=88;

Вредност

&ptr1 2350

ptr1 1950

*ptr1 88

&ptr2 2600

ptr2 1320

*ptr2 53

&x 1320

x 53

&y 1950

y 88 Из претходног примера: Ако су ptr1 и ptr2 променљиве типа показивачи онда наредба

ptr2=ptr1;

мења ptr2 на тај начин да он указује на исту промељиву на коју указује и показивач ptr1.

Додела почетне вредности показивачу Показивачу се може доделити почетна вредност и она треба да буде:

− адреса неког постојећег (претходно дефинисаног) податка; или

− 0 (нула).

Додељивање почетне вредности нула значи да показивачу у суштини није додељена почетна вредност али истовремено значи да његова почетна вредност није случајна!

Пример: main( ) { int x; int *pokazivac= &x; int *p2=NULL /* moze se napisati i int *p2=0 */ }

Генерички показивачи Показивач има тип податка који је једнак типу податка на који треба да указује. То значи да ако неки показивач треба да садржи адресу податка float типа, онда и тај показивач мора бити float типа. Међутим, постоје показивачи код којих није одређен тип податка на који указују, па се уместо типа податка (int, float, double...) ставља резервисана реч void Пример:

vоid *point; Ови показивачи се називају генеричким. Помоћу генеричких показивача није могућ директан приступ подацима!

Page 68: Uvod u programski jezik C Univerzitet Singidunum

12

Пример: main() { int x; void *p=&x; /* Greska! Tip pokazivaca nije odgovarajuci*/ void *p2; ... p2=&x; /* Greska! Tip pokazivaca nije odgovarajuci */ ... }

Да би се помоћу генеричких показивача приступило подацима, прво се тип показивача (из типа void) мора конвертовати у одговарајући тип. Пример:

main() { int x; void *p2; ... (int *)p2=&x; /* p2 ukazuje na adresu promenljive x */ *(int *)p2=3; /* na adresu promenljive x upisati 3 isto sto i x=3 */ ... }

Приказивање вредности показивача Вредност показивача је адреса податка у главној (оперативној) меморији. Начин приказивања (исписа) те адресе зависи од много фактора на самом рачунару.

Приказивање вредности показивача је могуће постићи применом функције printf и применом формата %p (као што је формат за int %d; float %f ...).

#include<stdio.h> main() { int x; int *px; px=&x; printf(“Pokazivac ukazuje na adresu: %p\n” ,px); } Резултат: Pokazivac ukazuje na adresu: 0012FF7C (Добијени резултат ће се може разликовати)

Рачунске операције над показивачима (Адресна аритметика) Дозвољене су следеће операције над показивачима:

− додела вредности једног показивача другом − додавање целобројног податка на вредност показивача и одузимање целобројног податка од

вредности показивача − упоређивање вредности два показивача − упоређивање вредности показивача са нулом

Page 69: Uvod u programski jezik C Univerzitet Singidunum

13

Додела вредности једног показивача другом: Оператором = може да се додели вредност једног показивача другом показивачу, али показивачи морају бити истог типа.

Ако два показивача указују на податке различитог типа потребно је извршити конверзију (cast), aли то може довести до нежељених ефеката!

Пример: main() { double x=1.23; double *px=&x; /* px ukazuje na adresu promenljive x */ double *p1; int *p2; .... p1=px; /* p1 ukazuje na adresu promenljive x */ p2=px; /* Greska! pokazivaci su razlicitih tipova */ p2=(double *)px; /* Ovako moze, ali na odgovornost programera */ }

Наредба p1=px; копира вредност px у p1. Након ове наредбе оба показивача p1 и px указују на исту меморијску локацију, у наведеном примеру на промељиву x.

Додавање и одузимање целобројне вредности показивачу: Операторима: +, ++ и += може да се израчуна збир вредности показивача и целог броја,

Операторима: −, − − и − = може да се израчуна разлика вредности показивача и целог броја.

Јединица мере при сaбирању и одузимању вредности показивача и целог броја је величина показиваних података.

Пример: main() { int *p; double *q; char *chPtr; ... p++; q++; chPtr++; p=p+2; ... }

Да би анализирали претходни пример усвојимо следеће: променљива типа int заузима 4 бајта меморије, променљива типа double 8 бајтова меморије а променљива типа char 1 бајт меморије.

Наредба p++; (што је исто као и p= p + 1;) повећава вредност показивача p за 4 бајта, тј. за величину податка на који указује.

Наредба q++; (што је исто као и q= q + 1;) повећава вредност показивача q за 8 бајтова.

Наредба chPtr++; (што је исто као и chPtr= chPtr + 1;) повећава вредност показивача chPtr за 1 бајт.

Наредба инкремента (++) повећава вредност показивача за величину меморије коју заузима тип променљиве на коју показивач указује, На сличан начин, оператор декремента (− −) умањује вредност показивача за онолико бајтова колико меморије заузима променљива на коју показивач указује.

Поред тога, наредба p= p + 2; повећава вредност показивача p за 8 бајтова.

Page 70: Uvod u programski jezik C Univerzitet Singidunum

14

На тај начин, када се цела вредност дода вредности показивача, вредност показивача се повећа за: (ту вредност) пута (величина меморије типа на који показивач указује). У наведеном примеру вредност показивача се увећала за 2 пута 4 јер је sizeof(int)=4.

Примена аритметичких операција над показивачима може бити веома „опасна” јер се на тај начин случајно може приступити меморијским локацијама неких (нежељених) променљивих и променити њихова вредност, а да на то не будете упозорени. Због тога је потребна велика пажња при њиховој употреби.

Упоређивање вредности два показивача: Операторима == и != два показивача истог типа могу се упоређивати како би се добио одговор да ли показују на исти податак или не.

Упоређивање вредности показивача са нулом: Овим се испитује да ли показивач указује на неки податак. Уколико показивач има вредност нула (или NULL) онда он не показује ни на један податак. Уколико се користи симболичка константа NULL пожељна је примена стандардне библиотеке <stdio.h>.

Показивачи и низови Нека имамо низ дефинисан на следећи начин:

int my_array[6]={1, 23, 17, 4, -5, 100}; Овај низ има 6 елемената и сваком од њих се може приступити преко индекса тј. употребом my_array[0] до my_array[5]. Међутим, постојање показивача нуди додатну могућност:

int *ptr; ptr=&my_array[0]; /* pokazivac ukazuje na prvi element niza*/

Коришћењем овог правила може се исписати садржај низа :

#include <stdio.h> main() { int my_array[6] = {1, 23, 17, 4, -5, 100}; int *ptr; int i; ptr = &my_array[0]; /* ptr ukazuje na prvi element niza */ printf("\n\n"); for (i = 0; i < 6; i++) { printf("my_array[%d] = %3d\t ",i,my_array[i]); /*<-- A */ printf("*(ptr + %d) = %3d\n",i, *(ptr + i)); /*<-- B */ } }

Пажљиво погледајте линије програма означене као А и B. Такође погледајте како је исписана вредност на коју указује поинтер у линији B.

Након извршења програма имамо следећи излаз:

my_array[0] = 1 *(ptr + 0) = 1 my_array[1] = 23 *(ptr + 1) = 23 my_array[2] = 17 *(ptr + 2) = 17 my_array[3] = 4 *(ptr + 3) = 4 my_array[4] = -5 *(ptr + 4) = -5 my_array[5] = 100 *(ptr + 5) = 100

Page 71: Uvod u programski jezik C Univerzitet Singidunum

15

У програмском језику C је дозвољено да се уместо наредбе ptr = &my_array[0];

користи наредба

ptr = my_array;

Обе наредбе имају потпуно исто значење, уз претпоставку да је ptr показивач. Управо због ове особине у литератури је могуће наћи да је име низа уствари показивач. Можда је јасније рећи да је име низа (без навођења индекса) адреса првог елемента низа. Међутим, иако је правилно да се напише:

ptr = my_array;

погрешно је написати

my_array = ptr;

Објашњење је следеће: ptr је променљива а my_array је константа (адреса првог елемента низа)

Показивач може указивати на било који елемент низа а не само на први. Тако ако желимо да показивач показује на 3. елемент низа (индекс 2) пишемо:

ptr = &my_array[2];

што је исто као и

ptr = &my_array[0] + 2;

односно

ptr = my_array + 2;

јер је my_array исто као и &my_array[0]

Поред тога ако име низа сматрамо променљивом онда се може написати

my_array[i] = *(my_array + i)

Пример: #include <stdio.h> main() { int a[8] = {1,23,17,4,-5,100,55,44}; int *pa, *pb; int x, y; pa=&a[4]; /* pa ukazuje na a[4] tj na vrednost -5 */ printf("Vrednost *pa nakon naredbe pa=&a[4] je: %d\n", *pa); x=*(pa+3); /* x=a[7]=44 */ printf("Vrednost x nakon naredbe x=*(pa+3) je: %d\n", x); y=*pa+3; /* y=a[4]+3=-2*/ printf("Vrednost y nakon naredbe y=*pa+3; je: %d\n", y); *pa++; /* povecava se sadrzaj pokazivaca, isto sto i *(pa++) ili pa=&a[5] */ printf("Vrednost *pa nakon naredbe *pa++ je: %d\n", *pa); (*pa)++; /* povecava se pokazani podatak a[5]=a[5]+1=101 */ printf("Vrednost *pa nakon naredbe (*pa++) je: %d\n", *pa); pb=&a[2]; }

Page 72: Uvod u programski jezik C Univerzitet Singidunum

16

Излаз: Vrednost *pa nakon naredbe pa=&a[4] je: -5 Vrednost x nakon naredbe x=*(pa+3) je: 44 Vrednost y nakon naredbe y=*pa+3; je: -2 Vrednost *pa nakon naredbe *pa++ je: 100 Vrednost *pa nakon naredbe (*pa++) je: 101

Динамичка додела меморије Видели смо како се дефинишу променљиве типа показивач, како се додељује адреса неке променљиве променљивој типа показивач и како се може манипулисати подацима применом показивача. Ипак, у свим наведеним примерима се радило са подацима који су били дефинисани пре превођења. Другим речима, показивачи су приступали само подацима у претходно дефинисаном меморијском простору. Каква је онда добит од употребе показивача? Све претходно показано се (мање, више) могло постићи и без употребе показивача.

Погоденост примене показивача се огледа у следећем: Могуће је дефинисати променљиве у току извршења програма. Променљиве које се дефинишу у току извршења програма се називају динамичке променљиве (dynamic variables) а меморијски простор који им се додељује динамички меморијски простор. Разлог је у томе што динамичке променљиве настају тек након извршавања одговарајућих инструкција у програму а њихов животни век не мора бити до краја програма, већ се опет одговарајућом наредбом додељена меморија може „ослободити” (deallocate).

Наредбама за дефинисање података, које смо до сада користили, врши се статичка додела меморијског простора а подаци који се дефинишу на овакав начин (укључујући и показиваче) се називају статички подаци. То значи да се након процеса превођења зна тачан број података (као и величина меморије који они заузимају) који ће бити коришћени у току извршавања програма.

У случајевима када се користе низови, при њиховом дефинисању је потребно навести тип података и величину низа. Како постоје ситуације када у напред програмер не може да зна колико елемената низ треба да има, најчешће дефинише величину низа према „најгорем” (највећем) случају, па се често непотребно заузима меморијски простор.

Део меморије у који се смештају подаци дефинисани на овај начин назива се статичка зона меморије.

Програмски језик C омогућава да се за потребе смештања података, резервише меморијски простор у току извршења програма. Део меморије у коме се врши динамичка додела меморије се назива динамичка зона меморије.

Подаци у динамичкој зони немају идентификаторе (имена) већ им се приступа преко показивача, а ти показивачи треба да буду дефинисани као статички подаци.

Подаци смештени у динамичку зону података постоје све док их програмер експлицитно не уништи или до завршетка програма.

Управљање меморијом у динамичкој зони се врши помоћу готових функција (библиотечких функцја) које су описне у стандардном заглављу <stdlib.h>.

Функције за динамичко додељивање меморије су

malloc(velicina) Ова функција додељује меморију од velicina бајтова. Вредност коју враћа функија је генерички показивач (тип void *) на додељени простор или NULL вредност уколико из било ког разлога простор не може да буде додељен. Садржај додељеног простора није дефинисан.

calloc(n,velicina) Ова функција додељује меморију за низ од n eлемената од којих сваки има по velicina бајтова. Вредност коју враћа функија је генерички показивач (тип void *) на додељени простор или NULL вредност уколико из било ког разлога

Page 73: Uvod u programski jezik C Univerzitet Singidunum

17

простор не може да буде додељен. Садржај додељеног простора је попуњен нулама.

realloc(p,velicina) Ова функција мења величину додељена меморије на коју указује показивач p тако да она има velicina бајтова. Нова величина може да буде већа или мања од претходне. У случају смањивања, смасивање се врши од краја, а садржај преосталих бајтова се задржава. У случају повећавања меморијског простора, нови бајтови се додају на крај постојеће меморије а њихов садржај није дефинисан. Вредност коју враћа функија је генерички показивач (тип void *) на додељени простор или NULL вредност уколико из било ког разлога простор не може да буде додељен.

free(p) Ова функција ослобађа простор на који указује показивач p. Показивач p мора да указује на адресу претходно додељене меморијске локације помоћу функција malloc, calloc или realloc.

Примери:

malloc #include <stdio.h> #include <stdlib.h> /* potrebno zbog malloc i free funkcije */ main() { int number; int *ptr; int i; printf("Koliko int vrednosti zelite da sacuvate? "); scanf("%d", &number); ptr = malloc(number*sizeof(int)); /* zahtev za dodelu memorije */ /* ptr = (int *)malloc(number*sizeof(int)); */ /* moze i ovako */ if(ptr!=NULL) { for(i=0 ; i<number ; i++) { *(ptr+i) = i; } for(i=number ; i>0 ; i--) { printf("%d\n", *(ptr+(i-1))); /* prikazi u suprotnom redosledu */ } free(ptr); /* oslobodi dodeljenu memoriju */ } else printf("\nDodela memorije nije izvrsena - nema dovoljno memorije.\n"); } Излаз: Koliko int vrednosti zelite da sacuvate? 3 2 1 0

Page 74: Uvod u programski jezik C Univerzitet Singidunum

18

calloc /* Program koji koristi calloc da dodeli memoriju za 40 long int promenljivih. Inicijalizuje svaku promeljivu na 0 */ #include <stdio.h> #include <stdlib.h> int main( void ) { long *buffer; buffer = (long *)calloc( 40, sizeof( long ) ); if( buffer != NULL ) printf( "Dodeljena memorija za 40 long int promenljivih\n" ); else printf( "Dodela memorije neuspesna\n" ); free( buffer ); } _____________________________________________________________________

Пример: #include <stdio.h> #include <stdlib.h> /* potrebno zbog malloc i free funkcije */ main() { float *ptr_c1, *ptr_c2, *ptr_m1, *ptr_m2; int i; ptr_c1 = calloc(3, sizeof(float)); /* moze se zahtevati konverzija */ ptr_c2 = calloc(3, sizeof(float)); ptr_m1 = malloc(3 * sizeof(float)); ptr_m2 = malloc(3 * sizeof(float)); if(ptr_c1!=NULL && ptr_c2!=NULL && ptr_m1!=NULL && ptr_m2!=NULL) { printf("adresa ptr_c1 je %05.5p,\n", ptr_c1); printf("adresa ptr_c2 je %05.5p,\n", ptr_c2); printf("adresa ptr_m1 je %05.5p,\n", ptr_m1); printf("adresa ptr_m2 je %05.5p,\n\n", ptr_m2); for(i=0 ; i<3 ; i++) { printf("*ptr_c1[%d] ima vrednost %05.5f,\n", i, *(ptr_c1+i)); printf("*ptr_c2[%d] ima vrednost %05.5f,\n", i, *(ptr_c1+i)); printf("*ptr_m1[%d] ima vrednost %05.5f\n", i, *(ptr_m1+i)); printf("*ptr_m2[%d] ima vrednost %05.5f,\n\n", i, *(ptr_c1+i)); } free(ptr_c1); free(ptr_c2); free(ptr_m1); free(ptr_m2); } else printf("Nema dovoljno memorije\n"); }

Page 75: Uvod u programski jezik C Univerzitet Singidunum

19

Излаз: adresa ptr_c1 je 00430120, adresa ptr_c2 je 004300E0, adresa ptr_m1 je 004300A0, adresa ptr_m2 je 00430060, *ptr_c1[0] ima vrednost 0.00000, *ptr_c2[0] ima vrednost 0.00000, *ptr_m1[0] ima vrednost -431602080.00000 *ptr_m2[0] ima vrednost 0.00000, *ptr_c1[1] ima vrednost 0.00000, *ptr_c2[1] ima vrednost 0.00000, *ptr_m1[1] ima vrednost -431602080.00000 *ptr_m2[1] ima vrednost 0.00000, *ptr_c1[2] ima vrednost 0.00000, *ptr_c2[2] ima vrednost 0.00000, *ptr_m1[2] ima vrednost -431602080.00000 *ptr_m2[2] ima vrednost 0.00000,

Пример: realloc #include<stdio.h> #include <stdlib.h> int main() { int *ptr; int i; ptr = calloc(5, sizeof(int)); /*dinamicki niz od 5 elemenata indeks 0-4 */ if(ptr!=NULL) /* ako je dodeljena memorija...*/ { *ptr = 1; /* isto sto i ptr[0]=1 ili *ptr=1 */ *(ptr+1) = 2; /* isto sto i ptr[1]=2 */ *(ptr+2) = 4; *(ptr+3) = 8; *(ptr+4) = 16; printf("\nNakon naredbe calloc(5, sizeof(int)) i dodele vrednosti...\n"); for(i=0 ; i<5 ; i++) printf("*(ptr+%d) ukazuje na vrednost %d\n", i, *(ptr+i)); ptr = realloc(ptr, 7*sizeof(int)); /* dodaj jos 2 int (bilo je vec 5)*/ if(ptr!=NULL) { printf("\n Nakon naredbe ptr = realloc(ptr, 7*sizeof(int));... \n"); for(i=0 ; i<7 ; i++) printf("*(ptr+%d) ukazuje na vrednost %d\n", i, *(ptr+i)); *(ptr+5) = 32; *(ptr+6) = 64;

Page 76: Uvod u programski jezik C Univerzitet Singidunum

20

printf("\n Nakon naredbe dodele vrednosti 6. i 7. elementu... \n"); for(i=0 ; i<7 ; i++) printf("*(ptr+%d) ukazuje na vrednost %d\n", i, *(ptr+i)); realloc(ptr,0); /* isto sto i free(ptr); */ } else printf("Nema dovoljno memorije - realloc neuspesan.\n"); } else printf("Nema dovoljno memorije - calloc neuspesan.\n"); } Излаз: Nakon naredbe calloc(5, sizeof(int)) i dodele vrednosti... *(ptr+0) ukazuje na vrednost 1 *(ptr+1) ukazuje na vrednost 2 *(ptr+2) ukazuje na vrednost 4 *(ptr+3) ukazuje na vrednost 8 *(ptr+4) ukazuje na vrednost 16 Nakon naredbe ptr = realloc(ptr, 7*sizeof(int));... *(ptr+0) ukazuje na vrednost 1 *(ptr+1) ukazuje na vrednost 2 *(ptr+2) ukazuje na vrednost 4 *(ptr+3) ukazuje na vrednost 8 *(ptr+4) ukazuje na vrednost 16 *(ptr+5) ukazuje na vrednost -842150451 *(ptr+6) ukazuje na vrednost -842150451 Nakon naredbe dodele vrednosti 6. i 7. elementu... *(ptr+0) ukazuje na vrednost 1 *(ptr+1) ukazuje na vrednost 2 *(ptr+2) ukazuje na vrednost 4 *(ptr+3) ukazuje na vrednost 8 *(ptr+4) ukazuje na vrednost 16 *(ptr+5) ukazuje na vrednost 32 *(ptr+6) ukazuje na vrednost 64

Page 77: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C (C programming Language)

Лекција 6

Садржај

ЗЗннааккооввии ......................................................................................................................................... 2 Кодирање знакова .................................................................................................................................. 2 Константе................................................................................................................................................. 3 Знаковне константе........................................................................................................................... 3 Константе типа знаковног низа ...................................................................................................... 4 Симболичке констнате ..................................................................................................................... 5

Матрице знакова .................................................................................................................................... 5 Улазно излазна конверзија .................................................................................................................. 6 Библиотечке функције .......................................................................................................................... 6 Читање и писање знакова без конверзије .................................................................................. 6 Испитивање знакова......................................................................................................................... 8 Рад са знаковним низовима ......................................................................................................... 14 Конверзија у нумеричке типове података ................................................................................. 18

Page 78: Uvod u programski jezik C Univerzitet Singidunum

2

ЗЗннааккооввии Програмски језик C има само нумеричке типове података. Међутим, веома често се јавља потреба за рад са

текстом. У програмском језику C постоји тип char који је у основи целобројан тип али је предвиђен за представљање појединачних карактера. За представљање низа знакова (текстова) променљиве дужине, који се називају знаковни низови користи се низ типа char.

Кодирање знакова Сваком знаку припада одговарајући број (који се често назива кôд) преко којег се он „памти" у меморији рачунара. Мада постоји више кôдова, један од најзаступљенијих је ASCII кôд.

Page 79: Uvod u programski jezik C Univerzitet Singidunum

3

Знакови који могу да се прикажу на монитору или да се одштампају називају се штампајућим знаковима. То су знакови унутар табеле ASCII кôдова чије су бројне вредности између 32 (размак или space) и 126 (~). Запамтите да је 48 кôд за 0, 49 кôд за 1, ..., 65 кôд за А, 66 кôд за В,..., 97 кôд за а, 98 кôд за b.... Међу ове знакове спадају бројеви, слова и специјални знакови (!"#$....+-...).

Знакови са кôдовима од 0 до 31 и знак са кôдом 127 се називају управљачким знаковима. Посебну групу знакова чине тзв. бели знакови. То су они знакови који остављају празнину приликом исписа: размак (space) скок на нови лист (ff - form feed), скок у нови ред (lf - line feed), повратак на почетак реда (cr carriage return)...

Константе Сваком податку типа char се може доделити константа, било да је реч о појединачном знаку или низу знакова (стринг).

Знаковне константе Знаковна константа је цели број чија је вредност једнака кôду жељеног знака (видети ASCII табелу).

Податку типа char се може доделити број који је једнак кôду жељеног знака или сам знак написан под једноструким знаковима навода.

Пример 1: #include <stdio.h> main() { char c1, c2, c3; c1='A'; c2=65; c3='#'; printf("Vrednost znakova: c1=%c, c2=%c, c3=%c\n",c1,c2,c3); } Излаз:

Vrednost znakova: c1=A, c2=A, c3=#

Само се децимални бројеви пишу без знакова навода! Уколико желите да податку типа char доделите кôд у облику хексадецималног или окталног броја онда то морате урадити применом знакова навода. Октални бројеви се пишу низом од једне две или три окталне цифре (0,1,2,...7) иза знака косе црте уназад (\) нпр октални број 1078 који одговара знаку G се пише као '\107'. Хексадецимални број се пише низом од једне или две хексадецималне цифре (0,1,...,а, b,...,f) иза знакова \x или \X. Нпр. хексадецимални број 5116 (који одговара децималном 81 односно знаку Q) се пише као '\x51.'

Пример 2: main() { char c1, c2, c3,c4; c1='A'; c2=65; c3='\101'; /* 65 dekadno je 101 oktalno */ c4='\x41'; /* 65 dekadno je 41 heksadecimalno */ printf("Vrednost znakova: c1=%c, c2=%c, c3=%c, c4=%c\n",c1,c2,c3,c4); } Излаз:

Vrednost znakova: c1=A, c2=A, c3=A, c4=A

Page 80: Uvod u programski jezik C Univerzitet Singidunum

4

За неке управљачке константе које се често користе обезбеђене су симболичке константе. Тако на пример, давање звучног сигнала (bell, кôд 07) има симболичку константу \a; скок на нави ред има симболичку константу \n; итд...

Константе типа знаковног низа Најчешћи термин који се користи за знаковне низове је стринг. Међутим, постоји значајна разлика између знаковног низа и стринга.

Стринг је секвенца од нула или више знакова (карактера) и стринг се пише у овиру ДВОСТРУКИХ знакова навода. Сваки стринг се завршава са тзв. нул знаком '\0' (null character) .

Знаковни низ у општем случају не мора да садржи нул знак ('\0'), али последњи знак у стрингу мора бити управо овај знак.

Пример стринга:

"John L. Johanson"

"Zdravo svima!"

Из дефиниције стринга јасно је да постоји разлика између 'A' и "A". Први случај ('A') представља један знак: А, док други ("A") представља стринг. Како се стринг завршава са нул карактером, "A" представља два знака: 'A' и '\0'. На сличан начин стринг "Zdravo" представља седам знакова: 'Z', 'd', 'r', 'a', 'v', 'o' и '\0'. Да би уписали у меморију 'А' потребна нам је само једна меморијска јединица типа char, док је за "А" потребно две меморијске јединице типа char једна за 'А' и једна за '\0'. На сличан начин се може показати да је за упис стринга "Zdravo" потребно седам меморијских јединица типа char.

Размотримо следећу наредбу:

char ime[16]

Овом наредбом се дефинише низ ime од 16 елемената типа char. Како стринг мора да се заврши са нул карактером а дефинисан је низ од 16 елемента, најдужи стринг који може да се запише у низ ime може да има 15 знакова. Ако у променљиву ime упишете стринг дужине 10, првих 11 елемената низа ће бити заузето а преосталих 5 ће остати слободно.

Пример 3: char ime[16]={'J', 'o', 'h', 'n', '\0'};

Ова наредба дефинише низ ime који садржи 16 елемената типа char и у њега уписује стринг "Jonh". За време дефинисања променљиве дозвољен је и други начин доделе почетне вредности:

Пример 4 char ime[16]="John";

Претходне две наредбе (Пример 3 и 4) су потпуно једнаке.

Обзиром да знаковни низ не мора садржати нул карактер, може се писати

Пример 5: char imeА[16]={'J', 'o', 'h', 'n'}; char imeB[16]="John";

али низови imeА и imeB (пример 5) нису исти.

Page 81: Uvod u programski jezik C Univerzitet Singidunum

5

При дефинисању низа, дужина низа се не мора навести уколико се низу додељује почетна (иницијална) вредност

Пример 6: char ime[]={'J', 'o', 'h', 'n', '\0'};

или

Пример 7: char ime[]="John";

стим што је (у примеру 5 и 6) дужина низа једнака 5, у складу са бројем иницијализованих вредности!

Међутим, у програму (сем на месту дефинисања) није дозвољена оваква додела вредности низу (као у примеру 7)

Пример 8: main() { char ime[]="John"; ... ime[0]='A'; /*ovo je dozvoljeno*/ ... ime="Marko"; /* Error! */ ... }

Симболичке констнате На сличан начин као и код нумеричких типова података могу да се дефинишу симболичке константе.

Пример 9: #define ZVONO '\a' #define NOVI_RED '\n' #define VALUTA "US dollar"

Матрице знакова Често је потребно да се сачува низ речи или реченица. Ако се они смештају у матрицу онда се свака реченица смешта у једну врсту матрице. Како реченице могу имати различите дужине узимају се дужине врста према најдужој реченици, што често није економично. Решење је у употреби показивача. char daniA[7][11]={"ponedeljak", "utorak", "sreda", "cetvrtak", "petak", "subota", "nedelja"}; char *daniB[7]={"ponedeljak", "utorak", "sreda", "cetvrtak", "petak", "subota", "nedelja"}; Првом наредбом се дефинише матрица од 7 пута 11 знакова а другом наредбом 7 показивача на знаковне низове који су тачно потребне дужине.

Page 82: Uvod u programski jezik C Univerzitet Singidunum

6

Улазно излазна конверзија За улазно излазну конверзију се користе библиотечке функције printf() i scanf() које су раније поменуте. У ту сврху постоје две врсте конверзија:

− за један знак (карактер) конверзија %c и − за стринг конверзија %s.

Пример:

#include <stdio.h> main() { char a[8] = {'A','l','e','k','s','a'}; char b[5] = "Pera"; printf("ispis karaktera: %c\nIspis stringa: %s\n", a[3], b); } Излаз: ispis karaktera: k Ispis stringa: Pera

Библиотечке функције Овде су описане стандардне библиотечке функције за рад са знаковним подацима.

Читање и писање знакова без конверзије

int getchar(void); Објашњење: Ова функција чита следећи знак, укључујући и беле знакове, са стандардног улаза (тастатуре). Вредност функције је кôд прочитаног знака или симболичка константа EOF уколико је прочитан знак за карј датотеке.

Праметри: нема их

Повратна вредност: Кôд знака који је прочитан

Пример: #include <stdio.h> int main() { char znak; printf ("Unesite tekst. Unesite tacku ('.') da bi izasli z programa:"); do { znak=getchar(); printf("%c",znak); } while (znak != '.'); return(0); }

Page 83: Uvod u programski jezik C Univerzitet Singidunum

7

int putchar(int character); Објашњење: Ова функција исписује знак преко главног излаза (монитор) рачунара.

Праметри: знак (карактер) који се исписује. Мада је дефинсан као int функција га конвертује у unsigned char пре исписа.

Повратна вредност: Кôд знака који се исписује уколико је операција успешна, иначе EOF уколико операција није успешна

Пример: #include <stdio.h> int main () { char znak; for (znak = 'A' ; znak <= 'Z' ; znak++) { putchar (znak); } return(0); } Излаз: ABCDEFGHIJKLMNOPQRSTUVWXYZ

char *gets(char *buffer); Објашњење: Ова функција чита стринг односно једну линију текста са стандардне улазне јединице и текст

смешта у знаковни низ типа char (који је у опису назван buffer). Под линијом текста се сматрају сви знаци закључно са знаком за прелазак у нови ред (\n). Знак за прелазак у нови ред се приликом записа у низ замењује нул карактером (\0).

Праметри: buffer поинтер на низ у који се смешта прочитани стринг.

Повратна вредност: Адреса низа (поинтер на низ) уколико је операција успешна, иначе NULL уколико операција није успешна.

Пример:

#include <stdio.h> main() { char niz[5]; char *p; int i; printf ("Upisite do 5 karaktera: "); p=gets(niz); printf ("Uneti karakteri su:\n"); for(i=0; i<5; i++) printf ("\t %c\n",*(p+i) ); }

Page 84: Uvod u programski jezik C Univerzitet Singidunum

8

Пример #include <stdio.h> main() { char niz[256]; printf ("Upisite vasu adresu: "); gets(niz); printf ("Vasa adresa je: %s\n",niz); }

int puts (const char *string); Објашњење: Ова функција исписује садржај знаковног низа string (типа char) до завршног знака \0

додајући знак за прелазак у нови ред \n иза последњег знака.

Праметри: string име низа или поинтер на низ који се исписује.

Повратна вредност: Не негативна вредност уколико је операција успешна, иначе EOF уколико операција није успешна

Пример: include <stdio.h> int main () { char string [] = "Hello world!"; char *ptr; ptr = &string[0]; puts(ptr); }

Пример: #include <stdio.h> int main () { char string [] = "Hello world!"; puts (string); }

Испитивање знакова

Функције за испитивање знакова испитују у коју категорију знакова (слово, цифра...) спада одређени знак. Ове функције су дефинисане у стандардном заглављу <ctype.h>. Повратна вредност наведених функција је 0 у случају да је одговор негативан, односно различита од нуле у случају да је одговор тачан. int isalnum(int c); /* с је slovo ili cifra? */ Објашњење: Ова функција тестира знак с да би одредио да ли је он из скупа алфанумеричких знакова:

('A'-'Z', 'a'-'z', или '0'-'9')

Праметри: с знак који се испитује.

Повратна вредност: 1 ако је знак алфанумерички, 0 иначе.

Page 85: Uvod u programski jezik C Univerzitet Singidunum

9

Пример: #include <ctype.h> #include <stdio.h> main() { short int i; for (i = 0; i < 256; i++) { if(isalnum(i)) printf("isalnum (%c) Da \n", i); else printf("isalnum (%c) Ne\n", i); } }

int isalpha(int c); /* c је slovo? */ Објашњење: Ова функција тестира знак с да би одредио да ли је он слово из скупа: ('A'-'Z', или 'a'-'z')

Праметри: с знак који се испитује.

Повратна вредност: 1 ако је знак слово, 0 иначе.

Пример: #include <ctype.h> #include <stdio.h> main() { short int i; for (i = 0; i < 256; i++) { if(isalpha(i)) printf("isalpha (%c) Da \n", i); else printf("isalpha (%c) Ne\n", i); } }

int islower(int c); /* c је malo slovo? */ Објашњење: Ова функција тестира знак с да би одредио да ли је он мало слово: ('a'-'z')

Праметри: с знак који се испитује.

Повратна вредност: 1 ако је знак мало слово, 0 иначе.

Page 86: Uvod u programski jezik C Univerzitet Singidunum

10

Пример: #include <ctype.h> #include <stdio.h> main() { short int i; for (i = 0; i < 256; i++) { if(islower(i)) printf("islower(%c) Da \n", i); /* else printf("islower(%c) Ne\n", i); */ } }

int isupper (int c); /* c је veliko slovo? */ Објашњење: Ова функција тестира знак с да би одредио да ли је он велико слово: ('A'-'Z')

Праметри: с знак који се испитује.

Повратна вредност: 1 ако је знак велико слово, 0 иначе.

Пример: #include <ctype.h> #include <stdio.h> main() { short int i; for (i = 0; i < 256; i++) { if(isupper(i)) printf("isupper(%c) Da \n", i); /* else printf("isupper(%c) Ne\n", i); */ } }

Page 87: Uvod u programski jezik C Univerzitet Singidunum

11

int isdigit (int c); /* c је cifra? */ Објашњење: Ова функција тестира знак с да би одредио да ли је он број: ('0'-'9')

Праметри: с знак који се испитује.

Повратна вредност: 1 ако је знак цифра, 0 иначе.

Пример: #include <ctype.h> #include <stdio.h> main() { short int i; for (i = 0; i < 256; i++) { if(isdigit(i)) printf("isdigit (%c) Da \n", i); /* else printf("isdigit (%c) Ne\n", i); */ } }

int isxdigit(int c); /* c је heksadecimalna cifra? */ Објашњење: Ова функција тестира знак с да би одредио да ли је он хексадецимална цифра: ('0'-'9', 'а'-'f',

'A'-'F')

Праметри: с знак који се испитује.

Повратна вредност: 1 ако је знак хексадецимална цифра, 0 иначе.

Пример: #include <ctype.h> #include <stdio.h> main() { short int i; for (i = 0; i < 256; i++) { if(isxdigit(i)) printf("isxdigit(%c) Da \n", i); /* else printf("isxdigit(%c) Ne\n", i); */ } }

int isspace (int c); /* c је beli znak? */ Објашњење: Ова функција тестира знак с да би одредио да ли је он бели знак: (размак, табулатор,...)

Праметри: с знак који се испитује.

Повратна вредност: 1 ако је знак бели знак, 0 иначе.

Page 88: Uvod u programski jezik C Univerzitet Singidunum

12

Пример: #include <ctype.h> #include <stdio.h> main() { short int i; for (i = 0; i < 256; i++) { if(isspace(i)) printf("isspace(%c) Da \n", i); /* else printf("isspace(%c) Ne\n", i); */ } }

int isgraph(int c); /* c је štampajući znak a nije razmak? */ Објашњење: Ова функција тестира знак с да би одредио да ли је он штампајући знак.

Праметри: с знак који се испитује.

Повратна вредност: 1 ако је знак штампајући, 0 иначе.

Пример: #include <ctype.h> #include <stdio.h> main() { short int i; for (i = 0; i < 256; i++) { if(isgraph(i)) printf("isgraph(%c) Da \n", i); /* else printf("isgraph(%c) Ne\n", i); */ } }

У ову групу функција спадају и функције за претварање слова

int tolower(int c); /* c se konvertuje u malo slovo */ Објашњење: Ова функција конвертује знак с у мало слово уколико је знак с слово.

Праметри: с знак који се испитује.

Повратна вредност: Одговарајће мало слово ако је знак с слово, иначе знак с.

Page 89: Uvod u programski jezik C Univerzitet Singidunum

13

Пример: #include <stdio.h> #include<ctype.h> main() { unsigned char i; for (i = 0x20; i < 0x7F; i++) /* od 32 do 127*/ printf ("tolower(%c) = %c\n", i, tolower(i)); }

int toupper(int c); /* c se konvertuje u veliko slovo */ Објашњење: Ова функција конвертује знак с у велико слово уколико је знак с слово.

Праметри: с знак који се испитује.

Повратна вредност: Одговарајће велико слово ако је знак с слово, иначе знак с.

Пример: #include <stdio.h> #include<ctype.h> main() { unsigned char i; for (i = 32; i < 128; i++) printf ("toupper(%c) = %c\n", i, toupper(i)); }

Page 90: Uvod u programski jezik C Univerzitet Singidunum

14

Рад са знаковним низовима

У језику C не постоји оператор за обраду знаковних низова. Зато постоји више готових (библиотечких) функција које су описане у стандардном заглављу <string.h>.

strcpy( str_dest, str_src ); /* kopira string */ Објашњење: Ова функција копира стринг str_src у стринг str_dest, укључујући и нул карактер. Треба

водити рачуна о величини стринга str_dest

Пример: #include <stdio.h> #include <string.h> main () { char str1[]="Sadrzaj stringa 1"; char str2[40]; char str3[40]; strcpy (str2,str1); strcpy (str3,"Kopiranje uspesno"); printf (" str1: %s\n str2: %s\n str3: %s\n",str1,str2,str3); } Излаз: str1: Sadrzaj stringa 1 str2: Sadrzaj stringa 1 str3: Kopiranje uspesno

strncpy( str_dest, str_src ,n); /* kopira n znakova */ Објашњење: Ова функција копира првих n знакова из стринга str_src у стринг str_dest, не укључујући

увек и нул карактер. Уколико је број знакова n већи од дужине str_src разлика се допуњава нулама.

Пример: #include <stdio.h> #include <string.h> main () { char str1[]= "To be or not to be"; char str2[30]="Pocetni sadrzaj pre kopiranja"; puts(str2); strncpy (str2,str1,10); str2[10]='\0'; /* !! */ puts(str2); }

Page 91: Uvod u programski jezik C Univerzitet Singidunum

15

Излаз:

Pocetni sadrzaj pre kopiranja To be or n

strcat(str_dest, str_src); /* dopisuje string */ Објашњење: Ова функција дописује стринг str_src на садржај стринга str_dest, и при томе преписује

нул карактер стринга str_dest. Резултујући стринг има нул карактер на крају.

Пример: #include <stdio.h> #include <string.h> main () { char str[80]; strcpy (str,"Stringovi "); strcat (str,"su "); strcat (str,"spojeni."); puts (str); } Излаз: Stringovi su spojeni.

strncat(str_dest, str_src, n); /* dopisuje n znakova */ Објашњење: Ова функција дописује стринг n знакова из str_src на садржај стринга str_dest, и при

томе преписује нул карактер стринга str_dest. Резултујући стринг има нул карактер на крају.

Пример: #include <stdio.h> #include <string.h> main () { char str1[20]; char str2[20]; strcpy (str1,"To be "); strcpy (str2,"or not to be"); strncat (str1, str2, 6); puts (str1); } Излаз: To be or not

Page 92: Uvod u programski jezik C Univerzitet Singidunum

16

strcmp(str1, str2); /* poredi dva stringa */ Објашњење: Ова функција пореди стринг str1 и стринг str2, полазећи од првог знака све док не наиђе на

различите знакове. Када су знаци различити пореди њихов број у АSCII табели.

Повратна вредност вредност објашњење <0 str1 је мањи од str20 str1 је исти са str2 >0 str1 је већи од str2

Пример: #include <stdio.h> #include <string.h> main () { char Resenje[] = "kruska"; char Odgovor[80]; do { printf ("Koje je moje omiljeno voce? "); gets (Odgovor); } while (strcmp (Resenje,Odgovor) != 0); printf ("Odgovor tacan!\n"); } Излаз: Koje je moje omiljeno voce? jabuka Koje je moje omiljeno voce? kruska Odgovor tacan!

strlen(str1); /* dužina stringa */ Објашњење: Ова функција одређује број карактера у стрингу пре нул карактера.

Пример: #include <stdio.h> #include <string.h> main () { char Ulaz[256]; printf ("Unesite tekst: "); gets (Ulaz); printf ("Uneto je %u znakova\n",strlen(Ulaz)); } Unesite tekst: nekoliko slova Uneto je 14 znakova

Page 93: Uvod u programski jezik C Univerzitet Singidunum

17

strchr(char *str1, c); /* trazi prvu poziciju karaktera u stringu */ Објашњење: Ова функција тражи позицију прве појаве карактера c у стрингу str1.

Повратна вредност: Aко је тражени знак пронађен враћа показивач на локацију на којој се налази тражени знак у супротном враћа NULL показивач

Пример: #include <stdio.h> #include <string.h> int main () { char str1[] = "Ovo je test recenica"; char * pch; printf ("Trazim karakter 'e' u \"%s\"...\n",str1); pch=strchr(str1,'e'); while (pch!=NULL) { printf ("pronasao sam 'e' na poziciji %d\n",pch-str1+1); pch=strchr(pch+1,'e'); } } Trazim karakter 'e' u "Ovo je test recenica"... pronasao sam 'e' na poziciji 6 pronasao sam 'e' na poziciji 9 pronasao sam 'e' na poziciji 14 pronasao sam 'e' na poziciji 16

strrchr(char *str1, c); /* traži poslednju poziciju karak. u stringu */ Објашњење: Ова функција тражи позицију последње појаве карактера c у стрингу str1.

Повратна вредност: Aко је тражени знак пронађен враћа показивач на локацију на којој се налази тражени знак у супротном враћа NULL показивач

Пример: #include <stdio.h> #include <string.h> int main () { char str1[] = "Ovo je test recenica"; char * pch; pch=strrchr(str1,'e'); printf ("Poslednja pojava karaktera 'e' je na poziciji %d\n",pch-str1+1); } Poslednja pojava karaktera 'e' je na poziciji 16

Page 94: Uvod u programski jezik C Univerzitet Singidunum

18

strstr(str1, str2); /* trži poziciju stringa 2 u stringu1 */ Објашњење: Ова функција тражи позицију појављивања подниза str2 у стрингу str1.

Повратна вредност: Aко је тражени подниз пронађен враћа показивач на локацију на којој се налази први знак из траженог подниза, у супротном враћа NULL показивач

Пример: #include <stdio.h> #include <string.h> int main () { char str[] ="Ovo je jedan primer"; char *pch; pch = strstr (str,"dan"); printf("Pronadjena pozicija je %d\n", pch-str+1); }

Pronadjena pozicija je 10

Конверзија у нумеричке типове података

Постоји више функција за конверзију бројева из знаковног облика (низ цифара) у нумеричке типове са којима могу да се обаве резна израчунавања. double atof(string); /* konverzija u tip double */ Објашњење: Ова функција врши конверзију реалног броја из низа цифара облика ±ccc.cccE±cc у тип

double. Пример:

#include <stdio.h> #include <stdlib.h> #include <math.h> main () { double n,m; double pi=3.1415926535; char Ulaz[256]; printf("Unesite vrednost u stepenima: "); gets(Ulaz); n =atof(Ulaz); m = sin(n*pi/180); printf("sin(%f) je %f\n" , n, m); } Unesite vrednost u stepenima: 90 sin(90.000000) je 1.000000

Page 95: Uvod u programski jezik C Univerzitet Singidunum

19

int atoi(string); /* konverzija u tip int */ Објашњење: Ова функција врши конверзију реалног броја из низа цифара облика ±cccc у тип int. Пример:

#include <stdio.h> #include <stdlib.h> main () { int i; char Ulaz[256]; printf ("Unesite broj: "); gets ( Ulaz ); i = atoi (Ulaz); printf ("Uneta vrednost je %d, a njena dvostruka vrednost je %d\n",i,i*2); } Unesite broj: 243 Uneta vrednost je 243, a njena dvostruka vrednost je 486

long atol(string); /* konverzija u tip long */ Објашњење: Ова функција врши конверзију реалног броја из низа цифара облика ±cccc у тип long. Пример:

#include <stdio.h> #include <stdlib.h> main () { int i; char Ulaz[256]; printf ("Unesite broj: "); gets ( Ulaz ); i = atol (Ulaz); printf ("Uneta vrednost je %d, a njena dvostruka vrednost je %d\n",i,i*2); } Unesite broj: 56324 Uneta vrednost je 56324, a njena dvostruka vrednost je 112648

Page 96: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C (C programming Language)

Лекција 7

Садржај

ФФууннккцциијјее .............................................................................................................................2 Библиотечке функције.............................................................................................................2 Корисничке функције...............................................................................................................3 Функције које враћају вредност ..............................................................................................3 Дефинисање функција ............................................................................................................4 Наредба return .........................................................................................................................5 Прототип функције (декларација функције) ..........................................................................8 Функције које не враћају вредност - void функције ...............................................................9

Void функције без аргумената.............................................................................................9 Void функције са аргуменатима ........................................................................................10

Прослеђивање вредности аргумената.................................................................................11 Рекурзивне функције.............................................................................................................19 Глобалне променљиве (идентификатори) ..........................................................................21 Досег идентификатора ......................................................................................................23

Page 97: Uvod u programski jezik C Univerzitet Singidunum

2

ФФууннккцциијјее Рачунарски програми који треба да моделују неки реалан проблем су углавном много већи од претходно навoђених примера. Најбољи начин да се напише неки велики програм је да се он подели на мање делове или модуле које је много лакше дефинисати и контролисати. Ти делови се називају потпрограми а у програмском језику C сви потпрограми се називају функцијама.

Поред наведеног образложења, постоје и други мотиви за поделу програма на функције.

Један од основних принципа програмирања је могућност примене функције као градивног елемента програма, односно могућност да се једном написана функција користи у више различитих програма.

Други мотив је да се омогући понављање извршавања истог кôда у програму. „Паковање” жељеног кôда у одређену функцију нуди могућност да се тај кôд изврши на различитим локацијама у програму једноставним позивањем те функције. C програм се најчешће пише као комбинација два типа функција: – функција које пише сам програмер (корисничке функције) и – функција које постоје у стандардним библиотекама (библиотечке функције) као што су: printf,

scanf, sin, malloc, free, и друге.

Библиотечке функције Пре формалног објашњења функција, подсетимо се значења функција у математици. Функција у математици представља скуп правила на основу којих улазне вредности, које се називају аргументи, дају резултат или вредност функције. На пример, ако је ( ) 2 5f x x= + , онда је (1) 7f = , (2) 9f = и (3) 11f = , где су 1, 2 и 3 аргументи функције f , а 7, 9 и 11 одговарајуће вредности функције f .

У програмском језику C концепт функција је, било да је реч о корисничким или библиотечким функцијама, сличан са функцијама у математици. На пример, свака функција има своје име и зависно од операција које су у њој дефинисане она обавља одређена израчунавања.

Неке од библиотечких функције су pow(x,y), sqrt(x) и floor(x). Функција степеновња pow(x,y) (power function) рачуна xy па је вредност функције pow(x,y)= xy. На пример pow(2,3)= 23 = 8.0, а pow(2.5,3)= 2.53 = 15.625. Како је вредност (резултат) pow(x,y) типа double кажемо да је функција pow типа double или да функција pow враћа вредност типа double. Поред тога, x и y се називају параметри или аргументи функције pow. У наведеном прмеру функција pow има два аргумента.

Функција sqrt(x) (square root) рачуна квадратни корен од x за x>=0.0. На пример, sqrt(2.25)=1.5. Функција sqrt је типа double и она има само један аргумент.

Функција floor(x) рачуна највећи цео број који је мањи или једнак од x. На пример floor(23.21)=23.0. Ова функција је типа double и има само један аргумент.

Пример коришћења библиотечких функција: #include <math.h> #include <stdio.h> main() { double y; y = floor( 2.8 ); /* poziv bibliotečke funkcije*/ printf( "The floor of 2.8 is %f\n", y ); y = floor( -2.8 ); printf( "The floor of -2.8 is %f\n", y ); } Излаз

Page 98: Uvod u programski jezik C Univerzitet Singidunum

3

The floor of 2.8 is 2.000000 The floor of -2.8 is -3.000000

Корисничке функције Библиотечке функције имају велику примену, њиховом употребом се може смањити сложеност програма, могу се позивати више пута у оквиру истог програма или из различитих програма... Међутим, библиотечке функције, у општем случају, не обухватају све „проблеме" које треба решити у току писања неког програма. Због тога постоји могућност да програмер сам дефинише нове функције, које називамо корисничким функцијама.

Корисничке функције се могу поделити у две категорије:

– Функције које имају тип податка, тј. функције које враћају неку вредност – Функције које не враћају вредност и оне се називају воид (void) функције.

Функције које враћају вредност Већ су поменуте функције pow(x,y), squrt(x) и floor(x). То су примери (библиотечких) функција које враћају вредност. Да би се те функције користиле у програму мора се знати име заглавља (header file) које садржи спецификацију функције. Затим је потребно у програму навести име заглавља користећи #include наредбу (нпр. #include<math.h>). Поред тога, програмер мора да зна својства функције :

1. Име функције коју жели да користи 2. Колико аргумената има функција 3. Тип податка сваког аргумента 4. Тип податка вредности која представља резултат функције (тип повратне вредности), односно тип

функције 5. Шта ради та функција, односно њен кôд.

Прва четири својства се називају заглавње функције (function heading) а пето својство се назива тело функције (function body). Свих пет својстава заједно се називају дефиниција функције. На пример, функција abs може да има следеће заглавље:

int abs(int x)

док њена дефиниција може да буде int abs(int x) { if(x < 0) x = -x; return x; }

Променљиве које су декларисане у заглављу функције се називају формални аргументи функције, па је x формални аргумент функције abs.

Претпоставимо да је постоји функција stepen и да је њено заглавље:

double stepen(double osnova, double eksponent)

Из заглавља функције stepen следи да су формални параметри функције stepen променљиве osnova и eksponent. Размотримо следећи пример:

Page 99: Uvod u programski jezik C Univerzitet Singidunum

4

main() { double u = 2.5; double v = 3.0; double x,y,w; x=stepen(u,v); /* Linija 1 */ y=stepen(2.0, 3.2); /* Linija 2 */ z=stepen(v, 7); /* Linija 3 */ ... }

У линији 1 функција stepen се позива са параметрима u и v. У том случају се вредности променљивих u и v прослеђују функцији stepen. Другим речима, вредност променљиве u се копира у променљиву osnova а вредност променљиве v се копира у променљиву eksponent. Променљиве u и v које се појављују у позиву функције stepen у линији 1 се називају стварни аргументи тог позива функције. У линији 2, функција stepen се позива са параметрима 2.0 и 3.2. У овом позиву вредност 2.0 се додељује променљивој osnova а вредност 3.2 се додељује промељивој eksponent. На сличан начин, у линији 3 приказана је могућност комбиновања претходна два позива.

Треба издвојити два појма:

Формални аргументи су променљиве које су наведене у заглављу функције унутар малих заграда.

Стварни аргументи су променљиве, изрази или вредности које се наводе приликом позива функкције.

Код библиотечких функција програмер треба да познаје само прва четири (од пет набројаних) својстава функције. Наравно, потребно је да зна шта функција ради, али програмер нема могућност да види начин на који је функција реализована тј. не зна њен кôд.

Дефинисање функција Општа синтакса за дефинисање функције је:

tip_funkcije ime_funkcije(lista formalnih argumenata) { naredbe; }

Тело функције Део синтаксе између витичастих заграда се назива тело функције, а наредбе могу бити наредбе за дефинисање променљивих (декларативне наредбе) или извршне наредбе. Обзиром да је тело функције по форми блок, на почетку блока је могуће дефинисати податке.

Подаци дефинисани на почетку тела функције су локални за ту функцију. То значи да они постоје само до краја функције, односоно до крајa блока у ком су дефинисани.

Формални аргументи функције су такође локални за ту функцију. tip_funkcije представља тип вредности коју враћа функција. Он (tip_funkcije) се такође назива тип повратне вредности (return type) функције. Уколико се изостави tip_funkcije подразумева се тип int. За функције које не враћају вредност резервисан је тип void, о чему ће касније бити више речи.

int ime_funkcije(lista formalnih argumenata) double ime_funkcije(lista formalnih argumenata) void ime_funkcije(lista formalnih argumenata)

Page 100: Uvod u programski jezik C Univerzitet Singidunum

5

ime_funkcije представља име функције и подлеже правилима за избор имена идентификатора.

Слично као код дефинисања променљивих, испред имена функције је могуће ставити *, што значи да је повратна вредност функције показивач на податке чији је тип наведен као tip_funkcije. У складу са тим вредност функције може да буде и генерички показивач (тип void *).

double *treci_stepen(lista formalnih argumenata) void *moja_funkcija(lista formalnih argumenata)

lista formalnih argumenata Унутар малих заграда наводи се, уколико постоји, листа аргумената. Потребно је навести тип податка и његово име. Уколико постоји више аргумената они се међусобно раздвајају зарезом.

tip_funkcije ime_funkcije(tip_prom1 ime_prom1, tip_prom2 ime_prom2,..., tip_promN ime_promN) float funPrimer(int a, double b)

Аргумената не мора бити али и у том случају се морају навести мале заграде.

tip_funkcije ime_funkcije() У случају да је листа формалних параметара (као у претходном примеру) празна, онда приликом позива те функције не треба наводити листу стварних аргумената, тј. треба навести име функције уз празне заграде.

Аргументи могу бити променљиве типа низ или типа показивач. Уколико је аргумент типа низ и то једнодимензионални низ онда се поред имена податка наводе угласте заграде али се не наводи број елемената низа.

tip_funkcije ime_funkcije(int a[], double b) tip_funkcije ime_funkcije(int *c)

У случају да је аргумент вишедимензионални низ, величина прве димензије се не наводи али је обавезно навести број елемената за остале димензије.

tip_funkcije ime_funkcije(int a[][10], double b)

Наредба return Након што се унутар тела функције израчуна вредност коју функција треба да врати, та вредност се враћа помоћу наредбе return.

Општи облик синтаксе за враћање вредности је

return izraz;

где izraz може да буде променљива, константа или неки израз чијим израчунавањем се добија жељена вредност.

Након извршења наредбе return функција се тренутно напушта, а контролу преузима део програма одакле је функција позвана. Поред тога, наредба којом је позвана функција се „замењује" са вредношћу коју враћа функција, тј. са вредношћу поред наредбе return.

Размотримо функцију која пореди два броја и враћа мањи број. Како функција треба да пореди два броја она мора имати два улазна аргумента. Усвојимо да је тип оба податка децимални, односно float. Резултат поређења два броја је један од ова два броја, тај број се враћа из функције, па је вредност функције или тип повратне вредности једнак типу броја тј. float. Нека се функција зове manji. Сада је још потребно написaти тело функције. Тело функције може бити написано на више начина:

Page 101: Uvod u programski jezik C Univerzitet Singidunum

6

float manji(float x, float y) { float pom; /* lokalna promenljiva*/ if(x<=y) pom=x; else pom=y; return pom; }

Oва функција може бити написана и на следећи начин: float manji(float x, float y) { if(x<=y) return x; else return y; }

Како извршење наредбе return прекида функцију, претходна функција је могла бити написана (и без наредбе else) као:

float manji(float x, float y) { if(x<=y) return x; return y; }

Први облик функције manji захтева употребу додатне променљиве pom (pom је локална променљива функције manji) друга два облика то не захтевају.

Приметите да су x и y формални аргументи, као и да се наредба return може појавити било где унутар тела функције. Након што се једном изврши наредба return све остале наредбе унутар тела функције се „прескачу".

Када је функција manji дефинисана, у наредном примеру је показано како се она позива из функције main. #include<stdio.h> main( ) { float prvi, drugi, MinBroj; /* Linija1 */ printf("Manji broj od 5 i 6 je %f\n", manji(5,6)); /* Linija2 */ printf("Unesite dva broja\n"); /* Linija3*/ scanf("%f %f", &prvi ,&drugi); /* Linija4*/ MinBroj=manji(prvi, drugi); /* Linija5 */ printf("Manji od brojeva %f i %f je %f\n", prvi, drugi, MinBroj); /* L6 */ printf("Manji od brojeva %f i %f je %f\n", 0.9, drugi, manji(0.9, drugi));

}

Page 102: Uvod u programski jezik C Univerzitet Singidunum

7

Излаз: Manji broj od 5 i 6 je 5.000000 Unesite dva broja 8 2 Manji od brojeva 8.000000 i 2.000000 je 2.000000 Manji od brojeva 0.900000 i 2.000000 je 0.900000

Израз manji(5,6) у линији 2 представља позив функције, а 5 и 6 су стварни аргументи функције. Израз manji(prvi, drugi) у линији 5 представља позивање функције (функцијски позив), а prvi и drugi представљају стварне параметре. Израз manji(0.9, drugi) у линији 7 представља позивање функције, а 0.9 и drugi представљају стварне параметре.

При позивању функције наводе се само стварни параметри а не и њихови типови! Следећи примери су погрешни:

MinBroj=manji(float prvi, float drugi); /* Greska, ne treba navoditi tip */ MinBroj=manji(float prvi, 0.9); /* Greska, ne treba navoditi tip */ MinBroj=manji(float prvi, float 0.9); /* Greska, ne treba navoditi tip */

Код функција типа void наредба return не сме да садржи izraz. У том случају, уколико је нареба return последња наредба у функције она може бити изостављена.

Претпоставимо да постоји функција poruka чији је једини задатак да испише неки текст у складу са улазним аргументом a и то за вредност a=1 треба да испише “poruka A“ а за било коју другу вредност исписује “poruka B“. Обзиром да функција не треба да враћа вредност, њен тип је void и треба да има један формални аргумент.

void poruka(short int a) { if(a==1) { printf("neka poruka A\n") return ; /* Nema izraza!*/ } printf("neka poruka B\n") return ; }

Приметите да у претходном примеру, обзиром на тип функције void, поред наредбе return нема израза. Такође је изостављена наредба else јер се у случају извршења наредбе return „прескачу" све остале наредбе у функцији а програм се наставља на месту oдакле је функција позвана. Само у случају када је тип функције void могуће је изоставити наредбу на крају функције (без обзира да ли се она претходно појављивала). Претходни пример се може писати као:

void poruka(short int a) { if(a==1) { printf("neka poruka A\n") return ; /* Nema izraza!*/ } printf("neka poruka B\n") }

Page 103: Uvod u programski jezik C Univerzitet Singidunum

8

Прототип функције (декларација функције) Ако је дефинисано како функцију треба написати, поставља се питање на ком месту у програму треба написати корисничку функцију. На пример, да ли треба функцију manji написати пре функције main?

У складу са правилом да се идентификатор мора декларисати пре прве употребе, и знајући да функција main позива функцију (идентификатор) manji следи да функција manji мора бити декларисана пре функције main.

У пракси се обично прво пише функција main пре свих осталих корисничких функција. Ово води ка грешци јер се, у том случају, позив било које корисничке функције јавља као недекларисани идентификатор. Овај проблем је могуће решити увођењем прототипа функције пре дефинисања било које функције (укључујући и функцију main)

Прототип функције представља заглавље функције (без навођења тела функције).

Прототип функције је обавезно завршити са знаком тачка-зарез (;) tip_funkcije ime_funkcije(lista formalnih argumenata);

Прототип за функцију manji је:

float manji(float x, float y);

Приликом писања прототипа функције није неопходно да се наведе име променљиве у листи аргумената. Тип променљивих се ипак мора навести. Зато претходни пример прототипа функције може да има облик:

float manji(float , float );

Потпуни облик разматраног програма може да има следећи облик: #include<stdio.h>

/* Prototip funkcije*/ float manji(float x, float y); /*ili float manji(float , float ); */ main( ) { float prvi, drugi, MinBroj; /* Linija1 */ printf("Manji broj od 5 i 6 je %f\n", manji(5,6)); /* Linija2 */ printf("Unesite dva broja\n"); /* Linija3*/ scanf("%f %f", &prvi ,&drugi); /* Linija4*/ MinBroj=manji(prvi, drugi); /* Linija5 */ printf("Manji od brojeva %f i %f je %f\n", prvi, drugi, MinBroj); /* L6 */ printf("Manji od brojeva %f i %f je %f\n", 0.9, drugi, manji(0.9, drugi));

} float manji(float x, float y) { if(x<=y) return x; else /* Moze i bez else */ return y; }

Приметите да је у линији 1 и 7 функције main функција manji позивана као израз. То је могуће зато што се позив функције након њеног израчунавања замењује са вредношћу коју функција враћа.

Прототип функције (назива се још и декларација функције) може као у претходном примеру бити дат изван тела било које друге функције. У том случају декларисана функција може бити позвана из било које друге

Page 104: Uvod u programski jezik C Univerzitet Singidunum

9

функције из програма. У претходном примеру функција manji може бити позвана из функције main али да је била дефинисана и нека друга функција из ње би такође могла да буде позвана.

Уколико се функција декларише унутар тела неке друге функције онда декларисану фуункцију може позвати само та функција.

Програмски језик C не дозвољава дефинисање једне функције унутар друге функције. Насупрот томе декларисање функције унутар неке друге је дозвољено.

Напоменимо још да корисничке функције које враћају вредност морају да врате вредност. Размотримо следећу функцију

float proba(int x) { if(x< 5) return 2.0*x; }

Како је повратна вредност типа float ова функција мора вратити вредност типа float. Претпоставимо да је x=2, oнда је израз x<5 тачан па ће наредба return вратити вредност 4.0. Међутим, шта ће се десити ако је x=10? За x=10 израз x<5 није тачан па се неће извршити наредба return 2.0*x, а функција по завршетку мора вратити неку вредност, она ће то и урадити али ће вратити неку случајну вредност. Због тога наведену функцју треба написати као:

float proba(int x) { if(x< 5) return 2.0*x; return x; }

Сада је дефинисана повратна вредност за све случајеве.

Функције које не враћају вредност - void функције Void функције имају веома сличну структуру као и функције које враћају вредност. Оба наведена типа функција имају заглавље и тело функције. Void функције немају тип функције јер не враћају вредност. Због тога tip_funkcije који представља тип повратне вредности као и наредба return немају посебног значаја. Без обзира на то, у void функцијама се може користити наредба return али без израза поред ње, и обично се користи да би се напустила функција пре њеног краја. Као и код функција које враћају вредност, void функција може али не мора имати формалне аргументе.

Void функције без аргумената

Општи облик дефиниције void функције без аргумената је:

void ime_funkcije() { naredbe }

Наредбе могу бити декларативне или извршне.

Позив void функције без аргумената има следећи облик:

ime_funkcije();

Page 105: Uvod u programski jezik C Univerzitet Singidunum

10

Како наведени тип функције нема аргументе у заградама се не наводе вредности које треба проследити функцији (не заборавите да void функције могу имати аргументе). Овакве функције се обично користе за приказивање информација о пограму.

#include<stdio.h> void upozorenje(); #include<stdio.h> main( ) { float prvi; int pom; printf("Unesite broj manji od 45.00\n"); pom=0; do { scanf("%f", &prvi); if(prvi >45.0) { upozorenje(); pom=1; } else { printf("Uneti broj je manji od 45\n"); pom=0; } }while(pom !=0); } void upozorenje() { printf("_________________________________________________\n"); printf("Ponovite unos. Niste uneli odgovarajucu vrednost!\n"); printf("_________________________________________________\n"); }

Void функције са аргуменатима

Општи облик дефиниције void функције са аргументима је:

void ime_funkcije(lista formalnih argumenata) { naredbe }

Наредбе могу бити декларативне или извршне. lista formalnih argumenata Унутар малих заграда наводи се листа аргумената. Потребно је навести тип податка и његово име. Уколико постоји више аргуменатата они се међусобно раздвајају зарезом.

Page 106: Uvod u programski jezik C Univerzitet Singidunum

11

void ime_funkcije(tip_prom1 ime_prom1,tip_prom2 ime_prom2,...,tip_promN ime_promN) void ime_funkcije(int a, double b)

Позив void функције са аргумената има следећи облик:

ime_funkcije(lista stvarnih argumenata); Листа стварних аргумената може бити израз или име неке променљиве. Као и код функција које враћају вредност, код void функција стварни параметри се морају одабрати на такав начин да се нјихови типови поклапају са типовима формалних аргумената.

Прослеђивање вредности аргумената Стварни аргументи представљају везу између дела програма одакле се функција позива и саме функције која је позвана. Они омогућавају да функција при сваком позиву обрађује различите податке, тј. да сваки позив функције буде са различитим подацима.

Приликом позива функције у малим заградама се наводи листа аргумената. По начину преношења вредности, уопштено гледано, постоји две врсте аргумената :

1. Аргументи који се прослеђују преко вредности (value arguments)

2. Аргументи који се преносе помоћу адресе (reference arguments)

Kод аргумената који се преносе преко вредности, са места позивања функције се преноси копија вредности стварног аргумента у функцију. Промена вредности пренетог аргумента у функцији нема директног утицаја на вредност стварног аргумента.

Пример: float moja_funk(float c); #include<stdio.h> main( ) { float a, b; printf("Unesite broj\n"); scanf("%f", &a); printf("Vrednost promenljive pre poziva funkcije je а=%f\n", a); b=moja_funk(a); printf("Vrednost promenljive nakon poziva funkcije je а=%f\n", a); printf("Povratna vrednost funkcije je %f\n", b); } float moja_funk(float c) { c=c*c; return c+5.4; }

Page 107: Uvod u programski jezik C Univerzitet Singidunum

12

Излаз Unesite broj 6.4 Vrednost promenljive pre poziva funkcije a=6.400000 Vrednost promenljive nakon poziva funkcije a=6.400000 Povratna vrednost funkcije je 46.360001

Приметите да је у функција позвана са стварним параметром а. У тренутку позива овај параметар је имао вредност 6.4 и та вредност је пресликана у локалну променљиву с. Унутар функције променљива с је мењала вредност али се та промена није директно одразила на вредност променљиве а.

Код аргумената који се преносе помоћу адресе, промена вредности пренетог аргумента у функцији има директног утицаја на вредност стварног аргумента.

#include<stdio.h> void moja_funk(float *c); /* deklaracija funkcije*/ /* argument je pokazivac na promen. tipa float*/ main( ) { float a; printf("Unesite broj\n"); scanf("%f", &a); printf("Vrednost promenljive pre poziva funkcije a=%f\n", a); moja_funk(&a); /* prenosi se adresa promenljive a */ printf("Vrednost promenljive nakon poziva funkcije a=%f\n", a); } void moja_funk(float *c) { printf("Vrednost promenljive na ulasku u funkciju je a=%f\n", *c); *c=512.5; printf("Vrednost promenljive pre izlaska iz funkcije je a=%f\n", *c); return; } Излаз: Unesite broj 6.4 Vrednost promenljive pre poziva funkcije a=6.400000 Vrednost promenljive na ulasku u funkciju je a=6.400000 Vrednost promenljive pre izlaska iz funkcije je a=512.500000 Vrednost promenljive nakon poziva funkcije a=512.500000

Како се у овом случају прослеђује адреса променљиве, промена вредности формалног аргумента има директног утицаја на вредност стварног аргумента.

Приликом позива функције, додељује се меморијски простор за формалне аргументе функције и променљиве функције (које се зову локалне променљиве) на основу података из дефиниције функције. Подсетимо се да приликом позива функције, уколико се стварни параметри прослеђују по вредности, вредност стварних параметара се копира у меморијску ћелију одговарајућег формалног параметра. У случају преноса вредности по адреси, у функцију се преноси адреса стварног параметра. Значи да је садржај формалног параметра адреса. За време обраде података унутар функције, садржај формалног параметра указује на адресу стварног параметра и самим тим све промене које се догоде унутар функције имају директног утицаја на садржај стварног аргумента.

Page 108: Uvod u programski jezik C Univerzitet Singidunum

13

Размотримо то на примеру: #include<stdio.h>

void funJedan(int a, int *b, char v); void funDva(int *x, int y, char *w); main( ) { int num1, num2; char ch; num1=10; /* Linija 1*/ num2=15; /* Linija 2*/ ch='A'; /* Linija 3*/ printf("Linija 4: Unutar funk. main num1=%d, num2=%d, ch=%c\n",num1,num2,ch); funJedan(num1, &num2, ch); /* Linija 5*/ printf("Linija 6: Unutar funk. main num1=%d, num2=%d, ch=%c\n",num1,num2,ch); funDva(&num2, 25, &ch); /* Linija 7*/ printf("Linija 8: Unutar funk. main num1=%d, num2=%d, ch=%c\n",num1,num2,ch); }

void funJedan(int a, int *b, char v) { int jedan; jedan=a; /* Linija 9*/ a++; /* Linija 10*/ *b=(*b)*2; /* Linija 11*/ v='B'; /* Linija 12*/ printf("Linija 13: Unutar funJedan a=%d, *b=%d, v=%c\n",a,*b,v); } void funDva(int *x, int y, char *w) { (*x)++; /* Linija 14*/ y=y*2; /* Linija 15*/ *w='G'; /* Linija 16*/ printf("Linija 17: Unutar funDva *x=%d, y=%d, *w=%c\n",*x,y,*w); } Излаз: Linija 4: Unutar funk. main num1=10, num2=15, ch=A Linija 13: Unutar funJedan a=11, *b=30, v=B Linija 6: Unutar funk. main num1=10, num2=30, ch=A Linija 17: Unutar funDva *x=31, y=50, *w=G Linija 8: Unutar funk. main num1=10, num2=31, ch=G

Непосредно пре линије 1 резервише се (алоцира) меморија само за променљиве из функције main. Ta меморија није иницијализована (променљиве немају почетну вредност).

Након линије 3 извршена је иницијализација променљивих а изглед меморијског простора је као на слици 1 (адресе су одређене насумично):

Page 109: Uvod u programski jezik C Univerzitet Singidunum

14

10

15

A

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

слика 1 Линија 4 даје следећи испис

Linija 4: Unutar funk. main num1=10, num2=15, ch=A

Наредба у линији 5 је позив функције funJedan. Функција funJedan има три параметра int a, int *b, char v и једну локалну променљиву int jedan. За ова три параметра и једну локалну промељиву се при позиву функције резервише меморија. Поред тога, треба приметити да се у b, стварни параметри прослеђују преко адресе па b може да садржи само адресу прослеђеног параметара. Друга два формална параметра примају вредност стварних параметара. Након линије 5 меморија има изглед као на слици 2.

10

15

A

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

10

1430

A

.

.

.

.

.

ime: а

ime: b

ime: v

ime: jedan ??

funJedan

.

. слика 2

У линији 9, након наредбе jedan=a; настаје ситуација као на слици 3.

Page 110: Uvod u programski jezik C Univerzitet Singidunum

15

10

15

A

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

10

1430

A

.

.

.

.

.

ime: а

ime: b

ime: v

ime: jedan 10

funJedan

слика 3 Након линије 10 (а++):

10

15

A

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

11

1430

A

.

.

.

.

.

ime: а

ime: b

ime: v

ime: jedan 10

funJedan

слика 4

Након наредбе *b=(*b)*2; у линији 11 настаје следећа ситуација

10

30

A

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

11

1430

A

.

.

.

.

.

ime: а

ime: b

ime: v

ime: jedan 10

funJedan

слика 5

Page 111: Uvod u programski jezik C Univerzitet Singidunum

16

Након наредбе v='B' у линији 12

10

30

A

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

11

1430

B

.

.

.

.

.

ime: а

ime: b

ime: v

ime: jedan 10

funJedan

слика 6 Линија 13 даје следећи излаз

Linija 13: Unutar funJedan a=11, *b=30, v=B

а након извршавања наредбе у линији 13 напушта се функција funJedan и програм наставља да се извршава у линији 6 main функције. Овде је меморија резервисана само за променљиве из функције main! Meморијски простор који је био резервисан за променљиве из функције funJedan се аутоматски ослобађа након напуштања функције.

10

30

A

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

слика 7

Линија 6 даје следећи испис

Linija 6: Unutar funk. main num1=10, num2=30, ch=A

Наредба у линији 7 је позив функције funDva. Функција funDva има три параметра: x, y и w. Овде x, и w примају вредности преко адресе стварних параметара а y преко вредности стварног параметра. Пре извршавања линије 14 имамо следећу ситуацију:

Page 112: Uvod u programski jezik C Univerzitet Singidunum

17

10

30

A

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

1430

25

1620

.

.

.

.

.

ime: x

ime: y

ime: w

funDva

слика 8

Након извршавања наредбе у линији 14, (*x)++

10

31

A

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

1430

25

1620

.

.

.

.

.

ime: x

ime: y

ime: w

funDva

слика 9 Приметите да ова наредба мења вредност променљиве num2. Наредба у линији 15 (y=y*2) даје следеће

10

31

A

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

1430

50

1620

.

.

.

.

.

ime: x

ime: y

ime: w

funDva

слика 10

Page 113: Uvod u programski jezik C Univerzitet Singidunum

18

Након линије 16 (*w='G')

10

31

G

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

1430

50

1620

.

.

.

.

.

ime: x

ime: y

ime: w

funDva

слика 11 Линија 17 даје следећи испис

Linija 17: Unutar funDva *x=31, y=50, *w=G

Након завршетка наредбе у линији 17 напушта се функциjа funDva и враћа се у линију 8 функције main. Меморијски простор који је био резервисан за формалне аргументе функције funDva се ослобађа. Меморијски простор је сада резервисан само за променљиве функције main

10

31

G

.

.

.

.

.

.

.

.

.

main

ime: num2

ime: ch

ime: num1

adresa:1430

adresa:1200

adresa:1620

слика 12 Линија 8 даје следећи испис

Linija 8: Unutar funk. main num1=10, num2=31, ch=G

Након линије 8 се прекида извршавање програма и ослобађа меморијски простор.

Page 114: Uvod u programski jezik C Univerzitet Singidunum

19

Рекурзивне функције Програми о којима је до сада било речи су написани у облику функција. Функције се међусобно могу позивати. За решавање неких проблема је корисно да постоје функције које могу да позову саме себе. Овакве функције се називају рекурзивне функције. Проблем рекурзивних функција је по својој природи вема сложен, а овде ће бити представљени само основни принципи. У основи, рекурзивне функције „знају” да реше неки једноставни проблем. Уколико се пред њих постави сложен проблем, исте природе, ове функције позивају семе себе са поједностављемим проблем, све док се позив не сведе на онај које знају да реше. Размотримо то на примеру рачунања факторијела.

Факторијел не негативног броја n које се обележава као n! је производ: n! =n*(n-1)*(n-2)*(n-3)*...*1

стим да је 1! = 1 0! = 1

На пример: 5! = 5*4*3*2*1= 120

Факторијел броја broj се може израчунати применом for петље:

faktorijel = 1; for(i=broj; i>=1; i--) { faktorijel *= i; /* faktorijel = faktorijel * i; */ } али се може добити и применом рекурзивне функције: Рекурзивна дефиниција факторијела се може извести из следећих релација:

5! = 5*4*3*2*1= 5*(4*3*2*1) = 5*(4!) 4!= 4*3*2*1=4*(3*2*1) = 4*(3!) 3! = 3*2*1=3*(2*1) = 3*(2!) 2! = 2*1=2*(1) = 2*(1!) 1! = 1 ...

Нека је дефинисана функција long faktorijel(long broj) { /* ako je broj 0 ili 1, 0!=1 i 1!=1 */ if( broj <= 1 ) { return 1; /* vrati 1 i zavrsi rada sa funkcijom */ } /* kraj if */ else /* jedan korak rekurzije */ { return (broj * faktorijel(broj - 1) ); /* rekurzivni poziv funkcije */ } /* kraj else */ } /* kraj funkcije faktorijel */ Ако се функцији faktorijel проследи позив са аргументом 0 или 1, онда ће функција одмах faktorijel вратити вредност 1 (наредба return 1), тј. функција „зна” да реши проблем, у противном (позив функције

Page 115: Uvod u programski jezik C Univerzitet Singidunum

20

са faktorijel(n), где је n ≠ 0 или n ≠ 1) ће функција поједноставити проблем и поново позвати саму себе на поједностављен начин (позив функције са faktorijel(n-1)), и тако све док се не дође до случаја који је решив, тј. док се не дође до случаја n=1.

Нека је позвана функција faktorijel(4). Toк извршавања функције можете пратити на следећој слици

broj=4 зато што je broj>1 return (4 * faktorijel(3));

faktorijel(4)

broj=3 зато што je broj>1 return (3 * faktorijel(2));

faktorijel(3)

broj=2 зато што je broj>1 return (2 * faktorijel(1));

faktorijel(2)

broj=1 зато што je broj==1 return 1;

faktorijel(1)

return 1

return 2*1

return 3*2

return 4*6

faktorijel(4)) = 24

слика 13

Page 116: Uvod u programski jezik C Univerzitet Singidunum

21

Стрелице надоле представљају сукцесивне позиве функције, а стрелице нагоре повратне вредности позване функције.

− Можете посматрати рекурзивну функцију као да имате неограничени број копија те функције

− Сваки позив рекурзивне функције (рекурзивни позив) има свој сопствени кôд ,своје аргументе (параметре) и своје локалне променљиве.

− Након што се изврши последња рекурзивна функција, контрола се враћа оној функцији која је упутила позив те рекурзивне функције.

Глобалне променљиве (идентификатори) Променљиве (или у општем случају идентификатори) дефинисане на почетку тела функције имају блоковски досег и локални су за ту функцију. То значи да тим променљивама може да се приступи само унутар функције. Другим функцијама ове променљиве нису приступачне, тј. оне их „не виде".

У складу са тим, дозвољена је употреба истог идентификатора у већем броју функција и ти идентификатори су међусобно независни.

Пример:

float mojaFun(int x);

main() { int a, b; float c ... a=4; c=mojaFun(a); } float mojaFun(int x) { int c double a float b ... return b; }

У претходном примеру је показано да у две функције mojaFun и main могу да постоје променљиве истог имена. Обзиром да су, у овом случају, све променљиве локалне оне су видљиве само у оквиру својих функција.

Међутим, постоје променљиве (идентификатори) које су заједничке за већи број функција. Такве променљиве (идентификатори) се називају глобалне променљиве (глобални идентификатори). Да би променљива (идентификатор) била глобална потребно је да се дефинише изван било које функције.

Досег глобалних идентификатора протеже се од места где је декларисан до краја фајла у ком се налази та наредба.

Глобалне променљиве су доступне из свих функција које се налазе иза њихове дефиниције. То значи да се подаци из једне функције могу прослеђивати у другу функцију не само посредством стварних параметара (позивом функције) већ и преко глобалних променљивих. Међутим, на тај начин функција престаје да буде независни модул, тј. њена употреба ван тог програма постаје попрлилично сложена.

Глобалне променљиве су трајне, тј. постоје док год траје програм.

Page 117: Uvod u programski jezik C Univerzitet Singidunum

22

Пример:

#include<stdio.h> void funOne(int *a,int *b); int t; /* Globalna promenljiva*/ main( ) { int num1, num2; num1=10; /* L1 */ num2=20; /* L2 */ t=15; /* L3 */ printf("L 4: Funk. main: num1=%d, num2=%d, t=%d\n",num1,num2,t); /* L4 */ funOne(&num1,&t); /* L5 */ printf("L 6: Funk. main posle funOne: num1=%d, num2=%d, t=%d\n",num1,num2,t); } void funOne(int *a, int *x) { int z; z=*a + *x; /* L7 */ printf("L 8: FunOne a=%d, x=%d, z=%d, t=%d\n",*a,*x,z,t); /* L8 */ *x=*x+5; /* L9 */ printf("L 10: FunOne a=%d, x=%d, z=%d, t=%d\n",*a,*x,z,t); /* L10 */ *a=*a+12; /* L11 */ printf("L 12: FunOne a=%d, x=%d, z=%d, t=%d\n",*a,*x,z,t); /* L12 */ t=t+13; /* L13 */ printf("L 14: FunOne a=%d, x=%d, z=%d, t=%d\n",*a,*x,z,t); /* L14 */ }

Излаз:

Linija 4: Unutar funk. main: num1=10, num2=20, t=15

Linija 8: Unutar funOne a=10, x=15, z=25, t=15

Linija 10: Unutar funOne a=10, x=20, z=25, t=20

Linija 12: Unutar funOne a=22, x=20, z=25, t=20

Linija 14: Unutar funOne a=22, x=33, z=25, t=33

Linija 6: Unutar funk. main posle funOne: num1=22, num2=20, t=33

Програм из претходног примера има променљиву t која је декларисана пре дефиниције било које функције. Ова променљива је због места дефинсања глобална, па јој се може приступити са било ког места у програму. Функција funOne има два формална параметра и оба се прослеђују по адреси.

У линији 5, функције main, позива се функција funOne и прослеђују јој се стварни параметри num1 и t преко својих адреса. Тако формални параметар а, функције funOne, прима адресу од променљиве num1 а формални параметар x, адресу променљиве t. Било која промена вредности променљивих а и x се због

Page 118: Uvod u programski jezik C Univerzitet Singidunum

23

тога директно одражава на промену вредности променљивих num1 и t. Променљива t је глобална, па она није морала бити прослеђена путем параметара већ јој се могло приступити и директно што је и урађено у линији 13.

Досег идентификатора

Мада је о досегу идентификатора већ било речи, увођењем појма глобалних идентификатора могу се прецизније објаснити:

1. Локални идентификатори: Идентификатор декларисани унутар функције (или блока). Локалним идентификаторима се не може приступити изван функције (блока)

2. Глобални идентификатори: Идентификатори декларисани ван било које функције. Глобалним идентификаторима се може приступити из било које функције ако:

a. Идентификатор је декларисан пре функције,

b. Име функције је различито од идентификатора,

c. Сви аргументи функције имају другачије име од глобалног идентификатора,

d. Сви локални идентификатори (у функцији) имају другачије име од глобалног идентификатора.

За програме који се састоје од више функција, није уобичајено да цео програм буде у једном фајлу. Изворни кôд функција или група сродних функција обично се смештају у посебне фајлове. На тај начин се кôд за један програм може наћи у више фајлова, а скуп свих фајлова се назива пројекат. Сваки фајл унутар пројекта се преводи независно. Након успешног превођења свих потребних фајлова врши се повезивање (linking) преведених фајлова у извршни програм.

Глобални идентификатори могу имати досег на цео програм односно пројекат. За њих се каже да имају спољашње повезивање, односно да означавају исти податак у свим фајловима пројекта.

Како се сваки фајл преводи независно у сваком од њих треба да постоји бар декларација сваког глобалног податка или функције која ће се у њему користити. Ако пре првог коришћења податка не постоји ни декларација ни дефиниција биће пријављена грешка.

У тачно једном од фајлова пројекта мора да се налази дефиниција за сваки глобални податак и за сваку функцију, ради додељивања меморијског простора за њих. У сваком фајлу у ком се користи глобална променљива која је дефинисана у другом фајлу мора постојати тзв. екстерн (extern) декларација.

Page 119: Uvod u programski jezik C Univerzitet Singidunum

24

Пример:

fajl1.c _ #include <stdio.h> extern int brojac; /* deklaracija promenljive koja je definisana kao globalna promenljiva u dugom fajlu */ int rezultat=2; /* definicija globalne promenljive */ void funkcija_1(); /* deklaracija (prototip) funkcije */ int main() /* pocetak funkcije main */ { int i; i=4; printf("M3: brojac=%d, rezultat=%d\n", brojac, rezultat); rezultat++; brojac=rezultat*i; printf("M6: brojac=%d, rezultat=%d\n", brojac, rezultat); funkcija_1(); /* poziv funkcije */ printf("M8: brojac=%d, rezultat=%d\n", brojac, rezultat); return 0; } fajl2.c _ #include <stdio.h> extern int rezultat; /* deklaracija promenljive koja je definisana kao globalna promenljiva u dugom fajlu */ int brojac=0; /* definicija i inicijalizacija globalne promenljive */ /* definicija funkcije */ void funkcija_1() { printf("F1: brojac=%d, rezultat=%d\n", brojac, rezultat); rezultat=5; brojac=1; printf("F4: brojac=%d, rezultat=%d\n", brojac, rezultat); return; } /* kraj funkcije */

У претходном примеру постоје два фајла, која се одвојено преводе и затим повезују у један извршни програм. Променљива brojac је дефинисана као глобална променљива у fajl2 а променљива rezultat је дефинисана као глобална променљива у fajl1. Kaко се поменуте променљиве користе у оба фајла као глобалне да би се то нагласило користи се кључна реч extern. Ова кључна реч означава преводиоцу да је променљива дефинисана у неком другом фајлу. Глобална променљива се дефинише само на једном месту у пројекту (без наредбе extern) и ту се за њу резервише меморијски простор. Декларација глобалне промељиве се појављује у сваком фајлу у ком се глобална променљива користи уз употребу кључне речи extern (нпр. extern int rezultat;). При декларацији се не резервише меморијски простор.

Page 120: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C (C programming Language)

Лекција 8

Садржај ППррееттппррооццеессооррссккее ннааррееддббее .......................................................................................2 Уметање садржаја фајла ( наредба #include) .......................................................................2 Замена лексичких симбола (наредба #define) ......................................................................3 Дефинисање симболичких константи ................................................................................3 Дефинисање макро-а ..........................................................................................................5

Условно превођење (наредба #if, #ifdef, ...) .................................................................6 Постојеће силмбпличке константе .......................................................................................10

Page 121: Uvod u programski jezik C Univerzitet Singidunum

2

ППррееттппррооццеессооррссккее ннааррееддббее Претпроцесорске наредбе не представљају део програмског језика C већ посебан алат. Претпроцесор је део преводиоца који треба да изврши предобраду кôда. Он у суштини обавља одговарајуће трансформације текста којима се добија коначан облик изворног кôда који се након тога преводи. Те трансформације текста обухватају:

• Уметање садржаја неког фајла на одређено место у изворном кôду,

• Замену лексичких симбола (низова знакова) новим низовима симбола

• Условно укључивање или изостављање кôда при превођењу.

Свака предпроцесорска наредба (директива) се пише у засебном реду и почиње са знаком #.

Почетак директиве не мора бити у првој позицији реда али испред ње не могу бити друге наредбе.

Поред тога, у једном реду може да буде само једна директива.

Пре превођења изворног кôда, преводилац програмског језика позива претпроцесор који претражује изворни кôда и на основу пронађених претпроцесорских наредби замењује текст у изворном кôду.

Како претпроцесорске наредбе не припадају директно програмском језику, њихова синтакса се не мора подударати са синтаксним правилима која важе у програмском језику C.

Претпроцесорске наредбе се не завршавају знаком „ ; ” као што је то уобичајено за остале наредбе.

Уз то, област важења ових наредби се одређује на посебан начин. Претпроцесорске наредбе могу да се уведу на било ком месту у програму. Њихово дејство се не протеже на цео изворни кôд, већ само на део од њиховог појављивања па надаље док се посебном наредбом не укину или до краја програма.

Неке од важнијих претпроцесорских наредби које препоручује АNSI стандард су следеће: #include #define #if #ifdef #ifndef #elif #else #endif #undef

Уметање садржаја фајла ( наредба #include) Уметање (дописивање) садржаја неког фајла у текући фајл се постиже директивом која има следећи облик:

#include ″ime fajla″ #include <ime fajla>

Уколико се име фајла стави између знакова навода (први пример), претпроцесор прво покушава да наведени фајл пронађе у фолдеру у ком се налази и фајл са изворним кôдом. Уколико у томе не успе или је име фајла стављено између знакова < и > (други пример) претрага се наставља по унапред дефинисаним (системским) фолдерима.

Тражени фајл може и сам да садржи наредбу #include.

Уобичајено је да екстензија имена фајла буде .h ( од header) мада то није обавеза. Уз програмски језик C кориснику се испоручује већи број библиотечких функција које су, у зависности од намене, разврстане у

Page 122: Uvod u programski jezik C Univerzitet Singidunum

3

неколико група. Прототипови тих функција из појединих група налазе се у заглављима (stdio.h, math.h string.h ...). Та заглавља често садрже и дефиниције одређених константи.

Програмер може да направи своје фајлове заглавља у којима се, између осталог, могу налазити прототипови функција, дефиниције, декларације глобалних података (extern) и сл. Ова заглавља се најчешће налазе у истом фолдеру где и изворни кôд па се име фајла у наредби #include ставља између знакова навода.

Заглавља фајлова не треба да садрже дефиниције функција или глобалних променљивих. Уколико постоји потреба, дефиниције и глобалне промељиве се могу издвојити у засебан фајл али он треба да буде класичан фајл са кôдом (и екстензијом .c).

Замена лексичких симбола (наредба #define) Претпроцесорска наредба #define се користи за дефинисање симболичких константи и макро-а.

Дефинисање симболичких константи Општи облик нардбе за дефинисање симболичке константе је:

#define SIMBOLICKA_KONSTANTA ZnakovniNiz Пример:

#define PI 3.1415926 #define NASLOV "Programski jezik C" #define ZAUVEK for( ; ;)

Претпроцесор претражује изворни кôд и након појављивања наредбе облика: #define SIMBOLICKA_KONSTANTA ZnakovniNiz у кôду се свако појављивање SIMBOLICKA_KONSTANTA замењује са дефинисаним ZnakovniNiz. Уколико се име симболичке константе (SIMBOLICKA_KONSTANTA) појављује у коментару или унутар неког другог знакованог низа неће доћи до замене.

Пример: #include<stdio.h> #define PI 3.1415926 main() { float a; float r=4.23; char ch[ ]="PILICI"; /* slogovi PI LI CI */ /* L3 */ a=2*r*PI; /* L5 */ printf("Niz ch je %s a obim a je %f\n ",ch,a); } Излаз Niz ch je PILICI a obim a je 26.577874

У линији 3 низ знакова неће бити замењен наредбом #define. Наиме, мада се у оквиру вреднисти (PILICI) која се додељује знаковном низу ch налази дефинисана симболичка константа (PI) она неће бити замењена знаковним низом (3.1415926), нити ће то бити учињено унутар коментара, у истој линији. У линији 5 ће се пре превођења програма симболичка константа (PI) заменити са (3.1415926).

Page 123: Uvod u programski jezik C Univerzitet Singidunum

4

Уобичајено је да се симболичка константа пише искључиво великим словима да би се скренула пажња програмеру или оном ко чита програм да ће пре превођења доћи до одговарајуће замене.

Пример: #include <stdio.h> #define WIDTH 80 /* L1 */ #define LENGTH ( WIDTH + 10 ) /* L2 */ main() { int var; var = LENGTH * 20; /* var = ( 80 + 10 ) * 20; */ printf("var=%d\n",var)... } var=1800

Уочите да је у линији 2 употребљена симболичка (WIDTH) константа која дефинисана у линији 1. Пример:

#include <stdio.h> #define WIDTH 80 main() { int x=2,y; y=WIDTH*x; printf("Vrednost y=%d\n",y); } Vrednost y=160

Замењивање дефинисаног знаковног низа одређеном симболичком константом може бити укинуто наредбом:

#undef SIMBOLICKA_KONSTANTA

У наредном примеру постоји покушај да се након наредбе #undef променљивој придружи вредност идентификатора који више није дефинисан, што резултује грешком.

include <stdio.h> #define WIDTH 80 main() { int x=2,y; y=WIDTH*x; printf("Vrednost y=%d\n",y); #undef WIDTH y=WIDTH; /* GRESKA! 'WIDTH': undeclared identifier */ }

Page 124: Uvod u programski jezik C Univerzitet Singidunum

5

Дефинисање макро-а Општи облик наредбе за дефинисање макро-а је:

#define IME(param_a, param_b,...) Lista_znakovnih _nizova где IME представља име макро-а, param_a, param_b,... су формални аргументи макроа, а Lista_znakovnih _nizova представља један или више занаковних низова којима се замењује свако појављивање имена макро-а у изворном програму.

Макро-и су веома слични функцијама, међутим начин њиховог коришћења и извршавања је различит. Они се користе када желимо да представимо један мали програмски део, за који не вреди писати посебну функцију. Макро-и се замењују пре превођења програма док функције постоје и за време извршавања програма.

Пример: #include<stdio.h> #define MAX(a, b) a>b ? a : b main() { int broj_a, broj_b; printf("Odredjivanje maksimuma dva broja\n\n"); printf("Upisite dva broja razdvojena zarezom\n"); scanf("%d,%d",&broj_a,&broj_b); printf("Veci broj je: %d\n",MAX(broj_a,broj_b)); } Излаз Odredjivanje maksimuma dva broja Upisite dva broja razdvojena zarezom 4,9 Veci broj je: 9

Листу знаковних низова код дефинисања макро-а је препоручљиво писати у заградама. Следећи пример може да оправда ову препоруку.

Пример: #include<stdio.h> #define KVADRAT(a) a*a main() { int broj_a; broj_a=8; printf("\n%d * %d = %d\n",broj_a,broj_a,KVADRAT(broj_a)); printf("\n%d * %d = %d\n",broj_a+1,broj_a+1,KVADRAT(broj_a+1)); } Излаз 8 * 8 = 64 9 * 9 = 17

Други део резултата је очигледно погрешан. Разлог је што знаковни низ није дат у заградама, због чега се израчунава следећи израз: 8+1*8+1, што јесте 17. Да би програм био исправан требало је #define наредбу писати као:

#define KVADRAT(a) ((a)*(a))

Page 125: Uvod u programski jezik C Univerzitet Singidunum

6

Условно превођење (наредба #if, #ifdef, ...) Наредбама условног превођења може се постићи да се делови изворног кôда (укључујући и заглавља или header-е) не преводе, ако су испуњени одређени услови. Помоћу ових наредби се може постићи да се направи одговарајућа универзална структура кôда за различите намене.

На пример, код писања сложенијих програма могу се за потребе тестирања увести различити исписи (printf наредбе), који нису потребни у коначној верзији програма. Могуће је писати две верзије програма или само једну уз употребу условног превођења.

Основна наредба за почетак условног превођења је: #if uslov

Oвде uslov представља целобројни константни израз. Ако услов није испуњен тј. логичка вредност израза uslov је једнака нула, део кôда који следи иза наредбе #if uslov неће бити преведен.

У изразу који чини uslov могу да се користе сви уобичајени оператори али и посебан оператор define чији једини оператор може да буде неки идентификатор.

Пример: #define VERZIJA 1.1 #define TESTIRANJE

У другом примеру је дефинисана вредност TESTIRANJE али јој није додељен знаковни низ, што је дозвољено и значи да се симболичка константа мења једним белим знаком (размак или space). Mеђутим оваква употреба наредбе #define се може користити и на следећи начин:

Вредност израза #ifdef TESTIRANJE

je логичка јединица ("1") уколико је претходила наредба #define TESTIRANJE односно логичка нула ("0") уколико није претходила наредба #define TESTIRANJE или је ова наредба претходила али је после ње следила наредба #undef TESTIRANJE. Наредба #ifdef TESTIRANJE има заначење "Ако ЈЕ ДЕФИНИСАН израз TESTIRANJE онда преведи део кôда који следи". Веома сличну али супротну намену има претпроцесорска наредба:

#ifndef TESTIRANJE

Она има значење "Ако НИЈЕ ДЕФИНИСАН израз TESTIRANJE онда преведи део кôда који следи"

После почетка условног превођења (наредбe #if, #ifdef или #ifndef) може (не мора) да следи једна или више претпроцесорских наредби #еlif uslov односно једна претпроцесорска наредба #еlse које се, уколико постоје, завршавају наредбом #еndif. Значење ових наредби је еквивалентно наредбама еlse if односно еlse у програмском језизику C.

Приликом употребе наредби #if, #ifdef или #ifndef мора се придржавати следећих правила. Свака #if наредба се мора завршити наредбом #еndif. Између ове две наредбе може постојати произвољан број #еlif наредби али је дозвољено да постоји највише једна #еlse наредба. Уколико постоји #еlse наредба она мора бити последња претпроцесорска наредба пре наредбе #еndif.

Page 126: Uvod u programski jezik C Univerzitet Singidunum

7

Пример:

#include<stdio.h>

#define TST #define DAN 2 main() { #ifdef TST int a=5; #endif #if (DAN==1) printf("Ponedeljak\n"); #elif (DAN==2) printf("Utorak\n"); #else printf("Nije ni ponedeljak ni utorak\n"); #endif #ifdef TST a=a+7; printf("Vrednost a= %d\n",a); #endif printf("DAN je %d\n",DAN); } Излаз Utorak Vrednost a= 12 DAN je 2 Обзиром да је дефинисана симболичка константа TST (наредба #define TST) биће преведене линија 2, линија 12 и линија 13. Симболичкој константи DAN је додељена вредност 2, па ће бити извршена наредба: printf("Utorak\n");

Page 127: Uvod u programski jezik C Univerzitet Singidunum

8

У наредном примеру је постављена друга вредност за идентификатор (симболичку константу) DAN па ће се у складу са вредношћу тог идентификатора превести одговарајући део кода

#include<stdio.h> #define TST #define DAN 5 main() { #ifdef TST int a=5; #endif #if (DAN==1) printf("Ponedeljak\n"); #elif (DAN==2) printf("Utorak\n"); #else printf("Nije ni ponedeljak ni utorak\n"); #endif #ifdef TST a=a+7; printf("Vrednost a= %d\n",a); #endif printf("DAN je %d\n",DAN); } Излаз Nije ni ponedeljak ni utorak Vrednost a= 12 DAN je 5

Page 128: Uvod u programski jezik C Univerzitet Singidunum

9

У наредном примеру није дефинисана вредност TST па део кода: #ifdef TST int a=5; #endif

као ни #ifdef TST a=a+7; printf("Vrednost a= %d\n",a); #endif

неће бити преведен

Пример: #include<stdio.h> #define DAN 2 main() { #ifdef TST int a=5; #endif #if (DAN==1) printf("Ponedeljak\n"); #elif (DAN==2) printf("Utorak\n"); #else printf("Nije ni ponedeljak ni utorak\n"); #endif #ifdef TST a=a+7; printf("Vrednost a= %d\n",a); #endif printf("DAN je %d\n",DAN); } Излаз Utorak DAN je 2

Page 129: Uvod u programski jezik C Univerzitet Singidunum

10

Постојеће силмбпличке константе ANSI C има неколико унапред дефинисаних симболичких константи (видети наредну табелу). Идентификатори (имена) саваке од тих константи почињу и завршавају се са две доње цртице (underscores). Ти идентификатори се не могу користити у #defined и #undefined директивама.

симболичка константа Објашњење

__LINE__ Број линије текућег кôда (целобројна константа)

__FILE__ Комплетна путања и име фајала у ком се налази кôд. (стринг) Може се десити да се добије само име фајла.

__DATE__ Датум када је компајлиран кôд (стринг облика Mmm dd yyyy, где је Mmm низ од три слова која представљају месец Apr Sep и сл, dd дан у месецу а yyyy година)

__TIME__ Време када је кôд компајлиран (стринг облика hh:mm:ss )

Пример:

Напишите програм који исписује вредности постојећих симболичких константи из претходне табеле.

#include <stdio.h> int main() { printf("Upotreba konstante __LINE__\n"); printf("\tTrenutno se nalazim u %d liniji koda\n", __LINE__); printf("\nUpotreba konstante __DATE__\n"); printf("\tDatum prevodjenja programa je: %s\n",__DATE__ ); printf("\nUpotreba konstante __TIME__\n"); printf("\tVreme prevodjennja programa je: %s\n",__TIME__ ); printf("\nUpotreba konstante __FILE__\n"); printf("\tProgram je zapisan u fajlu %s\n",__FILE__ ); return 0; }

Page 130: Uvod u programski jezik C Univerzitet Singidunum

11

Напишите програм који дефинише макро са једним аргументом и рачуна запремину лопте. Програм треба да израчуна запремину лопте за полупречнике од једног до десет метара и да испише резултате у табеларном облику. Формула за рачунање запремине лопте је:

3(4.0 / 3) , 3.14159rπ π = #include <stdio.h> #define PI 3.14159 #define ZAPREMINA_LOPTE(r) (4.0/3)*PI*(r)*(r)*(r) int main() { float rezultat; intpprecnik; printf("plouprecnik Zapremina lopte\n"); printf("________________________________\n"); for(pprecnik=1;pprecnik<11;pprecnik++) { rezultat=ZAPREMINA_LOPTE(pprecnik); printf("%11d\t%9.2f\n",pprecnik, rezultat); } printf("________________________________\n\n"); return 0; }

Page 131: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C (C programming Language)

Лекција 9

Садржај

ССттррууккттууррее ............................................................................................................................2 Операције над структурама ....................................................................................................5 Приступање појединим елементима структуре .................................................................5

Иницијализација стрктура .......................................................................................................8 Динамичке структуре података.............................................................................................10

Page 132: Uvod u programski jezik C Univerzitet Singidunum

2

ССттррууккттууррее У лекцији 4 jе било речи о низовима, сложеном типу података чији сви елементи имају исти тип. У овој лекцији су престављене структуре. Структура представља сложени тип података, који може да садржи елементе различитих типова. Поред тога, структура се сматра типом податка који дефинише програмер.

Структура повезује више података различитих типова у једну целину. Претпоставимо да треба да се напише програм који обрађује податке студената. Картон једног студента садржи, између осталог, његово име и презиме, годину рођења, годину студија и просечну оцену. Међутим, већина од тих подтака је различитог типа и због тога не може да се користи низ за груписање свих података у једну целину. Програмски језик C нуди сложени тип података који се зове структура и има могућност да групише податке различитих типова.

Општа синтакса структуре је следећа: struct ime_strukture { tip_podatka ime_podatka1; tip_podatka ime_podatka2; . . . tip_podatka ime_podatkaN; };

Реч struct је резервисана реч, ime_strukture је име дефинисане структуре и додељује се према правилима која важе за све идентификаторе. Поред тога, обавезно је да се на крају дефиниције структуре стави ";" јер је то део синтаксе.

Пример једне структуре би били неки подаци који се налазе у картону једног студента. Таква структура би могла да има следећи изглед:

struct student { char prezime[20]; char ime[20]; char dat_rodj[8]; int god_studija; float prosecna_ocena; };

Резервисана реч struct означава дати тип података као структуру и она се мора писати на почетку дефиниције структуре.

Свака структура се састоји из кључне речи struct, имена структуре (у претходном примеру student) и листе декларације података који се пишу између витичастих заграда. Овом дефиницијом се не додељује никакав меморијски простор, већ она представља само прототип типа податка који ће се касније користити у програму.

Када се тип податка једном дефинише могуће је дефинисати променљиву тог типа. Скреће се пажња да само име структуре (нпр. student) нема статус идентификатора структуре па се испред њега мора увек писати резевисана реч struct.

Пример дефиниције променљиве типа структуре:

struct student stud_pod; struct student stud_novi;

Oва дефиниција обавештава преводилац да резервише меморијски простор за променљиве stud_pod и stud_novi које садрже податке дате дефиницијом структуре student.

Page 133: Uvod u programski jezik C Univerzitet Singidunum

3

Претходним наредбама се резервише се меморија за обе структуре као што је приказано на слици 1.

stud_pod ?

? dat_rodj ?

?

ime

prezime

god_studija

Prosecna_ocena ?

?

?

stud_novi

dat_rodj ?

?

ime

prezime

god_studija

Prosecna_ocena ?

слика 1

Приметите да обе променљиве stud_pod и stud_novi имају елементе са истим именима. То је могуће зато што променљиве које су елементи структуре имају досег само унутар структуре. Вредности елемената нису унапред познате јер им није додељена почетна вредност.

Променљива која има тип структуре се може дефинисати заједно са структуром:

struct student { char prezime[20]; char ime[20]; char dat_rodj[8]; int god_studija; float prosecna_ocena; } stud_pod;

На овај начин се истовремено описује облик структуре и резервише меморијски простор за променљиву типа student која се зове stud_pod.

Могуће је истовремено дефинисати више променљивих на следећи начин:

struct student { char prezime[20]; char ime[20]; char dat_rodj[8]; int god_studija; float prosecna_ocena; } stud_pod, stud_novi;

Елементи једне структуре могу бити сви типови података који се користе у програмском језику C, осим функција. То истовремено значи да се унутар структура може појавити и друга структура.

Page 134: Uvod u programski jezik C Univerzitet Singidunum

4

Пример:

struct datum { int dan; int mesec; int godina; };

struct radnik { char prezime[20]; char ime[20]; struct datum datum_rodjenja; struct datum datum_namestenja; } Novi_zaposleni;

novi_zaposleni

?

?

datum_rodjenja

ime

prezime

datum_namestenja

dan

mesec

godina

?

?

?

dan

mesec

godina

?

?

?

слика 2 У претходном примеру постоје две структуре: datum и radnik. Структура radnik садржи структуру datum два пута. Структура datum се користи означавање датума рођења и датума ступања на посао.

Програмски језик C не дозвољава да структура садржи саму себе!

Посебан случај представљају неименоване структуре:

struct { int dan; int mesec; int godina; } datum_rodjenja, datum_namestenja;

Неименоване структуре је могуће користити када се истовремено врши и дефиниција свих променљивих типа те структуре (у претходном примеру datum_rodjenja, datum_namestenja). Дефиниција

Page 135: Uvod u programski jezik C Univerzitet Singidunum

5

неименоване структуре без истовремене дефиниције променљивих нема смисла, јер се таква структура не може користити.

Стуктуре могу бити елементи низова. Користећи структуру student коју смо раније декларисали могуће је дефинисати низ који се зове st_prve_godine а чији ће елементи (њих 250) бити структуре:

struct student st_prve_godine[250]

Овом дефиницијом се дефинише низ st_prve_godine који има 250 елемената (st_prve_godine[0],..., st_prve_godine[249]) и сваки елемент низа је представљен структуром student.

[1]

[2]

[3]

[248]

[249]

st_prve_godine

student

dat_rodj

ime

prezime

god_studija

Prosecna_ocena

.

.

.

слика 3

Дефиниције структуре се обично постављају изван било које функције (као што је случај код глобалних променљивих) па је тип дефинисане структуре доступан кôду који следи након дефинисања те структуре.

Када се једном дефинише тип структуре он се може користити као било који други тип (int, char, float...).

Операције над структурама Програмски језик C подржава релативно мали број операција над структурама:

− Приступање појединим елементима структуре применом оператора ”.” и ”->” − Oдређивање адресе структуре коришћењем адресног оператора & − Одређивање величине структуре применом оператора sizeof − Додељивање вредности свих елемената једне структуре елементима друге структуре истог типа.

Приступање појединим елементима структуре Приступање појединим елементима структуре се може извршити на два начина:

− Директним приступањем имену елемената структуре. Директно приступање се врши применом оператора ”.”

− Индиректно, преко показивача. Индиректно приступање се врши применом оператора ”->”

Page 136: Uvod u programski jezik C Univerzitet Singidunum

6

Примери директног приступања елементима структуре:

struct student { char prezime[20]; char ime[20]; char dat_rodj[8]; int god_studija; float prosecna_ocena; }; struct datum { int dan; int mesec; int godina; }; main() { struct student stud_novi;

struct datum datum_rodjenja; ... stud_novi.prezime[0]='B'; stud_novi.god_studija=4; sud_novi.prosecna_ocena=7.82; ... datum_rodjenja.dan=14; datum_rodjenja.godina=1983; ... }

У горњем примеру је првом знаку (индекс 0) знаковног низа prezime који је елемент структуре stud_novi додељена вредност 'B'. Следећом наредбом је променљивој god_studija која је елемент структуре stud_novi додељена вредност 4 итд.

За објашњење индиректног приступа елементима структуре, неопходно је прво објаснити везу између показивача и структура.

Као што је већ познато, показивачи могу указивати на било који тип података па и на податке типа структура.

На једном програмском примеру ће бити показано какве могућности пружају показивачи који указују на структуре.

Page 137: Uvod u programski jezik C Univerzitet Singidunum

7

Пример:

struct datum { int dan; int mesec; int godina; }; main() { struct datum datum_rodjenja, struct datum *point_struk_datum; point_struk_datum= &datum_rodjenja; point_struk_datum->dan=27; point_struk_datum->mesec=6; point_struk_datum->godina=1952; printf(”\nMesec rodjenja je %d \n”, datum_rodjenja.mesec); }

Излаз:

Mesec rodjenja je 6

У горњем примеру је дефинисан тип податка структура са именом datum а потом је дефинсана променљива datum_rodjenja (struct datum datum_rodjenja) која има тип структуре datum. Такође је дефинисан показивач point_struk_datum који указује на податак типа структура datum.

Да би се показивач point_struk_datum могао користити мора му се доделити адреса неке структуре. У програму се то постиже наредбом:

point_struk_datum= &datum_rodjenja;

Наредбом

point_struk_datum->dan=27;

ће елементу dan структуре datum_rodjenja бити додељена вредност 27.

Наредбе са оператором “->” је на веома једноставан начин могуће трансформисати у еквивалентне наредбе са знаком “.” и обрнуто. На пример, наредба

point_struk_datum->dan=27;

се може писати и у облику:

(*point_struk_datum).dan=27;

Писање израза point_struk_datum у малим заградама је у овом случају неопходно, јер оператор “.” има већи приоритет од оператора “*”.

Такође је битно приметити да додавање јединице показивачу који показује на неку структуру (point_struk_datum+1 или point_struk_datum++) значи повећање адресе на коју указује, за дужину целе структуре. То значи да инкремент садржаја показивача на структуру има смисла смо ако показивач указује на неку структуру која је елемент низа структура.

Page 138: Uvod u programski jezik C Univerzitet Singidunum

8

Иницијализација структура Иницијализација структура се може извршити на два начина:

− Директно код дефинисања структуре − Приликом дефиниције променљиве која има тип структуре

Пример:

struct datum { int dan; int mesec; int godina; }dan_u_godini={21,10,1968};

У овом примеру се истовремено врши дефинисање структуре datum, дефиниција и иницијализација променљиве dan_u_godini као структуре типа datum. Пример:

struct datum { int dan; int mesec; int godina; }; main() { struct datum dan_u_godini={21,10,1968}; ... }

У овом примеру се дефинисање структуре datum обавља одвојено од дефиниције и иницијализације променљиве dan_u_godini као структуре типа datum.

На сличан начини као и код иницијализовања вредности низа уколико је број почетних вредности мањи од броја елемената једне структуре, извршиће се додела почетних вредности онолико елемената колико има почетних вредности, док ће осталим елементима бити додељена вредност нула.

Page 139: Uvod u programski jezik C Univerzitet Singidunum

9

Додељивање вредности свих елемената једне структуре елементима друге структуре истог типа обавља се на стандардан начин оператором додењивања:

Пример:

struct student { char prezime[20]; char ime[20]; char dat_rodj[11]; int god_studija; float prosecna_ocena; }; main() { struct student prvi={”Petrovic”,”Marko”, ”02.10.1983”,1,8.96}; /* L1*/ struct student drugi; ... drugi=prvi; /* L3*/ ... }

Након линије 1 променљивој prvi се додељују вредности и имамо ситуацију као на слици 4.

prvi Petrovic

Marko

dat_rodj 02.10.83

1

ime

prezime

god_studija

Prosecna_ocena 8.96

?

?

drugi

dat_rodj ?

?

ime

prezime

god_studija

Prosecna_ocena ?

слика 4

Након линије 3 i наредбе drugi=prvi; променљивој drugi се додељују вредности променљиве prvi, имамо ситуацију као на слици 5.

prvi Petrovic

Marko

dat_rodj 02.10.83

1

ime

prezime

god_studija

Prosecna_ocena 8.96

Petrovic

Marko

drugi

dat_rodj 02.10.83

1

ime

prezime

god_studija

Prosecna_ocena 8.96

слика 5

Page 140: Uvod u programski jezik C Univerzitet Singidunum

10

Динамичке структуре података Структура може садржати елементе различитих типова, па и друге структуре али не саму себе. Међутим тај проблем је могуће решити употребом показивача.

Пример:

struct lista_jedna { int dan; int mesec; int godina; struct lista_jedna *point_na_listu; };

У претходном примеру структура lista_jedna садржи четири елемента од којих је једна показивач. Наредбом struct lista_jedna *point_na_listu; се дефинише показивач point_na_listu на дефинисану структуру. Мењањем вредности показивача, може се добити више истих структура, где показивач претходне структуре увек показује на наредну структуру.

Једна од веома честих примена динамичких структура је линеарна листа.

Линеарна листа је скуп компоненти који се називају чворови (node). Сваки чвор (изузев последњег) садржи адресу наредног чвора. Због тога сваки чвор у линеарној листи има бар два елемента: један који садржи податке (data) и један који садржи адресу наредног чвора (link). Података може бити више као и адреса, али овде ће бити разматран најједноставнији облик са једним податком и једном адресом.

Адреса првог чвора у листи се чува на посебној локацији која се зове: head (глава) или prvi.

adresa podatak

слика 7. структура чвора

Линеарна листа је листа која се састоји од чворова, а редослед чворава је одређен адресом која је записана у сваком чвору.

На следећој слици је приказан пример једне листе

45 65 34 76 рrvi

слика 8. Линеарна листа

Стрелица која полази из сваког чвора показује да се у једном његовом елементу (link) “чува” адреса наредног чвора. Закривљена стрелица у последњем чвору показује да је тај чвор последњи у листи тј. да је адреса коју он садржи једнака 0 (NULL), односно да не показје на неки други чвор.

Претпоставимо да је први чвор на адреси 1200 да се други чвор налази на меморијској локацији 1575 итд. Тада имамо следећу ситуацију:

15751200

157545 65рrvi 1200

слика 9

Page 141: Uvod u programski jezik C Univerzitet Singidunum

11

Како сваки чвор има бар две компоненте (две врсте податка) неопходно га је формирати као структуру. Тип податка који се налази у чвору зависи од конкретне примене, међутим други податак је увек истог типа, представља адресу па је према томе показивач. За наведени пример може се формирати следећа структура:

struct lista_jedna { int dan; struct lista_jedna *point_na_listu; };

поред тога потребно је дефинисати и променљиву

struct lista_jedna *prvi

која ће указивати на први чвор односно на структуру којом је описан први чвор (у претходном примеру садржи адресу 1200).

Својства линеарне листе

Претпоставимо да су дефинисане и иницијализоване четири структуре облика:

struct lista_jedna { int podatak; struct lista_jedna *sled_adresa; };

као и да је дефинисан један поинтер на такву структуру

struct lista_jedna *prvi

који указује на прву структуру. Нека су почетне вредности елемента структуре одабране као што је приказано на слици 10.

podatak sled_adresa podatak sled_adresa podatak sled_adresa

2000

2800 17

3600

0 45

2800 1500

360063

prvi

2000

podatak sled_adresa

92 1500

слика 10

То је линеарна листа од 4 чвора. Адреса првог чвора је записана у показивачу prvi. Сваки чвор (структура) се састоји од две компоненте: podatak у коме се чува жељена вредност и sled_adresa у којој је записана адреса наредног чвора. Ради једноставности је усвојено да постоји само један податак (у општем случају их може бити више) и да је његов тип int.

Претпоставимо даље, да је адреса првог чвора 2000, другог чвора 2800, трећег 1500 и четвртог 3600. Због тога је вредност адресе која је записана у показивачу prvi 2000, вредност sled_adresa првог чвора 2800, вредност sled_adresa другог чвора 1500, итд. Вредност 0 (NULL) кaо sled_adresa последњег чвора означава да тај поинтер не указује не неку локацију тј. да је последњи у листи.

У складу са приступом елементима структуре преко показивача може се написати следећа табела

Page 142: Uvod u programski jezik C Univerzitet Singidunum

12

вредност

prvi 2000

prvi->podatak 17 Зато што је вредност показивача prvi 2000 а вредност podatak на тој адреси је 17

prvi->sled_adresa 2800

prvi->sled_adresa->podatak 92 Зато што је вредност prvi->sled_adresa 2800 а вредност podatak чвора на адреси 2800 је 92

Дефинишимо још један показивач чије име је tekuci и нека је његова почетна вредност иста као и вредност показивача prvi.

struct lista_jedna *tekuci;

tekuci=prvi;

Након ове наредбе имамо ситуацију као на слици 11:

podatak sled_adresa podatak sled_adresa podatak sled_adresa

2000

2800 17

3600

0 45

2800 1500

360063

prvi

2000

podatak sled_adresa

92 1500

tekuci

2000

слика 11

Са слике 11 следи

вредност

tekuci 2000

tekuci->podatak 17

tekuci ->sled_adresa 2800

tekuci ->sled_adresa->podatak 92

Размотримо сада наредбу

tekuci=tekuci->sled_adresa;

Ова наредба копира вредност tekuci->sled_adresa (вредност 2800), у показивач tekuci. Због тога након ове наредбе показивач tekuci указује на следећи чвор у листи. Вредност променљивих је приказана на слици 12:

Page 143: Uvod u programski jezik C Univerzitet Singidunum

13

podatak sled_adresa podatak sled_adresa podatak sled_adresa

2000

2800 17

3600

0 45 2800 1500

360063

prvi

2000

podatak sled_adresa

92 1500

tekuci 2800

слика 12

односно

вредност

tekuci 2800

tekuci->podatak 92

tekuci->sled_adresa 1500

tekuci->sled_adresa->podatak 63

Ако се пође од показивача prvi онда је вредност променљивих:

вредност

prvi->sled_adresa->sled_adresa 1500

prvi->sled_adresa->sled_adresa->podatak 63

prvi->sled_adresa->sled_adresa->sled_adresa 3600

prvi->sled_adresa->sled_adresa->sled_adresa ->podatak 45

tekuci->sled_adresa->sled_adresa 3600

tekuci->sled_adresa->sled_adresa->podatak 45

tekuci->sled_adresa->sled_adresa->sled_adresa 0 (NULL)

tekuci->sled_adresa->sled_adresa->sled_adresa->podatak ? (није дефинисано)

Основне операције са линеарном листом обухватају: • Претраживање листе да би се приступило одговарајућем податку у листи (читање или писање) • Уметање новог члана (чвора) листе • Брисање постојећег члана листе

Све наведене операције захтевају „пролазак” криз листу. Претпоставимо да показивач prvi указује на први чвор у листи и да је вредност показивача sled_adresa последњег чвора у листи NULL. Показивач prvi се, уопштено гледано, не може користити за приступање појединим члановима листе јер би у том случају изгубили податак о адреси првог чвора! Проблем је последица чињенице да су вредности показивача (prvi и sled_adresa) једине вредности преко којих је могуће одредити везу међу чворовима и то само у једном смеру. Уколико показивачу prvi доделимо адресу било ког другог чвора, губи се информација о адреси свих предтходних чворова (и податка које они садрже), осим ако претходно не запамтимо првобитну вредност показивача.

Због тога је пракса да показивач prvi садржи адресу првог чвора, а да се за потребе кратања кроз листу користи додатни показивач истог типа. Уведимо показивач tekuci који је истог типа као и показивач prvi.

У наредном примеру је показано како се креће кроз листу:

Page 144: Uvod u programski jezik C Univerzitet Singidunum

14

Пример:

tekuci=prvi;

while(tekuci != NULL)

{

/* obrada cvora na koji ukazuje pokazivac tekuci */

tekuci= tekuci-> sled_adresa; /* prelazak na sledeci cvor*/

}

Ако претпоставимо да показивач prvi показује на неку уланчану листу следећи пример исписује вредност сваког податка у листи.

tekuci=prvi;

while(tekuci != NULL)

{

/* obrada cvora na koji ukazuje pokazivac tekuci */

printf("vrednost podatka je %d\n", tekuci -> podatak );

tekuci= tekuci-> sled_adresa; /* prelazak na sledeci cvor*/

}

Уметање новог и брисање постојећег чвора листе Размотримо следећу структуру (због једноставности усвојимо да је тип променљиве podatak, целобројан тј. int).

struct jedna_lista { int podatak; struct jedna_lista *sled_adresa; };

Користићемо следеће променљиве, декларисане са:

struct jedna_lista *prvi, *p, *novi_cvor,

Уметање новог чвора листе

Размотримо пример са слике 13.

podatak sled_adresa podatak sled_adresa podatak sled_adresa

17 0 45

63

prvi

podatak sled_adresa

92

p

слика 13

Претпоставимо да p указује на чвор са податком 92 и да треба креирати нови чвор са податком 50 и да тај чвор треба убацити након чвора на који указује чвор p. Наредни кôд то обавља:

Page 145: Uvod u programski jezik C Univerzitet Singidunum

15

novi_cvor = malloc(sizeof(jedna_lista)); /* kreiranje novog cvora */ novi_cvor -> podatak=50; /* upisi 50 u novi cvor */

Прва наредба (novi_cvor = malloc(sizeof(jedna_lista))) креира нови чвор, односно структуру, негде у меморији и додељује адресу новог чвора променљивој типа показивач (novi_cvor). Друга наредба (novi_cvor -> podatak=50;) уписује вредност 50 у променљиву podatak новог чвора (видети слику 14).

podatak sled_adresa podatak sled_adresa podatak sled_adresa

17 0 45

63

prvi

podatak sled_adresa

92

p

podatak sled_adresa

50

novi_cvor

слика 14

Следе наредбе које убацују нови чвор у листу: novi_cvor -> sled_adresa = p -> sled_adresa; p -> sled_adresa = novi_cvor;

Након прве наредбе (novi_cvor -> sled_adresa = p -> sled_adresa) имамо ситуацију као на слици

podatak sled_adresa podatak sled_adresa podatak sled_adresa

17 0 45

63

prvi

podatak sled_adresa

92

p

podatak sled_adresa

50

novi_cvor

слика 15

Након извршавања друге наредбе (p -> sled_adresa = novi_cvor;) резултујућа листа је приказана на слици 16.

Page 146: Uvod u programski jezik C Univerzitet Singidunum

16

podatak sled_adresa podatak sled_adresa podatak sled_adresa

17 0 45

63

prvi

podatak sled_adresa

92

p

podatak sled_adresa

50

novi_cvor

слика 16

Брисање постојећег чвора листе

Размотримо пример са слике 17.

podatak sled_adresa podatak sled_adresa podatak sled_adresa

17 0 45

63

prvi

podatak sled_adresa

92

p

слика 17

Претпоставимо да желимо да избришемо чвор кји садржи податак 63.

Следећа наредбе обављају тај посао: p -> sled_adresa = p -> sled_adresa -> sled_adresa;

Резултат ове наредбе је приказан на слици 18.

podatak sled_adresa podatak sled_adresa podatak sled_adresa

17 0 45

63

prvi

podatak sled_adresa

92

p

слика 18

Page 147: Uvod u programski jezik C Univerzitet Singidunum

17

Прављење уланчане листе

Претпоставимо да су чворови листе структуре које садрже две промељиве један посатак (типа int) и једну адресу на наредну структуру. Подаци које треба да садржи листа су

2 15 8 24 34

Решење: #include<stdio.h> #include<malloc.h> struct jedna_lista { int podatak; struct jedna_lista *sled_adresa; }; main() { 1 struct jedna_lista *prvi, *poslednji, *novi_cvor; 2 int vrednost[5]={2,15,8,24,34}; 3 int i; 4 prvi=NULL; 5 poslednji=NULL; 6 novi_cvor=NULL; 7 for(i=0; i<5; i++) 8 { 9 novi_cvor = malloc(sizeof(struct jedna_lista)); 10 if(novi_cvor) 11 { 12 novi_cvor->podatak=vrednost[i]; 13 novi_cvor->sled_adresa=NULL; 14 } 15 else 16 break; 17 if(prvi==NULL) /* ako je lista je prazna */ 18 { 19 prvi=novi_cvor; 20 poslednji=novi_cvor; 21 } 22 else 23 { 24 poslednji->sled_adresa=novi_cvor; 25 poslednji=novi_cvor; 26 } 28 } 29 }

Након 4, 5 и 6 линије имамо ситуацију као на слици 19

Page 148: Uvod u programski jezik C Univerzitet Singidunum

18

pоslednji

prvi

novi_cvorji

слика 19 Након наредбе у линији 9, доделјује се меморијски простор за једну структуру, адреса те структуре се уписује у показивач novi_cvor, након наредби novi_cvor->podatak=vrednost[i]; се у први чвор уписује податак 2. Након наредбе novi_cvor->sled_adresa=NULL; се адреса наредног (другог) чвора иницијализује на нулу, што значи да он још не постоји. Како основни показивач (prvi) још није иницијализован (prvi=NULL), обавиће се иницијализација наредбама prvi=novi_cvor; и poslednji=novi_cvor;Ситуација је приказана на слици 20.

pоslednji

prvi

novi_cvorji

podatak sled_adresa

2

слика 20

Следи дорада...

Page 149: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C (C programming Language)

Лекција 10

Садржај РРаадд ссаа ффаајјллооввииммаа........................................................................................................................ 2 Зашто користимо фајлове за У/И операције ....................................................................................... 2 Фајлови и У/И операције ....................................................................................................................... 2 Отварање и затварање фајла.............................................................................................................. 3 Отварање фајла ................................................................................................................................ 3 Затварање фајла............................................................................................................................... 4

Рад са текстуалним фајловима............................................................................................................ 5 Пренос података са конверзијом ................................................................................................... 10

Рад са бинарним датотекама ............................................................................................................. 10 Позиционирање унутар фајла............................................................................................................ 13

Page 150: Uvod u programski jezik C Univerzitet Singidunum

2

РРаадд ссаа ффаајјллооввииммаа Улаз и излаз података (У/И или Input/Output – I/O), као и приступ фајловима не представља стварни део програмског језика C, већ је он дат као додатак у облику стандардне библиотеке. Улазно излазне операције увек зависе од оперативног система рачунара на коме се извршавају, па се због тога имплементација ових операција врши у зависности од оперативног система на ком се преводилац налази.

Улаз и излаз података треба да омогући комуникацију програма са спољашњим светом, уношење података за обраду и приказивање (запис) добијених резултата. Улазни подаци могу да се добију са тастатуре или из фајла. На сличан начин, излазни подаци се могу приказати на монитору, штампачу и сл. или могу бити „записани” у фајл.

Зашто користимо фајлове за У/И операције Подаци које програм добија уносом са тастатуре односно подаци које програм приказује на монитору су у основи привременог трајања (temporary data). Уколико поново покренете програм улазни подаци се морају поново „укуцати”, а излазни подаци приказани на монитору „нестају” по завршетку рада програма. Рад са фајловима нуди могућнoст трајног чувања како улазних тако и излазних података. Садржај фајлова остаје непромењен све док га сами не променимо или док то не уради програм. Ако је програм тако написан да излазне податке уписује у фајл онда ти подаци остају трајно записани у фајлу и након завршетка рада програма. Улазни подаци, записани у фајл, могу да се корсте неограничен број пута за исти програм или за неки други програм а да при томе нема потребе да се ти подаци изнова уносе.

Фајлови са улазним и излазним подацима које користи програм се у великом броју случајева могу читати и мењати применом програма за обраду текста као што је editor који се користи за писање програма. То значи да можете да креирате фајл са улазним подацима или да прочитате садржај фајла са излазним подацима и онда када се програм не извршава.

Рад са фајловима такође нуди приступачан начин за рад са великим бројем података. Када програм захтева велики број улазних података, подаци могу да се претходно запишу у фајл уместо да се при сваком покретању програма ручно уносе са тастатуре.

Фајлови и У/И операције Кад програм користи улазне податке из фајла онда кажемо да се подаци читају (reading) из фајла, а кад програм шаље податке у фајл онда кажемо да пише (writing) податке у фајл.

Према начину записа података у фајл разликујемо: – текстуалне фајлове, и – бинарне фајлове

Текстуални фајлови се састоје од низа знакова (карактера), а сваки низ је знаком за прелазак у нови ред (\n) подељен у редове. Сваки знак који се налази у оваквом фајлу је могуће приказати на монитору (штампачу) или има одређену управљачку функцију (табулатор, прелазак у нови ред, прелазак на нови лист и сл.). У случају да програм треба да у текстуални фајл запише неки нумерички податак (број), који се у меморији рачунара представља у бинарном облику, неопходно је да се пре записа изврши конверзија у низ цифара (карактера). На сличан начин, приликом читања података из улазног фајла пре него што се учитана вредност додели некој променљивој у програму неопходно је извршити конверзију у бинарни облик.

Бинарни фајлови се састоје од низа бајтова чији је садржај верна слика начина представљања података у меморији рачунара. У току читања и записа података се не пимењује конверзија, већ се у оба случаја врши преношење података бајт по бајт. Подаци из оваквих фајлова се не могу директно приказивати на монитору или на штампачу већ је потребно претодно извршити њихову конверзију.

Сваки изворни програм који користи У/И функције мора да се повеже са стандардном програмском библиотеком, што се постиже претпроцесорском наредбом:

#include<stdio.h>

Page 151: Uvod u programski jezik C Univerzitet Singidunum

3

Фајл stdio.h садржи декларације и константе које су потребне за рад функција улаза и излаза.

Приликом извршавања неког C програма оперативни систем аутоматски отвара три стандардна фајла:

– Стандардни улазни фајл (stdin) представља тастатуру док се другачије не дефинише. – Стандардни излазни фајл (stdоut) представља монитор док се другачије не дефинише. – Стандардни излаз за поруке или грешке (stderr) представља монитор док се другачије не дефинише.

Сва три фајла су дефинисана у заглављу stdio.h

За извршне програме, сви периферијски уређаји представљају фајлове и овим уређајима се приступа на исти начин на који се приступа неком фајлу.

При раду да фајловима постоје следеће радње: – отварање фајла, – приступ фајлу, – испитивање стања фајла и – затварање фајла. Отварање фајла значи успостављање везе са постојећим фајлом. Том приликом се у меморији образује одговарајућа структура (struct) података која садржи више променљивих неопходних за рад са фајлом као што су: показивачи на почетак фајла, тренутну позицију податка који се чита или записује и сл.

Приступ фајлу представља читање или запис података са или без употребе конверзије.

Испитивање стања фајла представља наредбе које враћају информацију о могућим грешкама насталим приликом читања односно записа података.

Затварање фајла представља раскид везе са фајлом и поништавање вредности података унутар структуре која се формирала при отварању фајла.

Отварање и затварање фајла Све стандардне У/И функције користе меморију код преноса података. Да би нека функција могла управљати меморијом она мора да поседује информацију о почетној адреси меморије коју користи, тренутној вредности показивача на адресу унутар те меморије, броју бајтова које треба пренети, стању индикатора (могуће грешке и сл.) и бројној ознаци фајла.

Све ове информације су дефинисане у облику једне структуре, као сложени тип података, која се назива FILE. Садржај структуре FILE је део стандардне програмске библиотеке stdio.h. Податак типа FILE је различито дефинисан за различите оперативне системе, за DOS и Windows оперативни систем има облик

struct _iobuf { char _ptr; /* trenutna pozicija pokazivaca */ int _cnt; /* trenutno stanje broja bajtova */ char *_base; /* polazna adresa rezerv. memorije */ int _flag; /* indikator */ int _file; /* brojna oznaka fajla */ } FILE;

Отварање фајла У сваком програму који садржи претпроцесорску наредбу #include<stdio.h> могуће је извршити дефиницију показивача фајла на следећи начин:

FILE *ptr_ime;

где је ptr_ime име показивача које је произвољно у складу са правилима за избор имена идентификатора.

Page 152: Uvod u programski jezik C Univerzitet Singidunum

4

Да би се приступило неком фајлу, он прво мора да се отвори. Отворити фајл значи да се он повезује (применом показивача фајла) са програмом, који затим може да обавља операције читања и писања.

Отварање фајла се обавља наредбом fopen која има следећи облик (прототип):

FILE *fopen(const char *ime_fajla, const char *mod_rada);

ime_fajla je знаковни низ који представља име фајла (са екстензијом) који се отвара, а поред тога може да садржи комплетну путању (path). У случају да се путања не наведе, фајл се тражи у фолдеру у ком се налази извршни програм.

mod_rada представља начин на који се користи фајл. Може имати следеће вредности: – r (read) читање из фајла. Фајл мора постојати. – w (write) уписивање у фајл. Уколико фајл постоји брише се његов садржај, а уколико не

постоји формира се нови фајл. – a (append), дописује податке на крај постојећег фајла. Уколико фајл не постоји формира се

нови. – r+ Отварање фајла за читање и писање. Фајл мора постојати. – w+ Отварање фајла за читање и писање.

Ако фајл постоји брише се његов садржај. Уколико фајл не постоји формира се нови и отвара за читање и писање.

– a+ Отварање фајла за читање и писање. Ако фајл постоји дописује податке на крај фајла. Уколико фајл не постоји формира се нови и отвара за читање и писање.

Ако се другачије не нагласи подразумева се да је реч о текстуалним фајловима.

За рад са бинарним фајловома претходним ознакама треба додати и слово b. Тиме се добија шест нових ознака:

"rb", "wb","ab", "rb+", "wb+","ab+"

Систем пријављује грешку уколико покушамо да отворимо фајл за читање, а тај фајл не постоји. Код појаве грешке, функција fopen даје као резултат нул (NULL) показивач који је дефинисан у библиотеци stdio.h.

Поред ове, честе су грешке које се односе на занемаривање чињенице да се отварањем фајла за читање у моду рада "w", "w+","wb" или "wb+" бришу сви подаци који су претходно уписани у фајл.

Затварање фајла Код (регуларног) завршетка неког програма аутоматски се затварају сви фајлови који су претходно отворени. Упркос овој олакшици препоручљиво је да се експлицитно затвори сваки фајл чим престане потреба да се он користи (за читање или упис), првенствено зато што је број фајлова који истовремено могу да буду отворени ограничен.

Затварање фајла, који је претходно био отворен, се обавља функцијом fclose, чији прототип је:

int fclose(FILE *ptr_fajl);

Повратна вредност функције fclose је: – 0 ако је фајл успешно затворен – Вредност различита од нуле уколико је дошло до грешке.

Затварање фајла значи да се раскида веза која постоји између програма (показивача фајла) и имена фајла. Приликом извршења функције fclose пре него што се раскине веза између показивача и имена фајла празни се вредност бафера (меморије) у којој могу постојати подаци. То значи да се неке од операција читања и писања завршавају непосредно пре затварања фајла.

Page 153: Uvod u programski jezik C Univerzitet Singidunum

5

Пример: #include <stdio.h> void main( void ) { FILE *ptr_file1, *ptr_file2; int broj_zatvorenih; ptr_file1=NULL; ptr_file2=NULL; /* Otvori za citanje (ukoliko fajl "data1.txt" ne postoji sledi ERROR) */ if((ptr_file1 = fopen("data1.txt", "r" )) == NULL ) printf( "Fajl 'data1.txt' NIJE otvoren\n" ); else printf( "\nFajl 'data1.txt' je otvoren \n" ); /* Otvori fajl za citanje i upis */ if((ptr_file2 = fopen( "data2.txt", "w+" )) == NULL ) printf("Fajl 'data2.txt' NIJE otvoren\n" ); else printf("Fajl 'data2.txt' je otvoren\n" ); /* Zatvori drugi fajl */ if( fclose(ptr_file2 ) ) printf( " Fajl 'data2.txt' NIJE zatvoren\n " ); else printf( " Fajl 'data2.txt' je zatvoren\n " ); /* Svi preostali fajlovi se zatvaraju: */ broj_zatvorenih = fcloseall( ); printf("Broj fajlova zatvorenih naredbom fcloseall je: \ %d\n",broj_zatvorenih); } Излаз:

Fajl 'data1.txt' je otvoren

Fajl 'data2.txt' je otvoren

Fajl 'data2.txt' je zatvoren

Broj fajlova zatvorenih naredbom fcloseall je: 1

Рад са текстуалним фајловима Функцијама за читање и писање знакова врши се неформатирано учитавање односно писање једног или више бајтова из неког или у неки фајл. fgetc

Стандардна функција fgetc учитава један бајт из неког фајла. Њен прототип је дата са:

int fgetc( FILE *ptr_fajl );

Ова функција има само један параметар, показивач на фајл, односно на бајт унутар фајла. Учитани знак је типа unsigned char и он се аутоматски претвара у тип int.

Page 154: Uvod u programski jezik C Univerzitet Singidunum

6

Уколико се пре учитавања знака дошло до краја фајла, резултат функције fgetc ће бити EOF (End Of File). EOF je симболичка константа која означава крај фајла и има вредност -1.

Пример:

Претпоставимо да постоји фајл: opis.txt и да је његов садржај:

Следећи програм чита првих 80 карактера из тог фајла и исписује их на монитор.

#include <stdio.h> void main( void ) { FILE *ptr_ulaz; char buffer[81]; int i, ch; /* Otvori fajl za citanje: */ if( (ptr_ulaz = fopen( "opis.txt", "r" )) != NULL ) { /* Procitaj prvih 80 karaktera i smesti ih u "buffer": */ for( i=0; (i < 80 ) && ( ch!=EOF); i++ ) { ch = fgetc( ptr_ulaz ); buffer[i] = (char)ch; } /* Dodaj znak '\0' na kraj niza da bi formirao string */ buffer[i] = '\0'; printf( "%s\n", buffer ); fclose( ptr_ulaz ); /* zatvaranje fajla */ } }

Излаз FGETC.C: This program uses getc to read the first 80 input characters (or until

Уз стандардну функцију fgetc постоји еквивалентна функција getc. Једина разлика је та да је функција getc имплементирана као макро, што може утицати на брзину извршења функције.

Page 155: Uvod u programski jezik C Univerzitet Singidunum

7

fputc

Функција fputc уписује један бајт у неки фајл. Прототип ове функције је:

int fputc( int c, FILE *ptr_fajl );

int c је податак који треба уписати у фајл, типа је int али се аутоматски конвертује у тип unsigned char и потом се уписује у фајл.

Повратна вредност ове функције је једнака – Вредности карактера који је уписан – EOF уколико је дошло до грешке.

Уз стандардну функцију fputc постоји екввалентна функција putc. Једина разлика је та да је функција putc имплементирана као макро, што може утицати на брзину извршења функције.

Пример: #include <stdio.h> void main( void ) { FILE *ptr_out; char niz[] = "Ovo je primer za fputc!\n"; int i; /* Otvori fajl za upis: */ if( (ptr_out = fopen( "upis.txt", "w" )) != NULL ) { /* Upisi sve podatke niz-a u fajl primenom fputc. */ i=0; while(niz[i]!= '\0') { fputc(niz[i] , ptr_out ) i++; } } Излаз:

Page 156: Uvod u programski jezik C Univerzitet Singidunum

8

fgets

Функција fgets учитава стринг из улазног фајла и смешта га у неки дефинисани низ. fgets учитава стринг од тренутне позиције у фајлу до краја стринга или до знака за прелаз у нови ред (\n) или највише n-1 карактер. Њен прототип има облик:

char *fgets( char *niz, int n, FILE *ptr_fajl );

char *niz представља показивач на низ у који ће бити смештени подаци који се учитавају,

int n Максималан број карактера који може да се учита је n-1.

FILE *ptr_fajl представља показивач на фајл из ког се подаци учитавају.

Повратна вредност ове функције је једнака – Вредности карактера који је уписан – NULL уколико је дошло до грешке.

Пример:

Нека постоји фајл ulaz.txt

#include <stdio.h> void main( void ) { FILE *ptr_ulaz; char niz[50]; if( (ptr_ulaz = fopen( "ulaz.txt", "r" )) != NULL ) { if( fgets( niz, 40, ptr_ulaz ) == NULL) printf( "fgets error\n" ); else printf( "%s\n", niz); fclose( ptr_ulaz); } }

Излаз:

Ovo je primer za funkciju fgets. U ovom

Приметите да је прочитано 39 карактера (n=40).

Page 157: Uvod u programski jezik C Univerzitet Singidunum

9

fputs

Функција fputs исписује стринг на текућу позицију отвореног фајла. Нул карактер се не преноси. Њен прототип има облик:

int fputs(char *niz, FILE *ptr_fajl );

char *niz представља показивач на низ из ког ће се подаци „преписати” у фајл,

FILE *ptr_fajl представља показивач на фајл у окји се подаци уписују.

Повратна вредност ове функције је једнака – Ненегативној вредности ако је успешна – EOF уколико је дошло до грешке.

Пример: #include <stdio.h> void main( void ) { FILE *ptr_out; char niz[] = "Ovo je primer za fputs!"; /* Otvori fajl za upis: */ if( (ptr_out = fopen( "upis.txt", "w" )) != EOF ) { /* Upisi sve podatke niz-a u fajl primenom fputs. */ fputs(niz, ptr_out); } fclose(ptr_out); }

Излаз:

Page 158: Uvod u programski jezik C Univerzitet Singidunum

10

Пренос података са конверзијом Улазна и излазна конверзија података је детаљно објашњена када су обрађиване функције printf и scanf. Објашњење је било ограничено само на испис и читање стандардних улазно излазних јединица (тастатура и монитор). У раду са фајловима користе се две веома сличне функције fprintf и fscanf код којих важе сва правила за конверзију (форматирање) а разликују се по томе што имају додатни, први аргумент, који представља показивач на фајл из којег се читају или у који се записују подаци.

Пример: #include <stdio.h> FILE *ptr_fajl; void main( void ) { int i = 10; double fp = 1.5; char niz[] = "Ovo je jedan niz podataka"; char karakter = '\n'; ptr_fajl = fopen( "izlaz.txt", "w" ); fprintf( ptr_fajl, "%s%c", niz, karakter ); fprintf( ptr_fajl, "%d\n", i ); fprintf( ptr_fajl, "%f\n", fp ); fclose( ptr_fajl ); }

Излаз:

Рад са бинарним датотекама Код бинарних фајлова се за читање и писање користе функције fread и fwrite.

fwrite Прототип функције fwrite је:

int fwrite( void *bafer, int velicina, int broj, FILE *ptr_ime );

Функција fwrite пише у означени фајл (ptr_ime) највише до broj вредности. velicina представља број бајтова који је заузима свака вредност. Вредности које се пишу треба да буду смештене у променљивој (bafer) типа низ. Вредност показивача се након уписа повећава за број уписаних бајтова.

Повратна вредност ове функције је једнака броју вредности које су уписане. Овај број може бити мањи од задате вредности broj ако је наступила грешка.

Уколико желите да упишете 10 целобројних вредности (int заузима 4 бајта) из низа niz1 у fajl1.txt наредба је следећа:

fwrite( niz1, 4, 10, ptr_na_fajl );

Page 159: Uvod u programski jezik C Univerzitet Singidunum

11

Пример: #include <stdio.h> void main( void ) { FILE *ptr_izlaz; int list[]={1,2,3,4,5,16,7,683,9,10,11,12,13,14}; int broj_upisanih; /* Otvori fajl u binarnom modu */ if( (ptr_izlaz = fopen( "izlaz.txt", "wb" )) != NULL ) { broj_upisanih = fwrite( list, sizeof( int ), 10, ptr_izlaz ); printf( "Upisano %d vrednosti\n", broj_upisanih ); fclose( ptr_izlaz); } else printf( "Problem kod otvaranja fajla\n" ); }

Излаз:

Upisano 10 vrednosti

fread Прототип функције fread је:

int fread( void *bаfer, int velicina, int broj, FILE *ptr_ime );

Функција fread чита из означеног фајла (ptr_ime) највише broj вредности величине velicina бајтова и памти их у променљивој bаfer типа низ. Вредност показивача се након читања повећава за број прочитаних бајтова.

Повратна вредност ове функције је једнака броју вредности које су прочитане. Овај број може бити мањи од задате вредности broj ако се у међувремену дошло до краја фајла или је наступила грешка.

Page 160: Uvod u programski jezik C Univerzitet Singidunum

12

Пример: void main( void ) { FILE *ptr_izlaz, *ptr_ulaz; int list1[]={1,2,3,4,5,16,7,683,9,10,11,12,13,14}; int list2[12]; int i, broj_upisanih, broj_procitanih; /* Otvori fajl u binarnom modu */ if( (ptr_izlaz = fopen( "izlaz.txt", "wb" )) != NULL ) { broj_upisanih = fwrite( list1, sizeof( int ), 10, ptr_izlaz ); printf( "Upisano %d vrednosti\n", broj_upisanih ); fclose( ptr_izlaz); } else printf( "Problem kod otvaranja fajla za upis\n" ); if( (ptr_ulaz = fopen( "izlaz.txt", "rb" )) != NULL ) { /* citanje 10 vrednosti */ broj_procitanih = fread( list2, sizeof( int ), 10, ptr_ulaz ); printf( "Broj procitanih vrednosti je = %d\n", broj_procitanih); printf( "Sadrzaj niza list2 je \n"); for(i=0; i<10;i++) printf( "%d ",list2[i]); fclose( ptr_ulaz ); } else printf( "Problem kod otvaranja fajla za citanje\n" ); } Излаз: Upisano 10 vrednosti Broj procitanih vrednosti je = 10 Sadrzaj niza list2 je 1 2 3 4 5 16 7 683 9 10

Page 161: Uvod u programski jezik C Univerzitet Singidunum

13

Позиционирање унутар фајла. Све стандардне У/И функције, које су до сада представљене дозвољавају секвенцијални приступ фајловима. Под секвенцијалним приступом се подразумева такав приступ код кога се подаци у фајлу читају односно пишу редом од почетка до краја фајла. То значи да је време приступа поједином податку утолико дуже што је он ближеи крају фајла.

Многo бржи начин приступа фајлу представља директан приступ. Код директног приступа, време приступа неком податку у фајлу не зависи од места на ком се он налази.

Програмски језик C подржава директан приступ уз једно ограничење: Да би директно приступити неком податку потребно је да се зна његово одстојање (у бајтовима) од почетка фајла. fseek

Функција fseek се користи за позиционирање показивача на неко место унутар фајла. Њен прототип је:

int fseek( FILE *ptr_ime, long offset, int origin );

Функција fseek помера показивач на фајл (ptr_ime) на нову локацију која се за offset разликује од позиције дефинисане са origin.

Вредност origin може бити:

– SEEK_CUR: означава тренутну вредност показивача

– SEEK_ЕND: означава крај фајла

– SEEK_SET: означава почетак фајла

Повратна вредност

Уколико се функција обави успешно повратна вредност је једнака нула, у супротном је повратна вредност различита од нуле.

Битно је напоменути да функција fseek не чита нити уписује податке, она само позиционира показивач!

Page 162: Uvod u programski jezik C Univerzitet Singidunum

14

Пример:

#include<stdio.h> void main( void ) { FILE *ptr_fajl; char linija[81]; int rezultat; ptr_fajl = fopen( "izlaz.txt", "w+" ); if( ptr_fajl == NULL ) printf( "Fajl nije otvoren \n" ); else { fprintf( ptr_fajl, "Fajl pocinje ovde: Ovo je fajl 'izlaz.txt'.\n" ); rezultat = fseek( ptr_fajl, 19L, SEEK_SET); if( rezultat ) printf( "Fseek neuspesna\n" ); else { printf( "Pokazivac fajla je postavljen na 19-to mesto.\n" ); fgets( linija, 80, ptr_fajl ); printf( "%s", linija ); } fclose( ptr_fajl); } }

Излаз:

Pokazivac fajla je postavljen na 19-to mesto.

Ovo je fajl 'izlaz.txt'.

Page 163: Uvod u programski jezik C Univerzitet Singidunum

15

ftell

Функција ftell даје тренутну вредност показивача. Њен прототип је:

long ftell( FILE *ptr_ime);

Функција ftell даје тренутну вредност показивача у односу на почетак фајла. Резултат ове функције представља број, који означава број бајтова од почетка фајла до тренутне позиције.

Пример:

Нека постоји фајл

FILE *p_fajl; void main( void ) { long pozicija; char lista[10]; if( (p_fajl = fopen( "tekst.txt", "rb" )) != NULL ) { /* Pomeri pokazivac citajuci podatke: */ fread( lista, sizeof( char ), 10, p_fajl ); /* Odredi poziciju nakon citanja: */ pozicija = ftell( p_fajl ); printf( "Pozicija nakon citanja 10 bajtova je: %ld\n",pozicija ); fclose( p_fajl ); } }

Излаз:

Pozicija nakon citanja 10 bajtova je: 10

feof

Поред наведених функција веома важну улогу има функција feof за испитивање да ли се приликом читања дошло до краја фајла. Прототип функције је:

int feof(FILE *ptr_ime);

Повратна вредност ове функције је

– 0 уколико тренутна вредност показивача није дошла до краја фајла

– различита од нуле уколико се дошло до краја фајла.

Page 164: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C

Задаци за вежбу

Page 165: Uvod u programski jezik C Univerzitet Singidunum

2

1. Програм за демонстрацију операције инкремента (++) пре и после имена променљиве

/* Upotreba operatora inkrementa */ #include <stdio.h> /* funkcija main, mesto pocetka izvrsenja programa */ int main() { int c=5; /* definisanje promenljive */ int x; /* demonstracija operatora inkrementa NAKON imena promenljive (postincrement) */ printf( "Vrednost promenljive c je %d\n", c ); x=c; /* dodela vrednosti */ /* ispis vrednosti promenljive x */ printf( "\nNakon naredbe x=c, vrednost promenljive x je %d\n", x ); x=c++; printf( "\nNakon naredbe x=c++\n"); printf("\tvrednost promenljive x je %d\n", x ); printf("\tvrednost promenljive c je %d\n\n", c ); /* demonstracija operatora inkrementa PRE imena promenljive (preincrement) */ c = 5; /* dodela vrednosti */ printf( "\nVrednost promenljive c je %d\n", c ); x=++c; printf( "\nNakon naredbe x=++c\n"); printf("\tvrednost promenljive x je %d\n", x ); printf("\tvrednost promenljive c je %d\n\n", c ); return 0; } /* kraj funkcije main */

Page 166: Uvod u programski jezik C Univerzitet Singidunum

3

2. Програм за демонстрацију операције декремента (--) пре и после имена променљиве

/* Upotreba operatora dekrementa */ #include <stdio.h> /* funkcija main, mesto pocetka izvrsenja programa */ int main() { int c=5; /* definisanje promenljive */ int x; /* demonstracija operatora dekrementa NAKON imena promenljive (postdecrement) */ printf( "Vrednost promenljive c je %d\n", c ); x=c; /* dodela vrednosti */ printf( "\nNakon naredbe x=c, vrednost promenljive x je %d\n", x ); x=c--; printf( "\nNakon naredbe x=c--\n"); printf("\tvrednost promenljive x je %d\n", x ); printf("\tvrednost promenljive c je %d\n\n", c ); /* demonstracija operatora dekrementa PRE imena promenljive (predecrement) */ c = 5; /* dodela vrednosti */ printf( "\nVrednost promenljive c je %d\n", c ); x= --c; printf( "\nNakon naredbe x= --c\n"); printf("\tvrednost promenljive x je %d\n", x ); printf("\tvrednost promenljive c je %d\n\n", c ); return 0; } /* kraj funkcije main */

Page 167: Uvod u programski jezik C Univerzitet Singidunum

4

3. Написати програм који наизменично исписује знак + или знак -. Укупан број појављивања знакова је 10. Сваки знак треба да се налази у посебном реду.

Решење 1:

/* Upotreba while petlje i operatora "?" */ #include <stdio.h> /* funkcija main, odavde pocinje izvrsavanje programa */ int main() { int brojac = 1; /* inicijalizacija promenljive brojac */ char znak; /* definisanje promenljive znak */ while ( brojac <= 10 ) /* sve dok je brojac manji ili jednak 10, radi */ { znak = (brojac % 2) ? ('-') : ('+'); /* parne vrednosti +, neparne - */ /* Ispis na ekran */ printf( "%c \n", znak ); brojac++; /* inkrement brojaca, isto sto i brojac=brojac+1 */ } /* kraj while petlje */ return 0; /* povratna vrednost uspesnog zavrsetka programa */ } /* kraj funkcije main */

Решење 2. (He мора се уводити променљива znak)

/* Upotreba while petlje i operatora "?" */ #include <stdio.h> /* funkcija main, odavde pocinje izvrsavanje programa */ int main() { int brojac = 1; /* inicijalizacija promenljive brojac */ while ( brojac <= 10 ) /* sve dok je brojac manji ili jednak 10, radi */ { /* Ispisi na ekran */ printf("%c\n",(brojac % 2) ? ('-'):('+')); /* parni +, neparni - */ brojac++; /* inkrement brojaca, isto sto i brojac=brojac+1 */ }/* kraj while petlje */ return 0; /* povratna vrednost uspesnog zavrsetka programa */ }/* kraj funkcije main*/

Page 168: Uvod u programski jezik C Univerzitet Singidunum

5

4. Написати програм који захтева од корисника да унесе два цела броја а потом исписује њихову суму

Решење 1

/* Sabiranje dva broja */ #include <stdio.h> /* funkcija main, odavde pocinje izvrsavanje programa */ int main() { int integer1; /* prvi broj koji unosi korisnik */ int integer2; /* drugi broj koji unosi korisnik */ int sum; /* promenljiva u kojoj ce biti smesten rezultat */ printf( "Unesite prvi broj\n" ); /* Prikazivanje poruke */ scanf( "%d", &integer1 ); /* ucitavanje unetog broja u promen. integer1 */ printf( "Unesite drugi broj\n" ); /* Prikazivanje poruke */ scanf( "%d", &integer2 ); /* ucitavanje unetog broja u prom. integer2 */ sum = integer1 + integer2; /* racunanje zbira i dodelа vrednosti prom. sum */ printf( "Zbir je %d\n", sum ); /* Prikazi vrednost zbira (sum) */ return 0; /* povratna vrednost uspesnog zavrsetka programa */ } /* kraj funkcije main */

Решење 2. (He мора се уводити променљива sum)

/* Sabiranje dva broja */ #include <stdio.h> /* funkcija main, odavde pocinje izvrsavanje programa */ int main() { int integer1; /* prvi broj koji unosi korisnik */ int integer2; /* drugi broj koji unosi korisnik */ printf( "Unesite prvi broj\n" ); /* Prikazivanje poruke */ scanf( "%d", &integer1 ); /* ucitavanje unetog broja u promen. integer1 */ printf( "Unesite drugi broj\n" ); /* Prikazivanje poruke */ scanf( "%d", &integer2 ); /* ucitavanje unetog broja u prom. integer2 */ /* racunanje zbira i dodelа vrednosti prom. integer1 */ integer1 = integer1 + integer2; printf( "Zbir je %d\n", integer1 ); /* Prikazi vrednost zbira */ return 0; /* povratna vrednost uspesnog zavrsetka programa */ } /* kraj funkcije main */

Page 169: Uvod u programski jezik C Univerzitet Singidunum

6

Решење 3. (He мора се уводити променљива sum) /* Sabiranje dva broja */ #include <stdio.h> /* funkcija main, odavde pocinje izvrsavanje programa */ int main() { int integer1; /* prvi broj koji unosi korisnik */ int integer2; /* drugi broj koji unosi korisnik */ printf( "Unesite prvi broj\n" ); /* Prikazivanje poruke */ scanf( "%d", &integer1 ); /* ucitavanje unetog broja u promen. integer1 */ printf( "Unesite drugi broj\n" ); /* Prikazivanje poruke */ scanf( "%d", &integer2 ); /* ucitavanje unetog broja u prom. integer2 */ /* racunanje zbira i prikaz vrednosti */ printf( "Zbir je %d\n", integer1 + integer2); /* Prikazi vrednost zbira */ return 0; /* povratna vrednost uspesnog zavrsetka programa */ } /* kraj funkcije main */

Page 170: Uvod u programski jezik C Univerzitet Singidunum

7

5. Написати програм који очекује од корисника да унесе два цела броја а потом употребом релационих оператора (==, !=, <,<=, >, >=) исписује однос унетих бројева.

Решење 1. /* Upotreba if naredbe, relacionih operatora i operatora dodele */ #include <stdio.h> /* funkcija main, odavde pocinje izvrsavanje programa */ main() { int num1; /* prvi broj koji unosi korisnik */ int num2; /* drugi broj koji unosi korisnik */ printf( "Unesite dva cela broja a ja cu Vam reci\n" ); printf( "u kom su oni odnosu: " ); scanf( "%d%d", &num1, &num2 ); /* citanje dva cela broja */ if( num1 == num2 ) { printf( "%d je jednako sa to %d\n", num1, num2 ); } /* kraj if naredbe */ if( num1 != num2 ) { printf( "%d nije jednako sa %d\n", num1, num2 ); } /* kraj if naredbe */ if( num1 < num2 ) { printf( "%d je manje od %d\n", num1, num2 ); } /* kraj if naredbe */ if( num1 > num2 ) { printf( "%d je vece od %d\n", num1, num2 ); } /* kraj if naredbe */ if( num1 <= num2 ) { printf( "%d je manje ili jednako sa %d\n", num1, num2 ); } /* kraj if naredbe */ if( num1 >= num2 ) { printf( "%d je vece ili jednako sa %d\n", num1, num2 ); } /* kraj if naredbe */ return 0; /* povratna vrednost uspesnog zavrsetka programa */ } /* kraj funkcije main */

Page 171: Uvod u programski jezik C Univerzitet Singidunum

8

Решење 2. (Обзиром да if наредбе обухватају само једну наредбу витичасте заграде нису неопходне) /* Upotreba if naredbe, relacionih operatora i operatora dodele */ #include <stdio.h> /* funkcija main, odavde pocinje izvrsavanje programa */ main() { int num1; /* prvi broj koji unosi korisnik */ int num2; /* drugi broj koji unosi korisnik */ printf( "Unesite dva cela broja a ja cu Vam reci\n" ); printf( "u kom su oni odnosu: " ); scanf( "%d%d", &num1, &num2 ); /* citanje dva cela broja */ if( num1 == num2 ) printf( "%d je jednako sa to %d\n", num1, num2 ); if( num1 != num2 ) printf( "%d nije jednako sa %d\n", num1, num2 ); if( num1 < num2 ) printf( "%d je manje od %d\n", num1, num2 ); if( num1 > num2 ) printf( "%d je vece od %d\n", num1, num2 ); if( num1 <= num2 ) printf( "%d je manje ili jednako sa %d\n", num1, num2 ); if( num1 >= num2 ) printf( "%d je vece ili jednako sa %d\n", num1, num2 ); return 0; /* povratna vrednost uspesnog zavrsetka programa */ } /* kraj funkcije main */

Page 172: Uvod u programski jezik C Univerzitet Singidunum

9

6. Применом while петље и оператора ++ и – – написати програм који исписује табелу димензија десет врста и пет колона и то тако да су парне врсте попуњене знацима $ $ $ $ $, а непарне знацима # # # # #. /* upotreba while petlje, operatora "?", ++ i -- operatora */ #include <stdio.h> /* funkcija main, odavde pocinje izvrsavanje programa */ int main() { int vrsta = 10; /* definisanje i inicijalizacija promenljive vrsta */ int kolona; /* definisanje promenljive kolona */ while ( vrsta >= 1 ) /* kruzi u while petlji dve dok vrsta ne bude < 1 */ { kolona =1; /* postavi prom. kolona na 1 pri ulasku u while petlju */ while ( kolona <= 5 ) /* sve dok je kolona manje i jednako 5... */ { printf( "%c", (vrsta % 2) ? ('$'): ('#') ); /* Ispis na ekran */ kolona++; /* isto sto i kolona = kolona + 1 ili kolona += 1*/ } /* kraj unutrasnje while petlje */ vrsta--; /* isto sto i vrsta = vrsta – 1 ili vrsta -= 1 */ printf( "\n" ); /* prelazak u novi red */ } /* kraj spoljsnje while petlje*/ return 0; /* povratna vrednost uspesnog zavrsetka programa */ } /* kraj funkcije main */

Page 173: Uvod u programski jezik C Univerzitet Singidunum

10

7. Напишите програм који очекује од корисника да унесе тачно 5 целих бројева а потом израчунава њихову средњу вредност. Све дефинисане променљиве морају бити целобројне.

Решење 1 (Приметите ефекте целобројног дељења)

/* funkcija main, odavde pocinje izvrsavanje programa */ int main() { int brojac; /* Broj vrednosti koje treba uneti */ int vrednost; /* vrednost koja se unosi */ int zbir; /* suma unetih vrednosti */ int srednja_vred; /* srednja vrednost */ /* inicijalizacija */ zbir = 0; /* inicijalizacija promenljive zbir */ brojac = 1; /* inicijalizacija promenljive brojac */ /* faza obrade */ while ( brojac <= 5 ) /* sve dok je brojac manji ili jednak 5... */ { printf( "Unesite vrednost: " ); /* Ispis poruke na ekran */ scanf( "%d", &vrednost ); /* ucitaj unetu vrednost */ zbir = zbir + vrednost ; /* dodaj unetu vrenost prom. zbir */ brojac = brojac + 1; /* inkrement promenljive brojac */ } /* kraj while petlje */ /* zavrsna faza */ srednja_vred = zbir / 5; /* celobrojno deljenje, odbacivanje decimalnog dela */ printf( "Srednja vrednost je %d\n", srednja_vred ); /* prikaz rezultata */ return 0; /* povratna vrednost uspesnog kraja programa */ } /* kraj funkcije main */ Напомена:

Променљива zbir и променљива srednja_vred су целобројне променљиве (типа int ) па је резултат дељења srednja_vred = zbir / 5 цео број тј. одбацује се децимални део

Page 174: Uvod u programski jezik C Univerzitet Singidunum

11

Решење 2 (Превазилажење проблема целобројног дељења)

/* funkcija main, odavde pocinje izvrsavanje programa */ int main() { int brojac; /* Broj vrednosti koje treba uneti */ int vrednost; /* vrednost koja se unosi */ int zbir; /* suma unetih vrednosti */ float srednja_vred; /* srednja vrednost */ /* inicijalizacija */ zbir = 0; /* inicijalizacija promenljive zbir */ brojac = 1; /* inicijalizacija promenljive brojac */ /* faza obrade */ while ( brojac <= 5 ) /* sve dok je brojac manji ili jednak 10... */ { printf( "Unesite vrednost: " ); /* Ispis poruke na ekran */ scanf( "%d", &vrednost ); /* ucitaj unetu vrednost */ zbir = zbir + vrednost ; /* dodaj unetu vrenost prom. zbir */ brojac = brojac + 1; /* inkrement promenljive brojac */ } /* kraj while petlje */ /* privremena promena tipa prom. zbir iz int u float, */ srednja_vred = (float)zbir / 5; printf( "Srednja vrednost je %f\n", srednja_vred ); /* format %f */ return 0; /* povratna vrednost uspesnog kraja programa */ } /* kraj funkcije main */ Напомена:

Променљива zbir типа int а променљива srednja_vred је типа float. Без обзира на то резултат дељења zbir/5 је цео број. Да би се превазишао овај проблем довољно је да бар један операнд буде типа float или double. У овом примеру је извршена привремена конверзија типа променљиве zbir из типа int у тип float наредбом (float)zbir.

Page 175: Uvod u programski jezik C Univerzitet Singidunum

12

Решење 3 (Претходно решење без променљиве srednja_vred )

/* funkcija main, odavde pocinje izvrsavanje programa */ int main() { int brojac; /* Broj vrednosti koje treba uneti */ int vrednost; /* vrednost koja se unosi */ int zbir; /* suma unetih vrednosti */ /* inicijalizacija */ zbir = 0; /* inicijalizacija promenljive zbir */ brojac = 1; /* inicijalizacija promenljive brojac */ /* faza obrade */ while ( brojac <= 5 ) /* sve dok je brojac manji ili jednak 10... */ { printf( "Unesite vrednost: " ); /* Ispis poruke na ekran */ scanf( "%d", &vrednost ); /* ucitaj unetu vrednost */ zbir = zbir + vrednost ; /* dodaj unetu vrenost prom. zbir */ brojac = brojac + 1; /* inkrement promenljive brojac */ } /* kraj while petlje */ /* privremena promena tipa prom. zbir iz int u float, */ printf( "Srednja vrednost je %f\n", (float)zbir / 5 ); /* format %f */ return 0; /* povratna vrednost uspesnog kraja programa */ } /* kraj funkcije main */

Page 176: Uvod u programski jezik C Univerzitet Singidunum

13

8. Напишите програм који очекује од корисника да унесе произвољан број целих бројева а потом израчунава њихову средњу вредност. Крајем уноса се сматра унос броја -1.

#include <stdio.h> /* funkcija main, odavde pocinje izvrsavanje programa */ int main() { int brojac; /* broj unetih vrednosti */ int vrednost; /* uneta vrednost */ int zbir; /* zbir unetih vrednosti */ float srednja_vred; /* srednja vrednost definisana kao realan broj */ /* inicijalizacija */ zbir = 0; /* inicijalizacija promenljive zbir */ brojac = 0; /* inicijalizacija promenljive brojac */ /* Obrada */ /* Procitaj prvu vrednost od korisnika */ printf( "Unesite vrednost, -1 za prekid: " ); /* Prikaz na ekran */ scanf( "%d", &vrednost ); /* Ucitaj unetu vrednost */ /* Kruzi u while petlji dok korisnik ne unese vrednost -1 */ while ( vrednost != -1 ) { zbir = zbir + vrednost; /* dodaj unetu vrednost promenljivoj zbir */ brojac = brojac + 1; /* inkrement brojaca */ /* ucitaj sledecu vrednost od korisnika */ printf( "Unesite vrednost, -1 za prekid: " ); /* Prikaz na ekran */ scanf("%d", &vrednost); /* Ucitaj sledecu unetu vrednost */ } /* kraj while petlje */ /* zavrsna faza */ /* Ako je uneta bar jedna vrednost */ if ( brojac != 0 ) { /* izracunaj srednju vrednost svih unetih vrednosti */ srednja_vred = ( float )zbir / brojac; /* privremena promena tipa */ /* prikazi srednju vrednost sa dve decimalne vrednosti */ printf( "Srednja vrednost je %.2f\n", srednja_vred ); } /* kraj if naredbe */ else /* Ako nije uneta ni jedna vrednost, ispis poruke */ { printf( "Nije unesena ni jedna vrednost\n" ); } /* kraj else naredbe */ return 0; /* povratna vrednost uspesnog kraja programa */ } /* kraj funkcije main */

Page 177: Uvod u programski jezik C Univerzitet Singidunum

14

9. Демонстрација рада са битским операторима

#include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int a = 0x0003; /* a= 03h = 0000 0011b = 3d */ int b = 0x0022; /* b= 22h = 0010 0010b= 34d */ int c = 5; int d; int e; int f = 0x0100; /* f=100h = 0001 0000 0000b = 256d */ printf("a=%d, b=%d, c=%d, f=%d\n",a, b, c, f); printf("-------------------------\n"); printf("a bitsko AND b = %xh \n",a & b); printf("a logicko AND b = %d \n",a && b); printf("a bitsko OR b = %xh \n",a | b); printf("a logicko OR b = %d \n",a || b); printf("a bitsko EXOR b = %xh \n\n",a ^ b); d = (a < b) && (b > c); /* logicko i (AND) */ printf("Vrednost logicke operacije (a < b) && (b > c) je %d \n\n",d); e = a << 3; /* shift a za 3 u levo */ printf("Vrednost a je %x, vrednost a << 3 je %xh\n",a,e); printf("Vrednost f je %d ili %xh\n",f,f); f =f^(1 << 4); /* shift jedinice za 4 mesta u levo */ /* 1<<4= 0000 0001b << 4 = 0001 0000b = 10h = 16d */ /* f EXOR 16 = 100h EXOR 10h = 0001 0000 0000b EXOR 0000 0001 0000b */ printf("Vrednost f je 100h a f^(1 << 4) je %d ili %xh \n",f,f); return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 178: Uvod u programski jezik C Univerzitet Singidunum

15

10. Напишите програм који на основу унетих података исписује број студената који су положили испит односно број студената који нису положили испит. Корисник за сваког студента који је положио испит уноси вредност 1, а за сваког студента који није положио испит уноси вредност 2. Корисник уносом вредности било ког целог броја изузев 1 или 2 покреће процедуру обраде унетих података и испис жељених вредности

Pешење 1

/* Upotreba do while petlje i if elseif else naredbe */ #include <stdio.h> /* funkcija main, odavde pocinje izvrsavanje programa */ int main() { /* definisanje i inicijalizacija promenljivih */ int polozilo = 0; /* broj studenata koji su polozili ispit */ int palo = 0; /* broj studenata koji nisu polozili ispit */ int rezultat; /* uneta vrednost */ int izlaz=0; do { printf( "Unesite rezultat ( 1=polozio, 2=pao ): " ); /* Ispis na ekran */ scanf( "%d", &rezultat ); /* Ako je rezultat 1, povecaj vrednost promenljive polozilo za 1 */ if ( rezultat == 1 ) { polozilo = polozilo + 1; } /* kraj if naredbe */ else if(rezultat==2)/* inace inkrementiraj vrednost promenljive palo */ { palo = palo + 1; } /* kraj else if*/ else { izlaz = 1; } } while ( izlaz == 0 ); /* kraj do while petlje */ /* zavrsna faza */ printf( "Broj studenata koji su polozili ispit %d\n", polozilo ); printf( "Broj studenata koji nisu polozili ispit %d\n", palo ); /* Ako je polozilo vise od 8 studenata ispisi Odlican uspeh" */ if ( polozilo > 8 ) { printf( "Odlican uspeh\n" ); } /* kraj if */ return 0; /* povratna vrednost uspesnog zavrsetka programa */ } /* kraj funkcije main */

Page 179: Uvod u programski jezik C Univerzitet Singidunum

16

Pешење 2

/* Upotreba do while petlje i if elseif else naredbe */ #include <stdio.h> /* funkcija main, odavde pocinje izvrsavanje programa */ int main() { /* definisanje i inicijalizacija promenljivih */ int polozilo = 0; /* broj studenata koji su polozili ispit */ int palo = 0; /* broj studenata koji nisu polozili ispit */ int rezultat; /* uneta vrednost */ do { printf( "Unesite rezultat ( 1=polozio, 2=pao ): " ); /* Ispis na ekran */ scanf( "%d", &rezultat ); /* Ako je rezultat 1, povecaj vrednost promenljive polozilo za 1 */ if ( rezultat == 1 ) polozilo++; else if(rezultat==2)/* inace inkrementiraj vrednost promenljive palo */ palo++; else break; } while (1); /* kraj do while petlje, BESKONAČNA petlja */ /* zavrsna faza */ printf( "Broj studenata koji su polozili ispit %d\n", polozilo ); printf( "Broj studenata koji nisu polozili ispit %d\n", palo ); /* Ako je polozilo vise od 8 studenata ispisi Odlican uspeh" */ if ( polozilo > 8 ) printf( "Odlican uspeh\n" ); return 0; /* povratna vrednost uspesnog zavrsetka programa */ } /* kraj funkcije main */

Напомена:

У другом решењу је примењена бесконачна do while петља, услов је увек испуњен while(1). Напуштање петље је омогућено наредбом break.

Page 180: Uvod u programski jezik C Univerzitet Singidunum

17

11. Применом for петље табеларно испишите вредности куба бројева од 1 до 10. /* primena for petlje */ #include<stdio.h> /* funkcija main, program pocinje da se izvrsava odavde */ int main(void) { int num; printf("\tk\tk na treci stepen\n"); for(num=1; num <= 10 ;num++ ) { printf("%5d %5d\n", num , num*num*num); } return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

12. Пример декремента for петље

/* primena for petlje, dekrement; if else if else grananje */ #include<stdio.h> /* funkcija main, program pocinje da se izvrsava odavde */ int main(void) { int num; printf("\nPreostalo je:\n"); for(num=10; num >= 1 ;num-- ) { if( num >=5 ) printf("%2d sekundi\n", num); else if(num > 1) printf("%2d sekunde \n", num); else printf("%2d sekunda \n", num); } printf("\nVreme isteklo\n"); return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 181: Uvod u programski jezik C Univerzitet Singidunum

18

13. Применом for петље и оператора ++ и – – написати програм који исписује табелу произвољних димензија и то тако да је табела попуњена знацима #. /* Upotreba for petlje */ #include <stdio.h> /* funkcija main, program pocinje da se izvrsava odavde */ int main() { int x; int y; int i; int j; /* Prikaz na ekran */ printf( "Unesite dva cela broja: " ); scanf( "%d%d", &x, &y ); /* procitaj vrednosti za x i y */ for ( i = 1; i <= y; i++ ) /* broj od 1 do y */ { for ( j = 1; j <= x; j++ ) /* broj od 1 do x */ { printf( "#" ); /* ispisi # */ } /* kraj unutrasnje for petlje */ printf( "\n" ); /* predji u novu liniju */ } /* kraj spoljasnje for petlje */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 182: Uvod u programski jezik C Univerzitet Singidunum

19

14. Употребом while петље испишите вредности од 1 до 10 тако да свака буде у новом реду /* Upotreba while petlje */ #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int brojac = 1; /* inicijalizacija */ while ( brojac <= 10 ) /* uslov ponavljanja */ { printf ( "%d\n", brojac ); /* ispis vrednosti brojaca */ brojac++; /* povecaj vrednost brojaca za 1 */ } /* kraj while petlje */ return 0; /* oznacava uspesan kraj programa */ } /* kraj funkcije main */

15. Употребом fоr петље испишите вредности од 1 до 10 тако да свака буде у новом реду

Pешење 1 #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int i; for(i = 1; i < 11; i++) /* uslov ponavljanja */ { printf ( "%d\n", i ); /* ispis vrednosti brojaca */ } /* kraj for petlje */ return 0; /* oznacava uspesan kraj programa */ } /* kraj funkcije main */

Page 183: Uvod u programski jezik C Univerzitet Singidunum

20

Pешење 2 /* Upotreba for petlje */ #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int i; i = 1; for( ; i < 11; i++) /* uslov ponavljanja */ { printf ( "%d\n", i ); /* ispis vrednosti brojaca */ } /* kraj for petlje */ return 0; /* oznacava uspesan kraj programa */ } /* kraj funkcije main */

Напомена:

Сваки од три елемента унутар округлих заграда for петље може да се изостави (напише на другом месту). Приметите да је иницијализација променљиве i (i=0) обављена пре почетка for петље. Pешење 3

/* Upotreba while petlje */ #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int i; i = 1; for( ; i < 11; ) /* uslov ponavljanja */ { printf ( "%d\n", i ); /* ispis vrednosti brojaca */ i++; } /* kraj for petlje */ return 0; /* oznacava uspesan kraj programa */ } /* kraj funkcije main */

Напомена: Сваки од три елемента унутар округлих заграда for петље може да се изостави (напише на другом месту). Приметите да је: иницијализација променљиве i (i=0) обављена пре почетка for петље и операција инкремента променљиве i (i++) обављена унутар тела for петље.

Page 184: Uvod u programski jezik C Univerzitet Singidunum

21

Решење 4 /* Upotreba for petlje */ #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int i; i = 1; for( ; ; ) /* uslov ponavljanja */ { printf ( "%d\n", i ); /* ispis vrednosti brojaca */ i++; if(i > 10) break; } /* kraj for petlje */ return 0; /* oznacava uspesan kraj programa */ } /* kraj funkcije main */

Напомена:

Сваки од три елемента унутар округлих заграда for петље може да се изостави (напише на другом месту). Приметите да је: иницијализација променљиве i (i=0) обављена пре почетка for петље, услов напуштања for петље је реализован унутар тела петље ( if(i > 10)break; ) и операција инкремента променљиве i (i++) обављена унутар тела for петље.

16. Употребом fоr петље испишите парне бројеве од 1 до 20.

#include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int broj; /* definisanje promenljive */ for ( broj = 2; broj < 21; broj += 2 ) { printf("%2d\n", broj); /* ispis vrednosti */ } /* kraj for */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 185: Uvod u programski jezik C Univerzitet Singidunum

22

17. Употребом fоr петље испишите непарне бројеве од 1 до 20. #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int broj; /* definisanje promenljive */ for ( broj = 1; broj < 21; broj += 2 ) { printf("%2d\n", broj); /* ispis vrednosti */ } /* kraj for */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

18. Употребом fоr петље израчунајте збир свих парних бројева од 1 до 100.

/* Sumiranje sa for petljom 2+4+6+8+...+100 */ #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int zbir= 0; /* inicijalizacija promenljive zbir */ int broj; /* broj koji se dodaje zbiru */ for ( broj = 2; broj <= 100; broj += 2 ) { zbir += broj; /* dodaj broj zbiru */ } /* kraj for */ printf( "Suma je %d\n", zbir ); /* Ispisi zbir */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 186: Uvod u programski jezik C Univerzitet Singidunum

23

19. Демострација примене наредбе break за напуштање for петље

/* Upotreba naredbe break */ #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int i; /* brojac */ /* 10 puta uradi sledece naredbe... */ for ( i = 1; i <= 10; i++ ) { /* Ako je i jednako 5, napusti for petlju */ if ( i == 5 ) { printf( "\nNapustam for petlju naredbom break\n"); break; /* prekid for petlje */ } /* kraj if */ printf( "%d ", i ); /* prikazi vrednost promenljive i x */ } /* kraj for */ printf( "\nNakon izlaska iz for petlje vrednost brojaca je: %d\n", i ); return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

20. Демострација примене наредбе break за напуштање while петље /* Upotreba naredbe break */ #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int i=0; /* brojac */ /* 10 puta uradi sledece naredbe... */ while ( i <= 10) { i++; /* Ako je i jednako 5, napusti for petlju */ if ( i == 5 ) { printf( "\nNapustam while petlju naredbom break\n"); break; /* prekid for petlje */ } /* kraj if */ printf( "%d ", i ); /* prikazi vrednost promenljive i x */ } /* kraj for */ printf( "\nNakon izlaska iz while petlje vrednost brojaca je: %d\n", i ); return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 187: Uvod u programski jezik C Univerzitet Singidunum

24

21. Демострација примене наредбе continue за “прескакање” једног корака унутар for петље

/* Upotreba naredbe continue */ #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int i; /* brojac */ for ( i = 1; i <= 10; i++ ) /* 10 puta uradi sledece naredbe... */ { /* Ako je i jednako 5, napusti preskoci naredbe do kraja for petlje */ if ( i == 5 ) { printf( "\n\n Ne napustam for petlju, samo preskacem jednu vrednost\n\n"); continue; /* preskoci naredbe do kraja for petlje */ } /* kraj if */ printf( "%d\n", i ); /* prikazi vrednost promenljive i */ } /* kraj for */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 188: Uvod u programski jezik C Univerzitet Singidunum

25

22. Демострација примене наредбе continue за “прескакање” једног корака унутар while петље

/* Upotreba naredbe continue */ #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int i; /* brojac */ while( i <= 10) /* radi sve dok je ... */ { /* Ako je i jednako 5, preskoci naredbe do kraja while petlje */ if ( i == 5 ) { printf( "\n\n Ne napustam while petlju, preskacem jednu vrednost\n\n"); continue; /* preskoci naredbe do kraja while petlje */ } /* kraj if */ printf( "%d\n", i ); /* prikazi vrednost promenljive i */ } /* kraj while */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 189: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C Карактери и стрингови

Задаци за вежбу

Page 190: Uvod u programski jezik C Univerzitet Singidunum

2

Програм за демонстрацију функција isdigit, isalpha, isalnum, и isxdigit

/* funkcije: isdigit, isalpha, isalnum, isxdigit */ #include <stdio.h> #include <ctype.h> int main() { printf( "%s\n%s%s\n%s%s\n\n", "Prema funkciji isdigit: ", isdigit('8') ? ("8 je ") : ("8 nije "), "cifra", isdigit('#') ? ("# je ") : ("# nije "), "cifra" ); printf( "%s\n%s%s\n%s%s\n%s%s\n%s%s\n\n", "Prema funkciji isalpha:", isalpha( 'A' ) ? "A je " : "A nije ", "slovo", isalpha( 'b' ) ? "b je " : "b nije ", "slovo", isalpha( '&' ) ? "& je " : "& nije ", " slovo", isalpha( '4' ) ? "4 je " : "4 nije ", "slovo" ); printf( "%s\n%s%s\n%s%s\n%s%s\n\n", "Prema funkciji isalnum:", isalnum( 'A' ) ? "A je " : "A nije ", "cifra ili broj", isalnum( '8' ) ? "8 je " : "8 nije ", "cifra ili broj", isalnum( '#' ) ? "# je " : "# nije ", "cifra ili broj" ); printf( "%s\n%s%s\n%s%s\n%s%s\n%s%s\n%s%s\n", "Prema funkciji isxdigit:", isxdigit( 'F' ) ? "F je " : "F nije ", "heksadecimalna cifra", isxdigit( 'J' ) ? "J je " : "J nije ", "heksadecimalna cifra", isxdigit( '7' ) ? "7 je " : "7 nije ", "heksadecimalna cifra", isxdigit( '$' ) ? "$ je " : "$ nije ", "heksadecimalna cifra", isxdigit( 'f' ) ? "f je " : "f nije ", "heksadecimalna cifra" ); return 0; /* uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 191: Uvod u programski jezik C Univerzitet Singidunum

3

Програм за демонстрацију функција islower, isupper, tolower, toupper

#include <stdio.h> #include <ctype.h> int main() { printf( "%s\n%s%s\n%s%s\n%s%s\n%s%s\n\n", "Prema funkciji islower:", islower( 'p' ) ? "p je " : "p nije ", "malo slovo", islower( 'P' ) ? "P je " : "P nije ", "malo slovo", islower( '5' ) ? "5 je " : "5 nije ", "malo slovo", islower( '!' ) ? "! je " : "! nije ", "malo slovo" ); printf( "%s\n%s%s\n%s%s\n%s%s\n%s%s\n\n", "Prema fukciji isupper:", isupper( 'D' ) ? "D je " : "D nije ", "veliko slovo", isupper( 'd' ) ? "d je " : "d nije ", "veliko slovo", isupper( '8' ) ? "8 je " : "8 nije ", "veliko slovo", isupper( '$' ) ? "$ je " : "$ nije ", "veliko slovo" ); printf( "%s%c\n%s%c\n%s%c\n%s%c\n", "m konvertovano u veliko slovo je ", toupper( 'm' ), "7 konvertovano u veliko slovo je ", toupper( '7' ), "$ konvertovano u veliko slovo je ", toupper( '$' ), "K konvertovano u malo slovo je ", tolower( 'K' ) ); return 0; /* oznacava uspesan zavrsetak */ } /* kraj funkcije main */

Page 192: Uvod u programski jezik C Univerzitet Singidunum

4

Програм за демонстрацију функција isspace, isgraph

#include <stdio.h> #include <ctype.h> int main() { printf( "%s\n%s%s%s\n%s%s%s\n%s%s\n\n", "Prema funkciji isspace:", "Novi red", isspace('\n') ? " je " : " nije ", "beli znak", "Tab", isspace('\t') ? " je " : " nije ", "beli znak", isspace( '%' ) ? "% je " : "% nije ", "beli znak" ); printf( "%s\n%s%s\n%s%s%s\n", "Prema funkciji isgraph:", isgraph( 'Q' ) ? "Q je " : "Q nije ", "stampajuci znak ", "Space", isgraph( ' ' ) ? " je " : " nije ", "stampajuci znak " ); return 0; /* uspesan zavrsetak programa */ } /* kraj funkcije main */ Напишите програм који конвертује стринг "99.50" у поменљиву типа double и потом поменљиву типа double дели са 3 и исписује резултат. #include <stdlib.h> int main() { char str_a[]="99.50"; double nova; /* promenljiva koja cuva konvertovani string */ nova = atof(str_a); printf("String \"%s\" konvertovan u double je %.2f\n", str_a, nova); printf("Konvertovana vrednost podeljena sa 3 je %.2f\n",nova/3 ); return 0; /* uspesan zavrsetak programa */ } /* kraj funkcije main */ Напомена: Да пи исписали знаке навода унутар наредбе printf морате користити ескејп

секвенцу ( printf("String \"%s\"...)

Page 193: Uvod u programski jezik C Univerzitet Singidunum

5

Напишите програм који конвертује стринг "3245" у поменљиву типа int и потом од поменљиве типа int одузима 245 и исписује резултат. #include <stdio.h> #include <stdlib.h> int main() { char str_a[]="3245"; int celo; /* promenljiva koja cuva konvertovani string */ celo = atoi(str_a); printf("String \"%s\" konvertovan u integer je %d\n", str_a, celo); printf("Konvertovana vrednost minus 245 je %d\n",celo -245 ); return 0; /* uspesan zavrsetak programa */ } /* kraj funkcije main */ Напишите програм који од корисника захтева да унесе једну линију текста а потом исписује унети текст уназад Решење 1: #include<stdio.h> #include<string.h> int main() { char linija_teksta[80]; /* definisanje niza od 80 elemenata */ char *ptr; /* pokazivac na char promenljivu */ int indeks_poklapanja; int i; printf( "Unesite jednu liniju teksta:\n" ); /* Primena funkcije gets da se u zadati niz ucita jedna linija teksta */ gets(linija_teksta); /* Primena funkcije strchr da se u zadatom nizu pronadje pozicija (indeks) prvog pojavljivanja zadatog karaktera, ovde trazimo kraj stringa ili karakter '\0' */ ptr=strchr(linija_teksta, '\0'); indeks_poklapanja=ptr - linija_teksta - 1; /* moze i ovako: indeks_poklapanja=ptr-&linija_teksta[0] - 1; ili indeks_poklapanja=ptr-&linija_teksta - 1; */ printf( "\nLinija odstampana unazad je:\n" ); for(i=indeks_poklapanja;i>=0;i--) { printf("%c", linija_teksta[i]); } printf("\n"); return 0; /* oznacava uspesan kraj programa */ } /* kraj funkcije main */

Page 194: Uvod u programski jezik C Univerzitet Singidunum

6

Решење 2: #include<stdio.h> #include<string.h> int main() { char linija_teksta_a[80]; /* definisanje niza od 80 elemenata */ char linija_teksta_b[80]; /* definisanje niza od 80 elemenata */ char *ptr; /* pokazivac na char promenljivu */ int indeks_poklapanja; int i; printf( "Unesite jednu liniju teksta:\n" ); /* Primena funkcije gets da se u zadati niz ucita jedna linija teksta */ gets(linija_teksta_a); /* trazimo kraj stringa ili ADRESU na kojoj se nalazi karakter '\0' */ ptr=strchr(linija_teksta_a, '\0'); /* indeks se dobija kao razlika adresa */ indeks_poklapanja=ptr - linija_teksta_a - 1; printf( "\nLinija odstampana unazad je:\n" ); /* u niz linija_teksta_b upisi unazad sadrzaj niza linija teksta_a */ /* for petlja ima samo jednu naredbu, moze i bez { } */ for(i=0;i<=indeks_poklapanja;i++) linija_teksta_b[i]=linija_teksta_a[indeks_poklapanja-i]; /* string mora da se zavrsi nul karakterom! */ linija_teksta_b[i]='\0'; /* Primenom funkcije puts ispisujemo sadrzaj stringa */ /* puts zamenjuje '\0' sa '\n' pa automatski imamo novi red! */ puts(linija_teksta_b); return 0; /* oznacava uspesan kraj programa */ } /* kraj funkcije main */

Page 195: Uvod u programski jezik C Univerzitet Singidunum

7

Напишите програм који од корисника захтева да унесе једну линију текста а потом исписује унети текст. За прихват унетог текста користити функцију а за испис функцију Решење 1 /* Primena funkcija getchar i puts */ #include <stdio.h> int main() { char znak; /* promen. u koju se upisuje znak koji unese korisnik */ char recenica[80]; /* niz od 80 elemenata */ int i = 0; /* brojac inicijalizovan na 0 */ /* Ispis poruke na monitor */ puts( "Unesite jednu liniju teksta:" ); /* Inicijalizacija promenljive znak */ znak=' '; /* Primena funkcije getchar da se procita svaki uneti znak (character) */ while (znak != '\n') /* radi sve dok se ne pritisne ENTER */ { znak = getchar(); /* ucitaj jedan znak */ recenica[i] = znak; /* upisi ucitani znak u niz*/ i++; } /* kraj while petlje */ recenica[i] = '\0'; /* string se mora zavrsit ovim znakom */ /* funkcija puts ispisuje sadrzaj stringa na monitor */ puts( "\nUneli ste sledeci tekst:" ); puts(recenica); return 0; /* oznacava uspesan zavrsetak programa */ } /* karaj funkcije main */

Page 196: Uvod u programski jezik C Univerzitet Singidunum

8

Решење 2 #include <stdio.h> int main() { char znak; /* promen. u koju se upisuje znak koji unese korisnik */ char recenica[80]; /* niz od 80 elemenata */ int i = 0; /* brojac inicijalizovan na 0 */ /* Ispis poruke na monitor */ puts( "Unesite jednu liniju teksta:" ); /* Primena funkcije getchar da se procita svaki uneti znak (character) */ while ( ( znak = getchar() ) != '\n') { recenica[i++] = znak; } /* kraj while petlje */ recenica[i] = '\0'; /* string se mora zavrsit ovim znakom */ /* funkcija puts ispisuje sadrzaj stringa na monitor */ puts( "\nUneli ste sledeci tekst:" ); puts(recenica); return 0; /* oznacava uspesan zavrsetak programa */ } /* karaj funkcije main */

Дефинишите стринг Happy Birthday to You и запишите га у низ x . Дефинишите још двс низа: y и z. Применом наредбе strcpy ископирајте садржај стринга x у стринг y, а потом првих 14 знакова низа y ископирајте у низ z. Прикажите садржај сва три стринга.

Решење 1 /* Primena funkcija strcpy i strncpy */ #include <stdio.h> #include <string.h> int main() { char x[] = "Happy Birthday to You"; /* inicijalizacija niza x */ char y[ 25 ]; /* definisanje niza y */ char z[ 15 ]; /* definisanje niza z */ printf( "%s %s\n", "Sadrzaj stringa x je:", x); strcpy( y, x ); /* kopiraj sadrzaj stringa x u string y */ printf( "%s %s\n","Sadrzaj stringa y je:", y); /* Kopiraj prvih 14 karaktera iz x u z. Ne kopira se null character! */ strncpy( z, x, 14 ); z[14] = '\0'; /* dodaj nul karakter stringu z */ printf( "String u nizu z je: %s\n", z ); return 0; /* oznacava uspesan zavrsetaka programa */ } /* kraj fukcije main */

Page 197: Uvod u programski jezik C Univerzitet Singidunum

9

Решење 2 #include <stdio.h> #include <string.h> int main() { char x[] = "Happy Birthday to You"; /* inicijalizacija niza x */ char y[ 25 ]; /* definisanje niza y */ char z[ 15 ]; /* definisanje niza z */ puts("Sadrzaj stringa x je:"); puts(x); /* kopiraj sadrzaj stringa x u string y */ strcpy( y, x ); puts("\nSadrzaj stringa y je:"); puts(y); /* Kopiraj prvih 14 karaktera iz x u z. Ne kopira se null character! */ strncpy( z, y, 14 ); z[14] = '\0'; /* dodaj nul karakter stringu z */ puts( "\nString u nizu z je:"); puts(z); return 0; /* oznacava uspesan zavrsetaka programa */ } /* kraj fukcije main */

Page 198: Uvod u programski jezik C Univerzitet Singidunum

10

Дефинишите низ (стринг) s1 од 20 елемената и иницијализујте га на вредност "Srecna ", дефинишите низ (стринг) s2 и иницијализујте га на вредност "Nova godina " и дефинишите стринг s3 од 40 елемената та тако да буде "празан". Применом функције strcat спојте садржаје стринга s1 и s2, испишите вредност новодобијеног стринга. Дописите првих 7 знакова новодобијеног стринга s1 на стринг s3 и исписите садржај стринга s3. На садржај стринга s3 допишите садржај стринга s1 и испишите садржај стринга s3.

/*strcat, strncat */ #include <stdio.h> #include <string.h> int main() { char s1[20] = "Srecna "; /* inicijalizacija niza s1 */ char s2[] = "Nova godina "; /* inicijalizacija niza s2 */ char s3[40] = ""; /* inicijalizacija niza s3 као "prazаn" niz */ /* Ispis sadrzaja stringova s1 i s2*/ printf( "s1 = %s \ns2 = %s \n", s1, s2 ); /* na sadrzaj stringa s1 dopisi sadrzaj stringa s2 */ strcat( s1, s2 ); /* ispisi sadrzaj stringa s1 */ printf( "Nakon naredbe strcat(s1, s2),\t sadrzaj stringa s1 je: %s\n", s1 ); /* dopisi prvih 7 karaktera stringa s1 u string s3.*/ strncat( s3, s1, 7); printf( "Nakon naredbe strncat(s3,s1,7),\t sadrzaj stringa s3 je: %s\n",s3); /* na sadrzaj stringa s3 dopisi sadrzaj stringa s1 */ strcat( s3, s1); printf( "Nakon naredbe strcat(s3, s1),\t sadrzaj stringa s3 je: %s\n",s3); return 0; /* Oynacava uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 199: Uvod u programski jezik C Univerzitet Singidunum

11

Дефинишите низ (стринг) s1 и иницијализујте га на вредност "Happy New Year". Дефинишите низ (стринг) s2 и иницијализујте га на вредност "Happy New Year". Дефинишите низ (стринг) s3 и иницијализујте га на вредност " Happy Holidays". Применом наредбе strcmp поредите њихоове мођусобне односе. #include <stdio.h> #include <string.h> int main() { const char s1[] = "Happy New Year"; /* inicijalizacija stringa */ const char s2[] = "Happy New Year"; /* inicijalizacija stringa */ const char s3[] = "Happy Holidays"; /* inicijalizacija stringa */ printf("%s %s\n","Sadrzaj stringa s1 je:", s1); printf("%s %s\n","Sadrzaj stringa s2 je:", s2); printf("%s %s\n","Sadrzaj stringa s3 je:", s3); printf("\nRezultat funkcije %s je: %2d","strcmp(s1, s2) = ", strcmp( s1, s2 ) ); printf("\nRezultat funkcije %s je: %2d","strcmp(s1, s3) = ", strcmp( s1, s3 ) ); printf("\nRezultat funkcije %s je: %2d\n","strcmp(s3, s1) = ", strcmp( s3, s1 ) ); return 0; } /* kraj funkcije main */ Употребом функције strchr одредите да ли се знаци 'n' и 'z' налазе у стрингу "Ovo je jedan test" /* strchr */ #include <stdio.h> #include <string.h> int main() { const char niz_znakova[] = "Ovo je jedan test"; /* inicijalizacija niza */ char znak1 = 'n'; char znak2 = 'z'; /* Ako je znak1 (karakter) pronadjen u stringu... */ if ( strchr( niz_znakova, znak1 ) != NULL ) printf( "Znak \'%c\' je pronadjen u stringu \"%s\".\n", znak1, niz_znakova); else /* ako znak1 nije pronadjen */ printf( "Znak \'%c\' nije pronadjen u stringu \"%s\".\n",znak1,niz_znakova); /* Ako je znak2 (karakter) pronadjen u stringu... */ if ( strchr( niz_znakova, znak2 ) != NULL ) printf( "znak \'%c\' je pronadjen u stringu \"%s\".\n", znak2, niz_znakova); else /* ako znak2 nije pronadjen */ printf( "Znak \'%c\' nije pronadjen u stringu \"%s\".\n",znak2,niz_znakova); return 0; } /* kraj funkcije main */

Page 200: Uvod u programski jezik C Univerzitet Singidunum

12

Употребом функције strstr одредите да ли се и на којим местима стринг "def" појављује у стрингу "abcdefabcdef" /* strstr */ #include <stdio.h> #include <string.h> int main() { char str1[] = "abcdefabcdef"; /* string koji se pretrazuje */ char str2[] = "def"; /* string koji se trazi */ char *pch; printf( "%s \"%s\"\n", "string1 = ", str1); printf( "%s \"%s\"\n", "string2 = ", str2); pch=str1; /* pch=&str1[0] ili pch=&str1; ; */ do { pch=strstr(pch, str2); if(pch != NULL) { printf("String \"%s\" se nalazi u stringu \"%s\" na poziciji %2d\n", str2, str1, pch-str1+1); pch++; } /* kraj if naredbe */ }while(pch != NULL); return 0; } /* kraj funkcije main */ Употребом функције strlen одредите дужину стрингова: "abcdefghijklmnopqrstuvwxyz", "tri" и "Kikinda" /* strlen */ #include <stdio.h> #include <string.h> int main() { /* initialize 3 char pointers */ const char str1[] = "abcdefghijklmnopqrstuvwxyz"; const char str2[] = "tri"; const char str3[] = "Kikinda"; printf("%s \"%s\" %s %d\n","Duzina stringa", str1, "je", strlen(str1)); printf("%s \"%s\" %s %d\n","Duzina stringa", str2, "je", strlen(str2)); printf("%s \"%s\" %s %d\n","Duzina stringa", str3, "je", strlen(str3)); return 0; /* oznacava uspesan zavrsetak */ } /* kraj funkcije main */

Page 201: Uvod u programski jezik C Univerzitet Singidunum

Увод у програмски језик C Функције

Задаци за вежбу

Page 202: Uvod u programski jezik C Univerzitet Singidunum

1. Написати програм који исписује квадрате вредности од 1 до 10. Рачунање квадрата реализовати у посебној функцији.

Решење:

/* Kreiranje i primena funkcije */ #include <stdio.h> int kvadrat( int y ); /* deklaracija ili prototip funkcije */ /* funkcija main, mesto pocetka izvrsavanja programa */ int main() { int x; /* brojac */ /* 10 pita izracunaj i ispisi kvadrat x */ for (x = 1; x <= 10; x++) { printf( "%d ", kvadrat(x) ); /* poziv funkcije */ } /* kraj for petlje */ printf( "\n" ); return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj main */ /* funkcija kvadrat, definicija */ int kvadrat(int y) /* u y se kopira vrednost x */ { return y*y; /* vraca kvadrat y kao int */ } /* kraj funkcije kvadrat */

Page 203: Uvod u programski jezik C Univerzitet Singidunum

2. Написати програм који од три унета цела броја проналази највећи. Решење: /* Pronalazenje maksimuma od tri broja */ #include <stdio.h> int maximum( int x, int y, int z ); /* prototip funkcije */ /* funkcija main, mesto pocetka izvrsavanja programa */ int main() { int number1; /* prvi broj */ int number2; /* drugi broj */ int number3; /* treci broj */ printf( "Unesite tri cela broja: " ); scanf( "%d%d%d", &number1, &number2, &number3 ); /* number1, number2 i number3 su argumenti pri pozivu funk. maximum */ printf( "Maksimalna vrednost je: %d\n", maximum( number1, number2, number3 ) ); return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */ /* Funkcija maximum, definicija */ /* x, y z su formalni argumenti */ int maximum( int x, int y, int z ) { int max = x; /* pretpostavimo da je x najveci broj */ if(y > max) /* ako je y veci onda je max jednak y */ { max = y; } /* kraj if */ if(z > max) /* ako je z vece od max, onda je max jednak z */ { max = z; } /* kraj if*/ return max; /* max je najveca vrednost */ } /* kraj funkcije maximum */

Page 204: Uvod u programski jezik C Univerzitet Singidunum

3. Написати програм који исписује следеће вредности:

1 2 1 3 2 1 4 3 2 1 5 4 3 2 1 6 5 4 3 2 1 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1 Испис вредности реализовати у потпрограму (функцији) broji_unazad( int x) са једним улазним аргументом.

Решење:

/* prenos argumenta po vrednosti*/ #include <stdio.h> void broji_unazad(int x); /* prototip funkcije */ int main() { int br; for (br=1; br <= 10; br++) { broji_unazad(br); } return 0; } /* kraj funkcije main */ void broji_unazad(int x) /* definicija funkcije */ { int i; for (i = x; i > 0; i--) { printf("%d", x); x--; } putchar('\n'); /* novi red */ } /* kraj funkcije broji_unazad */

Page 205: Uvod u programski jezik C Univerzitet Singidunum

4. Демонстрација прослеђивања аргумената по адреси у функцију void zameni(int *a, int *b); /* prototip funkcije */ #include <stdio.h> int main() { int x=6, y=10; printf("Pre poziva funkcije, x = %d i y = %d\n\n", x, y); zameni(&x, &y); /* Poziv funkcije i prosledjivanje promen. po adresi */ printf("Nakon povratka iz funkcije x = %d i y = %d\n\n", x, y); return 0; } /* kraj funkcije main */ /* definicija funkcije */ void zameni(int *a, int *b) /* funkcija nema povratnu vrednost, tip void */ { /* u promenljivu a je preslikana adresa promen. x */ /* u promenljivu b je preslikana adresa promen. y */ int temp; temp= *a; /* u promen. temp upisi vrednost na koju pokazuje pointer a, tj. vrednost prom. x */ *a = *b; /* na adresu na koju ukazuje pointer a upisi vrednost sa adrese na koju ukazuje pointer b */ *b = temp; /* na adresu na koju ukazuje pointer b upisi vrednost promen. temp*/ } /* kraj funkcije zameni */

Page 206: Uvod u programski jezik C Univerzitet Singidunum

5. Написати програм који у четири реда исписује по пет случајних вредности између 1 и 6. (Симулација бацања коцке). Користити функцију rand().

rand Генерише псеудослучајан број.

декларација (прототип) int rand( void );

Пвратна (Return) вредност Враћа псеудослучајан број у распону од 0 до вредности дефинисане константом RAND_MAX. (32767), <stdlib.h>.

Уколико желите да се псеудослучајне вредности не понављају користите функцију srand пре позива функције rand.

Решење: #include <stdio.h> #include <stdlib.h> /* funkcija main, mesto pocetka izvrsavanja programa */ int main() { int i; /* brojac */ /* ponovi 20 puta */ for ( i = 1; i <= 20; i++ ) { /* uzmi slucajan (random) broj izmedju 1 i 6 i ispisi ga */ printf( "%10d", 1 + ( rand() % 6 ) ); /* moduo po 6 daje vrednosti od 0 do 5 */ /* ako je brojac deljiv sa 5, predji u novi red */ if(i % 5 == 0) { printf( "\n" ); } /* kraj if */ } /* kraj for */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */ Сваки пут када покренете овај програм добићете исте вредности. Каква је то функција rand? Проучите наредна два примера

Page 207: Uvod u programski jezik C Univerzitet Singidunum

6. Испитајте фреквенцију појављивања бројева од 1 до 6 применом функције rand() на узорку од 6000 покушаја.

Решење: #include <stdio.h> #include <stdlib.h> /* funkcija main, mesto pocetka izvrsavanja programa */ int main() { int frekvencija1 = 0; /* brojac pojavljivanja broja 1 */ int frekvencija2 = 0; /* brojac pojavljivanja broja 2 */ int frekvencija3 = 0; /* brojac pojavljivanja broja 3 */ int frekvencija4 = 0; /* brojac pojavljivanja broja 4 */ int frekvencija5 = 0; /* brojac pojavljivanja broja 5 */ int frekvencija6 = 0; /* brojac pojavljivanja broja 6 */ int i; int vrednost; /* dobijena vrednost od generatora pseudoslucajnih brojeva 6 */ /* ponovi 6000 puta i daj rezultat */ for (i = 1; i<=6000; i++ ) { vrednost = 1 + rand() % 6; /* pseudosl. broj od 1 do 6 */ /* odredi vrednost i povecaj odgovarajuci brojac */ switch ( vrednost ) { case 1: /* dobijena jedinica */ ++frekvencija1; break; case 2: /* dobijena dvojka */ ++frekvencija2; break; case 3: /* dobijena trojka */ ++frekvencija3; break; case 4: /* dobijena cetvorka */ ++frekvencija4; break; case 5: /* dobijena petica */ ++frekvencija5; break; case 6: /* dobijena sestica */ ++frekvencija6; break; } /* kraj switch */ } /* kraj for */ /* prikazi rezultat u tabelarnom formatu */ printf( "%s%13s\n", "Vrednost", "Frekvencija" ); printf( " 1%13d\n", frekvencija1 ); printf( " 2%13d\n", frekvencija2 ); printf( " 3%13d\n", frekvencija3 ); printf( " 4%13d\n", frekvencija4 ); printf( " 5%13d\n", frekvencija5 ); printf( " 6%13d\n", frekvencija6 ); return 0;/* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */ 7. Потпуно исто решење, реализовано употребом низа, уместо шест променљивих дато је у наставку

Page 208: Uvod u programski jezik C Univerzitet Singidunum

#include <stdio.h> #include <stdlib.h> /* funkcija main, mesto pocetka izvrsavanja programa */ int main() { int frekvencija[7] = {0,0,0,0,0,0,0}; /* brojac pojavljivanja brojеva 1 do 6 */ int i; int vrednost; /* dobijena vrednost od generatora pseudoslucajnih brojeva 6 */ /* ponovi 6000 puta i daj rezultat */ for (i = 1; i<=6000; i++ ) { vrednost = 1 + rand() % 6; /* pseudosl. broj od 1 do 6 */ /* odredi dobijenu vrednost i povecaj odgovarajuci brojac */ ++frekvencija[vrednost]; } /* kraj for */ /* prikazi rezultat u tabelarnom formatu */ printf( "%s%13s\n", " Vrednost", "Frekvencija" ); for(i=1;i<7;i++) printf( "\t%d%13d\n", i,frekvencija[i] ); return 0;/* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 209: Uvod u programski jezik C Univerzitet Singidunum

8. Претходни пример показује да се бројеви појављују са подједнаком вероватноћом. Суштински проблем је што функција rand представља псеудослучјни а не случајни генератор. То значи да бројеви које она генерише статистички гледано задовољавају потребна својства али је то (довољно дугачак) низ бројева који је периодичан (понавља се), и увек креће од истог места. Да би функција rand не би генерисала бројеве од истог места потребно је пре њеног позивања позвати функцију srand(unsigned int), која има unsigned int као аргумент и поставља место почетка функције rand. У наредном примеру је демонстрирана употреба функције srand(unsigned int)

#include <stdlib.h> #include <stdio.h> /* funkcija main, mesto pocetka izvrsavanja programa */ int main() { int i; /* brojac */ unsigned int seed; /* broj koji odredjuje mesto pocetka rada rand generatora */ printf( "Unesite vrednost parametra za rand generator: " ); scanf( "%u", &seed ); /* primetite %u za unsigned int */ srand( seed ); /* postavi pocetnu vrednost rand generatora */ /* petlja 10 puta */ for ( i = 1; i <= 10; i++ ) { /* uzmi slucajnu vrednost od 1 do 6 i ispisi je */ printf( "%10d", 1 + ( rand() % 6 ) ); /* ako je brojac deljiv sa 5 predji u novi red */ if ( i % 5 == 0 ) { printf( "\n" ); } /* kraj if */ } /* kraj for */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj main */

Page 210: Uvod u programski jezik C Univerzitet Singidunum

9. Сваки пут када унесете исту вредност добићете исте бројеве. Како обезбедити да функција rand увек почне од друге почетне вредности. Потребно је да се сваки пут функцији srand постави другачији аргумент. Један од начина је да се као аргумент постави излаз функције time(NULL) која враћа текуће време дефинисано у рачунару. Ова функција враћа време (број секунди) протекао од поноћи (00:00:00), 1. Јануара 1970. године. Aргумент NULL је потребан да би функција time враћала вредност у бројчаном формату. Претходни пример је сада могуће написати на следећи начин

#include <stdlib.h> #include <stdio.h> #include <time.h> /* sadrzi prototip funkcije time */ /* funkcija main, mesto pocetka izvrsavanja programa */ int main() { int i; /* brojac */ srand(time(NULL)); /* postavi pocetnu vrednost rand generatora svaki put kad se pokrene program na drugu vrednost*/ /* petlja 10 puta */ for(i = 1; i <= 10; i++) { /* uzmi slucajnu vrednost od 1 do 6 i ispisi je */ printf( "%10d", 1 + ( rand() % 6 ) ); /* ako je brojac deljiv sa 5 predji u novi red */ if( i % 5 == 0 ) { printf( "\n" ); } /* kraj if */ } /* kraj for */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj main */

Page 211: Uvod u programski jezik C Univerzitet Singidunum

10. Унутар функције се не може дефинисати нова функција али функција може позивати другу функцију па и сама себе. Појава када функција позива саму себе се назива рекурзија. Размотримо проблем рачунања факторијела. Факторијел не негативног броја n које се обележава као n! је производ:

n! =n*(n-1)*(n-2)*(n-3)*...*1 стим да је 1! = 1

0! = 1

На пример: 5! = 5*4*3*2*1= 120

Факторијел броја broj се може израчунати применом for петље:

faktorijel = 1; for(i=broj; i>=1; i--) { faktorijel *= i; /* faktorijel = faktorijel * i; */ } али се може добити и применом рекурзивне функције: Рекурзивна дефиниција факторијела се може извести из следећих релација:

5! = 5*4*3*2*1= 5*(4*3*2*1) = 5*(4!) 4!= 4*3*2*1=4*(3*2*1) = 4*(3!) 3! = 3*2*1=3*(2*1) = 3*(2!) 2! = 2*1=2*(1) = 2*(1!) 1! = 1 ...

Page 212: Uvod u programski jezik C Univerzitet Singidunum

#include <stdio.h> long faktorijel(long broj); /* prototip funkcije */ /* funkcija main, mesto pocetka izvrsavanja programa */ int main() { int i; /* brojac */ /* 11 puta ponovi naredbe, svaki put izracunaj faktorijel(i) i ispisi rezultat */ for ( i = 0; i <= 10; i++ ) { printf("%2d! = %ld\n", i, faktorijel(i) ); /* poziv funkcije faktorijel i */ } /* kraj for */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */ /* Definicija rekurzivne funkcije faktorijel */ long faktorijel(long broj) { /* ako je broj 0 ili 1, 0!=1 i 1!=1 */ if( broj <= 1 ) { return 1; /* vrati 1 i zavrsi rada sa funkcijom */ } /* kraj if */ else /* jedan korak rekurzije */ { return (broj * faktorijel(broj - 1) ); /* rekurzivni poziv funkcije */ } /* kraj else */ } /* kraj funkcije faktorijel */ Функција faktorijel користи рекурзију да би одштампала факторијеле бројева од 0 до 10. Ова рекурзивна функција прво тестира broj да би видела да ли је он различит од 1 и 0, и у том случају враћа вредност 1 што је уједно и прекид рекурзије односно напуштанје функције faktorijel. Ако је улазни аргумент (broj) већи од 1 наредба return(broj*faktorijel(broj-1)); у ствари позива функцију faktorijel са аргументом кој је за један мањи (broj-1)

Page 213: Uvod u programski jezik C Univerzitet Singidunum

11. Напишите програм који рачина Фибоначијеву вредност од унетог броја. Fibonacci-јев низ почиње бројем 0 и 1 а има својство да је сваки наредни елемент низа једнак збиру претходна два елемента низа, тј. 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... У сврху писања програма дефинишимо функцију са једним аргументом fibonacci( i ) каја ће враћати вредности

fibonacci(0)=0, fibonacci(1)=1, fibonacci(n) = fibonacci(n-1) + fibonacci(n-2)

Page 214: Uvod u programski jezik C Univerzitet Singidunum

Решење1 #include <stdio.h> long fibonacci( long broj ); /* prototip funkcije */ /* funkcija main, mesto pocetka izvrsavanja programa */ int main() { long rezultat; /* fibonacci vrednost */ long indeks; /* broj koji unosi korisnik */ /* Uzmi broj od korisnika */ printf( "Unesite celi broj: " ); scanf( "%ld", &indeks ); /* Izracunaj fibonacci vrednost ya uneti broj */ rezultat = fibonacci(indeks); /* prikazi rezltat */ printf( "Fibonacci( %ld ) = %ld\n", indeks, rezultat ); return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */ /* definicija rekurzivne funkcije fibonacci */ long fibonacci(long broj) { long i; long n_minus1=1, n_minus2=0, n; if (broj==0 || broj==1) return broj; for(i=1;i<broj;i++) { n=n_minus1+n_minus2; n_minus2=n_minus1; n_minus1=n; } return n; } /* kraj funkcije fibonacci */

Page 215: Uvod u programski jezik C Univerzitet Singidunum

Решење2 #include <stdio.h> long fibonacci(long broj); /* prototip funkcije */ /* funkcija main, mesto pocetka izvrsavanja programa */ int main() { long rezultat; /* fibonacci vrednost */ long indeks; /* broj koji unosi korisnik */ /* Uzmi broj od korisnikao */ printf( "Unesite celi broj: " ); scanf( "%ld", &indeks ); /* Izracunaj fibonacci vrednost ya uneti broj */ rezultat = fibonacci( indeks ); /* prikazi rezltat */ printf( "Fibonacci( %ld ) = %ld\n", indeks, rezultat ); return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */ /* definicija rekurzivne funkcije fibonacci */ long fibonacci(long n) { /* osnovni slucaj */ if(n == 0 || n == 1) { return n; } /* kraj if */ else { /* rekurzija */ return fibonacci(n - 1) + fibonacci(n - 2); } /* kraj else */ } /* kraj funkcije fibonacci */

Page 216: Uvod u programski jezik C Univerzitet Singidunum

12. Написти програм који проверава исправност унетог датума. #include <stdio.h> #include <string.h> /* potrebno zbog funkcije strchr() */ #include<stdlib.h> /* potrebno zbog funkcije atoi() */ #include <ctype.h> /* potrebno zbog funkcije isdigit() */ int ucitaj_datum(int *d, int *m, int *g ); /* prototip funkcije*/ int proveri_datum( int xd, int ym, int yg ); /* prototip funkcije*/ int main() { int dan; int mesec; int godina; int pom; pom=ucitaj_datum(&dan, &mesec, &godina); /* poziv funkcije, unos podataka */ /* ako je po formatu korektno unet datum poroveri da li je moguc */ if(!pom) /* funkcija vraca vrednost u pom */ pom =proveri_datum(dan, mesec, godina); /* poziv funkcije, provera unosa */ switch(pom) /* i druga funkcija vraca vrednost u pom */ { case 1: printf("\n"); break; case 2: printf("Niste uneli odgovarajuci mesec\n\b"); break; case 3: printf("Niste uneli odgovarajuci dan u mesecu\n\b"); break; default: printf("Uneti datum je korektan!\n"); break; } return 0; } /* kraj funkcije main*/

Page 217: Uvod u programski jezik C Univerzitet Singidunum

/* definicija funkcije ucitaj_datum */ int ucitaj_datum(int *d, int *m, int *g ) { char niz[80]; char podniz[5]; int kontrola; int duzina; int i; char *ptr; kontrola=1; do { printf("\nUnesite datum u formatu dd.mm.gggg ili dd.mm.gg, x za prekid\n" ); gets(niz); /* ucitaj unos korisnika */ duzina=strlen(niz); /* odredi broj unetih karaktera */ /* zahtev korisnika za prekid programa*/ if(niz[0]=='x' || niz[0]=='X') return 1; /* da li je broj unetih karaktera odgovarajuci? */ if(duzina != 10 ) printf("Broj unetih vrednosti nije odgovarajuci\n", duzina); else kontrola=0; /* ok */ /* Ako je broj unetih karaktera odgovarajuci... */ if(!kontrola) { /* da li je pozicija delimitera na pravom mestu (3. i 6.) */ ptr=strchr(niz, '.'); /* pozicija prvog delimitera */ if((ptr - niz+1) != 3) { printf("Pozicija delimitera ('.') nije odgovarajuca"); kontrola=1; /* greska u formatu */ } if(!kontrola) { ptr=strchr(ptr+1, '.'); /* pozicija drugog delimitera */ if((ptr - niz+1) != 6) { printf("Pozicija delimitera ('.') nije odgovarajuca"); kontrola=1; } } } if(kontrola) continue; for(i=0; i<10;i++) /* da li su na svim pozicijama (sem 3 i 6) brojevi */ { if(i==2 || i==5) continue; if( !( isdigit(niz[i]) ) ) {

Page 218: Uvod u programski jezik C Univerzitet Singidunum

printf("datum moze sadrzati samo brojeve\n"); kontrola =1; break; } } /* ako je format odgovarajuci, izdvoj podtke o danu mesecu i godini */ if(!kontrola) { strncpy( podniz, niz ,2); podniz[2]='\0'; *d=atoi(podniz); strncpy( podniz, niz+3 ,2); podniz[2]='\0'; *m=atoi(podniz); strncpy( podniz, niz+6 ,4); podniz[4]='\0'; *g=atoi(podniz); /* ukoliko je bar jedna od vrednosti 0 (uneto 00 ili slovo).. */ if( !(*d && *m && *g ) ) { printf("Unete vrednosti moraju bit brojevi razliciti od 00 odnosno 0000\n"); kontrola =1; } } }while(kontrola); return 0; }

Page 219: Uvod u programski jezik C Univerzitet Singidunum

int proveri_datum( int xd, int ym, int yg ) { int max_dana; int prestupna=0; /* proveri da li je godnia prestupna */ if((yg % 4)==0) prestupna =1; /* utvrdi maks broj dana u mesecu */ if(ym>12) return 2; /* mesec nije odgovarajuci*/ switch(ym) { case 4: case 6: case 9: case 11: max_dana = 30; break; case 2: if (prestupna) max_dana=29; else max_dana=28; break; default: max_dana=31; break; } if(xd>max_dana) return 3; /* broj dana u mesecu nije odgovarajuci */ return 0; }

Page 220: Uvod u programski jezik C Univerzitet Singidunum
Page 221: Uvod u programski jezik C Univerzitet Singidunum

Увод у програмски језик C

Задаци за вежбу НИЗОВИ

Page 222: Uvod u programski jezik C Univerzitet Singidunum

1. Иницијализујете низ од 10 елемената на вредност 50 применом for петље, испишите вредности низа у облику табеле.

/* Inicijalizacija niza */ #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int n[10]; /* n je niz od 10 celih brojeva */ int i; /* brojac */ /* inicijalizacija elementa niza na vrednost 50 */ for ( i = 0; i < 10; i++ ) { n[ i ] = 50; /* dodeli vrednost e0 elementu niza sa indeksom i */ } /* kraj for petlje */ printf( "%s %11s \n\n", "Niz n", "Vrednost" ); /* %s je format za string */ printf("-----------------\n"); /* ispis crtica */ /* ispis vrednosti elemenata niza u obliku tabele */ for ( i = 0; i < 10; i++ ) { printf( "n[%d] %6d\n", i, n[ i ] ); } /* kraj for */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

2. Иницијализујте дводимензионални низ димензија 2x5 на вредности 1, 2, 3, 4, 5 и 2, 4, 6, 8, 10, па испишите вредности низа. #include <stdio.h> int main() { /* Deklarisanje niza dimenzija 2 x 5 */ int x[2][5] = { {1, 2, 3, 4, 5}, {2, 4, 6, 8, 10} }; int red, kolona; /* Prikaz elemenata niza po redovima */ for (red=0; red<2; red++) { /* Prikaz elemenata niza po kolonama */ for (kolona=0; kolona<5; kolona++) { printf("%2d\t", x[red][kolona]); } /* kraj unutrasnje for petlje */ printf("\n"); /* putchar('\n');*/ } /* kraj spoljasnje for petlje */ return 0; }/* kraj funkcije main */

Page 223: Uvod u programski jezik C Univerzitet Singidunum

3. Демонстрација иницијализације низа и исписа вредности по елементима /* Inicijalizacija niza */ #include <stdio.h> /* funkcija main, pocetak izvrsavanja programa */ int main() { int n[ 10 ] = { 32, 27, 64, 18, 95, 14, 90, 70, 60, 37 }; int i; /* brojac */ printf( "%s%13s\n\n", "Niz n", "Vrednost" ); /* ispis vrednosti elemenata niza u obliku tabele */ for ( i = 0; i < 10; i++ ) { printf( "n[%d] %6d\n", i, n[ i ] ); } /* kraj for */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

4. Иницијализујте низ парним бројевима од 2 до 20 /* Inicijalizacija niza parnim brojevima od 2 do 20 */ #include <stdio.h> #define SIZE 10 int main()/* funkcija main, pocetak izvrsavanja programa */ { int s[ SIZE ]; int i; /* brojac */ for(i = 0; i < SIZE; i++) /* dodela pocetnih vrednosti */ { s[i] = 2 + 2 * i; } /* kraj for */ printf( "%s%13s\n\n", "Niz s", "Vrednost" ); /* ispis vrednosti elemenata niza u obliku tabele */ for(i = 0; i < 10; i++) { printf( "s[%d] %6d\n", i, s[ i ] ); } /* kraj for */ return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */

Page 224: Uvod u programski jezik C Univerzitet Singidunum

5. Напишите програм који рачуна суму елемената низа. Нека низ има 12 елемената са произвољно иницијализованим вредноситма.

/* Izracunavanje sume elemenata niza */ #include <stdio.h> #define SIZE 12 /* funkcija main, pocetak izvrsavanja programa */ int main() { int a[ SIZE ] = { 1, 3, 5, 4, 7, 2, 99, 16, 45, 67, 89, 45 }; int i; /* brojac */ int suma = 0; /* zbir vrednosti elemenata niza */ for ( i = 0; i < SIZE; i++ ) /* dodela pocetnih vrednosti */ { suma += a[ i ]; } /* kraj for */ printf( "Zbir svih vrednosti elemenata niza je %d\n", suma ); return 0; /* oznacava uspesan zavrsetak programa */ } /* kraj funkcije main */ 6. Напишите програм који из низа од 10 елемената типа float, произвољно иницијализованог, издваја

елемент који садржи највећу вредност.

/* Izdvajanje elementa niza koji ima najvecu vrednost*/ #include<stdio.h> int main() { #define BR_ELEMENATA 10 double niz[BR_ELEMENATA] = {117.0, 129.95, 276.22, 201.10,106.31, 358.11, 31.85, 256.45, 269.09, 197.81}; double max; int i; max = niz[0]; for (i = 1; i < BR_ELEMENATA; i++) { if (niz[i] > max) max = niz[i]; /* Primetite da i pocinje od 1 */ } printf("\nMaksimalna vrednost niza je %f\n", max); return 0; }

Page 225: Uvod u programski jezik C Univerzitet Singidunum

7. Анкетирано је четрдесет студената о квалитету хране у мензи. Сваки студент је дао оцену од 1 до 10. Написати програм који који ће одредити и исписати фреквенцију (број) појављивања сваке оцене. (Нпр. две јединице, три четворке....)

#include <stdio.h> #define BROJ_STUDENATA 40 /* definisi velicinu nizova */ #define FREKV_VELICINA 11 /* 10 ocena, 11 zbog zanemarivanja indeksa 0*/ /* funkcija main pocetak izvrsavanja programa */ int main() { int i; /* brojac koji kruzi od 1. do 40. odgovora */ /* inicijaliacija niza sa brojem dobijenih ocena na 0 */ int frekvOcena[FREKV_VELICINA] = { 0 }; /* inicijalizacija niza odgovor studenata, proizvoljno */ int odgovor[ BROJ_STUDENATA ] = { 1, 2, 6, 4, 8, 5, 9, 7, 8, 10, 1, 6, 3, 8, 6, 10, 3, 8, 2, 7, 6, 5, 7, 6, 8, 6, 7, 5, 6, 6, 5, 6, 7, 5, 6, 4, 8, 6, 8, 10 }; /* za svaki odgovor, odredite vrednost elementa niza odgovor i uzmite tu vrednost kao indeks niza frekvOcena i da bi se pozicionirali na taj element niza frekvOcena i povecajte ga za 1 */ for(i = 0; i < BROJ_STUDENATA; i++) { ++frekvOcena[ odgovor[i] ]; } /* kraj for petlje */ /* prikazi rezultate */ printf( "Ocena Broj glasova\n" ); /* Prikaz u tabelarnom formatu */ for ( i = 1; i < FREKV_VELICINA; i++ ) { printf("%5d%14d\n", i, frekvOcena[i]); } /* kraj for */ return 0; } /* kraj funkcije main */

Page 226: Uvod u programski jezik C Univerzitet Singidunum

8. Дефинисати низ од 10 елемената. Иницијализовати елементе низа на произвољне вредности мање од 30. Написати програм који исписује вредност сваког елемента низа а поред њега, у виду хистограма, број звездица једнак вредности елемента низа. #include <stdio.h> #define VELICINA 10 /* definisi velicinu niza */ int main() { /* inicializacija niza n */ int n[VELICINA] = { 19, 3, 15, 7, 11, 9, 13, 5, 17, 1 }; int i; /* brojac za elemente polja */ int j; /* brojac zvezdica */ printf("Element Vrednost Histogram\n" ); /* za svaki element polja ispisi index, vrednost i nacrtaj histogram */ for ( i = 0; i < VELICINA; i++ ) { /* ispis indeksa i vrednosti*/ printf( "%7d%10d ", i, n[i]) ; for ( j = 1; j <= n[i]; j++ ) { printf( "%c", '*' ); /* ispisi jednu zvezdicu */ } /* kraj unutrasnje petlje */ printf( "\n" ); /* kraj ispisa histigrama za jedan element niza */ } /* kraj spoljasnje petlje */ return 0; } /* kraj funkcije main */

Page 227: Uvod u programski jezik C Univerzitet Singidunum

9. Написати програм који применом методе bubble sort сортира елементе низа #include <stdio.h> #define DIMENZIJA 10 int main() { /* inicijalizacija niza a */ int a[DIMENZIJA] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 }; int prolaz; /* brojac prolaza */ int i; /* brojac poredjenjar */ int priv; /* privremeno cuvanje vrednosti */ printf( "Vrednosti niza u pocetnom redosledu\n" ); /* ispisi vrednosti u pocetnom redosledu */ for ( i = 0; i <DIMENZIJA; i++ ) printf( "%4d", a[ i ] ); /* bubble sort */ for ( prolaz = 1; prolaz < DIMENZIJA; prolaz++ ) { for ( i = 0; i < DIMENZIJA - 1; i++ ) { /* poredi vrednost susednih elemenata i yameni im mesta ako je vrednost prvog elementa veca od drugog */ if ( a[i] > a[i + 1] ) { priv = a[i]; a[i] = a[i + 1]; a[i + 1] =priv; } /* kraj if */ } /* kraj unutrasnje for petlje */ } /* kraj spoljasnje for petlje */ printf( "\nVrednosti niza nakon sortiranja\n" ); /* Ispis vrednosti */ for ( i = 0; i < DIMENZIJA; i++ ) printf( "%4d", a[ i ] ); printf( "\n" ); return 0; } /* kraj funkcije main */

Page 228: Uvod u programski jezik C Univerzitet Singidunum

Увод у програмски језик C

Задаци за вежбу ПОКАЗИВАЧИ

Page 229: Uvod u programski jezik C Univerzitet Singidunum

1. Демонстрација рада са показивачима. #include <stdio.h> int main() { int broj_a; int *ptr_br; float real1, real2; float *ptr_re; broj_a=4; real1=3.2f; ptr_br = &broj_a; /* pointer ukazuje na adresu prom. broj_a*/ broj_a = broj_a + *ptr_br + 1; /* *ptr_br je sadrzaj adrese na koju ukazuje */ printf("\n%d\n",broj_a); ptr_re=&real1; real2= *ptr_re; printf("%f\n",real2); return 0; } /* kraj funkcije main */ После извршења наредбе ptr_br = &broj_a; показивач ptr_br показује на адресу где је смештена променљива broj_a. Наредбом broj_a = broj_a + *ptr_br + 1; променљива broj_a добија вредност 4+4+1 ...

2. Демонстрација рада са показивачима. #include <stdio.h> int main() { int broj_a, broj_b, broj_c; int *ptr_br; broj_a=8; broj_b=4; ptr_br=& broj_c; /* pokazivac pokazuje na adresu promen. broj_c */ *ptr_br= broj_a + broj_b; /* isto sto i broj_c = broj_a + broj_b */ printf("\n%d\n",broj_c); ptr_br=& broj_a; /* pokazivac pokazuje na adresu promen. broj_a */ broj_a = broj_b * broj_c; /* mnozenje */ printf("\n%d\n",*ptr_br); ptr_br=& broj_b; /* pokazivac pokazuje na adresu promen. broj_b */ printf("\n%d\n",*ptr_br); return 0; } /* kraj funkcije main */

Page 230: Uvod u programski jezik C Univerzitet Singidunum

3. Написати програм који путем показивача приступа 5. и 7. елементу низа и исписати њихове вредности. Низ треба да има 10 елемената. Затим, такође, путем показивача у 5. елемент низа уписати вредност збира 6 и 7. елемента низа. Елементи низа могу да имају проиѕвољну вредност.

#include <stdio.h> int main() { int niz[10]={10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; int broj_a; int *ptr_br; /* pointer ukazuje na 0ti indeks niza */ ptr_br=niz; /* isto sto i ptr =niz[0] ili ptr=&niz[0] */ broj_a= *(ptr_br+4); /* *(ptr+4) citaj kao sadrzaj 5 elementa niza (indeks 4) */ printf("\n%d\n",broj_a); ptr_br=&niz[6]; /* isto sto i ptr =niz[6] */ printf("\n%d\n",*ptr_br); *(ptr_br-1)=*ptr_br + *(ptr_br +1); /* ptr pokazuje na niz[6], *(ptr_br-1) je sadrzaj niz[5], *ptr_br je sadrzaj niz[6] *(ptr_br +1); je sadrzaj niz[6], pa je ovo ekvival. niz[5]=niz[6]+ niz[7] */ printf("\n%d\n",*(ptr_br-1)); printf("\n%d\n",niz[5]); return 0; } /* kraj funkcije main */

Page 231: Uvod u programski jezik C Univerzitet Singidunum

4. Коришћењем показивача: уписати све елементе низа од 10 елемената а потом исписати сваки парни елемент низа. Решење 1 #include <stdio.h> int main() { int niz[10]; int i; int *pok; i=1; for(pok=niz; pok <= &niz[9]; pok++) { printf("upisite %d. element niza\n",i); scanf("%d", pok); i++; } i=2; for(pok=&niz[1];pok <= &niz[9]; pok = pok+2) { printf("%d. element je: %d\n",i, *pok); i+=2; } return 0; } Решење 2 #include <stdio.h> int main() { int niz[10]; int i; int *pok; for(pok=niz, i=1; pok <= &niz[9]; pok++, i++) { printf("upisite %d. element niza\n",i); scanf("%d", pok); } for(pok=&niz[1], i=2; pok <= &niz[9]; pok = pok+2, i+=2) printf("%d. element je: %d\n",i, *pok); return 0; } Напомена: приметите да су наредбе унутар петље раздвојене зарезом (,) и тача-зарезом (;).

Page 232: Uvod u programski jezik C Univerzitet Singidunum

5. Демонстрација рада са показивачима

#include <stdio.h> main() { int a[8] = {1,23,17,4,-5,100,55,44}; int *pa, *pb; int x, y; pa=&a[4]; /* pa ukazuje na a[4] tj na vrednost -5 */ printf("Vrednost *pa nakon naredbe pa=&a[4] je: %d\n", *pa); x=*(pa+3); /* x=a[7]=44 */ printf("Vrednost x nakon naredbe x=*(pa+3) je: %d\n", x); y=*pa+3; /* y=a[4]+3=-2*/ printf("Vrednost y nakon naredbe y=*pa+3; je: %d\n", y); *pa++; /* povecava se sadrzaj pokazivaca, isto sto i *(pa++) ili pa=&a[5] */ printf("Vrednost *pa nakon naredbe *pa++ je: %d\n", *pa); (*pa)++; /* povecava se pokazani podatak a[5]=a[5]+1=101 */ printf("Vrednost *pa nakon naredbe (*pa++) je: %d\n", *pa); pb=&a[2]; }

6. Рад са malloc функцијом

#include <stdio.h> #include <stdlib.h> /* potrebno zbog malloc i free funkcije */ main() { int number; int *ptr; int i; printf("Koliko int vrednosti zelite da sacuvate? "); scanf("%d", &number); ptr = malloc(number*sizeof(int)); /* zahtev za dodelu memorije */ /* ptr = (int *)malloc(number*sizeof(int)); */ /* moze i ovako */ if(ptr!=NULL) { for(i=0 ; i<number ; i++) { *(ptr+i) = i; } for(i=number ; i>0 ; i--) { printf("%d\n", *(ptr+(i-1))); /* prikazi u suprotnom redosledu */ } free(ptr); /* oslobodi dodeljenu memoriju */ } else printf("\nDodela memorije nije izvrsena - nema dovoljno memorije.\n"); }

Page 233: Uvod u programski jezik C Univerzitet Singidunum

7. Рад са calloc функцијом

#include <stdio.h> #include <stdlib.h> /* potrebno zbog malloc i free funkcije */ main() { float *ptr_c1, *ptr_c2, *ptr_m1, *ptr_m2; int i; ptr_c1 = calloc(3, sizeof(float)); /* moze se zahtevati konverzija */ ptr_c2 = calloc(3, sizeof(float)); ptr_m1 = malloc(3 * sizeof(float)); ptr_m2 = malloc(3 * sizeof(float)); if(ptr_c1!=NULL && ptr_c2!=NULL && ptr_m1!=NULL && ptr_m2!=NULL) { printf("adresa ptr_c1 je %05.5p,\n", ptr_c1); printf("adresa ptr_c2 je %05.5p,\n", ptr_c2); printf("adresa ptr_m1 je %05.5p,\n", ptr_m1); printf("adresa ptr_m2 je %05.5p,\n\n", ptr_m2); for(i=0 ; i<3 ; i++) { printf("*ptr_c1[%d] ima vrednost %05.5f,\n", i, *(ptr_c1+i)); printf("*ptr_c2[%d] ima vrednost %05.5f,\n", i, *(ptr_c1+i)); printf("*ptr_m1[%d] ima vrednost %05.5f\n", i, *(ptr_m1+i)); printf("*ptr_m2[%d] ima vrednost %05.5f,\n\n", i, *(ptr_c1+i)); } free(ptr_c1); free(ptr_c2); free(ptr_m1); free(ptr_m2); } else printf("Nema dovoljno memorije\n"); }

Page 234: Uvod u programski jezik C Univerzitet Singidunum

8. Рад са realloc функцијом

#include<stdio.h> #include <stdlib.h> int main() { int *ptr; int i; ptr = calloc(5, sizeof(int)); /*dinamicki niz od 5 elemenata indeks 0-4 */ if(ptr!=NULL) /* ako je dodeljena memorija...*/ { *ptr = 1; /* isto sto i ptr[0]=1 ili *ptr=1 */ *(ptr+1) = 2; /* isto sto i ptr[1]=2 */ *(ptr+2) = 4; *(ptr+3) = 8; *(ptr+4) = 16; printf("\nNakon naredbe calloc(5, sizeof(int)) i dodele vrednosti...\n"); for(i=0 ; i<5 ; i++) printf("*(ptr+%d) ukazuje na vrednost %d\n", i, *(ptr+i)); ptr = realloc(ptr, 7*sizeof(int)); /* dodaj jos 2 int (bilo je vec 5)*/ if(ptr!=NULL) { printf("\n Nakon naredbe ptr = realloc(ptr, 7*sizeof(int));... \n"); for(i=0 ; i<7 ; i++) printf("*(ptr+%d) ukazuje na vrednost %d\n", i, *(ptr+i)); *(ptr+5) = 32; *(ptr+6) = 64; printf("\n Nakon naredbe dodele vrednosti 6. i 7. elementu... \n"); for(i=0 ; i<7 ; i++) printf("*(ptr+%d) ukazuje na vrednost %d\n", i, *(ptr+i)); realloc(ptr,0); /* isto sto i free(ptr); */ } else printf("Nema dovoljno memorije - realloc neuspesan.\n"); } else printf("Nema dovoljno memorije - calloc neuspesan.\n"); }

Page 235: Uvod u programski jezik C Univerzitet Singidunum

1

Увод у програмски језик C Рад са фајловима

Задаци за вежбу

Page 236: Uvod u programski jezik C Univerzitet Singidunum

2

1. Написати програм који усписује у фајл (klijenti.dat) податке о броју рачуна, имену клијента и стању на рачуну. Број оваквих записа треба да је произвољан, све док корисник не унесе Ctrl+Z.

Решење:

/* Upis podataka u fajl */ #include <stdio.h> int main() { int broj_racuna; /* broj racuna korisnika */ char ime[ 30 ]; /* ime korisnika */ double stanje; /* stanje na racunu */ FILE *prt_na_fajl; /* prt_na_fajl = fajl pointer */ /* fopen otvara fajl, w za upisivanje. */ /* Ukoliko ne uspe pointer je jednak 0, izadji iz programa */ if ( ( prt_na_fajl = fopen( "klijenti.dat", "w" ) ) == NULL ) { printf( "Fajl se ne moze otvoriti\n" ); } /* kraj if */ else { printf( "Unesite broj racuna, ime korisnika i stanje.\n" ); printf( "Unesite Ctrl+Z za kraj rada.\n" ); printf( "? " ); scanf( "%d%s%lf", &broj_racuna, ime, &stanje ); /* Upisite broj racuna, ime i stanje u fajl nredbom fprintf */ while ( !feof( stdin ) ) { fprintf( prt_na_fajl, "%d %s %.2f\n", broj_racuna, ime, stanje ); printf( "? " ); scanf( "%d%s%lf", &broj_racuna, ime, &stanje ); } /* kraj while */ fclose( prt_na_fajl ); /* fclose zatvara fajl */ } /* kraj else */ return 0; /* oznacava uspesan zavrsetaka */ } /* kraj main */

Функција feof( stdin ) одређује да ли је прочитан end-of-file индикатор у дефинисаном улазу. У овом примеру је дефинисани улаз тастатура (stdin), мада дефинисани улаз може бити и фајл уколико се из њега читају подаци. Уколико функција прочита ознаку (Ctrl+Z) za крај фајла (end-of-file), функција feof враћа вредност 1. Приликом рада са овим програмом унесите вредности тако да неки од корисника имају стање на рачуну 0 неки позитивно стање а неки негативно (ради рада у наредним примерима) ? 100 Petric 324.22 ? 121 Markovic 0.00 ? 111 Simic 32.33 ? 246 Коvac -34.56 ? 343 Jack -200 ? 654 Jon 9567.34 ? ^Z

Page 237: Uvod u programski jezik C Univerzitet Singidunum

3

2. Прочитати све податке из фајла (претходни пример), и испишите их на екран у одговарајућем формату. Решење:

/* citanje podataka iz fajla*/ #include<stdio.h> int main() { int broj_racuna; /* broj racuna korisnika */ char ime[ 30 ]; /* ime korisnika */ double stanje; /* stanje na racunu */ FILE *prt_na_fajl; /* prt_na_fajl = fajl pointer */ /* fopen otvara fajl, r (read) za citanje. */ /* Ukoliko ne uspe pointer je jednak 0, izadji iz programa */ if ( ( prt_na_fajl = fopen( "klijenti.dat", "r" ) ) == NULL ) { printf( "Fajl se ne moze otvoriti\n" ); } /* kraj if */ else { printf( "%-14s%-16s%s\n", "Broj racuna","Ime","Stanje" ); fscanf( prt_na_fajl,"%d%s%lf", &broj_racuna, ime, &stanje ); /* Citanje podataka iz fajla naredbom fscanf */ while ( !feof( prt_na_fajl ) ) { printf( "%-14d%-16s%7.2f\n", broj_racuna,ime, stanje ); fscanf( prt_na_fajl,"%d%s%lf", &broj_racuna, ime, &stanje ); } /* kraj while */ fclose( prt_na_fajl ); /* fclose zatvara fajl */ } /* kraj else */ return 0; /* oznacava uspesan zavrsetaka */ } /* kraj main */

Page 238: Uvod u programski jezik C Univerzitet Singidunum

4

3. Написати програм који из фајла формираног у првом задатку чита податке и на основу захтева корисника: а. исписује податке о рачунима који имају стање 0 б. исписује податке о рачунима који имају негативно стање в. исписује податке о рачунима који имају позитивно стање г. прекида рад

Решење:

#include<stdio.h> int main() { int zahtev; int broj_racuna; /* broj racuna korisnika */ char ime[ 30 ]; /* ime korisnika */ double stanje; /* stanje na racunu */ FILE *prt_na_fajl; /* prt_na_fajl = fajl pointer */ /* fopen otvara fajl, r (read) za citanje. */ /* Ukoliko ne uspe pointer je jednak 0, izadji iz programa */ if ( ( prt_na_fajl = fopen( "klijenti.dat", "r" ) ) == NULL ) printf( "Fajl se ne moze otvoriti\n" ); else { /* prikazi zahtevane opcije */ printf( "Unesite zahtev\n" " 1 - Izlistaj racune sa stanjem NULA\n" " 2 - Izlistaj racune sa stanjem u MINUSu\n" " 3 - Izlistaj racune sa POZITIVNIM stanjem\n" " 4 - Kraj\n? " ); scanf( "%d", &zahtev ); while(zahtev !=4) { fscanf( prt_na_fajl,"%d%s%lf", &broj_racuna, ime, &stanje ); switch(zahtev) { case 1: printf( "\nRacuni sa stanjem NULA:\n" ); /* citaj sadrzaj fajla do kraja (sve do eof) */ while ( !feof( prt_na_fajl ) ) { if ( stanje == 0 ) printf( "%-14d%-16s%7.2f\n", broj_racuna, ime, stanje ); /* procitaj broj racuna, ime i stanje iz fajla */ fscanf( prt_na_fajl, "%d%s%lf", &broj_racuna, ime, &stanje ); } /* kraj while */ break;

Page 239: Uvod u programski jezik C Univerzitet Singidunum

5

case 2: printf( "\nRacuni sa stanjem u MINUSu:\n" ); /* citaj sadrzaj fajla do kraja (sve do eof) */ while ( !feof( prt_na_fajl ) ) { if ( stanje < 0 ) printf( "%-14d%-16s%7.2f\n", broj_racuna, ime, stanje ); /* procitaj broj racuna, ime i stanje iz fajla */ fscanf( prt_na_fajl, "%d%s%lf", &broj_racuna, ime, &stanje ); } /* kraj while */ break; case 3: printf( "\nRacuni sa POZITIVNIM stanjem:\n" ); /* citaj sadrzaj fajla do kraja (sve do eof) */ while ( !feof( prt_na_fajl ) ) { if ( stanje > 0 ) printf( "%-14d%-16s%7.2f\n", broj_racuna, ime, stanje ); /* procitaj broj racuna, ime i stanje iz fajla */ fscanf( prt_na_fajl, "%d%s%lf", &broj_racuna, ime, &stanje ); } /* kraj while */ break; }/* kraj switch */ fseek(prt_na_fajl, 0, SEEK_SET); /* pozicioniraj se na pocetak fajla*/ /*potpuno je isto sto i rewind( ptr_na_fajl); */ printf( "\n? " ); scanf( "%d", &zahtev ); } /* kraj while(zahtev !=4 ) */ printf("Kraj rada\n"); fclose( prt_na_fajl ); /* fclose zatvara fajl */ } /* kraj else */ return 0; /* oznacava uspesan zavrsetak */ } /* kraj main */

Page 240: Uvod u programski jezik C Univerzitet Singidunum

6

4. Дефинишите 100 рачуна применом структуре. Елементи структуре трегба да буду: број рачуна, презиме клијента, име клијента и стање на рачуну. Иницијализујте вредност сваке структуре на вредности 0 за број рачуна, празан стринг за име и презиме и 0 за стање на рачуну. Упишите 100 оваквих структура у фајл применом наредбе fwrite /* Rad sa binarnim fajlovima */ #include <stdio.h> /* Definicija strukture podaci_klijenta */ struct podaci_klijenta { int broj_racuna; /* broj_racuna klijenta */ char prezime[ 15 ]; /* prezime klijenta */ char ime[ 10 ]; /* ime klijenta */ double stanje; /* stanje na racunu */ }; /* kraj definicije strukture */ int main() { int i; /* brojac racuna od 1 do 100 */ /* kreiraj promen. novi_klijent tipa strukture podaci_klijenta i inicijalizuj */ struct podaci_klijenta novi_klijent = { 0, "", "", 0.0 }; FILE *ptr_fajl; /* fajl pointer */ /* fopen otvara fajl, izlaz iz programa ukoliko ne uspe */ if ( ( ptr_fajl = fopen( "kredit.dat", "wb" ) ) == NULL ) printf( "Fajl se ne moze otvoriti.\n" ); else { /* upisi 100 "praznih" zapisa (struktura) u fajl */ for ( i = 1; i <= 100; i++ ) fwrite( &novi_klijent, sizeof( struct podaci_klijenta ), 1, ptr_fajl ); fclose ( ptr_fajl ); /* fclose zatvara fajl */ } /* kraj else */ return 0; /* indicates successful termination */ } /* end main */

Page 241: Uvod u programski jezik C Univerzitet Singidunum

7

5. У бинарни фајл формиран у претходном примеру унесите потребне податке. Корисник треба да има могућност да унесе број рачуна од 1 до 100 и да за тај рачун унесе податке (име презиме и стање). /* Rad sa binarnim fajlovima */ #include <stdio.h> /* Definicija strukture podaci_klijenta */ struct podaci_klijenta { int broj_racuna; /* broj_racuna klijenta */ char prezime[ 15 ]; /* prezime klijenta */ char ime[ 10 ]; /* ime klijenta */ double stanje; /* stanje na racunu */ }; /* kraj definicije strukture */ int main() { struct podaci_klijenta klijent = { 0, "", "", 0.0 }; FILE *ptr_fajl; /* fajl pointer */ /* fopen otvara fajl, izlaz iz programa ukoliko ne uspe */ if ( ( ptr_fajl = fopen( "kredit.dat", "rb+" ) ) == NULL ) printf( "Fajl se ne moze otvoriti.\n" ); else { /* traziti od korisnika da unese broj racuna */ printf("Unesite broj racuna (1-100):"); scanf("%d", &klijent.broj_racuna); /* Korisnik treba da unese podatke za klijenta */ while(klijent.broj_racuna>0 && klijent.broj_racuna<101) { /* Unos prezimena, imena i stanja */ printf( "Unesite prezime, ime i stanje\n? " ); /* vrednosti */ fscanf( stdin, "%s%s%lf", &klijent.prezime, &klijent.ime, &klijent.stanje ); /* Pozicioniraj se na poziciju racuna (zadao je korisnik) */ fseek( ptr_fajl, ( klijent.broj_racuna - 1 ) * sizeof( struct podaci_klijenta), SEEK_SET ); /* upisi unete vrednosti u fajl */ fwrite( &klijent, sizeof( struct podaci_klijenta), 1, ptr_fajl); /* Omoguсi korisniku da unese podatke za druge racune */ printf( "Unesite broj racuna\n? " ); scanf( "%d", &klijent.broj_racuna ); } /* kraj while*/ fclose( ptr_fajl ); /* fclose zatvara fajl */ } /* kraj else */ return 0; /* oznacava uspesan zavrsetak */ } /* kraj main */

Page 242: Uvod u programski jezik C Univerzitet Singidunum

8

6. Написати програм који чита податке записане у облику рачуна (структура) у претхосном задатку. /* Rad sa binarnim fajlovima */ #include <stdio.h> /* Definicija strukture podaci_klijenta */ struct podaci_klijenta { int broj_racuna; /* broj_racuna klijenta */ char prezime[ 15 ]; /* prezime klijenta */ char ime[ 10 ]; /* ime klijenta */ double stanje; /* stanje na racunu */ }; /* kraj definicije strukture */ int main() { struct podaci_klijenta klijent = { 0, "", "", 0.0 }; FILE *ptr_fajl; /* fajl pointer */ /* fopen otvara fajl, izlaz iz programa ukoliko ne uspe */ if ( ( ptr_fajl = fopen( "kredit.dat", "rb" ) ) == NULL ) printf( "Fajl se ne moze otvoriti.\n" ); else { printf( "%-6s%-16s%-11s%10s\n", "Racun", "Prezime","Ime", "Stanje" ); /* Procitaj sve racune do kraja fajla */ while(!feof(ptr_fajl)) { fread( &klijent, sizeof( struct podaci_klijenta ), 1, ptr_fajl ); /* prikazi podatke racuna */ if ( klijent.broj_racuna != 0 ) printf( "%-6d%-16s%-11s%10.2f\n",klijent.broj_racuna, klijent.prezime, klijent.ime,klijent.stanje ); } /* kraj while */ fclose( ptr_fajl ); /* fclose zatvara fajl */ } /* kraj else */ return 0; /* oznacava uspesan zavrsetak */ } /* kraj main */