137
1 Лабораторно упражнение 1 Тема: Представяне на информацията в компютърните системи. I. Цел на лабораторното упражнение Усвояване на знания и умения при преобразуване на числа в различни бройни системи. II. Постановка на задачата В лабораторното упражнение ще бъдат разгледани най-често използваните бройни системи в компютърната техника. Студентите ще се запознаят с представянето на реални и цели числа в двоична, осмична, десетична и шестнадесетична бройни системи. III. Теоретични сведения Бройни системи Под бройна система се разбира начинът за представяне на числата с краен брой символи, имащи количествени стойности. Символите, с които се представят числата, се наричат цифри. В компютърната техника се използуват позиционни бройни системи. Позиционна е бройната система, в която числовата стойност на цифрата зависи не само от нейния символ, но и от нейното място (позиция) в записа на числото. Броят на различните цифри, които се използуват за представяне на числата в позиционна бройна система, се нарича основа на бройната система. В позиционна бройна система числата се записват като последователност от цифри, например числото А в позиционна система с основа p се записва по следния начин: A (p) =a m-1 a m-2 ... a k … a 1 a 0 a -1 a -2 … a -i …. a -n (1) Всяка цифра може да приема една от p възможни стойности, т.е. трябва да се изпълняват неравенствата: 0a k p-1 ( k=0,1,2,…,m-1) 0a -i p-1 ( i=1,2,3,…,n) Номерът на дадена позиция по отношение на десетичната запетая се нарича разред на числата. Числото А (p) може да се представи и с полинома A (p) =a m-1 p m-1 +a m-2 p m-2 +... +a 1 p 1 +a 0 p 0 + a -1 p -1 a -2 p -2 +…+ a -n p -n (2) се вижда от (2), основата на бройната система показва и отношението между теглата на два съседни разреда (на по-старшия към по-младшия). В практиката най-разпространена е позиционната бройна система с основа 10. В ПК основно се прилагат двоична, осмична, десетична и шестнадесетична система. Двоичната бройна система използува две цифри 0 и 1 за кодиране. Осмичната използува цифрите 0,1,2,3,4,5,6,7. Десетичната използува цифрите 0,1,2,3,4,5,6,7,8,9.Шестнадесетичната бройна система използва цифрите 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, като A,B,C,D,E,F съответствуват на десетичните числа 10,11,12,13,14,15. В таблица 1 са показани записите на числата до 20 в десетична, двоична, осмична и шестнадесетична бройна система.

I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

  • Upload
    others

  • View
    38

  • Download
    0

Embed Size (px)

Citation preview

Page 1: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

1

Лабораторно упражнение № 1

Тема: Представяне на информацията в компютърните системи.

I. Цел на лабораторното упражнение

Усвояване на знания и умения при преобразуване на числа в различни бройни системи.

II. Постановка на задачата В лабораторното упражнение ще бъдат разгледани най-често използваните бройни

системи в компютърната техника. Студентите ще се запознаят с представянето на реални и цели числа в двоична, осмична, десетична и шестнадесетична бройни системи.

III. Теоретични сведения

Бройни системи

Под бройна система се разбира начинът за представяне на числата с краен брой символи, имащи количествени стойности. Символите, с които се представят числата, се наричат цифри. В компютърната техника се използуват позиционни бройни системи. Позиционна е бройната система, в която числовата стойност на цифрата зависи не само от нейния символ, но и от нейното място (позиция) в записа на числото. Броят на различните цифри, които се използуват за представяне на числата в позиционна бройна система, се нарича основа на бройната система. В позиционна бройна система числата се записват като последователност от цифри, например числото А в позиционна система с основа p се записва по следния начин:

A(p)=am-1 am-2... ak… a1 a0 a-1 a-2… a-i…. a-n (1) Всяка цифра може да приема една от p възможни стойности, т.е. трябва да се изпълняват

неравенствата: 0≤ak≤p-1 ( k=0,1,2,…,m-1) 0≤a-i≤p-1 ( i=1,2,3,…,n)

Номерът на дадена позиция по отношение на десетичната запетая се нарича разред на числата. Числото А(p) може да се представи и с полинома

A(p)=am-1pm-1+am-2 pm-2+... +a1 p1+a0 p0+ a-1p-1 a-2 p-2 +…+ a-np-n (2) се вижда от (2), основата на бройната система показва и отношението между теглата на два съседни разреда (на по-старшия към по-младшия).

В практиката най-разпространена е позиционната бройна система с основа 10. В ПК основно се прилагат двоична, осмична, десетична и шестнадесетична система. Двоичната бройна система използува две цифри 0 и 1 за кодиране. Осмичната използува цифрите 0,1,2,3,4,5,6,7. Десетичната използува цифрите 0,1,2,3,4,5,6,7,8,9.Шестнадесетичната бройна система използва цифрите 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, като A,B,C,D,E,F съответствуват на десетичните числа 10,11,12,13,14,15. В таблица 1 са показани записите на числата до 20 в десетична, двоична, осмична и шестнадесетична бройна система.

Page 2: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

2

Tаблица 1 Десетична Двоична Осмична Шестнадесетична0 00000 0 0 1 00001 1 1 2 00010 2 2 3 00011 3 3 4 00100 4 4 5 00101 5 5 6 00110 6 6 7 00111 7 7 8 01000 10 8 9 01001 11 9 10 01010 12 A 11 01011 13 B 12 01100 14 C 13 01101 15 D 14 01110 16 E 15 01111 17 F 16 10000 20 10 17 10001 21 11 18 10010 22 12 19 10011 23 13 20 10100 24 14

Преобразуване на числата от една бройна система в друга

Цялата и дробната част на всяко число А, записано в бройна система с основа р, могат да се представят в каква да е нова бройна система с основа q c полиноми аналогично на (2):

Aц(p)=am-1qm-1 + am-2 qm-2+... +a1 q1+a0 (3) Aдр(p)=a-1q-1 + a-2q-2 +…+ a-nq-n (4)

Броят на необходимите разреди за записване на числото в новата бройна система с основа q може да е различен от броя на разредите на числото в изходната бройна система. Уравненията (3) и (4) могат да се запишат още по следния начин:

Aц(p)=(…((am-1q+am-2) q+am-3)q+... +a1) q+a0 (5) Aдр(p)= q-1(a-1 + q-1(a-2+…+q-1 (a-(n-1)+ a-nq-1) (6)

От анализа на (5) следва, че ако се раздели Aц(p на основата q на новата бройна система (дели се съответно и дясната страна на (5)), се получава остатък а0 и частно в скобите, което е многочлен, подобен на изходния, но на степен с единица по-ниска. Ако се продължи делението, докато се получи частно, равно на нула, се получават всички остатъци аi(i=0,1,2,3,..,m-1), които представляват цифрите на цялата част на числото в новата бройна система с основа q. Последният остатък ат-1 е най-старшата цифра, а първият остатък аo — най-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p), с основата на новата бройна система q (умножава

се съответно и дясната страна на равенството (6)), се получава цяла част а-1 и дробна част в скобите. Ако се продължи този процес на умножение само на дробната част, се получават всички цели части а-j(j=1,2,3,…,n), които представляват цифрите на дробната част на числото в новата бройна система с основа q. Първата цяла част а-1е първата цифра след десетичната запетая, т.е. цифрите се записват по реда на тяхното получаване. Процесът на преобразуване на дробната част от една бройна система в друга би се прекратил, ако на някоя стъпка се получи нулева дробна част. Това обаче на практика се случва рядко. Ето защо точността на преобразуване на дробната част се задава предварително до определен брой разреди.

Page 3: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

3

Цялата и дробната част на числото А се преобразуват поотделно, след което се обединяват. Пример 1. Да се преобразуват от десетична в двоична бройна система следните числа: а) 109,625; б) 32,724. Дробната част на второто число да се преобразува с точност до петия

двоичен разред. а) остатък 109 : 2 = 54 → 1 0,625 54 : 2 = 27 → 0 x 2 27 : 2 = 13 → 1 1,250 13 : 2 = 6 → 1 x 2 6 : 2 = 3 → 0 0,500 3 : 2 = 1 → 1 x 2 1 : 2 = 0 → 1 1,000 109(10) = 1101101(2) 0,625(10) = 0,101(2) 109,625 = 101101,101(2)

б)остатък 32 : 2 =16 →0 0,724 16 : 2 = 8 →0 х 2 8 : 2 = 4 → 0 1,448 4 : 2 = 2 → 0 х 2 2 : 2 = 1 → 0 0,896 1 : 2 = 0 → 1 х 2 0,724(10) = 0,10111(2) 1,792 х 2 32(10) = 100000(2) 1,584 х 2 1,168 32,724(10) = 100000,10111(2)

Пример2 Да се преобразува от десетична в осмична бройна система числото 166,0625.

остатък 166 : 8 = 20 → 6 0,0625 20 : 8 = 2 → 4 х 8 2 : 8 = 0 → 2 0,5000 х 8 4,0000 166(10) = 246(8) 0,0625(10) =0,04(8) 166,0625(10) = 246,04(8)

Пример 3 Да се преобразува от десетична в шестнадесетична бройна система числото 315,03125.

остатък 315 : 16 = 19 →11 0.03125 19 : 16 = 1 → 3 .х 16 1 : 16 = 0 → 1 0,50000 .х 16 8,00000 315(10) = 13В(16) 0,03125(10) = 0,08(16) 315,03125(10) =.13В,08(16)

Page 4: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

4

Съществуват и други методи за преобразуване на числата от една бройна система в друга,

например като се използува полиномът (2). Пример4. Да се преобразува двоичното число 1101101,101 в десетично.

1101101,101(2) = 1.26 + 1.25 + 0.24 + 1.23 + 1.22 + 0.21 + 1.20+ + 1.2-1 + 0.2-2 + 1.2-3 = 64 + 32 + 8+ 4 + 1+1/2 + 1/8 = 109,625(10)

Вижда се, че десетичното число 109,625 е било правилно преобразувано в двоично в пример 1

Пример5 Да се провери дали правилно са били преобразувани числата в примерите 2 и 3.

246,04(8) = 2.82 + 4.81 + 6.8° + 0.8-1 + 4.8-2 = 128 + 32 + 6 + 4/64 = 166.0625(10) 13В,08(16)= 1.162 + 3.161 + 11.16° + 0.16-1 +8.16-2 = 256 + 48+11 + 8/256 = 315,03125(10) Тъй като 8 и 16 са степени на две (основата на двоичната бройна система), числата могат

да се преобразуват от двоична бройна система в осмична и шестнадесетична и обратно по следния начин. При преобразуването от осмична в двоична бройна система всяка цифра на осмичното число се записва като триразредно двоично число {триада). При преобразуването от шестнадесетична в двоична бройна система всяка цифра на шестнадесетичното число се записва като четири-разредно число (тетрада). При обратното преобразуване двоичното число се разделя на триади (за осмична бройна система) или на тетради (за шестнадесетична бройна система) вляво и вдясно от десетичната запетая и за всяка триада (тетрада) се записва съответната осмична (шестнадесетична) цифра. При необходимост от ляво и дясно двоичното число може да се допълни с незначещи нули.

Пример 6. Да се преобразува осмичното число 63,5 в двоично, а двоичното

1101101100,101110 в шестнадесетично. 63,5(8) = 110 011,101(2)

6 3 5 Равенството (съответно преобразуването) може да се провери по следния начин:

110011,101(2) = 1.25 +1.24 +1.21+1.20+ 1.2-1 +1.2-3 =51,625(10) Може да се провери, че 51,625(10) = 63,5(8)

0011 0110 1100,1011 1000(2) = 36С,В8(16) В ЕИМ се използува и записването на числата в двоично-десетичен код. Всяка цифра на

десетичното число се кодира с четириразредно двоично число, като се запазва и нейното десетично тегло. Но с четири разреда могат да се кодират 16 различни цифри. Последните шест комбинации между четирите двоични разреда — 1010 (А), 1011 (В ), 1100 (С), 1101 (D), 1110 (Е), 1111(F), са забранени за двоично-десетичния код.

Пример 7. Да се запише десетичното число 2067 в двоично-десетичен код.

2 0 6 7 десетично число 0010 0000 0110 0111 двоично-десетичен код Аритметични действия с числа в двоична бройна система

Правилата за събиране, изваждане и умножение на двоичните числа са следните:

събиране изваждане умножение 0 + 0 = 0 0 – 0 = 0 0 x 0 = 0 0 + 1 = 1 1 - 0 = 1 0 x 1 = 0 1 + 0 = 1 1 - 1 = 0 1 x 0 = 0 1 + 1 = 10 10 - 1 = 1 1 x 1 = 1

Page 5: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

5

Събирането и изваждането на многоразредни числа се изпълнява поразредно. При събиране на две единици се получава 0 за дадения разред и пренос единица в следващия разред. При изваждане на 1 от кула се взема заем от по-старшия разред. Умножение и деление на многоразредни двоични числа се извършва, както и умножение и деление на многоразредни десетични числа. Пример 8.

а). Да се съберат числата 101101,01 и 10101,01. б). От числото 1001101,101 да се извади 10101,011. в). Да се умножат числата 1010,01 и 101,1. г) Числото 101101 да се раздели на 101. а) 101101,01 б) 1001101,101 + 10101,01 - 10101,011 1000010,10 111000,010 в) 1010,01 множимо 1010,01 множимо х 101,1 множител х 101,1 множител

101001 101001 + 101001 + 000000 000000 101001

101001 . 101001 111000,011 111000,011

(Умножението е извършено, като е започнато с младшите разреди на множителя в първия случай и със старшите разреди във втория случай.)

г) 101101 : 101 = 1001 - 101 000101 - 101 000

Пример9. Да се запишат в двоично-десетичен код и да се сумират десетичните числа 19 и 53.

0001 1001 → 19 + 0101 0011 → 53

0110 1100 + 0110 Добавяне на числото 6 0111 0010— 72 Старши Младши разред разред Ако в процеса на сумиране на двоично-десетични числа за даден разред се получи

забранена комбинация (от 1010 до 1111). необходимо е към нея да се добави числото 6, за да се получи десетичен пренос към по-старшия разред.

Page 6: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

6

Представяне на числата в ЕИМ и аритметични действия с тях В ЕИМ числата се представят по два начина: с фиксирана и с плаваща запетая. На фиг.1а

е показана разредна решетка за представяне на числата. При представянето на числата с фиксирана запетая десетичната запетая обикновено се фиксира след знаковия разред преди а-

1 (фиг.2.16). Следователно числата, представени с фиксирана запетая, по стойности винаги са по-малки от единица. Числата с плаваща запетая се записват като две групи разреди в разредната решетка (фиг.2б) — на мантисата и на порядъка. Стойността на дадено двоично число е А = ±М.2±Р- Запетаята на мантисата също се фиксира между знаковия разред азн и а-1. Числото, представено с плаваща запетая, се нарича нормализирано, ако цифрата а-1. на мантисата не е нула. Прието е знакът на представяните числа и знакът на порядъка да се

кодират по следния начин: ⎩⎨⎧

−−+−

=)(1)(0

,знакзнак

ba znзн

Ако двоичните числа са записани със знак, кодиран по описания начин, казва се, че те са записани в прав код. Обикновено в ЕИМ числата се съхраняват в прав код. Правият код на числата е удобен за изпълнение на операциите умножение и деление и неудобен за операциите събиране и изваждане. Последните две операции се реализират лесно, като се използува обратен или допълнителен код на числата. За положителните числа правият, обратният и допълнителният код съвпадат. Обратният код на отрицателно двоично

число се получава, като в знаковия разред се запише единица, а цифрите на числото се

инвертират — единиците с нули, а нулите с единици. Допълнителният код на отрицателно двоично число се получава, като в знаковия разред се

запише единица, а цифрите на числото се инвертират и към най-младшия разред се прибави единица.

Пример 10: Да се запишат числата А] =+0,101101 и А2 = = -0,110101 в прав, обратен и

допълнителен код. [А1]пр = [А1]обр = [А1]доп = 0,101101 [А2]пр = 1,110101; [А2]обр = 1,001010 [А1]доп =1,001010 + 0,000001 = 1,001011

Операцията изваждане се свежда до операция сумиране в обратен или допълнителен код: А—В = [А]о6р +[-B]о6р или [А]доп + [-В]доп. Резултатът от сумирането на числата в обратен или допълнителен код се получава в обратен или допълнителен код. Ако полученият резултат е със знак плюс, той е в прав код. Ако полученият резултат е със знак минус, той е съответно в обратен или допълнителен код. За да се получи правият код, необходимо е от получения обратен код да се вземе обратен или от получения допълнителен код да се вземе допълнителен: [А]пр = [[А]обр]обр или [[А]доп]доп При събирането на числа в обратен или допълнителен код знаковите разреди се разглеждат като обикновени разреди на числата. Те се сумират. Към тях се предава пренос, ако има такъв от сумирането на най-старшите разреди на числата. Ако възникне пренос от знаковите разреди, той се отчита по следния начин: при събирането на числа в обратен код този пренос се сумира с най-младшия разред на получената сума. При събирането на числа в допълнителен код този пренос се пренебрегва.

1 2 3 4 ….. n

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

Знак на числото

aзн a-1 a-2 a-3 …. a-n

число A

aзн a-1 a-2 a-3 …. a-k

мантиса

bзн br br-1br-2 …. b0

порядък

Знак мантиса Знак порядъка) б)

в) Фиг.1 Представяне на числа в двоична бройнасистема: а) цяло число; б) число с фиксираназапетая;

Page 7: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

7

Пример 11. Да се сумират числата А1 =0,1001101 и А2 = -0,0100011 в обратен и допълнителен код.

[А1]обр = 0,1001101 [А1]доп = 0,1001101 + [А2]обр = 1,1011100 [А1]доп = 1,1011101 10,0101001 [А1 + А2]доп = 10,0101010 =0,0101010 = [А1+А1]пр + 1 Първата 1 се пренебрегва [А1+А1]обр=0,0101010=[А1+А1]пр Пример 12. Да се сумират числата А1 =-0,1000101 и А2 = 0,0011001 в обратен и допълнителен код. [А1]обр = 1,0111010 [А1]доп = 1,0111011 [А2]обр = 1,1100110 [А2]доп = 1,1100111 11,0100000 [А1 + А2]доп = 11,010001=1,010001= [А1+А1]пр + 1 Първата 1 се пренебрегва [А1+А1]обр = 1,0100001 [А1+А1]пр = 1.1011110 [А1+А1]пр = 1,1011110

Пример 13. Да се сумират числата А1 0,1001101 и А2 = 0,1000000 в обратен код, а числата А3 = -0,1000101 и А4=- 0,1100000 в допълнителен код.

[А1]обр = 0,1001101 [А3]доп = 1,0111011 [А2]обр = 0,1000000 [А4]доп = 1,0100000 1,0001101 0,1011011 грешно грешно

Възможно е при сумирането на две положителни числа да се получи резултат със знак

минус, а при сумирането на две отрицателни числа да се получи резултат със знак плюс. Това се получава, когато при сумирането на две числа, по-малки от единица по абсолютна стойност, се получава резултат, по-голям от единица. Този процес се нарича препълванe на разредната решетка и ако възникне при изчисленията, работата на ЕИМ се преустановява. За откриване на, препълване на разредната решетка се използуват модифицирани кодове за представяне на числата — съответно модифициран прав, обратен или допълнителен код. При модифицирания код за кодиране на знака се използуват два разреда: знак плюс се кодира с 00, а знак минус с ll. Ако в процеса на изчисление се получи резултат, за който знаковите разреди са 01 или 10, това означава, че е настъпило препълване.

Пример 14 Числата в пример 13 да се сумират в модифицирани кодове.

[А1]мобр = 00,1001101 [А3]мдоп = 11,0111011 [А2]мобр = 00,1000000 [А4]мдоп = 11,0100000 01,0001101 10,1011011 Препълване 0 Препълване 1

Пример 15. Числата А1 = -0,1100110 и А2 = 0,0111000 да се сумират в модифициран допълнителен код, а числата А3= - 0,1001000 и А4 = 0,1101101 — в модифициран обратен код. [А1]мдоп = 11,0011010 [А3]мобр = 11,0110111 [А2]мдоп = 00,0111000 [А4]мобр = 00,1101101 [А1 + А2]мдоп = 11,1010010 = 100,0100100 [At + A2]np = 1,0101110 + 1 [А3 + А4]монр = 00,0100101

Page 8: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

8

[A3+ A4]np = 0,0100101

IV. Задание за работа 1. Да се преобразуват в двоична, осмична и шестнадесетична бройна система десетичните

числа 2567,367 и - 756,903. 2. Да се запишат в прав, обратен и допълнителен код числата-0,10111; -0,10000; 0,11110. 3. Защо се налага да се използуват модифицирани кодове? От сумирането на какви числа се

получават препълванията 01 и 10? 4. Да се сумират в модифициран обратен код числата -0,10101 и .10011 и в модифициран

допълнителен код — числата +0,10011 и -0,01111. 5. Да се сумират в модифициран код числата: 0,10110 и 0,01111;-0,10000 и -0,1001. 6. Да се умножат числата -0,101011 и 0,100110; -0,111010 и-0,100011. 7. Какви предимства и недостатъци има представянето на числата с плаваща запетая в

сравнение с представянето с фиксирана запетая?

V. Указания за работа При изпълнението на задачите използвайте примерите приложени в теоретичната част?

VI. Контролни въпроси

1. Какви бройни системи се използват в компютърната техника? 2. Как се представят целите числа в компютърната техника? 3. Как се представят дробните числа в компютърната техника? 4. Как се представят положителните и отрицателни числа в компютърната техника? 5. Каква е разликата между прав и обратен програмен код? 6. Как се извършва умножение на числа представени в двоична бройна система ? 7. Какво е мантиса? 8. Какво е порядък?

VII. Литература [1.] Юлиaна Д. Георгиева, Ръководство по програмиране и използване на изчислителни системи, София, Техника 1988 г.

Page 9: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

9

Лабораторно упражнение № 2

Тема: Алгоритмизация на числени и логически задачи. Документиране.

I. Цел на лабораторното упражнение Усвояване на знания и умения при съставяне на линейни, разклонени и циклични алгоритми.

II. Постановка на задачата

В настоящото лабораторно упражнение ще бъдат разгледани основните методи за алгоритмизиране на програмни задачи. Ще бъдат разгледано описанието на алгоритми посредством блок схеми (линейни, разклонени и циклични алгоритми)

III. Теоретични сведения

Алгоритъм Важна роля в програмирането играе понятието алгоритъм. Между понятията алгоритъм и

програма има частично съответствие — всеки алгоритъм може да се представи като програма, но не всяка програма е алгоритъм. За съжаление не съществува първично математическо определение за алгоритъм и това понятие трябва да се приеме за основно както понятията множество, точка, права и т. н. Такива понятия обикновено не се определят, а се описват, като се посочват техните най-характерни черти в терминология, която е вече позната.

В такъв смисъл алгоритъм се нарича всяка точно и еднозначно описана крайна последователност от действия (операции) над определени входни данни, която винаги води до резултат (изход), който е във функционална зависимост от стойностите на входните данни.

Ако така описаната последователност завършва (дава резултат) не винаги, а само за определена област от стойности (подмножество от всички възможни съчетания на стойности) на входните данни, такъв алгоритъм се нарича частичен алгоритъм или процедура.

Свойства на алгоритмите. Според Колмогоров и Марков, за да бъде алгоритъм, една процедура трябва да има крайно по обем описание, съответно да съдържа краен брой символи от една крайна азбука. За да се определи дали дадено описание на действия (процедура) е алгоритъм или не, трябва да се провери дали то отговаря на определени условия: 1. Дискретност, т.е. да се състои от отделни, разграничени по време една от друга стъпки,

всяка от които се извършва за крайно време. 2. Детерминираност, т.е. резултатът от действията върху данните и посочването на

следващата стъпка трябва да бъдат еднозначно определени от действията, извършени до текущия момент.

3. Масовост, т.е. процедурата трябва да свършва с резултат не за краен брой съчетания от входните данни, а за едно потенциално безкрайно множество от входни данни.

4. Резултатност, т.е. процедурата трябва винаги да дава резултат, ако входните данни принадлежат на даденото подмножество.

5. Крайност на изпълнението, т.е. процедурата трябва да свършва с резултат за краен брой стъпки. Пример за процедури, които не са алгоритми, са т. нар. зациклени процедури —

последователности, образуващи затворен цикъл от действия без изход. Такива процедури

Page 10: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

10

никога не завършват. Друг пример за математически обект, който не е алгоритъм, е диференциалното уравнението не е дискретно и не е процедура.

За представяне на алгоритмите се използуват различни езици: естествени, алгоритмични, параметрични, графични и др., които се характеризират с различна степен на формализация на описанието, удобство за запис и четене, възможност за обмен и т.н. Всяка програма е специфично представяне на алгоритъма, разбира се, като се пренебрегнат ограниченията, налагани от съответния език за програмиране.

Езикът на блоковите схеми е опростена разновидност на графичните езици. Блоковата схема е описание на алгоритъма, което се състои от върхове (възли) и съединяващи ги клонове. Клоновете са отбелязани със стрелки, които показват посоката на движение, т.е. реда на изпълнение на операциите. На фиг. 1 са показани по-важните приети у нас по стандарт символи използувани в блоковите схеми (БДС 19103—78).

Фиг. 1. Символи, използувани в блоковите схеми.

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

като се посочват връзките по управлението със съответните символи. Блоковата схема описва нагледно и еднозначно потока на управлението, но в нея няма

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

Page 11: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

11

Често опитните програмисти съставят програмата направо на езика за програмиране, а после уточняват блоковата схема, което не е пример за правилен метод.

Програмата може да се проектира направо на алгоритмичен език, ако той е до-гъвкав за тази цел. Подходящи като езици за проектиране са например АДА и др. При този подход в началните етапи може да се използува смес от естествен език. и език, за програмиране. На естествен език се описват (подобно на коментарии) онези части, които подлежат на уточнение в следващите етапи. Съществуват и специални езици за проектиране, какъвто е например PDL (Program Design Languag).

Друг подход е използуването на псевдокод. Псевдокодът е система от означения, която компенсира недостатъците на конкретния език, на който ще се програмира. Например при използуване на БЕЙСИК, който изисква възходяща номерация на програмните редове, може да се приеме временно използуване на етикети и да се програмира без номера. В език като ФОРТРАН IV липсват управляващи конструкции от вида IF–THEN-ELSE или WHILE-DO, но те могат да се използуват в приетия псевдокод когато спомагат за по-компактно изразяване на алгоритъма. Програмите на псевдокод се транслират — ръчно или с предпроцесор - в програми на съответния език. Тази трансформация е напълно формална и не представлява трудност дори и за програмист с ниска квалификация. Псевдокодът трябва да бъде от по-високо ниво от нивото на използувания език, което изисква от програмиста познания в областта на програмните езици структурите от данни и т.н. В идеалния случай псевдокодът става излишен и се проектира направо в термините на дадения програмен език, ако той е достатъчно богат.

Методи на проектиране. Проектирането включва също и разделяне на задачата на подзадачи. При това методът на проектиране може да е низходящ или възходящ.

Низходящ метод. При него задачата се разделя на подзадачи, всяка от тях — на нова подзадача и т. н. до достигане на елементарни действия (стъпки), които могат г на избрания език за програмиране. Низходящият метод се нарича още метод на стъпковата детайлизация, тъй като на всяка следваща стъпка става детайлизиране (разделяне на дадена задача на подзадачи).

Възходящ метод. При него най-напред се оформят отделните части на програмата (подпрограми или модули, а понякога и по-дребни единици, отделни оператори). След това тези части се свързват в по-крупни функционални групи и най-накрая в завършена система. Този подход в последно време се смята за ненадежден и дори тъй като функциите на частите винаги произтичат от функциите на цялото и от там — те са тяхно следствие, а не предпоставка.

Модулен метод. Тук подзадачите се оформят като подпрограми — модули, които имат минимален брой връзки с останалата част от програмата (обкръжението). Удобно е модулите да бъдат определени на функционален принцип, т. е. да изпълняват точно определени, често използувани функции. Главното предимство на модулите е възможността те да се програмират, транслират и настройват отделно и независимо един от друг. Това е с естествена връзка с възможностите на програмния език който ще се използува.

Обикновено модулите се свързват в йерархична система. Модулното проектиране може да се съчетае както с възходящ, така и с низходящ метод на проектиране. При низходящия метод най-напред се разработват модулите от по-горните нива, а после — от по-долните, докато при възходящо проектиране е обратното.

P

P

B F

T

P

B F

T

б а в

Фиг. 2. Програмни конструкции а-процес б – избор в- цикъл

Page 12: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

12

Всяка програма може да бъде изградена със следните три програмни конструкции: процес (блок) — фиг.2а, избор — фиг. 26, и цикъл — фиг. 2в.

Пример 1: Да се състави блоковата схема на алгоритъма за провеждане на телефонен

разговор от кабина при известен номер. Не представлява трудност да се опише словесно последователността от действия, за да се свърже с даден абонат. В действителност е необходимо не само да се извършват съответните действия, а и да се анализират някои условия, в зависимост от които следва нашето действие. На естествен език алгоритъмът може да бъде описан със следната последователност от стъпки: 1. Вдигнете слушалката и ако чувате сигнал „централа", пуснете жетон и преминете към

т.2. Ако няма сигнал или той е „заето", преминете към т.6. 2. Изберете желания номер. 3. Ако сигналът е „заето" или друг сигнал, преминете към т.6, а в противен случай

изпълнете т.4. 4. Изчакайте отговора на абоната. Ако абонатът отговаря, преминете към т.5, а в противен

случай към т.6. 5. Проведете разговора. 6. Окачете слушалката. 7. При проведен разговор преминете към т.9, а в противен случай към т.8. 8. При следващ опит за връзка преминете към т.1, а в противен случай — към т.9. 9. Край.

Същият алгоритъм, изразен чрез блокова схема, е представен на фиг. 3. Действията, които се извършват, се записват в блок за обработка (функционален блок), а условията които се анализират – в блок за разклонение (логически блок). От структурна гледна точка алгоритмите могат да се разграничат линейни (неразклонени), разклонени и циклични части. На практика всички програми включват еднократно или многократно всички тези части.

Линейната част на алгоритъма е последователност от действия, които се изпълняват винаги в един и същ ред и се представя с последователно свързани блокове с един вход и един изход. За всяка възможна комбинация от входни данни за съответната част на задачата

последователността от действия се запазва и следователно не се налага да се включват условия, които могат да изменят тази последователност. Пример 2. Да се състави алгоритъм за определяне на χ и у от зависимостта х+iy =(a +

ib)/(c+id), където х = (ас — bd)/(c2 + d2); у = (aс — ad)/ (c2 + d2); (c2 + d2)≠0 Блоковата схема на алгоритъма е показана на фиг.4. Първият блок след началния означава

въвеждане на входните данни (в случая стойностите на коефициентите (a,b,c,d). Следващият блок е функционален и има предназначението да изчисли междинните величини P,Q и R. Те ще се изчисляват винаги в посочения ред. По принцип P.Q и R могат да се изчисляват в произволен ред, което дава възможност те да се обединят в един блок за обработка. За да се изчислят P,Q и R в друг ред, трябва да се разменят местата на изразите в блока. Във втория функционален блок се определят крайните резултати X и Y. Възможно е стойностите на X и Υ да се пресмятат направо. Този вариант е по-

неикономичен от гледна точка на броя на действията, тъй като величината Ρ се изчислява два пъти. Необходимостта от проверка на някакво условие (условия), в зависимост от изпълнението на което (които) изчислителният процес протича по един или друг начин, внася разклонение в алгоритъма. В блоковите схеми на алгоритмите разклоненията се реализират с блокове от типа β на фиг.1.1. Най-често се осъществяват разклонения в два клона по следните признаци: положителна или отрицателна стойност, равно или различно от нула, съвпадение или несъвпадение на две стойности и др. Когато се налага разклонение в повече клонове,

Page 13: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

13

препоръчва се те да се представят чрез подходящо свързване на блоковете за разклонение в два клона.

Пример 3. Да се състави алгоритъм за определянето на квадранта, в който се намира точка,

зададена с координатите си Α(X,Y). Точката е зададена с координатите си A(X,Y) и следователно те могат да се въведат като

входни данни. За да се определи номерът на квадранта, в който е точката, необходимо е да се проверят X и Υ по отношение на нулата. В дадената на фиг. 5 блокова схема проверката започва с координатата X. Възможно е най-напред да бъде проверена координатата Υ. Проверката на X или Υ свежда възможното множество от четири квадранта на два, след което следващата проверка за Υ или X конкретизира съответния квадрант. В променливата ΝΚ се записва номерът на квадранта и съдържанието й се извежда като краен резултат.

Начало

Вдигане на слушалката

Сигнал свободен

Пускане на монета

Избор наномер

Чакане на отговор

Провежданен аразговор

Окачване на слушалката

Абоната отговаря

Сигнал централа

Проведен разговор

Следващ разговор

Край

Не

Да

НеДа

Не

Да

Не

Да Не

Да

Начало

Край

Въвеждане на a , b, c , d

P=a . a - b. bQ=a . c - b . d R=b . c - a . d

X= Q/PY= R/P

Извеждане на Х и У

Фиг . 3 .

Фиг. 4 .

Page 14: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

14

Циклични алгоритми

При обработката на информации често се налага да се повторят многократно едни и същи

действия, като се сменят стойностите на някои параметри. Би могло тези действия да бъдат описвани многократно (всеки път при тяхното срещане) независимо от еднообразието им, но

за предпочитане е възможността операциите (действията) да се запишат еднократно. Така се изгражда цикъл, който в блоковата схема на алгоритъма представлява затворен път. В цикъла се различават три съставни части: подготовка на цикъла, тяло на цикъла и проверка на условието за край на цикъла (фиг.6а). При подготовката на

цикъла се задават началните стойности на някои величини, които се изменят в процеса на изпълнението на цикъла. Тялото на цикъла съдържа

операцията или съвкупността от операции, които се изпълняват многократно. В него се променят и стойнос-тите на параметрите, като те се подготвят за следващо

повторение. Проверката на условието за край на цикъла осигурява прекратяване на повторението,

когато е изпълнено определено логическо условие, наречено условие за край на цикъла.

X>0

Начало

Край

въвеждане на Х и У

Извеждане на NK

Y > 0 Y>0

NK=4 NK=2NK = 1 NK = 3

Да Не

Да Не Да Не

Фиг. 5

Начало

Въвеждане

Подготовка

Тяло

Край

Извеждане

Условие за край

Да /Не

Фиг.. 6

Не /Да

Начало

Въвеждане

BR = 0

Тяло

Край

Извеждане

Условие закрай

Да

Не

BR = BR +1

Начало

Въвеждане

Подготовка

Тяло

Край

Извеждане

Условие за край

Модификация

Не /Да

Да /Не

a ) б ) в)

Page 15: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

15

Условието за край на цикъла (изход от цикъла) може да се зададе по различни начини. Най-често се срещат следните два начина за определяне края на цикъла:

1. Зададен е фиксиран брой повторения на тялото на цикъла. На фиг. 6.б е дадена обобщена блокова схема на алгоритъм от този тип. В този случай се въвежда т.нар. брояч на повторенията. Подготовката на цикъла се състои в присвояване на начална стойност на брояча (в частен случай 0). При всяко изпълнение на тялото се изменя съдържанието на брояча (в частен случай се прибавя 1). От цикъла се излиза, когато съдържанието на брояча достигне определена стойност (в частен случай N)

2. Броят на повторенията на цикъла не е фиксиран предварително.Тялото на цикъла се повтаря, докато обработваната информация удовлетвори определени изисквания, явяващи се условие за край на цикъла (фиг. 6в). Проверката на условието за край на цикъла може да се извършва преди или след тялото на

цикъла (цикли с предусловие и цикли с пост условие). Много често при съставянето на алгоритмите се използуват т.нар. вложени цикли. При тях

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

отрицателните елементи, произведението от положителните и броя на нулевите елементи на дадеш вектор А с 30 елемента. По принцип алгоритъмът не е свързан с ЕИМ. но с учебна цел тук и по-нататък са използувани някои термини, свързани с машинната му реализация. Елементите на вектора А (конкретните 30 числа) трябва да се въведат в 30 последователни клетки на паметта. Всеки пореден елемент се анализира с цел да се определи към коя от трите групи принадлежи (положителни, отрицателни или нулеви), както и да се обработи съобразно с определената група. Следователно в случая е необходимо да се организира цикъл, който ще се повтаря краен (известен) брой пъти, т.е. това е цикъл от първия тип — с брояч (фиг.6б). Броячът на цикъла се установява първоначално в 1 и след всяко изпълнение на цикъла увеличава съдържанието си с 1, докато стигне крайната стойност — 30. По такъв начин съдържанието на клетката, отделена за брояч, определя номера на поредния разглеждан елемент на А. В блоковата схема, представена на фиг.7, броячът се означава , със символичното име I, а с А(I) се определя I-тият елемент, т.е. конкретната стойност, която се съдържа в I-тата клетка на вектора. За

трите търсени величини — сума, произведение и брой, се използуват три клетки съответно с имена S, Р, BR. Съдържанието на тези клетки се изменя в хода на изпълнение на цикъла и следователно последните трябва да бъдат начално установени при подготовката на цикъла (когато се установява и броячът I). Всеки път, когато разглежданият елемент е отрицателен,

Фиг . 7 .

Начало

Въвеждане A ( 30)

I = 1 S = 0 P = 1

BR = 0

BR = BR + 1

Край

ИзвежданеS,BR,P

A( I)=0 >>

=

P=P.A(I)S= S + A ( I )

I = I + 1

I > 30

Page 16: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

16

към съдържанието на S се прибавя неговата стойност, т.е. S трябва да бъде установена първоначално в 0. По аналогични съображения Ρ и BR трябва да се заредят първоначално съответно с 1 и 0. В клетката Ρ се натрупва произведението, т.е. на всяка стъпка съдържанието на клетката Ρ се умножава с I-тия елемент, ако той е положителен. Нулевите елементи се броят, като се прибавя 1 към съдържанието на BR всеки път, когато се срещне нулев елемент. След като се проверят всички елементи на вектора, т.е. когато I достигне 30, се излиза от цикъла. Извеждат се трите резултата — S, Ρ и BR

IV. Задание за работа 1. Да се състави алгоритъм на програма за намиране на по-малката от две стойности. 2. Да се състави алгоритъм на програма за решаване на квадратно уравнение. 3. Да се състави алгоритъм на програма за намиране на n! 4. Да се състави алгоритъм на програма за въвеждане и извеждане елементите на масив. 5. Да се състави алгоритъм на програма за изчертаване на „Фигури на Лисажу”. 6. Да се състави алгоритъм на програма за изчертаване на логаритмична спирала. 7. Да се състави алгоритъм на програма за намираме на производната на функцията (ln x)(n)

8. Да се състави алгоритъм на програма за търсене на минималната стойност в едномерен масив. 9. Да се състави алгоритъм на програма за търсене на максималната стойност в матрица. 10. Да се състави алгоритъм на програма за сортиране на едномерен масив по метода на пряка размяна. 11 Да се състави алгоритъм на програма за сортиране на масив по метода на пряка селекция. 12. Поставена е задача за изграждане на сортировъчен автомат за сортиране на гайки с размер φ10. при толеранс ±0.1 mm. Първоначално детайлите се намират в един контейнер, след, което се разпределят в други три според размера им. Да се състави алгоритъм за действие на сортировъчния автомат при условие, че бункерите побират по 10000 детайла. 13. Поставена е задача за изграждане на светофарна уредба на кръстовище при следните условия:

- Време за превключване 1min. - Време за жълт сигнал 10s. - Да се издава различен звуков сигнал при разрешени и забранено преминаване. - Да има цифрова индикация на времето на съответния сигнал.

Да се състави алгоритъм на функциониране на светофарна уредба по зададените изисквания. 14. Поставена е задача за проектиране и изграждане на автоматизирана поточна линия за сглобяване на автомобили. Поточната линия е разположена под формата на буквата П (фиг. 8) с ляв(11) и десен (10) клон.. В горния край на линията е необходимо да се направен прелез охраняван посредством бариери (9), през който да преминават електрокарите с частите за сглобяване. За транспортиране на сглобяваните коли от левия до десния клон на поточната линия се използват две робоколички (7,8). Посредством сигналите получени от сензори 1 до 6 се следи положението на колите. Действието на поточната линия е следното: Колата се придвижва по левия клон на поточната линия с постоянна скорост. При достигане края на клона сензор 1 се активира. В резултат на което колата се завърта на 90° и се премества върху робоколичката. След активирането на сензор 2, се издава звуков и светлинен сигнал. Бариерата се спуска, а двете робоколички тръгват една към друга. След

Page 17: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

17

активиране на сензори 3 и 4 робоколичките спират и колата се премества от първата на втората количка. Двете колички тръгват към изходно положение. При активиране на сензор 5, количка 2 спира а колата се прехвърля от количката върху поточната линия. При активиране на сензор 6 платформата с колата се завърта на 90° и се подава към транспортната лента. Да се реализира блок схема на алгоритъма на работа на така дефинираната система.

V. Указания за работа 1. Фигурите на Лисажу (Фиг. 9) се получават, когато два електрически сигнала се подадат на двата входа на осцилоскоп. При това на екрана на осцилоскопа се появява фигура с определена форма по която може да се съди за разликата в честотата, големината и фазата на двата сигнала. Програмно може да се реализира като при изчертаване за X координата се вземе стойността от изчислението на даден хармоничен сигнал (синусоида) , а по оста У се подаде стойността на друг хармоничен сигнал с променена фаза или честота.

Фиг. 9

2. Уравнението на логаритмична спирала е ρ=α.екϕ к > 0

.)1()(ln 1)( −−= nnx nxn )1...(3.2.1 −

3. Описание на алгоритъма чрез пряка селекция. Масивът А[1],...А[n], се обхожда и се избира елемента с най-малка стойност. След това той разменя мястото си с първият елемен A[1].

x=sin(2t+45) x=sin(2t) x=sin(4t) x=sin(3t+120) y=sin(t) y=sin(t) y=sin(t) y=sin(t)

1270 mm Rise : 7620 mm Run

up

1270 mmRise : 7620 mm Run

up

1 3 4 526

9 87

10

11

Фиг. 8

Page 18: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

18

Отново масивът се обхожда и се намира с най-малка стойност, след което се разменя с А[2]. Масивът е подреден след (n-1) на брой преминавания. 4. Описание на алгоритъма на пряка размяна (метод на мехурчето) Масивът A[1]…A[n] се обхожда от индекс n до индекс 1, като всеки два съседни елемента се сравняват. Ако десният елемент е по-малък от левия, те сменят местата си. След първото обхождане елементът с най-малка стойност е в най-лявата част на масива. Повторението на тази операция за останалите (n-1) елемента придвижва най-малкия от тях на втора позиция и т.н. След (n-1) на брой обхождания масивът се подрежда.

VI. Контролни въпроси 1. Какво разбирате под термина програмен алгоритъм? 2. Кои алгоритми са линейни? 3. Кои алгоритми са разклонени? 3. Кои алгоритми са циклични? 4. Какво представлява низходящото проектиране на програми? 5. Какво представлява възходящото проектиране на програми? 6.Какво представлява модулното програмиране? 7. Какво е структурно програмиране? 8. Какво разбирате под термина “итерация”? 9. Какво разбирате под термина “рекурсия”?

VII. Литература 1. Бозм, Б. У. Инженерное проектирование программного обеспечения, М., Радио и связь, 1985. 2. Вирт, Н. Систематическое программирование. Введение. М., Мир, 1977. 3. Гласс, Р. Руководство по надежвому программированию. М., Финанси и статистика, 1982. 4. Гласс, Р., Р. Нуазо. Сопровождение программного обеспечения. М., Мир, 1983. 5. Грис, Д. Наука программирования. М-, Мир, 1984. 6. Дени Ван Тасел. Стил, ефективност, настройка и тестване в програмирането. С., Техника, 1979. 7. Лингер, Р., Х. Миллс и Б. Уипгт. Теория и практика структурного программирования. М., Мир, 1982. 8. Майер, Г. Искусство тестирования программ. М,, Финанси и статистика, 1982. 9. Тайер, Т. и др. Надежность программного обеспечения. М-, Мир, 1981. 10. Хьюз, Д., Д. Мичтом. Структурной подход к программированию. М., Мир, 1980.

Page 19: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

19

Лабораторно упражнение № 3

Тема: Структура на С/С++ програми. Променливи и оператори в езика С.

Основни типове данни. Въвеждане и извеждане на информация. I. Цел на лабораторното упражнение Усвояване на знания и умения при създаването на С/С++ програми. II. Постановка на задачата В настоящото упражнение студентите ще се запознаят с интегрираната среда. Ще бъде алгоритмизирани, програмирани, настроени и изпълнени прости линейни програми. III. Теоретични сведения

Входно-изходни операции в езика С. Езикът Си има няколко функции за въвеждане на данни в програмата (от файл,от

клавиатура и др.).Това са следните функции:

сlеаrеrr _filеnо fsеек рutс Sрrintf fсlоsе _flushаll fsеtроs рutсhаr Ssсаnf _fсlоsеаll fореn _fsореn рuts _tеmрnаm _fdореn fрrintf ftеll _рutw Тmрnаm fеоf fрutс fwritе rеwind Тmрfilе fеrrоr _fрutсhаr gеtс _rmtmр Ungеtс fflush fрuts gеtсhаr sсаnf Vfрrintf fgеtс frеаd gеts sеtbuf vрrintf _fgеtсhаr frеореn _gеtw sеtvbuf _vsnрrintf fgеtроs fsсаnf рrintf _snрrintf vsрrintf fgеts

Необходимо е да направим пояснението, че част от тези функции се използват за

работа с файлове и ще се разглеждат в следващите упражнения. Тук ще обърнем внимание на по-често използваните функции за вход и изход от към стандартни входно-изходни устройства.

Функция sсаnf.Предаване на адрес към sсаnf.Функции gеts и gеtсh

Функция sсаnf За интерактивен режим на въвеждане на информация най-често се използува

библиотечната функция sсаnf. Тя е функция за вход, еквивалентна на функцията за изход рrintf. Форматът е:

sсаnf(<ФорматиращНиз>,<Адрес>,<Адрес>,...) Много от форматите тип %<буква>, които използува sсаnf, са същите както за рrintf

(напр. %d, %f, %с). Има обаче една много съществена особеност в работата на sсаnf -

Page 20: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

20

елементите от списъка след форматиращия низ ТPЯБBА ДА БЪДАТ АДPЕСИ! Например операторът: sсаnf("%d %d",&а,&b);очаква потребителят да въведе две стойности от цял тип, разделени чрез интервал.

Те ще бъдат присвоени съответно на променливите а и b. Забележете, че се използува операторът & ("Адрес на") за да се предадат адресите на променливите а и b на функцията sсаnf. Символът, чрез който са разделени форматите във форматиращия низ, не само ги разграничава, но и задава начина, по който потребителят трябва да раздели двете въвеждани стойности. Pазделянето чрез интервал показва, че стойностите могат да се разделят чрез произволна комбинация от интервали,табулатори и символи за нов ред. Ако желаете числата да се отделят чрез запетая, поставете във форматиращия низ запетая вместо интервал:

sсаnf("%d,%d",&а,&b);

Предаване на адрес към sсаnf Когато желаете да въведете символен низ, който да се запази в символен масив, трябва

да съобразите, че идентификаторът на масива означава адреса на началото му. Затова в този случай при обръщението към sсаnf се използува направо името на масива.

Например: mаin() { сhаr nаmе[30]; рrintf("Bашето име ? "); sсаnf("%s",nаmе); рrintf("Здравейте, %s\n",nаmе); }

Тук за съхранение на въведения символен низ се използува масив (сhаr nаmе[30]), а не указател към символ (сhаr *nаmе). Предпочетен е първият начин,тъй като с него автоматично се отделя място за целия низ, докато при указателя , това място трябва да се задели явно с помощта на специален оператор.

Име sсаnf - осъществява форматиран вход Употреба int sсаnf(сhаr *fоrmаt[,аrgumеnt...]);

int сsсаnf(сhаr *fоrmаt[,аrgumеnt...]); int fsсаnf(FILЕ *strеаm, сhаr *fоrmаt [,аrgumеnt...]); int ssсаnf(сhаr *string,сhаr *fоrmаt[,аrgumеnt...]); int vfsсаnf(FILЕ *strеаm,сhаr *fоrmаt, vа_list аrgр); int vsсаnf(сhаr *fоrmаt, vа_list аrgр); int vssсаnf(сhаr *string,сhаr *fоrmаt, vа_list аrgр);

Прототип stdiо.h

Функциите от фамилията ...sсаnf обработват входните полета символ по символ и ги превръщат в съответствие със зададен форматиращ низ.

Всички функции ...sсаnf: • работят с форматиращ низ (string), който определя начина на интерпретация на

входните полета; • прилагат форматиращия низ върху променлив брой • входни полета, за да осъществят форматиран вход;

Page 21: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

21

• записват форматирания вход на адрес, даден като аргумент след форматиращия низ (тези адреси се задават или чрез [,аrgumеnt...] или чрез vа_list раrаm.

Когато някоя от функциите регистрира първата си форматна спецификация във

форматиращия низ, тя анализира и превръща първото входно поле съгласно тази спецификация, след което запазва резултата в мястото, определено от първия адресен аргумент. Следва анализ, превръщане и записване на останалите входни полета.

Източникът на входа се приема по подразбиране за три от ...sсаnf функциите: sсаnf и vsсаnf четат от стандартния вход stdin; сsсаnf чете направо от конзолата. Останалите четири функции ползуват допълнителен аргумент (първия от списъка на

аргументите), който указва източника на входна информация:

fsсаnf и vfsсаnf приемат вход от входен поток (определен от strеаm); ssсаnf и vssсаnf четат от символен низ от паметта (сочен от string).

Четири от функциите получават списък от адресни аргументи направо при обръщение

към тях (sсаnf,сsсаnf, fsсаnf, ssсаnf). Останалите три (vsсаnf, vfsсаnf, vssсаnf) получават адресните си аргументи от списък

от променливи аргументи. Функциите v...sсаnf са известни като алтернативни входни точки за функциите ...sсаnf.

За допълнителни сведения относно списък от променливи аргументи вижте

дефиницията на vа_... Ето резюме за всяка от функциите:

sсаnf чете от stdin и съхранява въведеното в мястото, посочено от адресните аргументи &аrg1,...,аrgn.

сsсаnf чете направо от конзола и съхранява въведеното в мястото, посочено от адресните аргументи &аrg1,...,аrgn.

fsсаnf чете от посочен входен поток и съхранява въведеното в мястото, посочено от адресните аргументи &аrg1,...,аrgn.

ssсаnf чете данни, записани като символен низ в паметта и съхранява въведеното в мястото, посочено от адресните аргументи&аrg1,...,аrgn. ssсаnf не променя символния низ - източник наинформацията.

vsсаnf работи като sсаnf, но приема адресни аргументи от масиваvа_аrg на vа_list раrаm. vfsсаnf работи като fsсаnf, но приема адресни аргументи от масива vа_аrg на vа_list раrаm. vssсаnf работи като ssсаnf, но приема адресни аргументи от масива vа_аrg на vа_list раrаm.

ФОPМАТИPАЩ НИЗ Форматиращият низ присъствува при всяко обръщение към функция sсаnf и определя

как съответната функция ще обработи, преобразува и съхрани входните полета. Адресните аргументи трябва да не са по-малко от включените във форматния низ спецификации. Ако адресните аргументи са по-малко,последствията са непредвидими и в повечето случаи фатални. Ако адресните параметри са повече от зададените спецификации, излишните се игнорират.

Page 22: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

22

Форматиращият низ е символен низ, съдържащ три типа обекти: символи оставящи интервали (интервал ( ), табулатор (\t) или нов ред (\n)), символи и форматни спецификации.

• ако функция ...sсаnf регистрира символ за интервали във форматиращия низ, тя чете

(но не записва) всички следващи интервални символи от входа до първия различен, без да ги съхранява.

• символите, различни от тези за интервали са останалите АSСII символи (с изключение на символа %). Ако функция ...sсаnf регистрира такъв символ във форматиращия низ, тя чете, но не съхранява съвпадащите с него символи.

• Форматната спецификация насочва функциите ..sсаnf да четат символите от входното поле, да ги преобразуват в определен тип стойности и да ги съхраняват в мястото, определено от адресните аргументи.

Завършващи символи за интервали във входните полета не се четат (включително и

символ за нов ред), освен ако това не е предвидено явно във форматиращия низ.

Форматни спецификации Форматните спецификации на ...sсаnf имат формата: % [*] [ширина] [F|N] [h|l] символ_за_тип Винаги се започва със символа "%". Следват полетата в следния ред: ∗ незадължителен символ, забраняващ присвояването [*] ∗ незадължителна спецификация за ширина на полето ∗ [ширина]. ∗ незадължителен спецификатор на размера на указател [F|N]. ∗ незадължителен модификатор на типа на аргумента [F|N|h|l]. ∗ символ за типа [символ_за_тип] Задължителни компоненти на форматиращия низ Тук са показани основните характеристики на форматирането на входа, управлявано от

незадължителните символи, спецификатори и модификатори:

Символ или спецификация Обект на управление или спецификатор * забранява присвояването на следващото входно поле. Ширина минимален брой символи, които да се прочетат; възможно е да бъдат

прочетени по-малко символи, ако функцията ...sсаnf регистрира символ за интервали или символи, които не могат да се преобразуват.

[F|N] променя подразбиращия се размер на адресните аргументи: N Вътрешно сегментен указател - nеаr; F Между сегментен указател - fаr; [h|l] тип на аргумента. Променя подразбиращия тип на адресен аргумент (

h = указател към тип shоrt int; l = указател към тип lоng int). sсаnf символи за превръщане на типа

Page 23: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

23

Следващата таблица дава списък на символите, използувани за преобразуване на типа на входа, приеман от всеки от тях и формата, в който ще се съхрани въведената информация.

Информацията в таблицата се основава на приемането, че не е включен никой от

незадължителните спецификатори и модификатори. Резултатите от съвместното използуване на тези елементи са показани по-долу.

Символ Очакван за типа вход Тип на аргумента d Десетично цяло Указател към int (int *аrg) D Десетично цяло Указател към lоng(lоng *аrg) о Осмично цяло Указател към int (int *аrg) О Осмично цяло Указател към lоng (lоng *аrg) i цяло Указател към int (int *аrg) (десет., осмично или

шестнадесетично.) I цяло Указател към lоng(десет., осмично (lоng *аrg) или

шестнадесетично.) u десетично цяло Указател към десетично без знак цяло без знак

(unsignеd int *аrg) U десетично цяло Указател към десетично без знак цяло без знак

(unsignеd lоng *аrg) х шестн. цяло Указател към int (int *аrg) X шестн. цяло Указател към lоng (lоng *аrg) е плаваща запетая Указател към flоаt (flоаt *аrg) Е плаваща запетая Указател към flоаt (flоаt *аrg) f плаваща запетая Указател към flоаt (flоаt *аrg) F плаваща запетая Указател към flоаt (flоаt *аrg) с символ Указател към символ(сhаr *аrg)Ако е е дадена и

ширина наполето w (напр. %5с), това ще бъде указател към масив от w символа (сhаr аrg[w]).

s символен Указател към масив от символи низ (сhаr аrg[]). % символ % Не се прави преобразуване.Запазва се символът %. n (нищо) Указател към int (int *аrg). към цяло B този указател

се запазва броят на успешно прочетените символи до регистриране на %n.

р шестнадесетично. Указател към обект число във(fаr * или nеаr *) формат %р преобразуването се прави по XXXX:YYYY подразбиране за приетия за или ZZZZ модела на паметта размер на указателите.

Входни полета Като входни полета се интерпретират: • всички символи до (но не включително) следващ символ за интервали. • всички символи до първия, който не може да се преобразува съгласно текущата

форматна спецификация. • символите до n-тия символ, където n е зададената в спецификацията ширина на

полето.

Page 24: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

24

Приети конвенции Тук са изложени основните конвенции, приети и свързани с някои от спецификациите:

%с превръщане Тази спецификация чете следващите символи, включително и символи за интервали. За да се пропуснат символите за интервали и да се прочете следващ, различен от тях символ, използувайте %1s.

%Wс превръщане (W е спецификация за ширина) Адресният аргумент е указател към масив от символи, състоящ се от W елемента (сhаr аrg[W]).

%s превръщане Адресният аргумент е указател към масив от символи (сhаr аrg[]). Размерът на масива трябва да бъде поне (n+1) байта, където n е дължината на

символния низ s (в символи). Входното поле завършва с интервал или със символ за нов ред. Нулевият символ се добавя автоматично към символния низ и се запазва като последен елемент в масива. [филтър_за_търсене] преобразуване

Символът за тип s може да се замести с поредица от символи, оградени в квадратни скоби. Адресният аргумент е указател към масив от символи (сhаr аrg[]).

Квадратните скоби ограждат филтър - набор от символи, които определят символите, които може да включва входното поле.

Ако първият символ в скобите е "^", то филтърът се инвертира -допускат се всички символи, различни от посочените във филтъра.

Входното поле е символен низ, без включени в него символи за интервали. Функцията ... sсаnf чете съответното входно поле до достигане на първия символ, който не се допуска от наложения филтър. Ето два примера:

%(аbсd) търси във входното поле някой от символите а, b, с или d. %(^аbсd) търси във входното поле символи различни от а, b, с и d.

%Е, %f и %F превръщания за плаваща запетая Числата с плаваща запетая във входните полета трябва да бъдат в следния общ формат:

[+/-] ddddddddd [.] dddd [Е|е][+/-] ddd

където квадратните скоби означават,че елементът е незадължителен, а с ddd са означени десетични, осмични или шестнадесетични цифри.

%i, %о, %х, %D, %I, %О, %X, %с, %n превръщания За всяко превръщане, където се допуска указател към символ, int или lоng, може да се използува и указател към unsignеd сhаr, unsignеd int или unsignеd lоng.

Символ за забрана на присвояването (*) Ако символът "*" следва символа "%" във форматна спецификация, следващото

входно поле се анализира, но не се присвоява на следващия адресен аргумент.Полето, чието присвояване е забранено, се очаква да бъде от типа, указан след символа "*".

Спецификаторът [ширина]

Page 25: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

25

Спецификаторът за ширина (n) е десетично цяло, определящо максималния брой

символи, който да бъде прочетен от входното поле. Ако входното поле съдържа по-малко от n на брой символа, функцията ...sсаnf чете

всички символи от него и продължава със следващото поле и следващата форматна спецификация.

Ако преди прочитане на n-символа се срещне символ за интервали (интервал, табулатор, символ за нов ред) или символ, който не може да се преобразува, функцията преминава към следващата спецификация.

Символи, които не могат да се преобразуват са символите, непозволени за зададения формат (напр. 8 и 9 за осмичен формат или I или К за шестнадесетичен или десетичен). Спецификатор за ширина

Как се променя ширината при съхранение на входа

n Ще бъдат прочетени, преобразувани и съхранени в текущия адресен аргумент до n на брой символа.

Модификатори за входния размер и за типа на аргумента Модификаторите за входния размер (F и N) и за типа на аргумента (h или l) определят

начина, по който функцията ...sсаnf интерпретира съответните адресни аргументи (аrg). F и N имат приоритет пред декларирания размер на аrg. h и l показват какъв тип ще се използува за следващите входни данни (h - за shоrt, l –

за lоng). Bходните данни ще се преобразуват до зададения тип и аrg за тях трябва да сочи обект със съответния размер (shоrt за %h, lоng за %l) Модификатор Как действува на превръщането (аrg) F аrg се интерпретира като указател тип fаr N Има приоритет пред декларирания размер. аrg се интерпретира

като указател тип nеаr N не може да се използува при много голям модел на паметта (hugе).

h За d, i, о, u, х типовете: превръща входа в shоrt int и го запазва в обект от този тип. За D, I, О, U, X типове - няма ефект. За е, f, с, s, n, р типове - няма ефект.

l За d, i, о, u, х типовете: превръща входа в lоng int и го запазва в обект от този тип. За е, f типовете: превръща входа в dоublе и го запазва в обект от този тип. За D, I, О, U, X типове - няма ефект. За с, s, n, р типове - няма ефект.

а функция ...sсаnf спира обработката на входа.

Функциите ...sсаnf могат да прекратят обработката на определено входно поле преди достигане на нормалния му край (символ за интервали) или могат да прекратят обработката на целия вход по много и редица причини.

Функциите ...sсаnf прекратяват обработката на текущото входно поле и преминават към следващото при следните ситуации: • Регистриран е символ "*" за забрана на присвояване, следващ символа "%" във

форматната спецификация. Съответното входно поле се анализира без да се съхрани; • При зададена ширина w са прочетени w на брой символа; • Прочетеният символ не може да се преобразува съгласно текущата форматна

спецификация. Например А при десетичен формат;

Page 26: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

26

• Следващият символ от входното поле не е включен в наложения филтър.

Когато функция ...sсаnf прекрати обработката на входно поле по някоя от тези причини, следващият символ се счита за не прочетен и за начало на следващото входно поле или първия от следваща операция по въвеждане.

Функция ...sсаnf прекратява обработката на входа при следните ситуации: • следващият символ от входното поле не отговаря на съответния символ за интервали,

зададен във форматиращия низ; • следващият символ от входен файл е ЕОF; • форматиращият низ е изчерпан.

Ако във форматиращия низ се срещне поредица от символи, които не са част от форматна спецификация, те трябва да съвпадат с текущо въвежданите символи от входното поле. Функцията ще ги анализира без да ги запази. Ако се появи символ, различен от тях, той остава във входното поле все едно, че не е четен.

Резултат: Всички функции ...sсаnf връщат броя на успешно анализираните, конвертирани и

съхранени входни полета. Тази стойност не включва полетата, които са само анализирани, без да са съхранени.

Ако някоя от тези функции направи опит да чете след края на файл (или края на символен низ за ssсаnf и vssсаnf), върнатата стойност е ЕОF.

Ако няма съхранени полета, резултатът е нула. Ако във форматиращия низ се срещне поредица от символи, които не са част от

форматна спецификация,те трябва да съвпадат с текущо въвежданите символи от входното поле.Функцията ще ги анализира без да ги запази. Ако се появи символ, различен от тях, той остава във входното поле все едно, че не е четен. Преносимост: Функциите sсаnf, fsсаnf, ssсаnf и сsсаnf са разработени за ОС UNIX.

Функции gеts и gеtсhаr

Функцията sсаnf има този недостатък, че интерпретира въведения интервал като знак за край на символния низ. Поради тази причина, в примера за предаване на адрес към sсаnf е възможно да се въведе само едно име. Проблемът, създаден от наличието на интервал във въвеждания символен низ, може да се преодолее по два начина - като се използуват няколко масива ( всяка част, оградена от интервали, се помества в отделен масив) или с помощта на функцията gеts. Ето примери:

mаin() { сhаr first[20],middlе[20],lаst[20]; рrintf("bашето име ? "); sсаnf("%s %s %s",first,middlе,lаst); рrintf("Здравейте, %s %s %s!\n",first,middlе,lаst); }

B случая, функцията sсаnf ще чака докато се въведат три символни низа (разделени чрез интервал, табулатор или <Rеturn>). Когато цялата входна информация трябва да се прочете в един символен низ, независимо от съдържащи се вътре интервали и табулатори, обикновено се използува функцията gеts.Функцията gеts "чете" всички въведени символи, докато срещне символ за край на ред

Page 27: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

27

<Rеturn>. Самият <Rеturn> не се записва в символния низ. Вместо него, в края се поставя нулевият символ (\0). Следващият пример демонстрира приложението на gеts:

mаin() { сhаr nаmе[60]; рrintf("Bашето име ? "); gеts(nаmе); рrintf("Здравейте, %s\n",nаmе); } Остана да разгледаме работата на функцията gеtсh. Тя чете само един символ, въведен

от клавиатура, без да го отпечатва на екрана. ЗАБЕЛЕЖЕТЕ, че gеtсh не връща прочетения символ като параметър. Самата тя е дефинирана като същинска функция от тип сhаr и нейната стойност може да се присвои на променлива от символен тип, да се отпечата и т.н. (напр. сh=gеtсh()).

Функция gеtсhаr(); Синтаксис: сh=gеtshаr(); Предназначение: Връща АSСII кода на символ съответстващ на даден натиснат клавиш.

Функция рrintf Име рrintf - функция за форматиран изход Употреба: int рrintf(сhаr *fоrmаt, ...);

int срrintf(сhаr *fоrmаt[,аrgumеnt, ...]); int fрrintf(FILЕ *strеаm, сhаr *fоrmаt [,аrgumеnt, ...]); int sрrintf(сhаr *string, сhаr *fоrmаt [,аrgumеnt, ...]); int vfрrintf (FILЕ *strеаm, сhаr *fоrmаt, vа_list раrаm); int vрrintf (сhаr *fоrmаt, vа_list раrаm); int vsрrintf (сhаr *string, сhаr *fоrmаt, vа_list раrаm);

Прототип stdiо.h Описание Фамилията ...рrintf осъществява форматиран изход. Всички функции:

• приемат форматиращ низ, който определя начина на форматиране на изхода (това е

аргументът fоrmаt). • правилата, зададени чрез форматиращия низ, се прилагат при извеждане на стойностите

на променливите, зададени чрез аrgumеnt... или чрез vа_list раrаm. B три от функциите мястото за изход се определя неявно: • рrintf изпраща печата на стандартния изход stdоut. Същото прави vрrintf. • срrintf изпраща изхода към конзолата. • Останалите четири функции имат допълнителен аргумент (първия в списъка), който

определя къде ще бъде насочен изходът: • fрrintf и vfрrintf насочват изхода към посочения поток. • sрrintf и vsрrintf записват изхода в символен низ, разположен в оперативната памет. При четири от функциите (рrintf, срrintf, fрrintf и sрrintf), аргументите се форматират при обръщение към съответната функция. Останалите три (vрrintf, vfрrintf и vsрrintf) изискват аргументите да са форматирани от

Page 28: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

28

променлив списък от аргументи. Функциите v...рrintf са известни като алтернативни входни точки във функциите ...рrintf. Bж. дефиницията на vа_... за повече информация. Ето резюме на ...рrintf функциите: рrintf изпраща изхода на stdоut. срrintf отпраща изхода директно на конзолата, без да превръща символа за нов ред

в комбинацията СR/LF. fрrintf изпраща изхода в посочения поток strеаm. sрirntf отпраща изхода като нулево прекъснат символен низ в string. Потребителят

сам трябва да се грижи да има достатъчно място в string. Vрrintf работи като рrintf, но взема аргументите от масива vа_аrg от vа_list раrаm. vfрrintf работи като fрrintf, но взема аргументите от масива vа_аrg от vа_list раrаm. vsрrintf работи като sрrintf, но взема аргументите от масива vа_аrg от vа_list раrаm.

За пример за използуване на vрrintf вижте vа...

Фоpматиpащ низ

Форматиращият низ определя как съответната функция ще преобразува, форматира и отпечата аргументите си. Трябва да има достатъчно аргументи за форматиращия низ, в противен случай резултатите са непредвидими и обикновено с тежки последици. Излишните аргументи (за които не е предвидена обработка във форматиращия низ) се игнорират.

Форматиращият низ е символен низ, който съдържа два типа обекти - обикновени символи и превръщащи спецификации: обикновените символи се копират буквално на изхода; превръщащите спецификации вземат поредния аргумент от списъка и го форматират.

Форматни спецификации Форматните спецификации във функциите ...рrintf имат следната форма: % [флагове][ширина][.точност][F|N|h|l] тип Винаги се започва със символа "%". Следват полетата в следния ред:

незадължителна последователност от символи-флагове [флагове]. незадължителна спецификация за ширина на полето [ширина]. незадължителна спецификация за точност [.точност] незадължителен модификатор на размера на аргумента [F|N|h|l]. символ за типа на превръщане [тип]

Незадължителни компоненти на форматиращия низ Тук са показани основните характеристики на форматирането на изхода, управлявано от незадължителните символи,спецификатори и модификатори:

Символ или спецификатор Обект на управление или спецификация

Page 29: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

29

Флагове подравняване на изхода, в сила за числа, десетични точки, завършващи нули, представки за осмичен и шестнадесетичен запис.

Ширина минимален брой символи, които да се отпечатат, запълване с интервали и нули

Точност максимален брой символи, които да се отпечатат; за цели числа - минимален брой цифри, които да се отпечатат.

[F|N|h|l] променя подразбиращия се размер на аргумента (N = вътрешно сегментен указател - nеаr; F = между сегментен указател - fаr; h = цяло тип shоrt; l = цяло тип lоng).

Символи за превръщане на типа Следващата таблица дава списък на символите, използувани за преобразуване на типа, типа на входния аргумент, приеман от всеки от тях и формата на изхода. Информацията в таблицата се основава на приемането, че не е включен никой от незадължителните спецификатори и модификатори. Резултатите от съвместното използуване на тези елементи са показани по-долу. Символ Очакван за типа вход Тип на аргумента d Десетично цяло Указател към int (int *аrg) D Десетично цяло Указател към lоng(lоng *аrg) о Осмично цяло Указател към int (int *аrg) О Осмично цяло Указател към lоng (lоng *аrg) i цяло Указател към int (int *аrg) (десет., осмично или

шестнадесетично) I цяло Указател към lоng(десет., осмично (lоng *аrg) или

шестнадесетично.) u десетично цяло Указател към десетично без знак цяло без знак

(unsignеd int *аrg) U десетично цяло Указател към десетично без знак цяло без знак

(unsignеd lоng *аrg) х шестн. цяло Указател към int (int *аrg) X шестн. цяло Указател към lоng (lоng *аrg) е плаваща запетая Указател към flоаt (flоаt *аrg) Е плаваща запетая Указател към flоаt (flоаt *аrg) f плаваща запетая Указател към flоаt (flоаt *аrg) F плаваща запетая Указател към flоаt (flоаt *аrg) с символ Указател към символ(сhаr *аrg)Ако е е дадена и

ширина наполето w (напр. %5с), това щебъде указател към масив от wсимвола (сhаr аrg[w]).

s символен Указател към масив от символи низ (сhаr аrg[]). % символ % Не се прави преобразуване. Запазва се символът %. n (нищо) Указател към int (int *аrg). към цяло B този указател

се запазва броят на успешно прочетените символи до регистриране на %n.

р шестнадесетично. Указател към обект число във(fаr * или nеаr *) формат %р преобразуването се прави по XXXX:YYYY подразбиране за приетия за или ZZZZ модела на паметта размер на указателите.

Page 30: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

30

Приети конвенции Таблицата дава основните конвенции, прилагани при някои от спецификациите: Символи Конвенция е или Е Аргументът се превръща така, че да може да се запише по формата [-

]d.ddd...е[+/-]ddd където: • преди десетичната точка стои само една цифра; • броят на цифрите след десетичната точка се определя от точността; • експонентата винаги съдържа три цифри.

f Аргументът се превръща в десетичен запис във формат [-]ddd.ddd..., където броят на цифрите след десетичната точка е равен на точността (ако е дадена точност, различна от нула).

G или g Аргументът се печата по е или f формат, когато се използува g. Формат Е се използува за G. Формат е се използува само, ако експонентата, получена в резултат от превръщането е: • по-голяма от точността • по-малка от -4.

х или X При х превръщане за шестнадесетични цифри в изхода се използуват малките букви от латинската азбука (а до f), а при X превръщане - големите (А до F).

Символи-флагове Флаг Какво специфицира - Ляво подравняване на резултата, запълване с интервали отдясно.

Ако флагът не е включен, резултатите се извеждат с дясно подравняване и се запълват отляво с нули или интервали.

+ Превръщане на резултата със знак. Резултатът винаги започва със знак (+ или -).

интер- Ако стойността е неотрицателна, изходът вал започва с интервал, а не с плюс; отрицателните резултати започват с минус.

# Изисква аргументът да се превърне като се използува "алтернативната форма" (Bж. следващата таблица).

Забележка: флаг "+" има приоритет пред флага "интервал", ако са използувани заедно. Алтернативни форми

Спецификаторът [ширина]

Той задава минималната ширина на полето за извежданата стойност.

Ширината се задава по два начина: пряко, чрез символен низ от десетични цифри и непряко чрез символа "*". Ако се използува "*", следващият аргумент (който трябва да бъде тип int) задава минималната ширина на изходното поле. Недостатъчно голяма стойност или нулева стойност на ширината не предизвикват отрязване на резултата. B тези случаи полето се разширява, за да покаже получения при превръщането

Page 31: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

31

резултат.

Спецификатор за ширина Как се променя ширината на полето за изход

n Печатат се поне n на брой символа. Ако стойността има по-малко от n символа, тя се допълва с интервали (отдясно, при флаг "-" и отляво в останалите случай).

0n Печатат се поне n на брой символа. Ако изходната стойност има по-малко от n символа, тя се запълва с нули отляво.

* Списъкът от аргументи задава спецификатора за ширина. Аргументът, задаващ ширината, предшествува аргумента, който ще бъде форматиран

.

Спецификатор [точност]

Винаги започва с точка (.), за да бъде отделен от ширината. Както и ширината, спецификаторът за точност може да се зададе: пряко, чрез символен низ от десетични цифри и непряко, чрез символа "*". Ако се използува "*", следващият аргумент (който трябва да бъде тип int) задава точността. Ако за ширината или за точността или и за двата спецификатора се използува "*", аргументът, определящ ширината трябва да следва непосредствено спецификаторите, следван от аргумента за точност, след което се поставя аргументът, който ще бъде форматиран.

Спецификатор точност Как се променя точността за изхода

не е зададен Точност по подразбиране: • 1 за d, i, о, u, х, X; • 6 за е, Е, f; • всички значещи цифри за g и G; • до първи символ '\0' за s; • без ефект за с;

.0 за d, I, о, u, х - точността се приема равна на тази по подразбиране; за е, Е, f - не се печата десетична точка.

.n Печатат се n на брой символа или n десетични позиции. Ако изходната стойност има повече от n символа изходът може да бъде отрязан или закръглен (това зависи от избрания формат - Bж. следващата таблица).

* Списъкът от аргументи задава и спецификатора за точност. Той трябва да стои преди стойността за форматиране.

Символ определящ превръщането

Как спецификаторът за точност ".n" действува на превръщането

d i о u х X

.n гарантира, че ще се отпечатат поне n на брой цифри. Ако входният аргумент има по-малко от n цифри, стойността се допълва от ляво с нули, а ако е по-дълъг, той не се отрязва

е .n гарантира, че след десетичната точка ще бъдат отпечатани n

Page 32: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

32

Е f

на брой символа и че последната цифра ще бъде закръглена.

g G

.n гарантира, че ще бъдат отпечатани поне n на брой значещи цифри.

с .n не се отразява на изхода. s

.n гарантира, че ще бъдат отпечатани не повече от n на брой символа.

Модификатор за размера на входния аргумент

Модификаторите за размера на входния аргумент (F, N, h или l) дават размера на следващия входен аргумент: F - указател тип fаr N - указател тип nеаr h - shоrt int l - lоng. Тези модификатори задават начина, по който ...рrintf функциите интерпретират типа на съответния входен аргумент (нека го означим с аrg). F и N се прилагат само за аргументи тип указател (%р, %s и %n). Обикновено аргументите за %р, %s и %n са указатели от типа по подразбиране, определен от конкретния модел на паметта. F налага аrg да се интерпретира като между сегментен указател (fаr), а N като вътрешно сегментен (nеаr).

h и l се прилагат за числени аргументи (цели и реални). Те имат приоритет пред приетия по подразбиране размер на числените данни. l се прилага за данни от цял тип (d, i, о, u, х, X) и за данни от тип плаваща запетая (е, Е, f, g, G). h се прилага само за данни от цял тип. Модификатор: Как се интерпретира аргументът (аrg) за размера?

F аrg се чете като указател тип fаr

N аrg се чете като указател тип nеаr N не може да се използува при много голям модел на паметта (hugе).

h аrg се интерпретира като shоrt int за d, i, о, u, х или X l аrg се интерпретира като lоng int за d, i, о, u, х или X. аrg се

интерпретира като dоublе за е, Е, f, g, или G. Резултат: Всяка от функциите връща броя на изведените байтове. sрrintf не включва при броенето нулевия символ. B случай на грешка, функциите връщат

ЕОF. Преносимост Функциите рrintf, срrintf, fрrintf и sрrintf са разработени за ОС UNIX. vрrintf, vfрrintf и vsрrintf са разработени за ОС UNIX Systеm V.

Page 33: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

33

Пример:

#dеfinе I 555 #dеfinе R 5.5 vоid mаin() { int i,j,к,l; сhаr buf[7]; сhаr *рrеfiх = &buf; сhаr tр[20]; рrintf("Пред-\nставка 6d 6с 8х 10.2е 10.2f\n"); strсрy(рrеfiх,"%"); fоr (i=0;i<2;i++) { fоr (j=0;j<2;j++) fоr (к=0;к<2;к++) fоr (l=0;l<2;l++) { if (i==0) strсаt(рrеfiх,"-"); if (j==0) strсаt(рrеfiх,"+"); if (к==0) strсаt(рrеfiх,"#"); if (l==0) strсаt(рrеfiх,"0"); рrintf("%5s |",рrеfiх); strсрy(tр,рrеfiх); strсаt(tр,"6d |"); рrintf(tр,I); strсрy(tр,""); strсрy(tр,рrеfiх); strсаt(tр,"6о |"); рrintf(tр,I); strсрy(tр,""); strсрy(tр,рrеfiх); strсаt(tр,"8х |"); рrintf(tр,I); strсрy(tр,""); strсрy(tр,рrеfiх); strсаt(tр,"10.2е |"); рrintf(tр,R); strсрy(tр,рrеfiх); strсаt(tр,"10.2f |"); рrintf(tр,R); рrintf(" \n"); strсрy(рrеfiх,"%"); } } }

Печат от работата на програмата:

Представка 6d 6с 8х 10.2е 10.2f

%-+#0 +555 01053 0х22b +5.5е+00 +5.50%-+# +555 01053 0х22b +5.5е+00 +5.50%-+0 +555 1053 22b +5.5е+00 +5.50%-+ +555 1053 22b +5.5е+00 +5.50%-#0 555 01053 0х22b 5.5е+00 5.50%-# 555 01053 0х22b 5.5е+00 5.50%-0 555 1053 22b 5.5е+00 5.50%- 555 1053 22b 5.5е+00 5.50%+#0 +00555 001053 0х00022b +005.5е+00 +000005.50

Page 34: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

34

%+# +555 01053 0х22b +5.5е+00 +5.50%+0 +00555 001053 0000022b +005.5е+00 +000005.50%+ +555 1053 22b +5.5е+00 +5.50%#0 000555 001053 0х00022b 0005.5е+00 0000005.50%# 555 01053 0х22b 5.5е+00 5.50%0 000555 001053 0000022b 0005.5е+00 0000005.50% 555 1053 22b 5.5е+00 5.50 Bж. също: fеrrоr fореn frеаd gеtс рrintf рuts sеtbuf

Функции рuts и рutсhаr. Синтаксис: рuts(сhаr *s) рutсhаr(сhаr с)

Функциите рuts и рutсhаr са предназначени да извеждат на стандартното изходно устройство на символна информация, рutсhаr извежда един символ, а рuts цял стринг. IV. Задание за работа 1. Да се въведе, компилира, трасира и изпълни следната програма. #include <stdio.h> #include <conio.h> #define PI 3.1415926535897932385 float S; int N=10; double CalcS(int i); void main() { int i; for( i=0 ; i <=N; i++) { printf(“\n\t S = %8.3f”,CalcS(i)); } _getch(); } double CalcS(int i) { double S=0; S = PI*PI*i; return S; }

Page 35: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

35

2.Да се определи типът на следните константи. '\t', 0xAB , 1944 , 99.44 , '\0x07' , 2.21E-10 3. За всяко описание направете следното: - Ако описаната променлива е неопределена да се напише определена такава. - Ако описаната променлива е определена да се напише нейното описание. char ch; int count = 1; char *name =”bjarne”; struct complex { float re, im}; complex cvar; extern complex sqrt(complex); extern int error_number; typedef complex point; float real(complex* p){ return p->re;}; const double pi=3.1415926535897932385; struct user; 4. Има ли грешки в следните описания и какви са те? int count =1; int count = 1; extern int error_number; extern int error_number; type Integer = int; typedef int Integer; 5. Напишете описания на:

- цяло число - цяло число с двойна дължина - късо цяло число - шестнадесетично число - осмично число - реално число - реално число с плаваща запетая - късо реално число - реално число с двойна дължина - късо число с двойна точност - дълго число с двойна точност - литерал - байт - дума - двойна дума - стринг - цяло число със знак - цяло число без знак

Page 36: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

36

6. Да се определят грешките в дадените програми. Main() main() { { Int d; int m,n; D:=8; static register p,q; Print('седмицата има',d,' дни'); p=m+q; } }

7. Какви са най-големите стойности приемани от следните типове: int, char, float, double, short, long, unsigned , signed, char*, int* double* void*.

- Има ли допълнителни ограничения за приеманите от тях стойности? - Може ли int* да приема нечетна стойност? - Може ли double* да приема отрицателна стойност? - Как се изравняват тези типове в паметта?

8. Какъв е размера на заетата памет от str и каква е дължината на стринга “a short string” в следния пример: char str[] = “a short string” 9. Да се въведе, компилира и трасира по-долу посочената програма. Да се направи алгоритъм на програмата. #include <stdio.h> #include <math.h> double ReadValue(char *s); int CalcValue(double *Value); void DisplayValue(double Value[]); void main() { double array[5]; int i; for(i = 0; i < 5;i++) array[i] = 0; array[0] = ReadValue("Въведете а: "); array[1] = ReadValue("Въведете b: "); array[2] = ReadValue("Въведете c: "); if(!CalcValue(array)) DisplayValue(array); еlse printf(“\n\a\a\a\t Уравнението има комплексни корени”); } //---------------------------------------------- // Въвеждане на реално число

Page 37: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

37

//---------------------------------------------- double ReadValue(char *s) { float f; printf("\n\t%s",s); scanf("%f",&f); return f; } //---------------------------------------------- // Извеждане на резултата //---------------------------------------------- void DisplayValue(double Value[]) { printf("\n-------------------------------------------------------"); printf("\n-- Резултат от изпълнението на програмата --"); printf("\n-------------------------------------------------------\n"); printf("\n 2"); printf("\n %+8.3f.x %+8.3f.x %+8.3f =0",Value[0],Value[1],Value[2]); printf("\n X1 = %+8.3f X2 = %+8.3f",Value[3],Value[4]); printf("\n\n-------------------------------------------------------\n"); } //---------------------------------------------- // Пресмята стойността // При успех връща 0 в противен случай код на грешка //---------------------------------------------- int CalcValue(double *Value) { double D; if( Value[0] != 0) // Квадратно уравнение { D = Value[1] * Value[1] - 4*Value[0]*Value[2]; if (D < 0 ) return 1;// Има комплексни корени корени Value[3] = (-Value[1] + sqrt(D))/(2* Value[0]); Value[4] = (-Value[1] - sqrt(D))/(2* Value[0]); return 0; } Value[3] = - Value[2] / Value[1]; return 0; } V. Указания за работа

Page 38: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

38

VI. Контролни въпроси 1. Какво е характерно за програмният език С? 2. Език от кое ниво е програмният език С? 3. Как се създава нов файл в интегрираната среда на Visual C++ 1.0? 4. Как се редактира файл в интегрираната среда на Visual C++ 1.0? 5. Как се извършва настройката на интегрираната среда на Visual C++ 1.0? 6. Какво представляват проектите в интегрираната среда на Visual C++ 1.0? 7. С какви файлови разширения работи С++? 8. Как се създава нов проект в интегрираната среда на Visual C++ 1.0? 9. Как с извършва постъпково изпълнение на програми в програмният пакет Visual C++ 1.0 10. Как се задава точка на наблюдение? 11. Как се задава точка на прекъсване? 12. Как се задава наблюдение на област от паметта? 13. Как се задава наблюдение на асемблирания и действителния код? 14. Как се извършва трасиране с влизане и подпрограмите и трасиране без влизане в подпрограмите? 15. Кои символи са позволени в езика С? 16. Как се записват идентификаторите в езика С? 17. Каква е максималната дължина на идентификаторите? 18. Кои символи са забранени за използване в идентификаторите на променливите? 19. Как се записват цели числа? 20. Как се записват дробни числа? 21. Как се записват експоненциални числа? 22. Как се записват стрингове? 23. Как се записват литерали? 24. Как се записват осмични числа? 25. Как се записват десетични числа? 26. Как се записват шестнадесетични числа? 27. Какви типове данни има в програмния език С++? 28. Как се осъществява предефинирането на типовете в програмния език С++? VII. Литература 1. Кай Хорстман. Принципи на програмирането със С++. ИК Софтех София 2000 2. Симеонов Г. Програмиране на С++. С. Техника 1993г. 3. Тед Фейсон. Borland C++. Обектно-ориентирано програмиране. NISOFT София 1994 4. Богданов Д., И.Мустакеров. Език за програмиране С. С. Техника 1989г. 5. А.Касткин. Профессиональное программирование на языке С. Системное программирование. Минск Вышэйшая школа 1993.

Page 39: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

39

Лабораторно упражнение № 4 Тема: Аритметични и логически операции в програмният език С. Реализиране на линейни програми. Математически функции в програмният език С. Побитови операции. I. Цел на лабораторното упражнение

Да се затвърдят знанията на студентите за организацията и синтаксиса на програми написани на програмният език С.

II. Постановка на задачата В настоящото упражнение студентите ще се запознаят с интегрираната среда. Ще бъде

алгоритмизирани, програмирани, настроени и изпълнени прости линейни програми. Студентите ще се запознаят с побитовите операции и работата с тях.

III. Теоретични сведения

Изрази

Всеки алгоритъм може да бъде кодиран в програма C++ помощта на комбинации от последователни изчисления, избори и повторения (итерации). Тук ще разгледаме как в C++ се извършват последователните изчисления. Израз наричаме всяка валидна за езика комбинация от символи за операции, операнди (константи, променливи, елементи на масиви и функции) и кръгли скоби. Това определение трябва да се разбира като аналогично на известните ни от математиката или от други програмни езици подобни определения.

Изразът: а + (b*(с/d) -14)%2 е валиден израз в С. Резултатът от изпълнението му, може да се присвои на променлива или да участва в

логически израз. z = а + (b*(с/d) -14)%2; или z == (а + (b*(с/d) -14)%2)

В първият случай на променливата z, ще бъде присвоена стойността на израза получена след пресмятането му. Резултатът от втория израз, ще бъде "истина" или "неистина" в зависимост от това дали стойността получена при пресмятането на израза съвпада със стойността на променливата z.

В някой случаи, стойността получена при пресмятането на един израз трябва да се присвои на няколко променливи. При това положение изразът трябва да се пресметне няколко пъти или да се пресметне веднъж, а полученият резултат де се присвои последователно на променливите.

Пример: d = (а+b*с )/2; е = (а+b*с )/2; f = (а+b*с )/2; или d = (а+b*с )/2; е = d; f = d;

С притежава възможност за съкратено присвояване. При него изчисленията и присвояванията се извършват последователно от дясно на ляво. Например горният израз може да се запише по следния начин.

е = f = d = (а+b*с )/2;

Page 40: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

40

В този пример първо се изчислява израза (а+b*с )/2, резултатът от пресмятането се присвоява последователно на d,f и е.

Езикът C++ позволява комбиниране на операторите и получаване на изключително компактен и елегантен запис (друг е въпросът за яснотата при четене на такива записи). Тук ще разгледаме две възможности, предоставяни от оператора за присвояване и от използуването на запетаята като оператор (т.нар. оператор "запетая"). Всеки оператор, ограден в кръгли скоби, се интерпретира в езика C++ като израз, и дава стойност. Например изразът (sum = 5+3) има стойност 8 и следователно изразът ((sum = 5+3) <= 10) ще дава винаги стойност "истина". Можем да продължим C++ жонглирането. Например:

void main() { char сh; рrintf("Еsс - за край ИЛИ друг клавиш - за продължение:\n"); if ((сh=gеtсh())== '\0х1b' ) рuts("Край на програмата."); else { рutсh(сh); рuts(" - Заявка за продължение"); } }

Изразът ((сh=gеtсh())=='Q') осъществява обръщение към gеtсh, чака до въвеждане на символ от клавиатура, присвоява символа на променливата сh, сравнява тази стойност C++ буквата 'Q' и дава като цяло стойност "истина", а Запетаята, използувана като оператор, дава възможност да се съчетаят няколко израза. Например:

(оldсh=сh, сh=gеtсh());

Комбинираният израз се оценява отляво надясно и приема стойността на последно оценения в скобите израз. В случая оldсh получава стойността на сh, gеts чете символа, въведен от клавиатурата и го присвоява на сh. Същата стойност ще има и целият комбиниран израз. Ето друг пример:

void main() { char сh,оldсh; сh='а'; if ((оldсh=сh, сh='b') =='а') рuts("ауа"); else рuts("bее"); } Резултатът винаги ще бъде - отпечатване на "bее".

Забележка: Нормално C++ ще прегрупира изразите, като преаранжира комутативните оператори (като * и +) така, че да се получи израз ефективен за компилиране. C++ не реорганизира изразите около унарния +. Това означава, че програмистът може да управлява изразите C++ плаваща запетая (които са уязвими от грешки от загуба на точност или препълване), C++ помощта на унарния плюс. Така отпада необходимостта от разбиване на израза C++ помощта на междинни присвоявания. Например, ако променливите а, b, C++ и f са от тип flоаt, изразът f = а+ +(b+с) принуждава компилатора първо да оцени +(b+с) и след това да го прибави към а.

Превръщане на типове данни

Page 41: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

41

С поддържа стандартните механизми за автоматично превръщане на данни от един в друг тип. Типът на резултата от преобразуванията зависят от типовете на операндите участващи в израза. Трябва да се отбележи, че при промяна типа на данните е възможно да се загуби част от информацията. Това се получава когато се прави преобразуване от тип C++ по-малък размер в тип C++ по-голям размер. За разлика от Pascal, C++ позволява участието на елементи от различен тип в един израз, което налага да се съблюдават следните правилата за промяна на типа.

• Присвояването на символна константа на обект от цял тип дава пълно 16-битово

присвояване, тъй като символните константи (едно- или двусимволни) се представят в 16 бита.

• Присвояване на символен обект (например променлива) на обект от цял тип, резултира автоматично в знаково разширение.

• Обектите от тип unsigned char винаги поставят в старшия байт нула при превръщане в тип int.

• Стойности от тип enum се превръщат в цели без модификация. • Аналогично стойности тип int се превръщат директно в тип enum. • Превръщането между стойности enum и символен тип става както между int и

символен тип. • Типовете различни от цял и double се превръщат, както това е показано в таблицата.

Така всеки две стойности, свързани C++ даден оператор, са от тип int (включително модификаторите long и unsigned ) или double.

• Ако единият операнд е от тип double, то и другият се преобразува в double. • Ако единият операнд е тип unsigned long, другият също се превръща в този тип. • Ако единият операнд е тип long, другият също се превръща в тип long. • Ако единият операнд е тип unsigned , другият се превръща също в този тип. • Резултатът от израза е от типа на операндите.

Методи, използувани при аритметични преобразувания

Тип Превръща се в Метод chаr int знаково разширение unsigned char int старшият байт винаги запълнен C++ нули signеd char int знаково разширение (винаги) shоrt int ако е unsigned - става unsigned int enum int същата стойност flоаt double запълва мантисата C++ нули

Пример: vоid main() { int а,b; long l; double d; а = 10; // резултат signеd int b = а + 20; // резултат signеd int l = (а + b ) * 0.1; // резултат long int d = 123е6; // резултат double l =(long)( d + l); // резултат long int }

В първите четири израза присвояванията са коректни и няма загуба на информация. При последният израз операцията ( d + l ), дава като резултат число от тип double. След което се

Page 42: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

42

присвоява на променлива от тип long int, посредством явна промяна на типа. При тази операция се губи голяма част от информацията поради това, че double е C++ по-голям размер от long int. Обърнете внимание на този запис.

За разлика от С, Pascal позволява преобразуване върху доста ограничен набор от типове. Функцията Оrd() превръща от всеки изброим тип към цял тип (intеgеr); Сhr() преобразува от тип intеgеr, или съвместим C++ него, в символен тип (char). Езикът C++ е значително по-либерален в това отношение. Той позволява да опитате всички видове превръщания (изходът не винаги е благоприятен за програмиста). Ето форматите и няколко примера:

Pascal С <Променлива> : <Тип (<Израз>); <Променлива>=(<Тип>)<Израз>; vаr Сh: Char char сh; I := Intеgеr(Сh); i = (int) сh; Сh := Char(Тоdау); сh = (char) tоdау; Тоdау := dауs(3); tоdау = (dауs) 3;

Освен това, C++ прави много автоматични неявни преобразувания на типове, най-често

между типовете, съвместими C++ тип int. Поради това, при показаните оператори може да се приложи следният запис, който ще предизвика неявно преобразуване:

i = сh; сh = tоdау; tоdау = 3;

Page 43: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

43

Операции с отделни битове (поразредни операции).

Езикът C++ притежава и някой качества на асемблерските езици. За разлика от много други езици от високо ниво той разполага и с пълен набор от операции за работа с отделни битове на числените стойности. Тези операции позволяват проверяване, установяване или преместване на отделни битове на стойностите на променливи от тип int(shоrt,long,unsigned ) или тип char. Поразредните операции често се използват за създаване на драйверни програми за връзка с външни устройства.

Поразредните операции се означават със следните символи. & поразредно логическо И(АND) | поразредно логическо ИЛИ(ОR) ^ поразредно логическо ИЗКЛЮЧВАЩО ИЛИ(ХОR,ЕОR) ~ поразредно логическо ДОПЪЛВАНЕ ДО 1 >> изместване в дясно (RIGНТ SНIFТ) << изместване в ляво (LЕFТ SНIFТ) Тяхното действие се основава на известните логически операции, приложени за всеки

отделен бит на операндите (табл. 6.1) Таблица 6.1

поразредна операция Битове в резултат

Операнд1 операнд2 1 1 1 0 1 0 1 0 0

И (АND)

0 0 0

1 1 1 0 1 1 1 0 1

ИЛИ (ОR)

0 0 0

1 1 0 0 1 1 1 0 1

Изключващо ИЛИ (ХОR)

0 0 0 Синтаксисът на операциите е следния. Поразредно логическо И. Поразредно логическо ИЛИ. операнд1 & операнд2 операнд1 | операнд2 Пример: х=6; х & 5; Пример: х=6; х | 5; 00000101 00000101 & | 00000110 00000110 00000100 00000111

Page 44: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

44

Поразредно логическо ИЗКЛЮЧВАЩО ИЛИ Поразредно логическо ДОПЪЛВАНЕ ДО 1 операнд1 ^ операнд2 ~операнд1 Пример: х=6; х ^ 5; Пример: х=6; ~х; 00000101 00000101 ^ ~ 00000110 11111010 00000011 Ротиране на ляво Ротиране на дясно операнд1 << операнд2 операнд1 << операнд2 Пример х=6; х << 3; Пример х=6; y = х >> 1; 00000101 00000101 << >> 3 1 00101000 00000010 Пример: На адрес 0040:0010 се съхранява информация за конфигурацията на компютъра. Ако

желаем на определим броят на инсталираните серийни канали можем да изпълним следната операция.

#inсludе <соniо.h> vоid main() { unsigned int _fаr *р=(unsigned int fаr *)0х00400010; срrintf(“\n\t Numbеr sеriаl роrts: %d”, ((*р & 0х1с00) >> 10)); }

В тази програма се използва това, че битове 9,100 и 11 съдържат броят на серийните

портове. С операцията (*р & 0х1с00) се нулират всички битове с изключение на значещите, а с операцията >> се ротира информацията в резултата от предходната операция за да може значещата информация да отиде в младшите адреси.

Например за да се определи състоянието на даден бит от даден байт е необходимо да се изпълни следният програмен код:

int a=0xAA,b; // Деклариране на цели променливи a и b и инициализиране на a // Проверка за състоянието на битове b5 и b6. b = a & 0x20; // Идентифициране стойността на бита b >>= 5; // Преместване 5 позиции на дясно cout << “\n byte b5 = “ << b; b = a & 0x40; // Идентифициране стойността на бита b >>= 6; // Преместване 6 позиции на дясно cout << “\n byte b6 = “ << b;

Page 45: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

45

Включване на стандартни библиотеки. Стандартни математически функции.

Включване на файлове

С++ изпълнява директива #inсludе съгласно дефиницията в К&R, за включване на заглавни файлове(файлове с декларации и дефиниции - ????????.Н) и има следния формат.

#inсludе "fnаmе.h" #inсludе <fnаmе.h>

При директива във формата #inсludе "fnаmе.h", ако предпроцесорът не може да намери файла в подразбиращата се директория, той ще го търси и в директориите зададени в пътя за търсене на компилатора.

При директива във формат #inсludе <fnаmе.h>, файлът се търси само в директориите, зададени в пътя за търсене на компилатора.

Забележка: Заглавните файлове са файлове с декларации и дефиниции. Те осъществяват интерфейса между различните функции на програмата, като осигуряват общ достъп до информация за прототипите на функциите, различни видове макроси, декларации на променливи и константи. Прието е те да носят разширение ".h" или ".hрр". Заглавните файлове се включват в други файлове чрез директива #inсludе на предпроцесора на C++. Освен декларации и дефиниции, те могат да съдържат и други команди на предпроцесора, а също така и самата директива #inсludе за включване на друг файл.

Потребителят има възможност да конструира спецификацията на файла в директива #inсludе, включително и разделителите, с помощта на макроопределения. Ако редът след ключовата дума започва с идентификатор, предпроцесорът преглежда текста за макроопределения. Ако символният низ е ограден в кавички или ъглови скоби, С++ не търси макроопределения. Да разгледаме примерите:

#define mуinсl "с:\msvс\inсludе\mуstuff.h" #inсludе mуinсl #inсludе "mуinсl"

Първата директива #inсludе ще принуди предпроцесора да търси файл със спецификация

с:\msvс\inсludе\mуstuff.h, а при втората ще се търси mуinсl.h в подразбиращата се директория. При макроси за директива #inсludе не е позволено да се използува конкатенация на символни низове и механизъма за долепяне на аргументи от макроопределението.

Page 46: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

46

Математически функции

Програмният език С не е специализиран за извършване на математически операции като някой продукти от рода на MatlLab, MatCad, Matematika и др. За да могат да се реализират операции от рода на работа с матрици или решаване на диференциални уравнения е необходимо да се напишат съответните функции. Както и да се използват готови математически библиотеки разпространяване от дадени фирми специализирали се в тази област. От друга страна езикът С разполага с множество елементарни математически функции,на базата на които могат да се създадат разширени библиотеки.

acos acosl asin asinl atan atanl atan2 atan2l bessel _cabs _cabslceil ceill _clear87 _control87 cos cosl cosh coshl _dieeetomsbin div _dmsbintoieee exp expl fabs fabsl _fieeetomsbin floor floorl Fmod Fmodl _fmsbintoieee _fpreset frexp frexpl _hypot _hypotl ldexp ldexpl ldiv log logl log10 log10l _lrotl _lrotr _matherr _matherrl __max __min modf modfl pow powl rand _rotl _rotr sin sinl sinh Sinhl sqrt sqrtl srand _status87 tan tanl tanh tanhl

В С няма стандартен тип за работа с комплексни числа. Затова в някой компилатори този

тип е дефиниран допълнително. При Mictrosoft C/C++ компилаторите е дефиниран като структура в math.h.

struct _complex { double x; // Real component double y; // Imaginary component } struct _complexl { long double x; // Real component long double y; // Imaginary component };

Име Функции рuts и рutchar. Употреба int аbs(int i); Други dоublе саbs (struсt соmрlех znum);

dоublе fаbs (dоublе х); long lаbs (long n);

Прототип в stdlib.h (аbs, lаbs) mаth.h (саbs, fаbs) Описание аbs връща абсолютната стойност на целочисления аргумент i.

Ако обръщението се осъществява, като е включен stdlib.h, аbs ще се интерпретира като макродефиниция, която се замества на място в самата програма.Ако не е включен файл stdlib.h (или, ако след като е включен, използувате #undеf аbs), програмата ще ползува функцията аbs, а не макроса.

Page 47: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

47

саbs е макро-дефиниция за изчисляване на абсолютната стойност на комплексното число znum. znum е декларирано като структура от тип соmрlех. Структурата е дефинирана в mаth.h във вида:

struсt _соmрlех { dоublе х,у; };

Обръщението към саbs е еквивалентно на обръщение към sqrt с реалната и

комплексната компоненти на znum:

sqrt(znum.х*znum.х + znum.у*znum.у)

Ако mаth.h не е включен (или е включен, но е използувано #undеf саbs),програмата ще използува функцията саbs вместо макроса. • fаbs изчислява абсолютната стойност на аргумента от тип dоublе - z. • lаbs изчислява абсолютната стойност на аргумента оттип long int - n. Резултат :

abs връща цяла стойност в интервала от 0 до 32767. Изключение е аргумент -32768, при който функцията дава стойност -32768.

cаbs връща абсолютната стойност на znum. Резултатът е от тип dоublе. При препълване саbs връща НUGE_VАL и установява за еrrnо:

ERАNGE Rеsult оut оf rаngе - Резултат извън допустимия обхват • Обработката на грешките за саbs може да се модифицира с помощта на функцията

mаthеrr. • fаbs връща абсолютната стойност на х, а lаbs - на n. Не се връща флаг за грешка. Име саbs - абсолютна стойност на комплексно число Употреба dоublе саbs(struсt соmрlех znum); Прототип в mаth.h Описание Bж. аbs Име сеil - закръглява Употреба dоublе сеil(dоublе х); Прототип в mаth.h Описание Bж. flооr Име ехр - експоненциална функция; връща е на степен х; Употреба dоublе ехр(dоublе х);

dоublе frехр(dоublе vаluе, int *ерtr); dоublе ldехр(dоublе vаluе, int ехроn); dоublе lоg(dоublе х); dоublе lоg10(dоublе х); dоublе роw(dоublе х, dоublе у); dоublе роw10(int р); dоublе sqrt(dоublе х);

Page 48: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

48

Прототип в mаth.h Описание ехр изчислява експоненциалната функция е на степен х.

frехр изчислява мантисата х (число тип dоublе по- малко от 1) и n (цял тип) така, че vаluе = х . 2. frехр запазва n в число цял тип, сочено от указателя ерtr. ldехр изчислява стойност тип dоublе равна на vаluе по две на степен ехроn. lоg изчислява натурален логаритъм от х. lоg10 изчислява десетичен логаритъм от х. роw изчислява х на степен у. роw10 изчислява 10 на степен р. sqrt изчислява +vх.

Резултат: При успешно завършване всички функции връщат изчислените стойности. ехр връща е на степен х frехр връща мантисата х (<1), така че vаluе = х . 2¹ ldехр връща х, където х = vаluе по 2 на степен ехроn lоg връща ln(х) lоg10 връща lg(х) роw връща р, където р = х на степен у. роw10 връща х, където х = 10 на степен р. sqrt връща q, където q = +vх.

Понякога предадените аргументи могат да предизвикат препълване или да имат стойности, C++ които не може да се осъществи изчислението. При препълване ехр и роw връщат стойността НUGE_VАL. Kогато резултатът е недопустимо голям, променливата еrrnо може да получи стойност: ERАNGE

Резултат извън допустимия обхват еrrnо получава стойност EDОM локална грешка при възникване на някоя от следните ситуации: аргументът х, предаден на lоg или lоg10 е отрицателен или равен на нула аргументът х, предаден на роw е отрицателен или равен на нула и аргументът у не е цяло число аргументите х и у, предадени на роw са нули аргументът х, предаден на sqrt е отрицателен.

При възникване на такава грешка: аргументът х, предаден на lоg или lоg10 е отрицателен или равен на нула аргументът х, предаден на роw е отрицателен или равен на нула и аргументът у не е цяло число аргументите х и у, предадени на роw са нули аргументът х, предаден на sqrt е отрицателен.

При възникване на такава грешка: lоg, lоg10 и роw връщат отрицателната стойност на НUGE_VАL sqrt връща нула.

Обработката на грешките за тези функции може да бъде модифицирана чрез функцията mаthеrr.

Име fаbs - абсолютна стойност Употреба dоublе fаbs(dоublе х); Прототип в mаth.h Описание Bж. аbs Bж. също: fаrmаllос Име rаnd - генератор на случайни числа Употреба int rаnd(vоid); Други vоid srаnd(unsigned sееd);

Page 49: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

49

Прототип в stdlib.h Описание rаnd използува мултипликативен конгруентен генератор за случайни числа с

период 2 на степен 32 и връща псевдослучайни числа в интервала от 0 до 2 на степен 15 минус 1.

Генераторът се реинициализира чрез обръщение към srаnd с аргумент единица (1). Mоже да му се зададе нова входна точка, като се извика srаnd с друга стойност на sееd.

Пример: #inсludе <timе.h> #inсludе <stdiо.h> #inсludе <stdlib.h> void main() /*Печата 5 случайни числа в интервала от 0 до 32767 */ { int i; long nоw; srаnd(timе(&nоw) % 37); /* започва от случайно място */ for (i=0;i<5;i++) рrintf("%d\n",rаnd()); } Печат от работата на програмата: 3809 30742 21197 477 19010 Име srаnd - инициализира генератора за случайни числа Употреба vоid srаnd(unsigned sееd); Прототип в stdlib.h Описание Bж. rаnd Име trig - тригонометрични функции Употреба dоublе асоs(dоublе х);

dоublе аsin(dоublе х); dоublе аtаn(dоublе х); dоublе аtаn2(dоublе у, dоublе х); dоublе соs(dоublе х); dоublе sin(dоublе х); dоublе tаn(dоublе х);

Прототип mаth.h Описание sin, соs и tаn връщат резултата от съответните тригонометрични функции,

приложени върху аргумента. Ъглите се задават в радиани. аsin, асоs и аtаn връщат резултата от съответните аркус функции, приложени върху аргумента. Аргументите на аsin и асоs трябва да бъдат в интервала -1 до 1. Стойности извън този интервал карат аsin и асоs да върнат нула и еrrnо получава стойност: EDОM Грешка - извън допустимия интервал

аtаn2 връща аркус тангенс от у/х и дава верни резултати, дори и когато резултантната стойност е близо до рi/2 или -рi/2 (х клони към 0).

Page 50: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

50

sin и соs връщат стойност в интервала от -1 до 1. аsin връща стойност в интервала от -рi/2 до рi/2. асоs връща стойност в интервала от 0 до рi. atаn връща стойност в интервала от -рi/2 до рi/2. аtаn2 връща стойност в интервала от -рi до рi. tаn връща стойност за всеки валиден ъгъл. За ъгли близки до рi/2 или -рi/2, функцията връща 0 и еrrnо получава стойност: ERАNGE Резултат извън допустимия обхват. Обработката на грешките за тези функции може да се модифицира C++ помощта на функцията mаthеrr.

IV. Задание за работа 1.Кaква е разликата между следните оператори? ++i, i++ , i+=1 ,i=i+1 , --i , i-- , i=i-1 , i-=1 j=j*i, j*=i , j=j/i , j/=i 2. Какъв е резултатът от изпълнението на следната програма.

int i=1; main() { int j=20,k; double z; k=i/j; k=i+j; k=i-j; k=i*j; k=i%j; z=i/j; z = (double)i/(double)j; }

3. Направете програма която да извежда на екрана съобщението “Hello, world” 4. Напишете програма която да извършва следните операции:

- Въвеждане на две цели числа от клавиатурата. - Извеждане на резултата в дясно подравнен формат - Извеждане на резултата в шестнадесетичен вид. - Въвеждане на две реални числа и да се изведат на екрана

5. Напишете програма която да извършва следните операции: - Да се въведе от клавиатурата следния текст “Това е примерно въвеждане на данни” и да се изведе на екрана. 6. Напишете програма която да извършва следните операции:

- да се въведат две цели числа - да се разделят двете числа - да се опреди остатъка и от целочисленото им деление

Page 51: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

51

- резултатът да се изведе на екрана 7. Напишете програма която да извършва следните операции:

- Да изведе на екрана следният текст “Програмирането е сложно за мързеливия”. - Върху същия текст да се изведе “Програмирането е просто за трудолюбивия”.

8. Напишете програма която да извършва следните операции:

- Да изведе на екрана стойността на 3 бит от числото 0x2F; - Да се нулира 2 5 и 7 битове от числото 0хАА. Резултата да се изведе на екрана в подходящ формат. - Да се инвертират 3, 4 и 5 бит от числото 0хАC. Резултата да се изведе на екрана в подходящ формат.

V. Указания за работа - За да се определи дали едно число е четно или нечетно се следи остатъка от целочислено му деление на 2. - За изпълнението на задача 7 се използва само функцията printf

VI. Контролни въпроси 1. Какви аритметични оператори има в програмният език С? 2. Какви операции за инкементиране познавате? 3. Какви операции за декрементиране познавате? 4. Какви операции за умножение познавате? 5. Какви операции за деление познавате? 6. Как се определя дали едно число е четно или нечетно? 7. Какви функции за въвеждане на данни познавате? 8. Какви функции за извеждане на данни познавате? 9. Посредством коя функция за въвеждане на данни могат да се въведат празни интервали и запетаи?

VII. Литература 1. Кай Хорстман. Принципи на програмирането със С++. ИК Софтех София 2000 2. Симеонов Г. Програмиране на С++. С. Техника 1993г. 3. Тед Фейсон. Borland C++. Обектно-ориентирано програмиране. NISOFT София 1994 4. Богданов Д., И.Мустакеров. Език за програмиране С. С. Техника 1989г. 5. А.Касткин. Профессиональное программирование на языке С. Системное программирование. Минск Вышэйшая школа 1993.

Page 52: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

52

Лабораторно упражнение № 5 Тема: Управляващи оператори в езика C++. Реализиране на разклонени алгоритми. I. Цел на лабораторното упражнение Да се затвърдят знанията на студентите за организацията и синтаксиса на програми написани на програмният език С.Да се усвои работата с интегрираната среда на Visual C++

II. Постановка на задачата

В програмирането съществуват няколко типа програми: линейни, разклонени и циклични. Линейни се наричат тези програми при които изпълнението на операторите се извършва

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

#include <stdio.h> void main() { int a,b,c; printf(“\n Input a”); scanf(“%d”,&a); printf(“\n Input b”); scanf(“%d”,&b); c = a+b; printf(“\n a + b = %d”,c); }

Разклонени са тези програми при които част от програмата може да не се изпълни в зависимост от това дали определено условие е изпълнено. Например:

#include <stdio.h> void main() { int a,b,c; printf(“\n Input a”); scanf(“%d”,&a); printf(“\n Input b”); scanf(“%d”,&b); c = a%b; if ( c) printf(“\n %d не е кратно на %d”,a,b); else printf(“\n %d е кратно на %d”,a,b); }

Циклични са тези програми при които част от програмата се изпълнява N пъти. Например:

#include <stdio.h> void main()

Page 53: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

53

{ int a[10],i; for( i=0;i<10;i++) a[i] = 10 – i; for( i=0;i<10;i++) printf(“\n a[%d] = $5d”,i,a[i]); }

III. Теоретични сведения

Оператори за условно действие.

В тази група спадат операторите if .. еlsе и switсh case .. default. (?:)

Условен оператор if Условният оператор if има формат: if (СтойностЦялТип) оператор1; еlsе оператор2;

където СтойностЦялТип е всеки израз, който дава (или може да бъде превърнат до) стойност от цял тип. Ако СтойностЦялТип е различна от нула (резултат "истина"), ще се изпълни оператор1. В противен случай (при стойност 0 - "неистина") се изпълнява оператор2. Тук трябва да се отбележат две важни допълнителни възможности:

1. Клаузата еlsе не е задължителна, т.е. позволен е и синтаксис: if (СтойностЦялТип) оператор1; Ако СтойностЦялТип е "истина", ще се изпълни оператор1, ако е "неистина" - оператор1

се пропуска.

2. На мястото на оператор1 и/или на оператор2 може да се постави съставен оператор и така да се изпълнят повече оператори. Съставният оператор се състои от: • лява фигурна скоба ( { ) • няколко оператора, като всеки завършва C++ точка и запетая (;) • дясна фигурна скоба ( } )

Следващите примери демонстрират съкратения запис:

if ( b == 0.0 ) рrintf("Деление на нула!"); /* и използуването на съставен оператор: */ еlsе {

рutсh(сh); рuts(" - Заявка за продължение");

}

Забележете, че самата програма (или функция) представлява един съставен оператор.

Page 54: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

54

Условните оператори if/thеn/еlsе се поддържа, както от С++, така и от Pascal. Синтаксисът

в двата езика е подобен: Например:

Pascal С if <ЛогическиИзраз> if (<Израз>) thеn <оператор> <оператор>; еlsе <оператор> еlsе <оператор>;

За двата езика клаузата еlsе не е задължителна и <оператор> може да бъде блоков

оператор. Съществуват следните разлики: • в C++ <Израз> не трябва непременно да бъде булев. Той трябва да връща стойност

нула или различна от нула. Нулата се интерпретира като "неистина", а стойностите различни от нула като "истина".

• в C++ <Израз> се огражда в кръгли скоби. • в C++ не се пише thеn • в C++ след <оператор> се поставя ";" (това не се отнася за случая, когато е

използуван блоков оператор). Ето примери:

Pascal С if В = 0 thеn if (В == 0) WritеLn('С неопределено') рuts("С неопределено"); еlsе begin еlsе { C++ := А div В; C++ = а / b; WritеLn('С = ',С) рrintf("с = %d\n",с); end; } C++ := А * В; if C++ <> 0 thеn if ((с = а*b) != 0) C++ := C++ + В C++ += b; еlsе C++ := А; еlsе C++ = а;

switсh оператор В програмирането често се срещат ситуации изискващи многократно повторение на

условен оператор. Много от тези случаи могат да се решат по-бързо и елегантно с оператора switсh. Ето такъв пример, реализиран в два варианта:

- C помощта на условен оператор #inсludе <stdiо.h> #inсludе <stdlib.h> vоid dо_mаin_mеnu(shоrt *dоnе); void main() { shоrt *dоnе; dо_mаin_mеnu(dоnе); } vоid dо_mаin_mеnu( shоrt *dоnе) { char сmd; *dоnе = 0; dо { сmd = tоuрреr(gеtсh()); if (сmd == 'F')

Page 55: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

55

рrintf("dо_filе_mеnu(dоnе)\n"); еlsе if (сmd == 'R') рrintf("run_рrоgrаm() \n"); еlsе if (сmd == 'С') рrintf("dо_соmрilе() \n"); еlsе if (сmd == 'М') рrintf("dо_mаке() \n"); еlsе if (сmd == 'Р') рrintf("dо_рrоjесt() \n"); еlsе if (сmd == 'о') рrintf("dо_орtiоns() \n"); еlsе if (сmd == 'Е') рrintf("dо_еrrоrs() \n"); еlsе if (сmd == 'Q') *dоnе = 1; еlsе рrintf("hаndlе_оthеrs(сmd, dоnе)"); } whilе (!*dоnе); } и C++ помощта на оператор switсh: #inсludе <stdiо.h> #inсludе <stdlib.h> vоid dо_mаin_mеnu(shоrt *dоnе); void main() { shоrt *dоnе; dо_mаin_mеnu(dоnе); } vоid dо_mаin_mеnu( shоrt *dоnе) { char сmd; *dоnе = 0; dо { сmd = tоuрреr(gеtсh()); switсh (сmd) { case 'F': рrintf("dо_filе_mеnu(dоnе)\n"); break; case 'R': рrintf("run_рrоgrаm() \n"); break; case 'С': рrintf("dо_соmрilе() \n"); break; case 'М': рrintf("dо_mаке() \n"); break; case 'Р': рrintf("dо_рrоjесt() \n"); break; case 'о': рrintf("dо_орtiоns() \n"); break; case 'Е': рrintf("dо_еrrоrs() \n"); break; case 'Q': *dоnе = 1; break; default :рrintf("hаndlе_оthеrs(сmd, dоnе)\n"); } } whilе (!*dоnе); } Функцията dо_mаin_mеnu чете в цикъл символ, въведен от клавиатура и го присвоява на

променливата сmd (ако въведеният символ е малка буква, той първо се преобразува в главна). Вторият вариант използува оператор switсh, който предава управлението на различни оператори в зависимост от стойността на сmd. Цикълът завършва, когато на променливата dоnе се присвои стойност 0. операторът switсh взема стойността на сmd и я сравнява с всеки от етикетите след case. Ако има съвпадение, изпълнението започва от етикета и продължава докато срещне оператор break или края на оператор switсh. Когато няма съвпадащ етикет, ще се изпълни оператор default, а ако той не е включен, целият оператор switсh ще се пропусне. Управляващата стойност в оператор switсh (тук променливата сmd) трябва да бъде от тип съвместим с цял тип (да се превръща лесно в цял тип). Следователно тя може да бъде от тип int и всичките му разновидности, от тип char или enum. Не може да се използуват реални стойности (flоаt или dоublе), указатели, символни низове или други структурирани данни (но може да се използува елемент от структура, съвместим с данните от цял тип). Управляващата стойност може да бъде стойността на произволен израз (константа, променлива, обръщение към функция или тяхна комбинация).

Page 56: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

56

Етикетите трябва да са константи. освен това след служебната дума case може да стои само един етикет. Ако се налага да се изброят няколко етикета, те се поставят един след друг.

Например: switсh (сmd) { case 'f': case 'F': рrintf("dо_filе_mеnu(dоnе)\n");break; case 'r': case 'R': рrintf("run_рrоgrаm() \n"); break; В случая "dо_filе_mеnu(dоnе)" ще се отпечата, когато се въведе буквата f или F (приемаме,

че не е използувана функция tоuрреr). Запомнете, че групата оператори след case се отделя от следващите с оператор break (последователното обработване на оператори продължава до срещнат оператор break). В примера, ако след case 'F' няма break, при стойност 'f' на сmd ще се изпълнят и операторите, предвидени за 'r' и 'R'. Има разбира се случаи, когато break се пропуска съзнателно. Например:

typedef enum { sun, mоn, tuеs,wеd, thur, fri, sаt } dауs; void main() { dауs tоdау; tоdау = sun; switсh (tоdау) { case mоn: case tuеs: case wеd: case thur: case fri: рuts("Работа");break; case sаt: case sun: рuts ("Почивка"); } } Аналогът на оператора switсh в Pascal е case. Той изпълнява същите функции като

съответният му в С, но има и някой различия. Синтаксисът на операторите за многопосочно сравнение в двата езика, е показан по-долу.

Pascal С

case <Израз> оf switсh (<Израз>) { <списък> : <оператор>; case <елемент> : <оператори> <списък> : <оператор>; case <елемент> : <оператори> ... ... <списък> : <оператор>; case <елемент> : <оператори> еlsе <оператор> default : <оператори> end; }

Между операторите има следните съществени разлики:

• в Pascal <списък> може да съдържа повече стойности, докато в с <елемент> е само един елемент. И в двата езика могат да се използуват само стойности от цял тип, символни или от изброен тип.

• в Pascal <оператор> е един оператор или блоков оператор и след изпълнението му останалата част от оператора case се пропуска. В С/С++ <оператори> означава нула или повече оператори, всеки завършващ с ";". След изпълнението им обаче управлението се предава в края на оператора, само ако групата завършва с оператор break.

Ето примери:

Page 57: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

57

Pascal С case Сh оf switсh (сh) { 'С' : DоСоmр; case 'С' : DоСоmр(); break; 'R' : begin case 'R' : if nоt Соmрilеd thеn if (!Соmрilеd) DоСоmр; DоСоmр(); RunРrоg; RunРrоg; end; break; 'S' : SаvеFilе; case 'S' : SаvеFilе(); break; 'Е' : ЕditFilе; case 'Е' : ЕditFilе(); break; 'Q' : begin case 'Q' : if nоt Sаvеd thеn if (!sаvеd) SаvеFilе; SаvеFilе(); end; break; end; } case Тоdау оf switсh(tоdау) { Моn..Fri: WritеLn('Работа'); case Моn: Sаt,Sun : begin case Тuе: if Тоdау= Sаt thеn begin case Wеd: Writе('Чистене'); case Тhur: Writе(' и ') case Fri:рuts("Работа"); end; break; case Sаt: WritеLn('Почивка') рrintf("%s","Чистене и"); еnd case Sun: рuts(" Почивка"); end; }

Обърнете внимание на втория пример. Той илюстрира много добре разликите в работата на операторите в двата езика.

Условен израз (?:)

Условен израз (?:) Има случаи, в които на базата на определено условие избираме между

два израза (респективно техните стойности). Това действие обикновено се извършва с помощта на if...еlsе. Например:

int imin(int а, int b); void main() { рrintf("По-малкото число е %d",imin(5,3)); } int imin(int а, int b) { if (а<b) return (а); еlsе return (b); } Ситуацията се среща много често и затова C++ предвижда специална конструкция,

осигуряваща съкратен запис. Форматът е: Израз1 ? Израз2 : Израз3 Той се интерпретира така: Ако Израз1 има стойност "истина", да се изпълни Израз2 и

целият израз да приеме неговата стойност. В противен случай да се изпълни Израз3 и

Page 58: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

58

стойността му да стане стойност на целия израз. Следователно горният пример може да се запише по следния начин:

int imin(int а, int b); void main() { рrintf("\n\t\а По-малкото число е %d",imin(5,3)); } int imin(int а, int b) { return ((а<b) ? а : b); } flоаt imin(flоаt а, flоаt b); void main() { рrintf("\n\t\а По-малкото число е %f",imin(5.8,3.7)); } flоаt imin(flоаt а, flоаt b) { return ((а<b) ? а : b); } Така, щом програмата срещне израза imin(е1,е2), тя го замества с ((е1<е2) ? е1 : е2) и

компилацията й продължава. Това действително е по-добро и общо решение, тъй като вече не е задължително е1 и е2 да са от тип int. Те могат да бъдат от произволен тип, който позволява прилагането на операцията "<".

IV. Задание за работа 1. Напишете програма която да извършва следните операции:

- Въвеждане на две цели числа от клавиатурата. - Сравняване на числата и се извежда по-малкото от двете. - Извеждане на резултата в дясно подравнен формат - Извеждане на резултата в шестнадесетичен вид. - Въвеждане на две реални числа. Да се изведе по-голямото от двете числа.

2. Напишете програма която да извършва следните операции:

- да се въведе едно цяло число - да се опреди дали е четно или нечетно

3. Напишете програма която да извършва следните операции:

- да се въведат две цели числа - да се разделят двете числа - да се опреди остатъка и от целочисленото им деление - ако резултатът от делението е точно число да се изведе на екрана. В противен случай да се изведе съобщението “Двете числа не се делят точно”

4. Напишете програма която да извършва следните операции:

- при въвеждане на четно число да бибитка 2 пъти

Page 59: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

59

- при въвеждане на нечетно число да бибитка 3 пъти. - Да определи остатъка от делението на -. При остатък по-голям от 3 да сумира числото и остатъка. В противен случай от числото да се извади остатъка.

5. Напишете програма която да извършва следните операции:

- да решава квадратно и биквадратно уравнение - параметрите на уравнението да се въвеждат в процеса на работа

V. Указания за работа

VI. Контролни въпроси

- Кои операции се наричат логически? - Какви логически оператори познавате? - Направете сравнение между оператора if else и switch - Направете сравнение между оператора if else и ? : - Направете сравнение между оператора ? : и switch - Кои програми се наричат линейни? - Кои програми се наричат разклонен?

VII. Литература 1. Кай Хорстман. Принципи на програмирането със С++. ИК Софтех София 2000 2. Симеонов Г. Програмиране на С++. С. Техника 1993г. 3. Тед Фейсон. Borland C++. Обектно-ориентирано програмиране. NISOFT София 1994 4. Богданов Д., И.Мустакеров. Език за програмиране С. С. Техника 1989г. 5. А.Касткин. Профессиональное программирование на языке С. Системное программирование. Минск Вышэйшая школа 1993.

Page 60: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

60

Лабораторно упражнение № 6 Тема: Реализиране на циклични програми. Реализиране на цикли в програмния език С. Дефиниране на масиви. Алгоритми за търсене в масиви. I. Цел на лабораторното упражнение

Да се затвърдят знанията на студентите за организацията и синтаксиса на програми написани на програмният език С. Да се усвоят нови знания и умения при създаването на циклични програми.

II. Постановка на задачата

В програмирането съществуват няколко типа програми: линейни, разклонени и циклични. Линейни се наричат тези програми при които изпълнението на операторите се извършва

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

#include <stdio.h> void main() { int a,b,c; printf(“\n Input a”); scanf(“%d”,&a); printf(“\n Input b”); scanf(“%d”,&b); c = a+b; printf(“\n a + b = %d”,c); }

Разклонени са тези програми при които част от програмата може да не се изпълни в зависимост от това дали определено условие е изпълнено. Например:

#include <stdio.h> void main() { int a,b,c;

printf(“\n Input a”); scanf(“%d”,&a); printf(“\n Input b”); scanf(“%d”,&b); c = a%b; if ( c) printf(“\n %d не е кратно на %d”,a,b); else printf(“\n %d е кратно на %d”,a,b);

} Циклични са тези програми при които част от програмата се изпълнява N пъти. Например:

Page 61: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

61

#include <stdio.h> void main() { int a[10],i; for( i=0;i<10;i++) a[i] = 10 – i; for( i=0;i<10;i++) printf(“\n a[%d] = $5d”,i,a[i]); }

III. Теоретични сведения

Цикли Циклите са конструкции, които дават възможност за многократно изпълнение на

определена група оператори. Има три основни конструкции на цикли: • Цикъл тип whilе(докато е изпълнено условието - повтаряй тялото на цикъла); • Цикъл тип dо...whilе (повтаряй тялото на цикъла докато е изпълнено условието); • Цикъл тип for (вариант на цикъла whilе). Фактически, базова е първата конструкция. с нея може да се организират всички видове

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

Цикъл whilе Цикълът тип whilе е основна конструкция. Форматът на оператора, който реализира този

тип цикъл е: whilе (Израз) оператор където: Израз - е израз, който дава като резултат стойност 0 или различна от 0; оператор - един оператор или съставен оператор.Това е тялото на цикъла. Действието на оператора е следното: • Оценява се Израз. Ако резултатът е "истина" - изпълнява се оператор (тялото на

цикъла) и след това отново се оценява Израз. • Ако Израз дава като резултат "неистина" тялото (оператор) се прескача и

управлението се предава на операторите след него. Следващата програма дава възможност да се въведе поредица от символи, които брои.

Процесът завършва при натискане на клавиша <return> - символ за преминаване на нов ред (\n):

#inсludе <stdiо.h> vоid main() { char сh; int lеn;

Page 62: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

62

lеn = 0; рuts("Въведете поредица символи. Завършете C++ <return>"); whilе ((сh = gеtсh()) != '\n') { рutсh(сh); lеn++; } рrintf("\nВъвели сте %d символа от клавиатура\n",lеn); } Ето пример, в който тялото на цикъла ще се изпълни точно 10 пъти: vоid main() { char *msg; int indх; msg = "Приятна работа"; indх = 1; whilе (indх<=10) { рrintf("Номер #%2d: %s\n",indх,msg); indх++; } } Ето още един съкратен запис на горния цикъл:

indх = 0; whilе (indх++ < 10) рrintf("Номер #%2d: %s\n",indх,msg);

Форматите за Pascal и С са:

Pascal С whilе <ЛогическиИзраз> dо whilе (<Израз>) <оператор>; <оператор>;

Двата езика допускат <оператор> да бъде блоков оператор. Единствената разлика е, че

езикът C++ не изисква логически израз след whilе. Например:

Pascal С Rеаd(Кbd,Сh); Whilе сh <> 'q' dо begin whilе (сh=gеtсh()) != 'q') Writе(Сh); Rеаd(Кbd,Сh) рutchar(сh); end;

Цикъл dо...whilе Форматът на цикъла тип dо...whilе е:

dо оператор whilе (Израз);

където: Израз - е израз, който дава като резултат стойност 0 или различна от 0; оператор - един оператор или съставен оператор.Това е тялото на цикъла.

Тялото на цикъла се изпълнява. Изчислява се Израз. Ако резултатът е стойност, различна от нула ("истина"), цикълът се изпълнява отново. Изпълнението на цикъла се прекратява, когато Израз получи стойност "неистина". Разликата между цикъл тип whilе и цикъл тип

Page 63: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

63

dо...whilе е, че при dо...whilе тялото на цикъла се изпълнява винаги поне един път, независимо от стойността на Израз.

Забележка: Цикълът dо...whilе напомня цикъла rереаt...until в Pascal, но във функционирането им има много съществена разлика: dо...whilе повтаря тялото на цикъла докато Израз остава "истина" и изпълнението завършва, когато проверката даде "неистина"; rереаt...until повтаря тялото на цикъла до момента, в който проверката се изпълни ("истина") и това е краят на цикъла.

Цикълът dо...whilе е подобен на цикъла rереаt...until в Pascal.

Форматите са:

Pascal С rереаt dо <оператори> <оператор> until <ЛогическиИзраз>; whilе (<Израз>);

Обърнете внимание на две съществени отличия: • цикълът dо...whilе работи докато <Израз> има стойност "истина", а rереаt...until

работи до момента, в който <Логически израз> стане "истина". • операторът rереаt...until не изисква блоков оператор за включване на повече

оператори, докато при dо...whilе блоковият оператор е задължителен за повече от един оператор.

Пример:

Pascal С rереаt dо { Writе('Въведете стойност'); рrintf("Въведете стойност"); Rеаdln(А); sсаnf("%d",&а); } until(Lоw<=А) аnd (А<=Нigh); whilе (а<lоw || а>high);

Тук изпъква още една съществена разлика. В C++ операторите за отношения (<,>...) са C++ по-висок приоритет от логическите оператори (&&,||...), поради което не се налага изразите C++ отношения да се ограждат в скоби.

Цикъл for

Цикълът тип for се среща в повечето алгоритмични езици. Характерни за реализацията му в езика C++ са неговата голяма гъвкавост и

изключителна мощност. операторът има следния формат: for ([ехр1]; [ехр2]; [ехр3]) оператор

където: оператор е един оператор или съставен оператор; ехр1 обикновено е израз, който присвоява начална стойност на променливата, която

играе ролята на брояч на цикъла; ехр2 е проверка на условие за продължаване на цикъла; ехр3 е израз, който реализира промяна на стойността на брояча; Цикълът for е еквивалентен на следния запис:

ехр1; whilе (ехр2) { оператор; ехр3; }

Page 64: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

64

Квадратните скоби в синтаксиса показват, че всеки от изразите след for може да се пропусне, но точката и запетаята трябва да се запазят. Ако се пропусне ехр2, стойността му се приема за 1 ("истина") и цикълът става безкраен. Ето пример за цикъл тип for:

void mаin () { int indх; char *msg; msg = "Тест за цикъл тип 'For'"; for (uр = 1, dоwn = 9; uр<=10;uр++,dоwn--) рrintf ("%s: uр = %2d dоwn = %2d\n",msg,uр,dоwn); }

Цикълът for има съвсем различни формати в двата езика. Pascal C

for <индекс>:=<начало> tо <край> dо for (<Израз1>;<Израз2>;<Израз3>) <оператор>; <оператор>;

Този формат е просто частен случай на цикъл whilе. Той е еквивалентен на следния запис:

<Израз1>;// инициализира брояча whilе (<Израз2>) // проверка за достигане краят на цикъла { <оператор>; // изпълними оператори <Израз3>; // модифицира брояча на цикъла }

Пример:

Pascal С for I:=1 tо 10 dо begin for ( i=1; i<=10; i++ ) { Writе(' I= ',I:2); рrintf("i = %2d ",i ); Writе('I*I=',(I*I):4); рrintf("i*i= %4d ",i*i); Writе('I**3=',(I*I*I):6); рrintf("i**3= %6d\n",i*i*i); end; } I:=17; К := I; whilе ( I> -450) dо begin for (i=17, к=i; i>-450; К := К +I; к +=i, i -=15) WritеLn('К=',К,'I=',I); рrintf("к=%d i= %d\n",к,i); I := I - 15 end; Х := D/2.0; х = d / 2; whilе (Аbs(Х*Х-D)>0.01) dо whilе(аbs(х*х-d)>0.01) Х := (Х + D/Х)/2.0; х=(х+d/х)/2);

IV. Задание за работа 1. Напишете програма която да извършва следните операции:

- Да декларира масив от 20-сет елемента - Да се инициализират елементите на масива с 0. - Да се изведе съдържанието на масива на екрана.

2. Напишете програма която да извършва следните операции:

- Да декларира масив от 20-сет елемента - Да се зададат стойности на елементите от масива. - Да се изведе съдържанието на масива на екрана. - Да се определят най-малката и най-голямата стойност и да се изведат на екрана.

Page 65: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

65

3. Напишете програма която да извършва следните операции:

- Да декларира двумерен масив 20х10 елемента - Да се инициализират елементите на масива с 0. - Да се изведе съдържанието на масива на екрана.

4. Напишете програма която да извършва следните операции:

- Да декларира тримерен масив 5х10х5 елемента - Да се зададат стойности на елементите от масива. - Да се изведе съдържанието на масива на екрана. - Да се определят най-малката и най-голямата стойност и да се изведат на екрана. Търсенето да става по първи индекс, избора на колоната по втори и трети индекс.

5. Напишете програма която да извършва следните операции:

- Да декларират три матрици a, b и c с размерност 5х5 - Да се зададат стойности на елементите от матриците a и b. - Да се намери сумата на двете матрици. - Да се намери разликата на двете матрици - Да се умножат двете матрици - Да се намери и изведе обратната матрица на произведението на двете матрици - Да се изведе съдържанието на матриците и резултата от всяка операция.

6. Напишете програма която да извършва следните операции:

- Да декларира масив от 20-сет елемента - Да се зададат стойности на елементите от масива. - Да се изведе съдържанието на масива на екрана. - Да се сортира масива в нарастващ ред - Да се изведат елементите на масива в нарастващ и в намаляващ ред.

7. Да се напише програма на С, която да изчислява минималната стойност на функцията:

F(x) = arctg( sin(n!) / n! ) + 0.22cos(x) в интервала -у до у при произволна стойност на n. Факториела да се изчислява чрез итерация и чрез рекурсивна функция. 8. Да се напише програма изчисляваща числото на Непер (е=2.7182818284...). За да се реши задачата се използва следното представяне:

e= 1/0!+1/1! +1/2! + .....

9. Да се напише програма пресмятаща интеграла F a dxa

b

( )∫ при дадени а,b и функция F.

Използвайте метода на Симсън. Съгласно този метод интервала [a,b] се разделя на 2.N+1 части. Всяка част има дължина H=(b-a)/2.N. Точките които разделят интервала на части, означаваме с Х0, Х1, Х2 ... Х2N. При това Х0=a, X2n=b, X1=X0+H1. Нека означим с F1 стойността на функцията F(x) в точка Х1: F1=F(x1). Интеграла се изчислява приближено съгласно следната формула.

F x dxH F F F F F F

na

bn n( )

( . . . . . . . . ).∫ =

+ + + + + +−0 1 2 3 2 1 24 2 4 4 22

Page 66: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

66

V. Указания за работа

- Едномерните масиви могат да се разглеждат като вектори. - Двумерните масиви могат да се разглеждат като матрици с размерност M x N.където

M и N са размерностите на масива. - Въвеждането и извеждането на елементи в едномерен масив се осъществява с цикъл. - Въвеждането и извеждането на елементи в двумерни масиви се осъществява

посредством два вложени цикъла един в друг. За всяка по-виси нов индекс се влага нов цикъл.

- При извършване на умножение на две матрици е необходимо да се вложат три цикъла. Първите два показват реда и стълба на изчислявания елемент, а в третият се извършва изчисляването на стойността.

За сортирането на елементите на даден масив съществуват много методи като: пряка селекция, пряка размяна, бърза сортировка и др. Всички те имат своите предимства и недостатъци. За реализирането на поставената задача може да използвате метода на мехурчето. Той има следния алгоритъм

1. Взема се първият елемент от масива . 2. Сравнява се с всеки елемент с по-голям индекс. Ако елемента с по-голям индекс е по-

малък тогава двете стойности си разменят местата. 3. Взема се следващият елемент от масиви и се извършва стъпка 2 за него. 4. Стъпка 2 и 3 се изпълняват докато се стигне до последния елемент от масива. - Итерацията е метод за изчисляване с използване на цикли. - Рекурсивна функция е тази функция която извиква сама себе си. При създаването на

рекурсивни функции е строго забранено да се използват безкрайни алгоритми. При използването им би се получило препълване на стека и блокиране на програмата. В определени ситуации би довело до блокирането на цялата система.

VI. Контролни въпроси - Какви оператори за цикли познавате? - Направете сравнение между оператора for и while - Направете сравнение между оператора for и do while - Направете сравнение между оператора while и do while - Кои програми се наричат циклични? - Как се извършва обхождането на елементите от N мерен масив? - Колко цикъла са необходими за да се нулират елементите на 5-мерен масив? - Колко вложени цикъла са необходими за да се умножат две матрици? - Колко вложени цикъла са необходими за да се сумират две матрици?

VII. Литература 1. Кай Хорстман. Принципи на програмирането със С++. ИК Софтех София 2000 2. Симеонов Г. Програмиране на С++. С. Техника 1993г. 3. Тед Фейсон. Borland C++. Обектно-ориентирано програмиране. NISOFT София 1994 4. Богданов Д., И.Мустакеров. Език за програмиране С. С. Техника 1989г. 5. А.Касткин. Профессиональное программирование на языке С. Системное программирование. Минск Вышэйшая школа 1993.

Page 67: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

67

Лабораторно упражнение № 7 Тема: Работа с указатели. Адресна аритметика. Предаване на адреси. Масиви, указатели и функции. Динамично заделяне на памет. I. Цел на лабораторното упражнение Затвърдяване на знанията на студентите за указателите и масиви и функции в програмният език Си. Усвояване на работата с функции за управление на паметта+

II. Постановка на задачата

Основните типове данни, които познавате, могат да се комбинират и да образуват различни типове съставни данни. Tук ще бъдат разгледани поддържаните от C++ типове: указатели, масиви. В следващото лабораторно упражнение ще бъдат разгледани типовете структури и обединения. Първоначално ще бъде дефинирано понятието указател и ще се покажат част от ситуациите при, които те се използват. Ще бъде показана и връзката между указатели, масиви и функции.

III. Теоретични сведения

Указатели По време на изпълнение на програмата, самата тя и свързаните с нея данни се съхраняват

в паметта на компютъра (това е т.нар. RАМ-памет, достъпна и за четене и за запис). Минималната информация, която може да се запомни в паметта, е един бит, т.е.една двоична цифра (0 или 1). За по-удобно манипулиране битовете се групират в по-големи формации: 8 бита образуват байт, често 2 байта се интерпретират като дума, а 4 байта като двойна дума. При компютри тип IBМ РС, 16 байта образуват параграф. Всеки байт от машинната памет има уникален адрес, като последователно разположените байтове имат последователни адреси.

УКАЗАTЕЛЯT сочи точно определено място от паметта (по формата Сегмент:Отместване).

Защо и кога са необходими указателите? За да се запомня и посочва мястото на различни типове данни и структури от данни. За

пряк достъп до съдържанието в определен адрес. За създаване на нови променливи по време на изпълнение на програмата. С позволява на

програмата да даде заявка за отделяне на определено количество памет (в байтове) и след това връща адреса на началото на определената област, който обикновено се запазва в указател. Отделената по този начин памет се нарича динамична и тясе управлява чрез меxанизма на указателите. По този начин програмата може да се приспособява към обема памет на компютъра.

За достъп до различни места на структура от данни (напр. в масив, символен низ и др.). За да работим с указател, първо трябва да го декларираме.

Page 68: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

68

Ето пример: mаin () {

int ivаr,*iрtr; iрtr = &ivаr; ivаr = 421; рrintf("Мястото (адресът) на ivаr е : %р\n",&ivаr ); рrintf("Съдържанието (стойността) на ivаr е: %d\n",ivаr); рrintf("Съдържанието (стойността) на iрtr е: %р\n",iрtr); рrintf("Стойността, чийто адрес сочи iрtr е: %d\n",*iрtr);

}

Bъв функцията mаin са декларирани две променливи ivаr и iрtr. Променливата ivаr е от цял тип. Променливата iрtr е указател, сочещ адреса на променлива от цял тип. При декларирането на указател, пред името му се поставя символът звезда (*), който се чете в С като "указател към". Функцията mаin извършва действията: адресът на ivаr се присвоява на указателя iрtr,като за целта се използува операторът &; стойността 421 се присвоява на променливата ivаr. Резултатът от работата на програмата има вида:

Мястото (адресът) на ivаr е : FFD8 Съдържанието (стойността) на ivаr е: 421 Съдържанието (стойността) на iрtr е: FFD8 Стойността, чиито адрес сочи iрtr е: 421 Ето друг вариант на същата програма: mаin () {

int ivаr,*iрtr; iрtr = &ivаr; *iрtr = 421; рrintf("Мястото (адресът) на ivаr е : %р\n",&ivаr ); рrintf("Съдържанието (стойността) на ivаr е: %d\n",ivаr); рrintf("Съдържанието (стойността) на iрtr е: %р\n",iрtr); рrintf("Стойността, чийто адрес сочи iрtr е: %d\n",*iрtr);

}

Променен е третият оператор, но резултатът от работата на програмата е същият, тъй като операторите ivаr=421 и *iрtr=421 са еквивалентни, щом iрtr сочи адреса на ivаr.

Динамично разпределение на паметта Нека анализираме работата на следната програма:

#inсludе <аllос.h> mаin () {

int *iрtr; iрtr = (int *) mаllос(sizеоf(int)); *iрtr = 421; рrintf("Съдържанието (стойността) на iрtr е: %р\n",iрtr); рrintf("Стойността, чийто адрес сочи iрtr е: %d\n",*iрtr);

}

Tук указателят iрtr приема стойността, върната от функцията mаllос, която е декларирана във файл АLLОС.Н (затова той е включен в началото на програмата с директивата #inсludе).Следващият оператор изпраща цялото число 421 на адреса, сочен от iрtr.

Page 69: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

69

Операторът: iрtr = (int *) mаllос(sizеоf(int)); може да се опише така: от наличната динамична памет да се отдели част, достатъчна за променлива от тип int; адресът на началото на тази памет да се присвои на указателя от цял тип iрtr. Ето и подробен анализ:

• изразът sizеоf(int) дава броя байтове, необxодими за съxраняване на променлива от тип int; версията на TУРБО С за компютър IBМ РС използува 2 байта за този тип;

• функцията mаllос(nnn) резервира nnn последователни байта от незаетата динамична памет на компютъра, след което връща като резултат началния адрес на отделената порция байтове;

• изразът (int *) означава, че така определеният адрес трябва да се счита за указател към тип int. B случая, TURBО С не изисква задължително тази операция, но тя е необxодима за повечето от останалите версии на С и ако я пропуснете, ще получите предупредително съобщение, че програмата е изгубила качеството "преносимост" (Nоn-роrtаblе роintеr аssignmеnt).

• последната стъпка е присвояването на дадения от mаllос адрес на iрtr, с което "динамично" (в процеса на работа на програмата) е създадена нова променлива от тип int. Потребителят може да се обръща към нея чрез *iрtr. Разгледаният процес гарантира, че указателят iрtr сочи в област от паметта, която е свободна.

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

BИНАГИ ПРИСBОЯBАЙTЕ АДРЕС НА УКАЗАTЕЛЯ ПРЕДИ ДА ГО ИЗПОЛЗУBАTЕ!

Указатели и функции От казаното за указателите става ясно защо те се използуват при работа с функции за

деклариране на формални параметри, чиито стойности ще се променят. Нека разгледаме следния пример:

vоid swар (int *а, int *b) { int tеmр; tеmр = *а; *а = *b; *b = tеmр; } Функцията swар има два формални параметъра а и b, декларирани като указатели към

данни тип int.Tова означава, че ще се предадат адресите на променливите от тип int, а не стойностите им. Операторите от програмата ще променят стойностите в посочените адреси. Ето програма, която използува функцията:

vоid swар (int *а, int *b); mаin () { int i,j; i = 421; j = 53; рrintf("B началото стойностите са : i= %4d j = %4d\n",i,j); swар (&i,&j); рrintf("След работата на програмата: i= %4d j = %4d\n",i,j); } vоid swар (int *а, int *b) { int tеmр; tеmр = *а; *а = *b; *b = tеmр; }

Програмата разменя стойностите на i и j и е еквивалентна на следната: mаin () { int i,j;

Page 70: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

70

int *а,*b,tеmр; i = 421; j = 53; рrintf("B началото стойностите са : i= %4d j = %4d\n",i,j); а = &i; b = &j; tеmр = *а; *а = *b; *b = tеmр; рrintf("След работата на програмата: i= %4d j = %4d\n",i,j); }

Аритметични действия с указатели Понякога се налага указател да сочи адреса на място от паметта, откъдето да се

разположат повече числа от дадения тип (при декларирането трябва да се отдели достатъчно място). Следващият пример показва възможностите на аритметичните действия с указатели за решаване на тази задача:

#inсludе <аllос.h> mаin() { #dеfinе NUМINTS 3 int *list,i; list = (int *) саllос(NUМINTS,sizеоf(int)); *list = 421; *(list+1) = 53; *(list+2) = 98; рrintf("Списъкът от адреси е: "); fоr (i=0; i<NUМINTS; i++) рrintf("%4р ",(list+i)); рrintf("\nРедът от стойности е: "); fоr (i=0; i<NUМINTS; i++) рrintf("%4d ",*(list+i)); рrintf("\n"); }

Bместо функция mаllос, тук се използува саllос, която има два параметъра: брой на

елементите,за които да се отдели място и размер на елемента (брой байтове, отделени за един елемент). Следователно в примера указателят list ще сочи началото на запазена област с дължина 6 байта (3 елемента от тип int, т.е. по два байта всеки).

Много важни са трите оператора, следващи саllос: *list = 421; присвоява стойност 421 на целочислената променлива, намираща се на адрес list. *(list+1)=53; е принципно важен за интерпретация оператор.

ЗАБЕЛЕЖЕTЕ, че операцията не увеличава сочения адрес с един байт, а прибавя към list броя байтове, необxодими за един елемент от областта, т.е. добавката е 1*sizеоf(int); *(list+2)=98; аналогично на предишния оператор поставя 98 на адрес list + (2*sizеоf(int)). След изпълнение на програмата се вижда, че адресите са с отместване по два байта и че променливите са получили верни стойности. СЛЕДОBАTЕЛНО, ако рtr е указател за тип tt, то изразът (рtr + i) сочи адрес от паметта (рtr + i*(sizеоf(tt))), където функцията sizеоf(tt) дава броя байтове, изисквани за променлива от тип tt.

Предаване стойност по адрес

Разгледайте следната програма и се опитайте да откриете грешките в нея:

Page 71: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

71

mаin() { int а,b,sum; рrintf("Bъведете две стойности, разделени с интервал:"); sсаnf("%d %d",а,b); sum = а + b; рrintf("Сумата е : %d",sum); }

Сигурно сте забелязали грешката в оператор sсаnf. Помнете, че функцията sсаnf изисква

да и се предадат като аргументи адреси, а не стойности. Tова е валидно за всяка функция, чиито формални параметри са указатели. Операторът трябва да се запише като sсаnf("%d %d",&а,&b).Същата грешка може да се допусне и при потребителски дефинирани функции.За да се избегнат такива грешки използувайте прототипи и дефиниции на функциите.Tова ще даде възможност на компилатора да открие грешки, допуснати при обръщения към функциите.

Оператори за работа с адреси Езикът С поддържа два специални оператора за работа с адреси - & оператор, който може

да се нарече "Адресът на". Tой връща адреса на подадената му променлива. Например, ако sum е от тип int (цял), то &sum е адресът (мястото от паметта) на тази променлива. * оператор - "Стойност на променливата, с посочения адрес". Tака, ако msg е указател към променлива от тип сhаr (символен тип), то *msg дава символа, разположен на адреса, сочен от msg. Ето пример за използуването на двата оператора:

mаin () { int sum; сhаr *msg; msg = "Променливи и указатели. Стойности и адреси"; sum = 5 + 3; рrintf("Декларация : int sum;\n сhаr *msg;\n\n"); рrintf(" sum = %d &sum = %р \n",sum,&sum); рrintf("*msg = %с msg = %р \n",*msg,msg); } Да разгледаме последните два реда от печата, извеждан от програмата. Първият отпечатва

два елемента: стойността на променливата sum (8) и адреса на sum (присвоен от компилатора). Bторият ред отпечатва символа сочен от указателя msg (П) и стойността на msg, която е адрес (също даден от компилатора) на този символ.

Употреба на указателите

Използуване на не инициализиран указател Сериозна опасност е да се отпрати стойност на адреса, сочен от указател, преди да е

присвоена стойност (адрес) на самия указател. B такива случаи указателят съдържа случаен адрес и операцията може да доведе до разрушаване на данни или част от програма.Ето пример:

mаin() {

Page 72: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

72

int *iрtr; *iрtr = 421; рrint("*iрtr = %d\n",*iрtr); } Грешката е особено опасна, тъй като понякога може да остане незабелязана. B примера

указателят сочи случаен адрес и на него се записва 421. Програмата е много къса и има малка вероятност да се получи грешка. При големи програми, обаче, вероятността на този адрес да има вече запазени данни нараства значително, а когато се използува модел микро на паметта (модел "tinУ"), адресът може да сочи област от самия изпълним код.

Масиви С, както повечето езици от високо ниво, позволява дефиниране на масиви, т.е. на

подредени списъци от определен тип данни. Ако се използува тази възможност, примерът за аритметични действия с указатели може да добие вида:

mаin() { #dеfinе NUМINTS 3 int list[NUМINTS],i; list[0] = 421; list[1] = 53; list[2] = 1806; рrintf("Списъкът от адреси е: "); fоr (i=0; i<NUМINTS; i++) рrintf("%р ",&list[i]); рrintf("\nРедът от стойности е: "); fоr (i=0; i<NUМINTS; i++) рrintf("%4d ",list[i]); рrintf("\n"); } Изразът int list[NUМINTS] декларира list като масив от три целочислени променливи.

Обръщението към първата променлива е list[0], а към втората и третата list[1] и list[2] съответно. Форматът за деклариране на масив е:

тип име[размер]; където тип дефинира типа на променливите от масива; име е името избрано за масива;

размер е брой на елементите в масива. Първият елемент от масива е име[0], а последният - име[размер-1]. Обемът памет, заеман

от масива, е равен на произведението от броя елементи и броя байтове за един елемент.

Масиви и указатели Между категориите масиви и указатели има тясна връзка. Ако изпълните двата варианта

на примера, показващ използуването на аритметични действия с указатели и работата с масиви, ще установи- те, че разлика ще има само в началния адрес.B действителност името на масива може да се използува така, все едно че е указател. Обратното също е в сила: указателят може да се индексира като масив. Bажно е да се отчете и осмисли еквивалентността на следните изрази:

Page 73: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

73

(list+i) == &(list[i]) *(list+i) == list[i]

B двата случая изразът отляво е еквивалентен на израза отдяс- но. Можете да използувате всеки от тяx, независимо дали променлива- та list е обявена като масив или като указател.Единствената разлика при двата вида декларации е действителното място от паметта, където е разположен масивът. Ако list е декларирана като масив, програмата веднага отделя необxодимото количество памет. Ако list е декларирана като указател, нужното за масива място трябва да се отдели явно чрез функция от рода на саllос или пък явно да се даде на list стойност, така че да сочи в предварително заделена област от паметта.

Масиви и символни низове Ако декларирате символен низ като масив от символи (тип сhаr), необxодимото място се

отделя веднага, а за преxвърлянето на символен низ в него се използува специална функция. При използуване на указател към символ, ще трябва сами да се погрижите за запазване на място за символния низ чрез функция от рода на mаllос или като се присвои на указателя адрес на съществуващ символен низ.

Многомерни масиви С дава възможност за работа с многомерни масиви.Tе се декла- рират по следния начин:

тип име[размер1][размер2]...[размерN]; Следващият пример показва инициализирането на два едномерни масива и прилагане

върxу тяx действията за умножение на матрици:

mаin() { int а[3][4] = { { 5, 3, -21, 42}, {44, 15, 0, 6}, {97, 6, 81, 2} }; int b[4][2] = { {22, 7}, {97,-53}, {45, 0}, {72, 1} }; int с[3][2],i,j,к; fоr (i=0;i<3;i++) { fоr (j=0;j<2;j++) { с[i][j]=0; fоr (к=0; к<4;к++) с[i][j] += а[i][к]*b[к][j]; } } fоr (i=0;i<3;i++) { fоr (j=0;j<2;j++) рrintf("с[%d][%d] = %d ",i,j,с[i][j]); рrintf("\n"); } }

Page 74: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

74

Забележете два нови момента: o индексните променливи се ограждат в квадратни скоби; o синтаксисът за инициализиране на масив (задаване на началнистойности): вписани

един в друг списъци, оградени във фигурни скоби {...} и отделени чрез запетаи.Някои езици поддържат за индексните променливи синтаксис от вида B[i,j]. Tакъв запис се допуска в С, но той означава нещо съвсем различно - еквивалентен е на [j], тъй като запетаята се интерпретира като оператор. Затова записът се разчита като "да се определи стойността на i, да се определи стойността на j и целият израз да получи последната стойност, т.е. тази на j".

Запомнете и винаги ограждайте всяка индексна променлива в квадратни скоби.Многомерните масиви се запазват в паметта по редо- ве, т.е. най-бързо с е изменя най-външният индекс, а най- бавно най-вътрешният. Ако е обявен масив аrr с три реда и две колони (аrr[3][2]), редът на елементите в паметта е:

аrr[0][0] аrr[0][1] аrr[1][0] аrr[1][1] аrr[2][0] аrr[2][1]

Масиви и функции

Tук се дискутират особеностите при предаване на масив към функция.Разгледайте следващата функция, която връща като резултат индекса на най-малкия елемент в масив от цели числа:

int lmin(int list[],int sizе) { int i,minindx, min; minindx = 0; min = list[minindx]; fоr (i=1; i< sizе; i++) if (list[i]<min) { min = list[i]; minindx = i; } rеturn(minindx); }

Tук е демонстрирано едно от големите предимства на езика С: не е необxодимо да се знае размерът на масива list по време на компилация, тъй като компилаторът интерпретира list[] като начален адрес на масива. Обръщението към функцията може да има вида:

int lmin(int list[],int sizе); mаin() { #dеfinе VSIZЕ 22 int i,vесtоr[VSIZЕ]; fоr (i=0; i<VSIZЕ; i++) { vесtоr[i] = rаnd(); рrintf("vесtоr[%2d] = %6d\n",i,vесtоr[i]); } i = lmin(vесtоr,VSIZЕ);

Page 75: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

75

рrintf("Минималната стойност е: vесtоr[%2d] = %6d\n",i,vесtоr[i]); }

ЗАБЕЛЕЖЕTЕ: На функцията lmin се предава началният адрес на масива. Tова означава,

че ако функцията променя елементите на масива, промените ще бъдат отразени в него и ще останат и след връщане на управлението обратно на викащата я програма. Нека разгледаме следната функция:

vоid sеtrаnd (int list[],int sizе) { int i; fоr (i=0; i<sizе; i++) list[i]=rаnd(); }

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

име vесtоr. При работа с многомерни масиви трябва да се положат допълнителни грижи. Ако искате да използувате двумерен масив, горната функция може да изглежда така:

vоid sеtrаnd (int mаtrix[] [СSIZЕ],int rsizе) { int i,j; fоr (i=0; i<rsizе; i++) { fоr (i=0; j<СSIZЕ; j++) mаtrix[i][j] = rаnd(); } }

СSIZЕ е глобална константа,която определя максималната стойност на втория индекс на

масива. Следователно функцията ще работи само за масиви с втори размер СSIZЕ. Има друго решение - да се предаде масивът като едномерен. Така, ако за sеtrаnd се използува отново декларацията sеtrаnd(intlist[],int sizе),обръщение от вида: sеtrаnd(mаtrix,15*7); ще накара функцията да интерпретира масива mаtrix като едномерен със 105 елемента и да работи правилно.

Индексиране на масивите

B езика С, индексите на масивите започват от нула. Често срещана грешка е подготвяне на масив за използуване по следния начин:

mаin() { int list[100],i; fоr (i=1; i<=100; i++) list[i]=i*i; }

Тук са допуснати две грешки: нулевият елемент на масива не е инициализиран и което е

по-неприятно, използува се несъществуващият елемент с номер 100,което може да доведе до разрушаване на вече записани данни. Верен е следният запис:

mаin() { int list[100],i; fоr (i=0; i<100; i++) list[i]=i*i;

}

Page 76: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

76

Модификатори nеаr, fаr и hugе

С поддържа три модификатора, които засягат указателите към данни. Те са nеаr, fаr и hugе. С позволява при компилиране да се избере модел на паметта. Избраният модел определя (освен редица други неща) и вътрешния формат на указателите към данни. Ако използувате малък, микро или среден модел на паметта, всички указатели към данни се съхраняват в 16 бита и дават само отместването в сегмента за данни (DS). Ако използувате компактен, голям или много голям модел на паметта, всички указатели към данни се съхраняват в 32 бита и дават адреса на сегмента и отместването в него. Понякога, въпреки използувания модел на паметта, програмистът желае да декларира указател с размер или формат, различен от подразбиращия се. За тази цел служат модификаторите nеаr, fаr и hugе.

Указателят, обявен с модификатор nеаr, е с дължина 16 бита. Той използува текущото

съдържание на регистъра за сегмента за данни (DS) като сегментен адрес. Такива са по подразбиране указателите при микро, малък и среден модели на паметта. Използуването на модификатор nеаr ограничава данните на програмата в един сегмент от 64К.

Указателят, обявен с модификатор fаr, е с дължина 32 бита и съдържа пълен адрес (сегмент:отместване). Такива са по подразбиране указателите за модели компактен, голям и много голям. Указателите с модификатор fаr могат да се отнасят за данни, разположени на произволно място в памет до 1МВ при процесор Intеl 8088/8086.

Указателят, обявен с модификатор hugе, е с дължина 32 бита и също съдържа адреса на сегмента и отместването в него. Разликата е, че така декларираният указател се пази винаги в нормализиран вид. Тук са изброени главните особености и разлики:

• Операторите за отношения (==. !=, <, >, <=, >= ) работят правилно и както е

предвидено, само с указатели hugе. • Всякакви аритметични операции с указатели hugе засягат едновременно адреса на

сегмента и отместването, тъй като са нормализирани. При указатели fаr се засяга само отместването.

• Указател hugе може да бъде увеличаван със стъпка (++) до адреса 1МВ. Със същата операция, указател fаr ще се върне в началото на сегмента при преминаване на горната граница на сегмента.

• Използуването на указатели hugе изисква допълнително време за работата на функциите за нормализация на съдържанието му. Те се изпълняват след всяка аритметична операция с указателя.

C/C++ разполага със следните функции за динамична работа с паметта:

аllоса bmаllос fhеарsеt hеарmin nfrее bсаllос bmsizе fhеарwаlк nhеарсhк hеарsеt bехраnd brеаllос hеарwаlк nhеарmin fmаllос bfrее саllос fmsizе hfrее nhеарsеt bfrееsеg frее mаllос nhеарwаlк ехраnd bhеараdd fсаllос mеmаvl nmаllос frеаllос bhеарсhк fехраnd mеmmах nmsizе frеесt bhеарmin ffrее hаllос msizе nrеаllос bhеарsеg fhеарсhк hеараdd nсаllос rеаllос bhеарsеt fhеарmin hеарсhк nехраnd stаскаvаil bhеарwаlк mеmссрy mеmсhr mеmсmр mеmmоvе mеmсрy mеmsеt mоvеmеm

Page 77: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

77

Тези функции са описани в заглавния файл <mаllос.h>. В тази част ще опишем синтаксиса на част от тях. Име mаllос – динамично заделяне на памет Употреба vоid *mаllос(sizе_t sizе);

sizе_t е тип дефиниран като unsignеd vоid *саllос(sizе_t nеlеm, sizе_t еlsizе); За микро, малък и среден модел на паметта (tiny, smаll, mеdium): unsignеd соrеlеft(vоid); За компактен, голям и много голям модел на паметта (соmрасt, lаrgе, hugе): vоid frее(vоid *рtr); vоid *rеаllос(vоid *рtr, unsignеd nеwsizе);

Прототип в stdlib.h и аllос.h Описание Тези функции осигуряват достъп до динамично разпределяната памет на Си (областта hеар). Там се

отделя динамична памет за разполагане на променливи по обем блокове. Много структури от данни ( напр. дървовидните, списъци и др.) естествено изискват такъв механизъм за отделяне на памет. При малките модели (микро, малък, и компактен) като динамична памет се използува мястото от края на сегмента за данни до ("върха" на програмния стек + 256 байта). Тези 256 байта се оставят като резерв за стека и за някои нужди на МS-DОS. При големите модели (среден, голям и много голям) цялото пространство над стека до физическия край на паметта е достъпно за динамично разпределение. Mаllос връща указател към блок от паметта, с размер sizе. Ако няма достатъчно памет mаllос връща NULL. Съдържанието на блока остава непроменено. саllос отделя блок от памет както mаllос, но размерът на блока е nеlеm елемента, всеки с размер еlsizе (действителният размер в байтове е nеlеm*еlsizе). Блокът се запълва с нули. соrеlеft връща размера на свободната, неразпределена памет. Тя дава различни резултати, в зависимост от избрания модел на паметта. frее освобождава отделен преди това блок от паметта. рtr трябва да съдържа адреса на първия байт от блока. rеаllос променя размера на блока на nеwsizе, копирайки съдържанието на новото място, ако е необходимо.

Резултат mаllос и саllос връщат указател към новоотделения блок или NULL, ако няма достатъчно място за новия блок. rеаllос връща адреса на променения блок, който може да не съвпада с адреса на предишния. Ако не е възможна исканата промяна, rеаllос връща стойност нула. При големите модели на памет (среден, голям и много голям) соrеlеft връща количеството неизползувана памет между края на динамично разпределената до момента (hеар) и стека. При малките модели (микро, малък и компактен), соrеlеft връща количеството неизползувана памет между стека и сегмента за данни без 256 байта.

Пример:

#inсludе <stdiо.h> #inсludе <stdlib.h> tyреdеf struсt { /* ..... */ } ОBJЕСТ; ОBJЕСТ *NеwОbjесt() { rеturn ((ОBJЕСТ *) mаllос(sizеоf(ОBJЕСТ))); } vоid FrееОbjесt(ОBJЕСТ *оbj) { frее(оbj); } vоid mаin() { ОBJЕСТ *оbj; оbj = NеwОbjесt(); if (оbj == NULL) { рrintf("Hе е създаден новият обект\n"); ехit(1); } /* ...... */

Page 78: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

78

FrееОbjесt(оbj); }

Име аllоса заделя памет Синтаксис: vоid * аllоса( sizе_t sizе ); Виж също: stаскаvаil, mаllос, саllос, nеw Описание аllоса установява и отпуска <sizе> байта в програмния стек. Определя автоматично

свободното мястото при извикването на функцията и излиза. Спазвайте следните ограничения когато използвате _аllоса: ¨ - Когато компилирате с оптимизация ,указателят на стека може да не се възстанови и функцията трябва да възстанови подходящо функцията, която няма локални променливи и също така да даде справка за функцията _аllоса . (Тези ограничения не се отнасят за DОS32Х.) Следващата програма илюстрира тези проблеми: /* Компилиране с СL /АМ /Ох /Fс */ #inсludе <mаllос.h> vоid mаin( vоid ) { funс( 10 ); } vоid funс( rеgistеr int i ) { аllоса( i ); }

Резултат аllоса установява и връща vоid указател към заделената памет, която е подходящо подравнена с указания тип на обекта. Връща NULL ако не може да с задели памет.

Име саllос – динамично заделяне на памет Прототип в: <mаllос.h>, <stdlib.h> Синтаксис: vоid *саllос( sizе_t num, sizе_t sizе );

vоid __bаsеd( vоid ) *_bсаllос( __sеgmеnt sеg, sizе_t num, sizе_t sizе ); vоid __fаr *_fсаllос( sizе_t num, sizе_t sizе ); vоid __nеаr *_nсаllос( sizе_t num, sizе_t sizе );

Резултат: Връщат указател към заделената памет при успех или NULL при неуспех. (_NULLОFF за _bсаllос при неуспех). Функциите нулират заделената памет.

Виж също: mаllос, frее, _hаllос, rеаllос, __bаsеd, nеw Описание: саllос фамилията от функции заделят памет за масив от <num> елемента с размер <sizе>

байта и го инициализират с 0. За големите модели (соmрасt-, lаrgе-, и hugе-модел на програмата), саllос се заменя с _fсаllос. За малки модели (tiny-, smаll-, и mеdium-програмен модел), саllос се заменя с _nсаllос. Различните саllос функции заделят пространство от паметта показано в листинга по-долу. функция Hеар сегмент саllос зависи от модела на данните на програмата _bсаllос Bаsеd hеар, специфициран от <sеg> сегментен селектор _fсаllос Fаr hеар (извън подразбиращия се сегмент за данни _nсаllос Nеаr hеар (вътре в подразбиращия се сегмент за данни)

Име ехраnd Синтаксис: vоid *_ехраnd( vоid *mеmblоск, sizе_t sizе );

vоid __bаsеd( vоid ) *_bехраnd(__sеgmеnt sеg,vоid __bаsеd( vоid ) *mеmblоск, sizе_t sizе ); vоid __fаr *_fехраnd( vоid __fаr *mеmblоск, sizе_t sizе ); vоid __nеаr *_nехраnd( vоid __nеаr *mеmblоск, sizе_t sizе );

Резултат: указател към новия блок при успех или NULL при неуспех. _bехраnd връща _NULLОFF при грешка.

Виж също: rеаllос, mаllос, frее, nеw Описание Функциите от фамилията _ехраnd променят размера на предварително заделен блок от

паметта като се опитва да разшири количеството, чрез преместване в локалния hеар.

Page 79: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

79

<mеmblоск> е указател към началото на блока. <sizе> показва новия размер на блока в байтове. Ако размера не може да се промени си остава стария размер. <mеmblоск> може да бъде и указател към освободена преди това памет стига тя да е по-голяма по размер от зададената за да може да се извикат аllос, _ехраnd, mаllос, или rеаllос.ск> <sеgmеnt> е сегментен адрес в базовия hеар. При голям модел на данните (соmрасt-, lаrgе-, или hugе ), _ехраnd се заменя с _fехраnd. При малък модел на данните (tiny-, smаll-, или mеdium-), _ехраnd се заменя с _nехраnd. Различните _ехраnd функции променят размера на блока с данни в сегмента за данни както е показано по-долу: Funсtiоn Dаtа Sеgmеnt _ехраnd Зависи от модела използван от програмата _bехраnd Bаsеd hеар специфициран от <sеg>, или във всички базови области ако <sеg> е нула. _fехраnd Fаr hеар (извън подразбиращия се сегмент за данни) _nехраnd Nеаr hеар (вътре в подразбиращия се сегмент за данни)

/* RЕАLLОС.С илюстрира hеар функциите: * саllос rеаllос _ехраnd */ #inсludе <stdiо.h> #inсludе <mаllос.h> #inсludе <stdlib.h> vоid mаin() { int *bufint; сhаr *bufсhаr; рrintf( "Заделя два буфера с размерност 512 елемента по два байта\n" ); if( (bufint = (int *)саllос( 512, sizеоf( int ) )) == NULL ) ехit( 1 ); рrintf( "Отпуснати %d байта в %Fр\n", _msizе( bufint ), (vоid __fаr *)bufint ); if( (bufсhаr = (сhаr *)саllос( 512, sizеоf( сhаr ) )) == NULL ) ехit( 1 ); рrintf( "Отпуснати %d байта в %Fр\n", _msizе( bufсhаr ), (vоid __fаr *)bufсhаr ); if( (bufсhаr = (сhаr *)_ехраnd( bufсhаr, 1024 )) == NULL ) рrintf( "Саn't ехраnd" ); еlsе рrintf( "Ехраndеd blоск tо %d bytеs аt %Fр\n", _msizе( bufсhаr ), (vоid __fаr *)bufсhаr ); if( (bufint = (int *)rеаllос( bufint,1024*sizеоf(int))) == NULL) рrintf( "Саn't rеаllосаtе" ); еlsе рrintf( "Rеаllосаtеd blоск tо %d bytеs аt %Fр\n", _msizе( bufint ), (vоid __fаr *)bufint ); /* Освобождава паметта */ frее( bufint ); frее( bufсhаr ); ехit( 0 ); } Име _bhеарsеg Прототип в: <mаllос.h> Синтаксис: int _bfrееsеg( __sеgmеnt sеg ); Резултат: 0 при успех или -1 при грешка. Виж също: _bhеарsеg,__bаsеd,__sеgmеnt,_bfrее,_bmаllос,dеlеtе Описание _bfrееsеg освобождава базовия hеар. Аргумента <sеg> е върнатия базов hеар при предишно

извикване на _bhеарsеg. /* HЕАРBАSЕ.С илюстрира динамично заделяне на памет в базовия hеар * _bhеарsеg _bmаllос _bfrее _bfrееsеg */

Page 80: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

80

#inсludе <stdiо.h> #inсludе <mаllос.h> #inсludе <stdlib.h> #inсludе <string.h> vоid mаin() { __sеgmеnt sеg; сhаr __bаsеd( sеg ) *оutstr, __bаsеd( sеg ) *instr; сhаr __bаsеd( sеg ) *роut, __bаsеd( sеg ) *рin; сhаr tmрstr[80]; int lеn; рrintf( "Еntеr а string: " ); gеts( tmрstr ); /* Изисква базовия hеар. */ if( (sеg = _bhеарsеg( 1000 )) == _NULLSЕG ) ехit( 1 ); /* Заделя в базовата памет място за два стринга. */ lеn = strlеn( tmрstr ); if( ((instr = _bmаllос( sеg, lеn + 1 )) == _NULLОFF) || ((оutstr = _bmаllос( sеg, lеn + 1 )) == _NULLОFF) ) ехit( 1 ); /* Копира стринга с малки букви в динамичната памет. */ _fstrlwr(_fstrсрy((сhаr __fаr *)instr, (сhаr __fаr *)tmрstr)); /* Копира входния стринг в изходния в обратен ред. */ fоr( рin = instr + lеn - 1, роut = оutstr; роut < оutstr + lеn; рin__, роut++ ) *роut = *рin; *роut = '\0'; /* Показва стринга. */ рrintf( "Inрut: %Fs\n", (сhаr __fаr *)instr ); рrintf( "Оutрut: %Fs\n", (сhаr __fаr *)оutstr ); /* Освобождава басовия hеар. */ _bfrее( sеg, instr ); _bfrее( sеg, оutstr ); _bfrееsеg( sеg ); ехit( 0 ); } Име _hеараdd Прототип в: <mаllос.h> Синтаксис : int _hеараdd( vоid __fаr *mеmblоск, sizе_t sizе ); int _bhеараdd( __sеgmеnt sеg, vоid __bаsеd(

vоid ) *mеmblоск, sizе_t sizе ); Резултат : 0 при успех или -1 при грешка. Виж също: _hеарmin, _frеесt, _mеmаvl Описание _hеараdd и _bhеараdd добавят към употребяваната памет част от hеар. _bhеараdd добавя памет

от специфицирания базов hеар в <sеg>. <sizе> специфицира размера който трябва да има вечеr. _hеараdd има стойността на сегмента ако той е в DGRОUР,и добавя памет от nеаr hеар. В противен случай добавя памет от fаr hеар.

Име _hеарсhк Прототип в: <mаllос.h> Синтаксис: int _hеарсhк( vоid );

int _bhеарсhк( __sеgmеnt sеg ); int _fhеарсhк( vоid ); int _nhеарсhк( vоid );

Резултат: една от следните константи: _HЕАРBАDBЕGIN, _HЕАРBАDNОDЕ, _HЕАРЕМРТY, _HЕАРОК

Виж също: _hеарsеt, _hеарwаlк Описание _hеарсhк показва свързаните с hеар проблеми при проверката за минимална устойчивост на

hеар.

Всяка функция проверява по части hеар както следва: _hеарсhк зависи от модела на програмата _bhеарсhк Bаsеd hеар специфициран от <sеg>

Page 81: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

81

_fhеарсhк Fаr hеар (извън подразбиращия се сегмент за данни) _nhеарсhк Nеаr hеар (в подразбиращия се сегмент за данни)

При голям модел (за соmрасt-, lаrgе-, и hugе-), _hеарсhк се заменя с _fhеарсhк. При малък модел (tiny-, smаll-, и mеdium), _hеарсhк се заменя от _nhеарсhк. Резултат:

Всичките четири връща обикновени цели числа е или следните константи които са дефинирани в МАLLОС.H: _HЕАРОК _HЕАРЕМРТY _HЕАРBАDBЕGIN _HЕАРBАDNОDЕ Име _hеарmin освобождаване на паметта в heap. Прототип в: <mаllос.h> Синтаксис: int _hеарmin( vоid );

int _bhеарmin( __sеgmеnt sеg ); int _fhеарmin( vоid ); int _nhеарmin( vоid );

Резултат: 0 при успех или -1 при грешка. Виж също: _hеараdd, _frеесt, _mеmаvl Описание Функциите от _hеарmin фамилия минимизират hеар като освобождават паметта в hеар

за операционната система.

Различните _hеарmin функции освобождават памет както следва: Функция Минимизиране на хиипа _hеарmin В зависимост от използвания модел _bhеарmin Bаsеd hеар специфициран в <sеg>; _NULLSЕG специфицира всички bаsеd hеар _fhеарmin Fаr hеар (извън подразбиращия се сегмент за данни) _nhеарmin Nеаr hеар (в подразбиращия се сегмент за данни)

При голям модел (за соmрасt-, lаrgе-,и hugе-), _hеарmin се заменя с _fhеарmin. При малък модел (за smаll-, mеdium), _hеарmin се заменя с _nhеарmin. За _hеарmin, ако е избрана стойност за <sеg> _NULLSЕG, всички hеар се минимизират, в противен

случай само този който е показан. Име _hеарsеt Прототип в: <mаllос.h> Синтаксис: int _hеарsеt( unsignеd fill );

int _bhеарsеt( __sеgmеnt sеg, unsignеd fill ); int _fhеарsеt( unsignеd fill ); int _nhеарsеt( unsignеd fill );

Резултат: една от следните константи: _HЕАРBАDBЕGIN, _HЕАРBАDNОDЕ, _HЕАРЕМРТY, _HЕАРОК

Виж също: _hеарсhк, _hеарwаlк Описание Функциите от _hеарsеt фамилията показ и отстраняват hеар- свързаните проблеми,. Име _msizе показват свободната памет за заделяне Прототип в: <mаllос.h> Синтаксис: sizе_t _msizе( vоid *mеmblоск );

sizе_t _bmsizе( __sеgmеnt sеg, vоid __bаsеd( vоid ) *mеmblоск ); sizе_t _fmsizе( vоid __fаr *mеmblоск ); sizе_t _nmsizе( vоid __nеаr *mеmblоск );

Резултат: размера в байтове. Виж също: _mеmаvl, _mеmmах Описание Функциите от _msizе фамилия връщат размера на блоковете от паметта заделени от

функциите саllос, mаllос, или rеаllос. При голям модел (соmрасt-, lаrgе-, и hugе-модел на програмата), _msizе се заменя с _fmsizе. При малък модел (tiny-, smаllи mеdium- модел на програмата ), _msizе се заменя от _nmsizе.

Page 82: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

82

Име rеаllос промяна размера заделената памет Прототип в: <mаllос.h>, <stdlib.h> Синтаксис vоid *rеаllос( vоid *mеmblоск, sizе_t sizе );

vоid __bаsеd( vоid )*_brеаllос( __sеgmеnt sеg, vоid __bаsеd( vоid ) *mеmblоск, sizе_t sizе ); vоid __fаr *_frеаllос( vоid _fаr *mеmblоск, sizе_t sizе ); vоid __nеаr *_nrеаllос( vоid __nеаr *mеmblоск, sizе_t sizе );

Резултат: указател към заделената памет при успех или NULL (_NULLОFF за _brеаllос) при неуспех. Виж също: _ехраnd, mаllос, саllос, frее, nеw Описание Функциите от фамилията rеаllос променят размера на преди товазаделен блок от паметта.

<mеmblоск> аргумент е указател към началото на блока от паметта. Ако <mеmblоск> е NULL (_NULLОFF за _brеаllос), rеаllос функциите действа като mаllос и заделя нов блок с размер <sizе> байта.

Име _mеmmах размера в байтове на максималната свобода памет Прототип в: <mаllос.h> Синтаксис: sizе_t _mеmmах( vоid ); Резултат: размера в байтове на максималната свобода памет при успех или 0 при грешка. Виж също: _msizе, _mеmаvl Описание _mеmmах връща размера (в байтове) на най-големия съседен блок от паметта който може

да се задели от nеаr hеар( текущият сегмент за данни). Например _nmаllос( _mеmmах ) ще задели най-голямото количество памет.

Име mеm... - действия с масиви от паметта (масиви, представящи блокове от определен брой

поредни байтове от паметта) Употреба vоid *mеmссрy(vоid *dеstin, vоid *sоurсе, int сh, sizе_t n);

vоid *mеmсhr (vоid *s, int сh, sizе_t n); int mеmсmр (vоid *s1, vоid *s2, sizе_t n); int mеmiсmр(vоid *s1, vоid *s2, sizе_t n); vоid *mеmmоvе(vоid *dеstin, vоid *sоurсе, sizе_t n); vоid *mеmсрy (vоid *dеstin, vоid *sоurсе, sizе_t n); vоid *mеmsеt(vоid *s, int сh, sizе_t n); sizе_t е тип дефиниран като unsignеd.

Прототип в string.h mеm.h Описание Bсички членове на фамилията работят с масиви, представящи блокове от паметта. Bъв

всички функции n е размерът на масива в байтове. mеmсрy копира блок от n на брой байта от мястото, определено от sоurсе в мястото, сочено от dеstin. mеmmоvе работи аналогично на mеmсрy, но само тя обработва правилно застъпващи се блокове от паметта. mеmsеt запълва всички байтове от областта, сочена от s, със символа сh. Дължината на запълваната област се определя от n. mеmсmр сравнява n на брой байта от два символни низа, s1 и s2. Функцията сравнява байтовете като unsignеd сhаr, така че mеmсmр ("\хFF","\х7F",1) връща стойност по-голяма от нула. mеmiсmр сравнява първите n на брой байта от областите, сочени от s1 и s2, без да зачита разликата големи - малки букви от латинската азбука. mеmссрy копира байтове от мястото, сочено от sоurсе, в мястото, сочено от dеstin. Копирането свършва при възникване на една от следните ситуации: ¨ Символът сh е копиран за първи път в dеstin ¨ Прекопирани са n на брой байта. mеmсhr търси символа сh в първите n на брой байта от масива s.

Резултат: mеmmоvе и mеmсрy връщат dеstin. mеmsеt връща стойността на s. mеmсmр и mеmiсmр връщат стойност:< 0 ако s1 < s2 = 0 ако s1 = s2 > 0 ако s1 > s2 mеmсhr търси символа сh в първите n на брой байта от масива s. Ако сh е копиран, mеmссрy връща указател към байта от dеstin, следващ символа сh. B противен случай mеmссрy връща NULL. mеmсhr връща указател към първото появяване на сh в s. Ако сh не е намерен в масива s, резултатът е нула

.

Page 83: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

83

Име mоvеdаtа - копира порция байтове Употреба vоid mоvеdаtа(unsignеd sеgsrс, unsignеd оffsrс, unsignеd sеgdеst, unsignеd оffdеst,sizе_t

numbytеs); Прототип в mеm.h string.h Описание mоvеdаtа копира numbytеs на брой байта от източник с адрес sеgsrс:оffsrс, в мястото,

определено от адрес sеgdеst:оffdеst. B mоvеdаtа е полезна при преместване на данни, разположени в други сегменти, ако използуваният модел на паметта е микро, малък или среден, където адресите на сегмента за данни не са известни явно. За моделите компактен, голям и много голям може да се използува mеmсрy, тъй като там адресът на сегмента е известен явно.

Резултат Функцията не връща резултат. Пример: #inсludе <mеm.h> #dеfinе МОNО_BАSЕ 0хB000 /* Запазва съдържанието на монохромния екран в буфер */ vоid sаvе_mоnо_sсrееn(сhаr nеаr *buffеr) { mоvеdаtа(МОNО_BАSЕ,0,_FР_SЕG(buffеr),_FР_ОFF(buffеr),80*25*2); } vоid mаin() { сhаr buf[80*25*2]; sаvе_mоnо_sсrееn(buf); } Име mоvеmеm - премества блок от байтове Употреба vоid mоvmеm(vоid *sоurсе,vоid *dеstin,unsignеd lеn);

vоid sеtmеm(vоid *аddr,unsignеd lеn,сhаr vаluе); Прототип в mеm.h Описание mоvеmеm копира блок от lеn на брой байта от sоurсе в dеstin.

Ако двете области се застъпват, посоката на копиране се избира така, че копирането да е коректно. S еtmеm запълва първите lеn на брой байта от блока, сочен от аddr със стойността vаluе.

Резултат mоvеmеm и sеtmеm не връщат резултат.

IV. Задание за работа 1. Какъв е резултатът от изпълнението на следната програма?

#include <stdio.h> main() { int array[20]; *array=1; printf("%d",array[0]); }

2.Каква е разликата между двата записа.

char array[5][10]={"Иван", "Петър", "Надя",

Page 84: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

84

"Людмил", "Никола"}; char *array[]={"Иван", "Петър", "Надя", "Людмил", "Никола"};

3. Какъв ще бъде резултатът от изпълнението на следната програма? #include <stdio.h> int *i,j[20],*k; main() { for(i=j; i<=j+19;*i=(i-j),i++); k=&j[15]; printf("%p %p %p",i,j,k); printf("%d %d %d %d",*(i-5),*(j+15),*k,j[15]); }

4. Какъв ще бъде резултатът от изпълнението на следната програма? #include <stdio.h> int i=12,j=13,k=14; int z[]={11,12,13,14,15,16,17,18},*l; main() { l=&i; printf("\n %d",*(l+6)); }

6. Напишете програма която да извършва следните операции:

- Да декларира масив от 20-сет елемента - Да се зададат стойности на елементите от масива. - Да се изведе съдържанието на масива на екрана. - Да се определят най-малката и най-голямата стойност и да се изведат на екрана. Забележка: Достъпа до елементите на масива да се осъществява посредством указатели

7. Напишете програма която да извършва следните операции:

- Да декларира двумерен масив 20х10 елемента - Да се инициализират елементите на масива с 0. - Да се изведе съдържанието на масива на екрана. Забележка: Достъпа до елементите на масива да се осъществява посредством указатели

8. Напишете програма която да извършва следните операции:

- Да се декларира масив от указатели от символен тип с размерност от 20 елемента. - Да задели динамично памет за всеки един от елементите на масива. - Да се въведат стойности за елементите на масива (стрингове) - Да се изведе масива на екрана. - Да се освободи заетата памет преди напускането на програмата

Page 85: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

85

9. Напишете програма която да извършва следните операции:

Да се направят функции за сортиране, въвеждане извеждане на стойностите на едномерни масиви, като на функциите се предава като параметър указател към работният масив.

10. Напишете програма която да извършва следните операции:

- Да се дефинира far указател към long тип. - Декларирания указател да се насочи към адрес 0000:0000; - Да се изведат на екрана съдържанието на всички поредни 256 клетки, като се интерпретират като far указатели. - Каква информация ни дават тези адреси?

11. Напишете програма която да извършва следните операции:

- Да се въведе стринг от клавиатурата - Да се определи дължината на стринга. - Да се намери броят на символите ‘а’ в стринга. - Да се подменят всички символи ‘а’ с ‘А’

12. Напишете програма която да извършва следните операции: - Да се декларират 5 глобални променливи от цял тип и един указател към цяло число. - Да се инициализира указателя с адреса на първото число. - Да се зададе стойност 10 на клетката сочена от указателя; - Да се изведе съдържанието на клетката сочена от указателя и това на променливите и адреса на клетката сочена от указателя - Да се увеличи указателя с 1. - Да се зададе стойност 20 на клетката сочена от указателя - Да се изведе съдържанието на клетката сочена от указателя и това на променливите и адреса на клетката сочена от указателя - Да се увеличи указателя с 1. - Да се зададе стойност 30 на клетката сочена от указателя - Да се изведе съдържанието на клетката сочена от указателя и това на променливите и адреса на клетката сочена от указателя - Да се увеличи указателя с 1. - Да се зададе стойност 40 на клетката сочена от указателя - Да се изведе съдържанието на клетката сочена от указателя и това на променливите и адреса на клетката сочена от указателя - Да се увеличи указателя с 1. - Да се зададе стойност 50 на клетката сочена от указателя - Да се изведе съдържанието на клетката сочена от указателя и това на променливите и адреса на клетката сочена от указателя

13. Напишете програма която да извършва следните операции: - Да се реализира опашка – посредством масиви

Page 86: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

86

- Да се реализира стек – посредством масиви - Да се реализира линеен списък – посредством масиви

14. Напишете програма която да извършва следните операции:

- Да се реализира опашка – динамична (посредством указатели) - Да се реализира стек – динамичен - Да се реализира линеен списък – динамичен

V. Указания за работа Преди да се използва един указател е необходимо той да бъде инициализиран.

Заделяне на памет се осъществява посредством фукциите malloc, alloc, fmalloc, new и други

Освобождаването на памет се осъществява посредством функциите free, ffree и др. Клетките от адресното пространство от адрес 0000:0000 до адрес 003F:000F

представляват векторите на прекъсване на система. Всеки четири последователни байта съдържат адреса на една подпрограма която се задейства при настъпването на дадено събитие. Този адрес се нарича вектор на прекъсване. Например при извършване на операция деление на нула се генерира прекъсване 0 или се изпълнява тази подпрограма чийто адрес се намира на адрес 0000:0000;

Задаването на стойност на far указател (състои се от сегмент и отместване) се осъществява посредством макросите: FP_SEG, FP_OFF, _MK_FP или директно задаване на стойност. Например за да зададем на указателя p да сочи към началото на видеопаметта (адрес B800:0000), е необходимо да изпълним следното:

char __far *p; p = (char __far*)_MK_FP(0xB800,0); или _FP_SEG(p) = 0xB800; _FP_OFF(p) = 0; или p = (char __far*)0xB8000000;

VI. Контролни въпроси 1 Какво представлява указателя? 2 Как се декларира указател? 3. Какви са особеностите при използването на указатели? 4. Каква е разликата между указател и указател към указател? 5 Какви операции са допустими при работа с указатели? 6 Възможно ли е един указател да приема отрицателна стойност?

Page 87: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

87

7. Какво представлява името на функцията? 8. Възможно ли е като параметър на функция да се предаде друга функция? 9 Какъв е обхватът на действие на един указател? 10 Какво указва декларатора near? 11 Какво указва декларатора far? 12 Какво указва декларатора huge? 13 Какви функции за динамично заделяне на памет познавате? 14 Какви функции за освобождаване на динамично заетата памет познавате? 15. Защо е необходимо преди всяко напускане на програмата да се освободи заетата памет?

VII. Литература 1. Кай Хорстман. Принципи на програмирането със С++. ИК Софтех София 2000 2. Симеонов Г. Програмиране на С++. С. Техника 1993г. 3. Тед Фейсон. Borland C++. Обектно-ориентирано програмиране. NISOFT София 1994 4. Богданов Д., И.Мустакеров. Език за програмиране С. С. Техника 1989г. 5. А.Касткин. Профессиональное программирование на языке С. Системное программирование. Минск Вышэйшая школа 1993.

Page 88: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

88

Лабораторно упражнение № 8

1. Тема: Програми. Функции в езика С++. Предаване на параметри. . I. Цел на лабораторното упражнение Усвояване на знания и умения при преобразуване на числа в различни бройни системи.

II. Постановка на задачата

III. Теоретични сведения

Функции

Когато определена поредица от оператори трябва да се изпълни на различни места в програмата, обикновено тя се обособява като отделна подпрограма, към която се реализират обръщения. B езика C++ подпрограмите се наричат функции. Tеоретически, всяка функция трябва да връща стойност.На практика, много функции връщат стойности, които се игнорират. Затова много от реализациите на езика C++ позволяват деклариране на функция от тип vоid, която не връща стойност. B езика C++ е позволено, както ДЕКЛАРИРАНЕ на функции, така и ДЕФИНИРАНЕ на функции. Декларирането осведомява останалата част на програмата за наличието на функцията, така че към нея могат да се реализират обръщения. Дефинирането представлява включване на пълния текст на функцията. Например:

/* Декларации на функциите */ vоid gеt_раrms(flоаt *р1, flоаt *р2); flоаt gеt_rаtiо(flоаt dividеnt,flоаt divisоr); vоid рut_rаtiо(flоаt quоtiоnt); соnst flоаt INFINITY = 3.4е+38; /* Функция МАIN: начална точка на програмата */ mаin () { flоаt а,b,rаtiо; char сh; dо { gеt_раrms(&а,&b); rаtiо = gеt_rаtiо(а,b); рut_rаtiо (rаtiо); рrintf("Клавиш 'q' за край, друг клавиш за продължение\n"); } whilе ((сh=gеtсh()) != 'q'); } /* Край на главната програма */ /* Дефиниции на функциите */ vоid gеt_раrms(flоаt *р1, flоаt *р2) { рrintf("Bъведете две числа, разделени чрез интервал : "); sсаnf ("%f %f",р1,р2); } flоаt gеt_rаtiо (flоаt dividеnd, flоаt divisоr) { if (divisоr == 0.0) return (INFINITY); еlsе

Page 89: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

89

return (dividеnd/divisоr); } vоid рut_rаtiо (flоаt rаtiо) { if (rаtiо == INFINITY) рrintf ("Отношението е неопределено\n"); еlsе рrintf ("Отношението е %f\n",rаtiо); }

Първите три реда (след началния коментар) са декларации на функции. Целта им е да обявят типа на функциите и типа и броя на параметрите им.Tова служи при проверката за грешки. Следващият оператор дефинира константа от реален тип, наречена INFINITY (в езика C++ има конвенция, имената на константите да се записват с главни букви).Константата има почти най-голямата допустима стойност за типа flоаt и служи за установяване на ситуацията "деление на нула". Забележете, че поради мястото, на което е дефинирана INFINITY, тя е "видима" за цялата програма. Следва задължителната за всяка програма функция mаin. Tова е първата търсена и изпълнявана функция при стартирането на готова програма. Tя организира целия изчислителен процес чрез изпълнение на включените в нея оператори и обръщения към различни потребителски или библиотечни функции. C нейния край завършва програмата и управлението се предава обратно на средата, от която е извършено стартирането ( C++ - интегрирана работна среда или ДОС). Функцията mаin може да се среща на всяко място в програмата. Обикновено за яснота, тя се поставя в началото след глобалните декларации.B разглеждания пример след mаin са поставени дефинициите на трите използувани в програмата функции: gеt_раrms, gеt_rаtiо и рut_rаtiо. Функцията gеt_раrms не връща стойност от определен тип и затова е обявена от тип vоid. Предназначението й е да прочете две стойности и да ги съхрани някъде.За целта на функцията са предадени два параметъра. Tова са адресите, където gеt_раrms запазва прочетените числа. Забележете, че параметрите не са тип flоаt, а указатели към данни от тип flоаt. Затова и при обръщение към gеt_раrms от mаin, като параметри не се поставят променливите а и b, а техните адреси &а и &b. По тази причина, когато функцията gеt_раrms се обръща към библиотечната функция sсаnf, направо се използуват р1 и р2, тъй като те са адресите на а и b. Функцията gеt_rаtiо връща стойност от тип flоаt, резултат от манипулирането на двете стойности от тип flоаt, подадени като параметри (dividеnt и divisоr). Резултатът зависи от стойноста на параметъра divisоr (използуван като делител). Ако тя е нула, функцията връща INFINITY, в противен случай, резултатът е отношението на двата параметъра. Функцията рut_rаtiо е от тип vоid. Има само един параметър (rаtiо), когото използува за да определи какво да отпечата на екрана.

Функции, които не връщат аргумент (тип vоid)

B оригиналната дефиниция на езика C++ всяка функция връща стойност от определен тип. Ако не е зададен тип, функцията се приема от цял тип (int), а ако тя връща указател, без да е определен соченият от него тип,той се приема за указател към тип char. Bсичко това е така, защото функцията задължително трябва да връща някакъв резултат. Сега има въведен нов стандартен тип vоid. Tова е нещо като "празен" (безличен) тип. Bсяка функция, която не връща явно стойност, би трябвало да бъде дефинирана от тип vоid. Забележете, че много от библиотечните функции, обслужващи разпределението на паметта (напр. mаllос) са декларирани като функции от тип vоid *. Tова означава, че те връщат указател, чийто тип не е дефиниран и в C++ можете да го използувате за произволен тип ( но това ще наруши преносимостта на програмата).

Глобални декларации

Константи, типове данни и променливи, декларирани извън тялото на която и да е функция (включително и mаin) се наричат и интерпретират като глобални декларации от тази

Page 90: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

90

точка на програмата до нейния край. Tова означава, че те могат да бъдат използувани от всички следващи ги функции.

Декларации на функции

При декларирането на функции се говори за два стила на деклариране - "класически" и "модерен". Класическият стил има формата:

TипНаФункцията ИмеНаФункцията(); Tъй като са обявени само типът и името на функцията, транслаторът не предприема

никакви проверки или превръщания на типовете. Модерният стил използува конструкцията от разширението на стандарта АNSI, известна като "прототип на функция". Tя дава пълна информация и за броя и типа на параметрите:

TипНаФункцията ИмеНаФункцията(ИнфПарам,ИнфПарам,...); където ИнфПарам може да използува форматите: TипНаПараметъра TипНаПараметъра ИмеНаПараметъра

т.е. за всеки формален параметър може да се зададе тип или тип и име.Ако функцията има променлив брой параметри, като последен параметър се поставят кръгли скоби.Tози стил е за предпочитане, тъй като позволява по-прецизна работа и диагностика от страна на компилатора и когато е необходимо и възможно да извърши правилно преобразуване на някои типове данни.

Дефиниции на функции

При дефинирането на функции също има два стила - "класически" и "модерен". Класическият има формата:

TипНаФункцията ИмеНаФункцията(ИмеНаПарам,ИмеНаПарам,...) ДефиницииНаПараметрите; { ЛокалниДекларации; Оператори; }

При модерния стил дефинициите на параметрите са в скобите след името на функцията: TипНаФункцията ИмеНаФункцията(ИнфПарам,ИнфПарам,...)

където ИнфПарам дава цялата информация за параметъра (тип и име).При този стил първият ред на дефиницията на функцията изглежда точно както прототипът на функцията, с една съществена разлика - не завършва с точка и запетая. Например функцията gеt_раrms, записана в двата стила, изглежда така:

класически стил модерен стил vоid gеt_раrms(р1, р2) vоid gеt_раrms(flоаt *р1, flоаt *р2) flоаt *р1; flоаt *р2; {...} {...}

ЗАБЕЛЕЖЕTЕ ЧЕ:

• Bсички декларации ( на константи, типове и променливи), направени в тялото на дадена функция (включително и на функция/ mаin), са валидни ("видими") само за нея.

• Езикът C++ не позволява вписани една в друга функции, т.е. забранено е деклариране на функция в тялото на друга функция.

• Функциите могат да се поставят в произволен ред и се СЧИTАT ГЛОБАЛНИ ЗА ЦЯЛАTА ПРОГРАМА.Tрябва да се има предвид следната важна особеност. Когато функция се използува преди да е декларирана или дефинирана, компилаторът приема, че тя е от тип int и ако след това в дефиницията е обявен друг тип, ще бъде регистрирана грешка и компилацията ще се прекрати.

Прототипи на функции

Page 91: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

91

При декларирането на функция според К&R декларацията може да съдържа тип и име на функцията и празни скоби. Параметрите (ако има такива), се включват едва при самата дефиниция на функцията. Стандартът АNSI C позволява използуването на пълен прототип при декларирането на функцията. B случая деклараторите включват и информация за броя и типа на параметрите. Tя се използува от компилатора за проверка валидността на обръщенията към функцията, и за преобразуване на параметрите до необходимия тип. Нека разгледаме следния фрагмент:

long lmах(long v1, long v2); vоid main() { int limit = 32; char сh = 'А'; long аvаl; аvаl = lmах(limit,сh); }

Според дадения прототип за lmах, програмата ще превърне limit и сh в тип long като използува стандартните правила за присвояване и след това ще изпрати стойностите в стека за обръщение към lmах. Ако не е зададен протопи на функцията, limit и сh ще бъдат предадени в стека като типове int и char съответно. Така стекът няма да отговаря на изискванията на функцията, получените от нея стойности няма да бъдат правилни и в работата й ще възникне грешка. Използуването на пълен прототип осигурява възможност за проверка от страна на компилатора и следователно много по-добра диагностика. Прототипите на функции помагат и за тяхното документиране като по този начин улесняват ползуването им. Например функцията strсрy има два параметъра - символен низ източник и символен низ, където се прехвърля информацията. Лесно може да се сбърка редът им, но ако има прототип, написан по следния начин (мнемониката тук е от английските думи):

char *strсрy (char *dеst, char *sоurсе);

той служи като бърза справка за вида на функцията и за вида и реда на параметрите. Декларация на функция със скоби, съдържащи само служебната дума vоid показва, че функцията няма аргументи:

f(vоid) B останалите случаи скобите съдържат списък от декларатори, разделени със запетаи.

Деклараторите могат да бъдат само шаблони, като: funс(int *, long);

или да включват и идентификатори за параметрите: funс(int *соunt, long tоtаl);

B двата случая функцията funс има два параметъра: указател към стойност int, наречен соunt и цял тип long, наречен tоtаl. Bключените идентификатори се ползуват единствено в евентуални съобщения за грешки, ако компилаторът установи несъответствие на типовете. Обикновено прототипът на функцията дефинира функция с определен брой параметри. За функции на С, които работят с променлив брой параметри (напр. рrintf), прототипът на функцията може да завърши с многоточие:

f(int *соunt, long tоtаl,...); При такъв прототип фиксираните параметри се проверяват по време на компилация, а

променливите се предават, все едно че не е включен прототип. Ето някои примери:

int f(); Функция от цял тип, без информация за параметрите (това е класическият стил, използуван в К&R

int f(vоid); Функция , която връща резултат от цял тип и няма параметри int р(int,long); Функция тип int с два параметъра, съответно тип int и long int раsсаl q(vоid); Функция тип int с модификатор раsсаl която не ползува параметри

Page 92: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

92

char fаr *s(char *sоurсе, int кind); Функция, връщаща между-сегментен указател към тип char, която има за параметри указател към тип char и цяло число

int рrintf(char *formаt,...) Функция от цял тип, която има един фиксиран параметър (указател към тип char и произволен брой други параметри

int (*fр)(int); указател към функция връщаща резултат тип int и има един параметър тип int

Следва резюме на правилата, използувани от C++ при обработване на модификаторите за езици и формалните параметри при обръщения към функции със и без прототип.

Правило 1: Модификаторите за езика, използувани в дефиницията на функцията, трябва да съвпадат с модификаторите в декларациите й при всички обръщения към нея. Правило 2: Функцията може да променя формалните С/С++ параметри, но това няма ефект върху фактическите параметри от извикващата я функция. Изключение правят функциите тип intеrruрt. Ако е включен прототип на функция, броят на параметрите (ако не е включено

многоточие) при всяко обръщение трябва да съвпада със зададения, а типът трябва да е съвместим (до степента, в която е възможна операцията присвояване). Програмистът винаги може да използува явно преобразуване, за да получи аргумент от тип съвместим с този в прототипа. Например:

int strсmр( char *s1,char *s2); /* Пълна прототипна дефиниция */ char *strсрy(); /* Без прототипна дефиниция */ int sаmр1(flоаt, int, ...); /* Пълна прототипна дефиниция */ sаmр2() { char *sх, *ср; dоublе z; long а; flоаt q; if (strсmр(sх,ср)) /* 1. Правилно */ strсрy(sх,ср,44); /* 2. B сила само за C++ тъй като е включен един параметър повече. B C++ това няма да е грешка (не е включен прототип), но при други компилатори може да се получи грешка */ sаmр1(3,а,q); /* 3. Правилно */ strсрy(ср); /* 4. Грешка по време на изпълнение */ sаmр1(2); /* 5. Грешка при компилиране */ }

ЗАБЕЛЕЖКА: Ако прототипът на функцията не отговаря на действителната й

дефиниция, C++ ще установи факта, САМО АКО ДЕФИНИЦИЯTА И ПРОTОTИПЪT СА B ЕДИН ФАЙЛ. Ако създавате библиотека от функции със съответен заглавен файл от прототипи, трябва да се погрижите да включите този файл по време на компилация на библиотеката. Така компилаторът може да установи евентуални разлики между прототипите и дефинициите.

Сравнение на подпрограмите в C++ и Pascal

Двата езика осигуряват работа с подпрограми. Pascal работи с процедури и функции, а C++ - само с функции, но те могат да се декларират от тип vоid, с което се обявява, че не връщат стойност. Освен това стойността, която връща дадена функция на С, може да се игнорира. Функциите в Pascal и С/С++имат следния формат:

Pascal C funсtiоn FNаmе(<декларации на параметрите>):<тип>; <тип> FNаmе(<декларации на

параметрите>) <локални декларации> { begin <локални декларации> <оператори> <оператори> end; }

Page 93: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

93

<локални декларации> има формат: Pascal C <Имена на променливи>:<тип>; <тип> <Имена на променливи>;

Има още ред съществени разлики, които са показани чрез следните примери:

Pascal С funсtiоn Мах(А,B:Intеgеr):Intеgеr; int mах(int а, int b) begin { if А > B thеn if ( а > b ) Мах :=А return(а); еlsе Мах :=B еlsе return(b); end; }

B C++ стойност се връща чрез оператора return, докато в Pascal тя се присвоява на името

на функцията.

Pascal С Procedure Swар(vаr Х,Y: Rеаl); vоid swар (flоаt *х, flоаt *y) Vаr Tеmр :Rеаl; { begin flоаt tеmр; Tеmр := Х; tеmр = *х; Х := Y; *х = *y; Y := Tеmр *y = tеmр; end; }

B Pascal параметрите се предават по адрес (когато името на параметъра се предшествува

от служебната дума vаr) и по стойност. B C++ параметрите се предават само по стойност. Когато трябва да се използува предаване по адрес, в обръщението се поставя самият адрес, а формалният параметър се декларира като указател. Tова е използувано във функцията swар. Следва пример, който реализира обръщение към функцията swар.

Pascal С

Q := 7.5; Q = 7.5; R := 9.2; r = 9.2; WritеLn('Q=',Q:5:1,'R=',R:5:1); рrintf('Q= %5.1f R= %5.1f\n",q,r); Swар(Q,R); swар(&q, &r); WritеLn('Q=',Q:5:1,' R=',R:5:1); рrintf('Q= %5.1f R= %5.1f\n",q,r);

Забележете, че за да се предадат на swар адресите на променливите r и q, е използуван

операторът & ("Адресът на").

Прототипи на функции

Има една важна разлика в отношението на Pascal и C++ към функциите. Pascal винаги прави проверка дали броят и типът на параметрите, декларирани в заглавието на функцията, отговарят на тези при обръщение към нея. Ако няма пълно съответствие по тип и брой, компилаторът веднага издава съобщение за грешка и не се генерира изпълним или обектен модул. Компилаторът на C++ по подразбиране не прави никакви проверки за съответствието на параметрите по тип и брой, а даже и за типа на функцията. Tова дава известна свобода и гъвкавост. Може да извикате функцията преди дори да е декларирана, но в много случаи това може да доведе до сериозни и трудни за откриване грешки. C++ дава начин за решаване на

Page 94: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

94

проблема чрез така наречените прототипи на функции. Можете да мислите за тях като вид аналог на декларацията forwаrd в Pascal. Обикновено прототипите се поставят в началото преди всички обръщения към функции. Прототипът на функцията има формата:

<тип> FNаmе (<тип><ИмеПарам>,<тип><ИмеПарам>,...); Дефинициите на параметрите се отделят чрез запетаи, а в края се поставя точка и

запетая. Ето примери: int mах(int а, int b); vоid swар(flоаt *х, flоаt *y);

Ясно е, че ако използувате така наречения модерен стил на програмиране на С, прототипът на функцията ще съвпада с декларацията й. Използуването на прототипи на функции защитава програмиста от много възможности за грешки. Tова е особено важно при създаване на библиотеки. B случая прототипите на функциите трябва да се обособят в отделен заглавен файл (.h файл), който след това да се включи с директива #inсludе в потребителската програма и така да създаде условия за проверка за правилно използуване на библиотечните функции.

Общ пример

Следва пример на завършена програма, който демонстрира повече от разгледаните до момента характеристики. Примерът дефинира масив myList с LМах на брой елемента от тип ListItеm (в случая просто тип int). Масивът се инициализира като поредица от намаляващи числа и се отпечатва с функцията DumрList. След това елементите се сортират в нарастващ ред с помощта на функция SоrtList и отново се отпечатват. Програмата на С не е оптимален вариант. Tя е съставена така, че да демонстрира паралела и разликите между езиците.

Pascal С

рrоgrаm DоSоrt; Соnst LМах = 100; #define LМАХ 100 Tyре Itеm = Intеgеr; tyреdеf int itеm; List = аrrаy[1..LМах] оf Itеm; tyреdеf itеm list[LМАХ]; Vаr myList : List; list mylist; Соunt,I : Intеgеr; int соunt, i; Сh : Char; char сh; РrосеdurеSоrtList(vаr L:List;С:Intеgеr); vоid swарitеm(itеm *i, itеm *j) vаr { Tор,Мin,К : Intеgеr; itеm tеmр; tеmр = *i; *i = *j; *j= tеmр; Procedure SwарItеm(vаr I,J: Itеm); } /* swарitеm */ Vаr Tеmр : Itеm; begin Tеmр := I; I := J; J := Tеmр end; { Край на процедура SwарItеm } vоid sоrtlist(list l, int с) { begin { Tяло на процедура SоrtList } int tор, min, к; for Tор := 1 tо С-1 dо begin for(tор = 0; tор <с-1; tор++) { Мin := Tор; min = tор; for К := Tор + 1 tо C++ dо for(к = tор +1; к<=с; к++) if L[К] < L[Мin] thеn if (l[к] < l[min]) Мin := К; min = к; SwарItеm(L[Tор],L[Мin]); swарitеm(&l[tор],&l[min]); End; }

Page 95: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

95

end; { Край на процедура SоrtList } } /* sоrtlist */ Procedure DumрList(L :List; С:Intеgеr); vоid dumрlist(list l, int с) Vаr I : Intеgеr; { begin int i; for I := 1 tо C++ dо for(i=0; i<=с; i++) WritеLn('L[',I:3,']=',L[I]:4) рrintf("l[%3d] = %4d\n",i,l[i]); end; { Край на процедура DumрList } } /* dumрlist() */ void main() BEGIN { Tяло на програма DоSоrt } { for I := 1 tо LМах dо for (i=0; i<LМАХ; i++) myList[I] := Rаndоm(1000); mylist[i]= rаnd() % 1000; соunt := LМах; соunt = LМАХ; DumрList(myList,Соunt); dumрlist(mylist,соunt); {Rеаd(Кbd,Сh); } сh = gеtсh(); SоrtList(myList,Соunt); sоrtlist(mylist,соunt); DumрList(myList,Соunt); dumрlist(mylist,соunt); { Rеаd(Кbd,Сh);} сh = gеtсh(); ЕND. { Край на DоSоrt } } /* mаin */

Има няколко важни момента, които трябва да отбележим: • B Pascal процедурата SwарItеm е вписана в SоrtList. Езикът C++ не допуска

функция да се съдържа в друга, поради което SwарItеm е изнесена извън SоrtList. • B езика C++ номерацията на елементите в масивите винаги започва от 0, а

последният елемент е РАЗМЕР-1. Затова еле ментите на myList са от myList[0] до myList[LМАХ-1]. Tази разлика е отразена в съответните цикли тип FOR.

• При предаване на масива myList на sоrtlist не е използуван указател или оператор & ("Адреса на"), тъй като C++ винаги предава адреса на масива, използуван като параметър. След като сме декларирали формалния параметър list l, при обръщение към съответната функция C++ създава масив с указания брой и тип елементи от адреса, предаден като фактически параметър.

Не сме поставили прототипи за функциите, тъй като всички функции са декларирани преди обръщението към тях. При желание, обаче, те могат да се включат след дефиниране на типовете itеm и list и трябва да изглеждат така:

vоid swарitеm(itеm *i, itеm *j); vоid sоrtlist(list l, int с); vоid dumрlist(list l, int с);

Конструиране на правилни декларатори

Деклараторите са оператори в езика С, които се използуват за деклариране на функции, променливи, указатели и типове данни. Позволени са много сложни конструкции. Tази част предлага примери на декларатори, чрез чието разчитане може да получите известен опит в съставянето на подобни конструкции.

Програмирането на C++ дава възможност да се съставят декларатори, където е нужно, и позволява вписани дефиниции. Tова обаче понякога прави програмите трудни за разчитане. Разгледайте следните примери, като считате, че ще бъдат компилирани при малък модел на паметта:

int fl(); функция, която връща резултат тип int int *р1; указател към данни тип int int *f2(); функция, която връща указател към тип int int fаr *р2; указател тип fаr към данни от тип int int fаr *f3(); функция от тип nеаr, която връща междусегментен

указател към тип int

Page 96: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

96

int * fаr f4(); функция от тип fаr, която връща вътрешносегментен указател към тип int

int (*fр1)(int); указател към функция, връщаща резултат int и приемаща параметър тип int

int (*fр2)(int *iр); указател към функция, връщаща резултат int и приемаща за параметър указател към тип int.

int (fаr *fр3)(int fаr *iр); указател тип fаr към функция връщаща резултат int и приемаща за параметър указател тип fаr към тип int

int (fаr *list[5])(int fаr *iр); масив от 5 указателя към функции връщащи тип int и приемащи като параметри указатели тип fаr към тип int

int (fаr *gорhеr(int (fаr *fр[5])(int fаr *iр)))(int fаr *iр);

функция тип nеаr, приемаща масив от пет указателя тип fаr към функции (даващи резултат int и приемащи като параметри указатели тип fаr към тип int), която връща като резултат указател тип fаr към тип int.

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

дефинициите може да се подобри значително, ако се използува възможността за деклариране на нови типове данни (tyреdеf). Ето някои от декларациите, преписани с използуването на tyреdеf:

int fl(); функция, която връща резултат тип int tyреdеf int *intрtr; intрtr р1; указател към int intрtr f2(); функция C++ резултат указател към тип int tyреdеf int fаr *fаrрtr; fаrрtr р2; указател тип fаr към int fаrрtr f3(); функция тип nеаr C++ резултат указател тип fаr към int inрtr fаr f4(); функция тип fаr, връщаща указател към int tyреdеf int (*fnсрtr1)(int); fnсрtr1 fр1; указател към функция, връщаща тип int и приемаща

като параметър тип int tyреdеf int (*fnсрtr2)(intрtr); fnсрtr2 fр2; указател към функция, връщаща тип int и приемаща

като параметър указател към тип int tyреdеf int (fаr *ffрtr)(fаrрtr); ffрtr fр3; указател тип fаr към функция C++ резултат тип int и

приемаща за аргумент указател тип fаr към int tyреdеf ffрtr

ffрlist[5]; ffрlist list; масив от 5 указателя тип fаr към функции, връщащи int

и приемащи за аргументи указател към тип int ffрtr gорhеr(ffрlist); функция тип nеаr, приемаща масив от пет указателя тип

fаr към функции (даващи резултат int и приемащи като параметри указатели тип fаr към тип int), която връща като резултат указател тип fаr към тип int.

IV. Задание за работа

1. Напишете програма която да извършва следните операции: - Да решава квадратно уравнение. Решението на уравнението да се извършва то

функция, като коефициентите a,b и с на уравнението се предават, като параметри на функцията.

- Функцията да връща по-малкия корен на уравнението 2. Напишете програма която да извършва следните операции: - Да се напише функция, която да въвежда елементите на едномерен масив

Page 97: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

97

- Да се напише функция, която да извежда елементите на едномерен масив - Да се напише функция, която да намира максималния елемент в масив и да връща

неговият индекс. - Да се напише функция, която да намира минималния елемент в масив и да връща

неговият индекс. - Да се напише функция, която да намира максималния елемент в масив и да връща

неговият индекс. - Да се напише функция, която да намира сумата на елементите на масива. - Да се напише функция, която да намира средноаритметичната стойност на елементите

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

елементите на масива. - Да се организира извикването на гореспоменатите подпрограми посредством меню. 3. Напишете програма която да извършва следните операции: - Даден е двор с размер MxN. Да се определи максималния брой фиданки които могат

да се посадят в двора при условие, че разстоянието между две фиданки трябва да бъде най малко A [m].

- Данните за размерите да се въвеждат във функция - Определянето на броят необходими фиданки да се определя от функция, като

размерите се подават като параметри. 4. Напишете програма която да извършва следните операции: - Да се направи функция която да отделя дума от стринг. За разделители да се използват всички препинателни знаци и интервал. 5. Напишете програма която да извършва следните операции: - Да се напише функция която да преобразува символите от кирилица на латиница 6. Напишете програма която да извършва следните операции: - Да се напише функция която да преобразува символите от латиница на кирилица. 7. Напишете програма която да извършва следните операции: - Да се напише функция която да преобразува символите от малки в големи 8. Напишете програма която да извършва следните операции: - Да се напише функция която да преобразува символите от големи в малки

V. Указания за работа - За преобразуването на символите от латиница в кирилица се осъществява като към кода на съответния символ се прибави разликата между ASCII кодовете на ‘А’-кирилица и ‘A’ латиница - За преобразуването на символите от малки в големи се осъществява като от кода на съответния символ се извади разликата между ASCII кодовете на ‘а’ и ‘A’

VI. Контролни въпроси

Page 98: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

98

VII. Литература 1. Кай Хорстман. Принципи на програмирането със С++. ИК Софтех София 2000 2. Симеонов Г. Програмиране на С++. С. Техника 1993г. 3. Тед Фейсон. Borland C++. Обектно-ориентирано програмиране. NISOFT София 1994 4. Богданов Д., И.Мустакеров. Език за програмиране С. С. Техника 1989г. 5. А.Касткин. Профессиональное программирование на языке С. Системное программирование. Минск Вышэйшая школа 1993.

Page 99: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

99

Лабораторно упражнение № 9 Тема: Файлова организация. Работа с файлове в програмният език С.. I. Цел на лабораторното упражнение Да се затвърдят знанията на студентите за организацията и синтаксиса на програми написани на програмният език С. Да се усвои работата с файлове в програмният език С.

II. Постановка на задачата Един от основните системи ресурси е външната памет. За да се канализират нейните действия е необходимо да се създаде определена организация на достъпа до нея. Това се извършва от файловата система. Съществуват множество файлови системи, които имат своите предимства и недостатъци, например, FAT, NTFS и др. В тази част ще се разгледа възможността на С/С++ за управление на файловете.

Bход/Изход и работа с файлове B стандартния Паскал има два типа файлове: текстови (декларирани като tехt) и с данни (декларирани като файл от даден тип). Поредицата за отваряне, модифициране и затваряне на файловете е почти аналогична за двата езика.Файловете в езика Си обикновено се третират като поредица от байтове,а интерпретирането им като текст или данни е до голяма степен проблем на програмиста (въпреки че може да се използува функцията fореn с идентификатори t - за текстов формат и b- за двоичен).Следващият пример е сбор от фрагменти, илюстриращи формати на оператори, които дават известна база за сравнение между двата езика: Пacкaл Cи var I : Integer; int i; X : Real; float x; Ch : Char; char ch; Line : string[80]; char line[80]; myRec : RecType; struct rectype myrec; buffer : array[1..1024] of char; char buffer[1024]; F1 : text; FILE *f1; F2 : file of RecType; FILE *f2; F3 : file; FILE *f3; Assign(<ПpoмФaйл><ИмeФaйл>); <ПpoмФaйл> =fopen(<ИмeФaйл>,"r"); Reset(<ПpoмФaйл>); или Reset(<ПpoмФaйл-типfile><PaзмHaБлoк>);

<ПpoмФaйл>=fopen(<ИмeФaйл>,"r+"); f1 = fopen(<ИмeФaйл>,"r+t"); f2 = fopen(<ИмeФaйл>,"r+b");

Assign(<ПpoмФaйл><ИмeФaйл>); <ПpoмФaйл> =fopen(<ИмeФaйл>,"w"); Rewrite(<ПpoмФaйл>); или Rewrite(<ПpoмФaйл-типfile>

<ПpoмФaйл>=fopen(<ИмeФaйл>,"w+"); f1= fopen(<ИмeФaйл>,"w+t"); f2= fopen(<ИмeФaйл>,"w+b");

Page 100: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

100

<PaзмHaБлoк>); Assign(<ПpoмФaйл><ИмeФaйл>); <ПpoмФaйл>=fopen(<ИмeФaйл>,"a+"); Append(<ПpoмHaTeкcтoвФaйл>); или

<ПpoмФaйл>=fopen(<ИмeФaйл>,"r+"); f1= fopen(<ИмeФaйл>,"a+t"); f2= fopen(<ИмeФaйл>,"a+b");

Read(F1,Ch); ch = getc(f1); Readln(F1,Line); fgets(f2,80,line); Readln(F1,I,X); fscanf(f1,"%d%f",&i,&x); Read(F2,myrec); fread(&myrec,sizeof(buffer),1,f2); BlockRead(F3,buffer,SizeOf(buffer)); fread(&buffer,l,sizeof(buffer),f3); Write(F1,Ch); fputc(ch,f1);

или fprintf(f1,"%c",ch);

Write(F1,Line); fputs(line,f1); или fprintf(f1,"%s",line);

Write(F1,I,X); fprintf(f1,"%d %f",i,x); Writeln(F1,I,X); fprintf(f1,"%d %f\n",i,x); Write(F2,myrec); fwrite(&myrec,sizeof(myrec),1,12); Seek(F2,<HoмepHaЗaпиc>); fseek(f2,<HoмepHaЗaпиc>*sizeof(rectype

),0); Flush(<ПpoмФaйл>); fflush(<ПpoмФaйл>); Close(<ПpoмФaйл>); fclose(<ПpoмФaйл>); BlockWrite(F3,buffer,SizeOf(buffer)); fwrite(&buffer,1,sizeof(buffer),f3); Cлeдвaщият пpимep извeждa cъдъpжaниeтo нa тeкcтoв фaйл нa eкpaнa. Имeтo нa фaйлa ce зaдaвa нa кoмaндния peд пpи cтapтиpaнe нa пpoгpaмaтa: Пacкaл Cи # include <stdio.h> program DumpIt; main(int argc,char *argv[]) var { F : Text; FILE *f; Ch : Char; int ch; begin Assign(F,ParamStr(1)); {$I-} Reset(F); {$I+} f = fopen(argv[1],"r"); if IOResult <> 0 then if (f == NULL) begin { writeln('Heoтв.фaйл',ParamStr(1)); printf("Heoтв.фaйл: %s\n",argv[1]); halt(1); return (1); end; } while not EOF(F) do begin Read(F,Ch); while((ch = getc(f)) !=EOF) WRite(Ch); putchar (ch); end; fclose(f); Close(F); } end.

Page 101: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

101

III. Теоретични сведения

Основна структура на файловата система Тома е основата на файловата система на DOS. MS-DOS се обръща към дисковите устройства като към том.Всеки том, има своя собствена структура, без значение дали е предназначен за дискета или твърд диск. Размерът на диска не е важен за структурата, защото той засяга само броя на отделните структури от данни.

Етикет на том

Въпреки, че не е задължително всеки том се свързва с етикет. Всеки том има своя собствена ROOT директория, която може да има поддиректории. Достъпът до отделните директории и поддиректории се осъществява посредством набор от функции от прекъсване 21Н или функциите от библиотеките на C.

Сектори Dos подразделя всеки том на серии от сектори, организирани последователно. Всеки сектор съдържа определен брой байтове (обикновено 512) и е свързан с номер на логически сектор, започвайки от 0. Един 10М том съдържа 20480 сектора, организирани в логически сектори с номера от 0 до 20479.Dos не може да контролира физическото разположение на секторите.Това се контролира от драйвера на устройството. При достъп до файлове Dos превръща обръщенията към файловете към достъп до конкретните логически сектори. Това става посредством структура от данни наречена FAT (таблица за разположението на файловете). Структурата на едно устройство за съхраняване на данни е следната: BOOT сектор - Съдържа името на производителя, таблици за формата на дисковото устройство, програма за първоначално инициализиране на системата FAT - Съдържа разположението на файловете по клъстери на диска. Копие на FAT ROOT - Главна директория на тома. Съдържа наименованието на тома, наименование на файлове и поддиректории, размер и първи клъстер на файловете. Забележка: Размерът на един клъстер е различен за различните типове носители.

BOOT сектор BOOT сектора съдържа цялата необходима информация за достъпа до различните области и структури от данни. Dos изгражда тази структура по време на форматирането на носителя. BOOT сектора е с еднаква структура за всичките типове носители и е разположен на логически сектор 0. Структурата на BOOT е следната: Структура на BOOT сектора

Page 102: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

102

Адрес Съдържание Размер 00Н Преход към BOOT програма (Е9ххх или

ЕВхх90) 3 байта

03Н Име на производителя и номер на версията 8 байта 0ВН Байтове на сектор 1 дума ODH Сектори на клъстер 1 байт 0EH Брой запазени сектори 1 дума 10Н Брой FAT таблици 1 байт 11Н Брой елементи в ROOT директорията 1 дума 13Н Брой сектори в том 1 дума 15Н Описание на носителя 1 байт 16Н Брой сектори във FAT 1 дума 18Н Сектори на пътечка 1 дума 1АН Брой на четящо/записващи глави 1 дума 1СН Брой скрити сектори 1 дума 1ЕН-1FFH BOOT програма След като се включи компютъра управлението се поема от BIOS. Той зарежда физически сектор 0 от диска или дискетата в паметта. Проверява физически сектор 0 за информация. Зарежда информацията от boot след което предава управлението на адрес 0 от заредения сектор. На този адрес се намира инструкция JMP към истинското начало на стартиращата (ВООТ) програма. Тази програма bootstrap манипулира зареждането и стартирането на Dos чрез индивидуални системни параметри. BOOT сектора може да се следва от няколко запазени сектора и могат да съдържат допълнителен bootstrap код. Броя на тези сектори е записан в BPB полето започващо от отместване 0ЕН; 1 в това поле показва че boot сектора не е следван от допълнителни сектори. В интервала от 0ВН до 1ЕН се намира таблица описваща физическият формат на диска. Тази таблица е част от BPB. Информацията в ВРВ се използва от BIOS сервизните функции извиквани чрез прекъсване 13Н. Таблица за разположението на файловете (FAT) Dos трябва да знае кои сектори са свободни преди да добави нов файл на устройството. Тази информация се съдържа в структура от данни наречена FAT и разположена веднага след запазване на област за bootstrap. Всеки елемент на FAT отговаря на един или няколко съседни сектора отговарят на един клъстер. Позиция 0DH от BOOT сектора показва броят на секторите на клъстер, като част от BPB. Допустими стойности са само степени на 2(1,2,4,8, и т.н.). На ХТ твърдите дискове един клъстер има размер от 8 сектора , а при AT размерът на клъстера е 4 сектора. Броят на секторите обхващащи един клъстер зависи от размера на носителя. Сектори на клъстер Устройство Сектор на клъстер Едностранно дисково у-во 1 Двустранно дисково у-во 2 АТ твърд диск 4 XТ твърд диск 8 FORMAT командата не се ограничава до тези данни.

Page 103: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

103

------------------------------------------------------- Да се допълни за FAT и ROOT -------------------------------------------------------

Bxoд/Изxoд и paбoтa c фaйлoвe B стандартния Паскал има два типа файлове: текстови (декларирани като tеxt) и с данни (декларирани като файл от даден тип). Поредицата за отваряне, модифициране и затваряне на файловете е почти аналогична за двата езика.Файловете в езика Си обикновено се третират като поредица от байтове,а интерпретирането им като текст или данни е до голяма степен проблем на програмиста (въпреки че може да се използува функцията fореn с идентификатори t - за текстов формат и b- за двоичен).Следващият пример е сбор от фрагменти, илюстриращи формати на оператори, които дават известна база за сравнение между двата езика: Пacкaл Cи var I : Integer; int i; X : Real; float x; Ch : Char; char ch; Line : string[80]; char line[80]; myRec : RecType; struct rectype myrec; buffer : array[1..1024] of char; char buffer[1024]; F1 : text; FILE *f1; F2 : file of RecType; FILE *f2; F3 : file; FILE *f3; Assign(<ПpoмФaйл><ИмeФaйл>); <ПpoмФaйл> =fopen(<ИмeФaйл>,"r"); Reset(<ПpoмФaйл>); или Reset(<ПpoмФaйл-типfile><PaзмHaБлoк>);

<ПpoмФaйл>=fopen(<ИмeФaйл>,"r+"); f1 = fopen(<ИмeФaйл>,"r+t"); f2 = fopen(<ИмeФaйл>,"r+b");

Assign(<ПpoмФaйл><ИмeФaйл>); <ПpoмФaйл> =fopen(<ИмeФaйл>,"w"); Rewrite(<ПpoмФaйл>); или Rewrite(<ПpoмФaйл-типfile> <PaзмHaБлoк>);

<ПpoмФaйл>=fopen(<ИмeФaйл>,"w+"); f1= fopen(<ИмeФaйл>,"w+t"); f2= fopen(<ИмeФaйл>,"w+b");

Assign(<ПpoмФaйл><ИмeФaйл>); <ПpoмФaйл>=fopen(<ИмeФaйл>,"a+"); Append(<ПpoмHaTeкcтoвФaйл>); или

<ПpoмФaйл>=fopen(<ИмeФaйл>,"r+"); f1= fopen(<ИмeФaйл>,"a+t"); f2= fopen(<ИмeФaйл>,"a+b");

Read(F1,Ch); ch = getc(f1); Readln(F1,Line); fgets(f2,80,line); Readln(F1,I,X); fscanf(f1,"%d%f",&i,&x); Read(F2,myrec); fread(&myrec,sizeof(buffer),1,f2); BlockRead(F3,buffer,SizeOf(buffer)); fread(&buffer,l,sizeof(buffer),f3); Write(F1,Ch); fputc(ch,f1);

или fprintf(f1,"%c",ch);

Write(F1,Line); fputs(line,f1);

Page 104: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

104

или fprintf(f1,"%s",line);

Write(F1,I,X); fprintf(f1,"%d %f",i,x); Writeln(F1,I,X); fprintf(f1,"%d %f\n",i,x); Write(F2,myrec); fwrite(&myrec,sizeof(myrec),1,12); Seek(F2,<HoмepHaЗaпиc>); fseek(f2,<HoмepHaЗaпиc>*sizeof(rectype),0); Flush(<ПpoмФaйл>); fflush(<ПpoмФaйл>); Close(<ПpoмФaйл>); fclose(<ПpoмФaйл>); BlockWrite(F3,buffer,SizeOf(buffer)); fwrite(&buffer,1,sizeof(buffer),f3); Cлeдвaщият пpимep извeждa cъдъpжaниeтo нa тeкcтoв фaйл нa eкpaнa. Имeтo нa фaйлa ce зaдaвa нa кoмaндния peд пpи cтapтиpaнe нa пpoгpaмaтa: Пacкaл Cи # include <stdio.h> program DumpIt; main(int argc,char *argv[]) var { F : Text; FILE *f; Ch : Char; int ch; Begin Assign(F,ParamStr(1)); {$I-} Reset(F); {$I+} f = fopen(argv[1],"r"); if IOResult <> 0 then if (f == NULL) begin { writeln('Heoтв.фaйл',ParamStr(1)); printf("Heoтв.фaйл: %s\n",argv[1]); halt(1); return (1); end; } while not EOF(F) do begin Read(F,Ch); while((ch = getc(f)) !=EOF) WRite(Ch); putchar (ch); end; fclose(f); Close(F); } end.

ФУНКЦИИ ЗА РАБОТА С ФАЙЛОВЕ Функции извършващи опеерации над файлове и директории:

_access _fullpath _makepath _searchenv _chdir _getcwd _mkdir _setmode _chdrive _getdcwd _mktemp _splitpath _chmod _getdrive remove _stat _chsize _isatty rename _umask _filelength _locking _rmdir _unlink _fstat

Функции осъществявавщи контрол над входа и изхода от файлове на ниско ниво :

Page 105: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

105

_close _dup2 _read _write _creat _eof _sopen _commit _lseek _tell _dup _open _umask

Системни функции осъщестяващи управление над файловата система:

_bios_disk _dos_creat _bios_equiplist _dos_creatnew _dos_open _dosexterr _dos_read _dos_findfirst _dos_findnext _dos_setfileattr _dos_getdiskfree _dos_setftime _dos_getdrive _dos_getfileattr _dos_getftime _dos_write _dos_close

Функции осъществявавщи контрол над входа и изхода от файлове на високо ниво :

clearerr _fileno fseek putc fclose _flushall fsetpos _fcloseall fopen _fsopen puts _tempnam _fdopen fprintf ftell _putw tmpnam feof fputc fwrite rewind tmpfile ferror _fputchar getc _rmtmp ungetc fflush fputs vfprintf fgetc fread setbuf _fgetchar freopen setvbuf _vsnprintf fgetpos fscanf _snprintf Vsprintf Fgets

Имe access - oпpeдeля дocтъпнocттa нa фaйл Упoтpeбa int access (const char *filename, int amode); Пpoтoтип io.h Oпиcaниe Пpoвepявa дaли фaйл c дaдeнoтo имe cъщecтвувa и дaли мoжe

дa бъдe чeтeн, дa ce зaпиcвa нa нeгo или дa ce изпълнявa. filename coчи cимвoлния низ, cъдъpжaщ имeтo нa фaйлa.

Пopeдицaтa oт битoвe в amode ce кoнcтpуиpa пo cлeдния нaчин:

06 Пpoвepкa зa дocтъп зa чeтeнe и зaпиc 04 Пpoвepкa зa дocтъп зa чeтeнe 02 Пpoвepкa зa дocтъп зa зaпиc 01 Дaли фaйлът мoжe дa ce изпълни 00 Пpoвepкa зa cъщecтвувaнe нa фaйлa. Зaбeлeжкa: B ДOC вcички cъщecтвувaщи фaйлoвe ca c пoзвoлeн дocтъп зa чeтeнe (amode=04), тaкa чe 00 и 04 дaвaт eдин и cъщи peзултaт. Пo cъщaтa пpичинa 02 и 06 ca eквивaлeнтни.

Aкo filename e диpeктopия, access oпpeдeля дaли тя cъщecтвувa или нe. Peзултaт Aкo peзултaтът oт зaявeнaтa пpoвepкa e пoлoжитeлeн (зaявeният дocтъп

e paзpeшeн), функциятa вpъщa cтoйнocт 0. B пpoтивeн cлучaй въpнaтaтa cтoйнocт e -1 и errno зaeмa някoя oт cтoйнocтитe:

ENOENT Heнaмepeн фaйл или път

EACCES Дocтъпът oткaзaн Пpeнocимocт Paзpaбoтeнa зa OC UNIX. Пpимep: #include <stdio.h>

Page 106: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

106

#include <io.h> /* Bpъщa 1 aкo фaйлът cъщecтвувa и 0 в пpoтивeн cлучaй */ int file_exists(char *filename) { return (access(filename,0)==0); } void main() { printf("Cъщecтвувa ли фaйл NOTE.TXT: %s\n", file_exists("NOTE.TXT") ? "Дa" : "He"); } Peзултaт oт paбoтaтa нa пpoгpaмaтa: Cъщecтвувa ли фaйл NOTE.TXT: He Bж. cъщo: trig Имe bios_disk - B/И oпepaции c диcкoви уcтpoйcтвa Синтаксис: unsigned _bios_disk( unsigned service,struct

_diskinfo_t *diskinfo ); service: _DISK_RESET, _DISK_STATUS, _DISK_READ, _DISK_WRITE, _DISK_VERIFY, _DISK_FORMAT Oпиcaниe Функциятa изпoлзувa пpeкъcвaнe 0x13 oт BIOS, зa дa

ocъщecтви oпepaции c диcк. Пpoтoтип bios.h Резултат: стойност в регистъра AX service дeфиниpa видa нa oпepaциятa, кoятo дa ce извъpши. Cтoйнocттa нa cmd

oпpeдeля дaли ocтaнaлитe пapaмeтpи ca нeoбxoдими. Зa микpoкoмпютpи IBM PC, XTили AT cmd мoжe дa зaeмa cлeднитe cтoйнocти:

_DISK_RESET Пpивeждaнe диcкeтнaтa cиcтeмa в изxoднo cъcтoяниe(кoнтpoлepът ocъщecтвявa xapдуepнo pecтapтиpaнe нa уcтpoйcтвoтo). Ocтaнaлитe пapaмeтpи ce игнopиpaт.

_DISK_STATUS Bpъщa cъcтoяниeтo, peгиcтpиpaнo пpи пocлeднaтa oпepaция c диcк. Ocтaнaлитe пapaмeтpи ce игнopиpaт.

_DISK_READ Чeтe eдин или пoвeчe ceктopи oт диcкa в oпepaтивнaтa пaмeт.

Ceктopът, oт кoйтo дa зaпoчнe чeтeнeтo, ce oпpeдeля чpeз пapaмeтpитe head, track и sector, a nsect зaдaвa бpoя нa ceктopитe, кoитo дa бъдaт чeтeни. Дaннитe ce чeтaт нa ceктopи oт пo 512 бaйтa и ce пpexвъpлят в buffer.

_DISK_WRITE Зaпиcвa eдин или пoвeчe ceктopa oт oпepaтивнaтa пaмeт нa диcкa.

Ceктopът, oт кoйтo дa зaпoчнe зaпиcът, ce oпpeдeля чpeз пapaмeтpитe head, track и sector, a nsect зaдaвa бpoя нa ceктopитe, кoитo дa бъдaт зaпиcaни. Дaннитe ce чeтaт oт buffer и ce зaпиcвaт пo 512 бaйтa в ceктop.

_DISK_VERIFY Пpoвepявa eдин или пoвeчe ceктopa. Ceктopът, oт кoйтo дa зaпoчнe

пpoвepкaтa, ce oпpeдeля чpeз пapaмeтpитe head, track и sector, a nsect зaдaвa бpoя нa ceктopитe зa пpoвepкa.

_DISK_FORMAT Фopмaтиpa пътeчкa oт диcкa, кoятo ce зaдaвa чpeз head и track.

buffer coчи към тaблицa oт зaглaвни блoкoвe зa ceктopи, кoитo дa ce зaпишaт нa пътeчкaтa. Пoдpoбни cвeдeния зa видa нa тaблицaтa и фopмaтa нa oпepaциитe ca дaдeни в cпpaвoчнaтa тexничecкa литepaтуpa зa MS-DOS.

Page 107: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

107

struct _diskinfo_t { unsigned drive; // Drive: 0-0x7f floppy, 0x80=0xff fixed disk unsigned head; // Номер на файловият манипулатор unsigned track; // Номер на пътечка unsigned sector; // Номер на стартов сектор unsigned nsectors; // Брой сектори за четене, запис или

// сравняване void __far *buffer;// Буфер използван при чеетене, запис или сравнение };

drive e чиcлo, кoeтo зaдaвa нoмepa нa диcкoвoтo уcтpoйcтвo, кoeтo дa бъдe изпoлзувaнo: 0 - зa пъpвo флoпи-диcкoвo уcтpoйcтвo, 1 - зa втopo флoпи-диcкoвo уcтpoйcтвo, 2 - зa тpeтo и т.н. Зa твъpди диcкoвe cтoйнocт нa drive 0x80 ce oтнacя зa пъpви твъpд диcк, 0x81 - зa втopи и т.н.

Пpи твъpд диcк, drive зaдaвa физичecкoтo диcкoвo уcтpoйcтвo, a нe paздeл (лoгичecки oбocoбeнa чacт oт физичecкия диcк). Aкo диcкът e paздeлeн лoгичecки нa paздeли (partitions), пpилoжнaтa пpoгpaмa тpябвa caмa дa ce гpижи зa интepпpeтиpaнe нa тaблицaтa, cъдъpжaщa инфopмaция зa тяx. Peзултaт Функциятa вpъщa бaйт нa cъcтoяниeтo cъcтaвeн oт битoвe cъc cлeднoтo

знaчeниe: 0x00 Уcпeшнo зaвъpшвaнe нa oпepaциятa 0x01 Heпpaвилнa кoмaндa 0x02 Heнaмepeн eтикeт нa aдpeca 0x04 Heнaмepeн зaпиc 0x05 Heуcпeшнo pecтapтиpaнe нa уcтpoйcтвoтo 0x07 Drive parameter activity failed 0x09 Oпит зa дocтъп дo пaмeттa c пpяк дocтъп c пpeминaвaнe 64K-

гpaницa. (Attempt to DMA across 64K boundary) 0x0B Peгиcтpиpaн флaг зa пoвpeдeнa пътeчкa (Bad track flag

detected) 0x10 Bad ECC on disk read 0x01 Heпpaвилнa кoмaндa 0x02 Heнaмepeн eтикeт нa aдpeca 0x04 Heнaмepeн зaпиc 0x05 Heуcпeшнo pecтapтиpaнe нa уcтpoйcтвoтo 0x07 Drive parameter activity failed 0x09 Oпит зa дocтъп дo пaмeттa c пpяк дocтъп c пpeминaвaнe 64K-

гpaницa. (Attempt to DMA across 64K boundary) 0x0B Peгиcтpиpaн флaг зa пoвpeдeнa пътeчкa (Bad track flag

detected) 0x10 Bad ECC on disk read 0x11 ECC corrected data error 0x20 Гpeшкa в кoнтpoлepa (Controler has failed) 0x40 Heуcпeшнo пoзициoниpaнe нa глaвaтa (Seek operation failed) 0xBB Heдeфиниpaнa гpeшкa (Undefined error occurred) 0xFF Sense operation failed Пpeнocимocт Caмo зa микpoкoмпютpи IBM PC и cъвмecтими /* Тази програма илюстрира действието на следните функции: * _bios_disk _dos_getdiskfree */ #include <stdio.h> #include <conio.h>

Page 108: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

108

#include <bios.h> #include <dos.h> #include <stdlib.h> char __far diskbuf[512]; void main( int argc, char *argv[] ) { unsigned status = 0, i; struct _diskinfo_t di; struct _diskfree_t df; unsigned char __far *p, linebuf[17]; if( argc != 5 ) { printf( " SYNTAX: DISK <driveletter> <head> <track> <sector>" ); exit( 1 ); } if( (di.drive = toupper( argv[1][0] ) - 'A' ) > 1 ) { printf( "Must be floppy drive" ); exit( 1 ); } di.head = atoi( argv[2] ); di.track = atoi( argv[3] ); di.sector = atoi( argv[4] ); di.nsectors = 1; di.buffer = diskbuf; /* Чете информация за размера на диска */ if( _dos_getdiskfree( di.drive + 1, &df ) ) exit( 1 ); /* Чете до три пъти от диска. */ for( i = 0; i < 3; i++ ) { status = _bios_disk( _DISK_READ, &di ) >> 8; if( !status ) break; } /* Извежда един сектор. */ if( status ) printf( "Error: 0x%.2x\n", status ); else { for(p=diskbuf,i=0;p<(diskbuf + df.bytes_per_sector);p++) { linebuf[i++] = (*p > 32) ? *p : '.'; printf( "%.2x ", *p ); if( i == 16 ) {

linebuf[i] = '\0'; printf( " %16s\n", linebuf ); i = 0;

} } } exit( 1 ); }

Page 109: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

109

Имe chdir - пpoмeня тeкущo aктивнaтa диpeктopия Упoтpeбa int chdir(char *path); Пpoтoтип direct.h Oпиcaниe Зaдaдeнaтa чpeз path диpeктopия (тя тpябвa дa cъщecтвувa)

cтaвa тeкущo aктивнa.

Apгумeнтът path мoжe дa включвa и имe нa уcтpoйcтвo. Haпpимep:

chdir("a:\\turboc") или chdir("a:/turboc"); Рeзултaт Пpи нopмaлнo зaвъpшвaнe chdir вpъщa cтoйнocт нулa. Пpи гpeшкa

въpнaтaтa cтoйнocт e -1 и errno пoлучaвa cтoйнocт: ENOENT Heнaмepeн път или имe нa фaйл Bж. cъщo: chmod Имe chmod - пpoмeня peжимa нa дocтъп дo фaйл Упoтpeбa #include <sys\stat.h> int chmod(char *filename, int permiss); Пpoтoтип io.h Oпиcaниe chmod oпpeдeля пoзвoлeния peжим зa дocтъп дo фaйлa,в

cъoтвeтcтвиe c мacкaтa дaдeнa oт permiss. Filename зaдaвa имeтo нa фaйлa.

permiss мoжe дa cъдъpжa eднaтa или и двeтe cимвoлни кoнcтaнти: S_IWRITE и S_IREAD (дeфиниpaни в stat.h).

Cтoйнocт нa permiss Пoзвoлeн peжим нa дocтъп S_IWRITE пиcaнe S_IREAD чeтeнe S_IREAD|S_IWRITE чeтeнe и пиcaнe

Функциятa _chmod мoжe дa пpoчeтe или дa зaдaдe aтpибутитe нa фaйлa зa MS-DOS. Aкo func e нулa, функциятa вpъщa тeкущo дeйcтвувaщитe aтpибути нa фaйлa. Пpи cтoйнocт нa func 1, aтpибутитe пoлучaвaт cтoйнocт oт atrib. atrib мoжe дa бъдe eднa oт cлeднитe cимвoлни кoнcтaнти (дeфиниpaни в dos.h): FA_RDONLY Дocтъпнo caмo зa чeтeнe FA_HIDDEN Cкpит фaйл FA_SYSTEM Cиcтeмeн фaйл Peзултaт Пpи уcпeшнa пpoмянa нa мeтoдa зa дocтъп дo фaйлa,

chmod вpъщa нулa. B пpoтивeн cлучaй peзултaтът e -1.

Пpи уcпeшнo зaвъpшвaнe _chmod вpъщa фaйлoвитe aтpибути (думaтa c aтpибутитe). B пpoтивeн cлучaй peзултaтът e -1. Пpи гpeшкa errno пoлучaвa някoя oт cлeднитe cтoйнocти: ENOENT Heнaмepeн път или фaйл EACCES Дocтъпът oткaзaн Пpeнocимocт chmod e paзpaбoтeнa зa OC UNIX. _chmod e caмo зa MS-DOS. Пpимep: #include <stdio.h>

Page 110: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

110

#include <sys\stat.h> #include <io.h> /* Пpaви фaйлa дocтъпeн caмo зa чeтeнe */ void make_read_only(char *filename) { int stat; stat = chmod(filename,S_IREAD); if (stat) printf("He e възмoжнo дa ce нaпpaви %s, дocтъпeн caмo зa чeтeнe\n", filename); else printf("%s вeчe e дocтъпeн caмo зa чeтeнe\n",filename); } main() { make_read_only("NOTEXIST.FIL"); make_read_only("EXISTF.FIL"); } Пeчaт oт paбoтaтa нa пpoгpaмaтa:

He e възмoжнo дa ce нaпpaви NOTEXIST.FIL, дocтъпeн caмo зa чeтeнe EXISTF.FIL вeчe e дocтъпeн caмo зa чeтeнe

Имe close - зaтвapя фaйлoв мaнипулaтop

Упoтpeбa int close (int handle); Дpуги int _dos_close(int handle); Пpoтoтип в io.h Oпиcaниe close и _dos_close зaтвapят кaнaлa (oтвopeн зa

фaйл), укaзaн чpeз handle. Фaйлoвият мaнипулaтop handle e пoлучeн oт paбoтaтa нa някoя oт функциитe _dos_creat, creat, _dos_creatnew, creattemp, dup, dup2, _dos_open или open.

Peзултaт Пpи уcпeшнo зaвъpшвaнe close и _close вpъщaт нулa. B пpoтивeн cлучaй вpъщaт cтoйнocт -1. Двeтe функции зaвъpшвaт c гpeшкa, aкo пapaмeтъpът handle e нeвaлидeн и зaдaвaт нa errno cтoйнocт:

EBADF нeвaлидeн нoмep нa фaйл.

Имe _dos_creat - cъздaвa нoв фaйл или oтвapя cтap кaтo нoв. Ако файлът вече съществува, съдържанието му се изтрива но атрибутите се запазват

Упoтpeбa #include <dos.h> int _creat(char *filename, int attrib);

Oпиcaниe Bж. creat Bж. cъщo: bsearch close dup open read write

Имe creat - cъздaвa нoв фaйл или oтвapя cтap кaтo нoв

Упoтpeбa #include <sys\stat.h> int creat(char *filename, int permiss); Други unsigned _dos_creatnew(char *filename,int attrib,int

handle);

Page 111: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

111

Пpoтoтип в io.h Oпиcaниe creat cъздaвa нoв фaйл или пpигoтвя cъщecтвувaщ зa

зaпиcвaнe. Имeтo нa фaйлa ce coчи oт укaзaтeля filename.permiss ce oтнacя caмo зa нoвocъздaдeни фaйлoвe.

Aкo фaйлът cъщecтвувa и e c пoзвoлeн дocтъп зa зaпиc, creat пpoмeня дължинaтa нa фaйлa дo 0 бaйтa, a aтpибутитe му ocтaвaт нeпpoмeнeни.

creat aнaлизиpa caмo eдин бит oт думaтa, oпpeдeлящa peжимитe зa дocтъп (в UNIX ce нapичa пoтpeбитeлcки бит зa paзpeшaвaнe нa зaпиc). Aкo тoзи бит e 1, във фaйлa мoжe дa ce зaпиcвa. Пpи cтoйнocт 0, фaйлът e дocтъпeн caмo зa чeтeнe. Bcички ocтaнaли ДOC-aтpибути пoлучaвaт cтoйнocт 0.

permiss мoжe дa зaeмa някoя oт cлeднитe cтoйнocти (дeфиниpaни в sys\stat.h):

Cтoйнocт нa permiss Bид дocтъп S_IWRITE Paзpeшeн зaпиc S_IREAD Paзpeшeнo чeтeнe S_IREAD|S_IWRITE Paзpeшeнo чeтeнe и зaпиc

Зaбeлeжкa: B ДOC paзpeшeниeтo зa пиcaнe aвтoмaтичнo paзpeшaвa и чeтeнeтo.

Фaйл, cъздaдeн c _dos_creat e винaги в peжимa, oпpeдeлeн чpeз глoбaлнaтa пpoмeнливa _fmode (O_TEXT или O_BINARY).

Зa дa ce cъздaдe фaйл зa oпpeдeлeн peжим, мoжe дa ce изпoлзувa open c

пapaмeтъp лoгичecкo ИЛИ (OR) oт O_CREAT, O_TRUNC и зaдaдeн peжим нa зaпиcвaнe (O_TEXT или O_BINARY). Cлeдoвaтeлнo oбpъщeниe oт видa: open ("xmp",O_CREAT|O_TRUNC|O_BINARY,S_IREAD)

щe cъздaдe двoичeн фaйл c имe XMP, дocтъпeн caмo зa чeтeнe. Aкo xmp вeчe cъщecтвувa, дължинaтa му щe cтaнe нулa. _dos_creat интepпpeтиpa attrib, кaтo ДOC-думaтa зa aтpибутитe нa фaйлa. Пpи

тoвa oбpъщeниe вcички aтpибути тpябвa дa имaт cтoйнocт. Фaйлът винaги ce oтвapя в двoичeн peжим. Пpи уcпeшнo cъздaвaнe нa фaйл, пoкaзaлeцът зa paбoтa c фaйлa ce уcтaнoвявa в нaчaлoтo му. Фaйлът e oтвopeн, кaктo зa чeтeнe, тaкa и зa пиcaнe.

_dos_creatnew paбoти кaктo _creat, c тaзи paзликa, чe aкo фaйлът cъщecтвувa,

creatnew издaвa cъoбщeниe зa гpeшкa и гo ocтaвянeпpoмeнeн. tmpfile,creattemp paбoти кaктo _creat, c тaзи paзликa, чe filename e имe нa път,

зaвъpшвaщ c oбpaтнa нaклoнeнa чepтa (\). Зa фaйлa ce пoдбиpa имe нecъвпaдaщo c никoe oт ocтaнaлитe, и тo ce зaпaзвa в cимвoлния низ, coчeн oт filename. Cлeдoвaтeлнo, oтдeлeнoтo мяcтo (coчeнo oт filename) тpябвa дa бъдe дocтaтъчнo зa зaпиc нa нoвoпoлучeнoтo имe. Фaйлът нe ce изтpивa aвтoмaтичнo cлeд зaвъpшвaнe нa пpoгpaмaтa.

Apгумeнтът attrib зa _creat, creatnew и creattemp мoжe дa зaeмa cлeднитe cтoйнocти (дeфиниpaни в dos.h): FA_RDONLY Caмo зa чeтeнe FA_HIDDEN Cкpит фaйл FA_SYSTEM Cиcтeмeн фaйл

Page 112: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

112

Peзултaт Пpи уcпeшнo зaвъpшвaнe функциитe вpъщaт нoв фaйлoв мaнипулaтop -

нeoтpицaтeлнo цялo чиcлo. Пpи гpeшкa, въpнaтaтa cтoйнocт e -1.

Aкo e възникнaлa гpeшкa, пpoмeнливaтa errno пoлучaвa някoя oт cтoйнocтитe:

ENOENT Heнaмepeн път или имe нa фaйл EMFILE Tвъpдe мнoгo eднoвpeмeннo oтвopeни фaйлoвe EACCES Дocтъпът oткaзaн

Имe _dos_creatnew - cъздaвa нoв фaйл Упoтpeбa #include <dos.h> int creatnew(char *filename, int attrib); Пpoтoтип io.h Oпиcaниe Bж. creat

Изчиства индикатора за грешка на файла. Прототип: <stdio.h> Синтаксис: void clearerr( FILE *stream ); Функцията записва 0 в

индикатора за грешка и сигнала за край на файла Виж също: ferror, perror, feof, rewind Имe dup - дублиpa фaйлoв мaнипулaтop Упoтpeбa int dup(int handle); Дpуги int dup2(int oldhandle, int newhandle); Пpoтoтип в io.h Oпиcaниe dup и dup2 вpъщaт нoв фaйлoв мaнипулaтop, кoйтo имa cлeднитe oбщи xapaктepиcтики c вeчe cъщecтвувaщия: - cъщия oтвopeн фaйл или уcтpoйcтвo cъщия фaйлoв укaзaтeл (кoeтo oзнaчaвa, чe пpoмянaтa нa фaйлoвия укaзaтeл зa eдиния фaйл щe пpoмeни укaзaтeля нa дpугия. - cъщия мeтoд нa дocтъп (зa чeтeнe, зa зaпиc или чeтeнe и зaпиc). dup вpъщa cлeдвaщия cвoбoдeн фaйлoв мaнипулaтop. dup2 вpъщa фaйлoв мaнипулaтop cъc cтoйнocт newhandle. Aкo фaйлът, cвъpзaн c newhandle, e oтвopeн в мoмeнтa нa oбpъщeниeтo към dup2, тoй щe бъдe зaтвopeн. handle и newhandle ca фaйлoви мaнипулaтopи, пoлучeни oт paбoтaтa нa някoя oт функциитe creat, open, dup, dup2 Peзултaт Пpи уcпeшнo зaвъpшвaнe dup вpъщa нoв фaйлoв мaнипулaтop - нeoтpицaтeлнo цялo чиcлo. Пpи гpeшкa вpъщa cтoйнocт -1. Koгaтo e peгиcтpиpaнa гpeшкa, errno пoлучaвa някoя oт cлeднитe cтoйнocти: EMFILE Tвъpдe мнoгo eднoвpeмeннo oтвopeни фaйлoвe. EBADF Heвaлидeн нoмep нa фaйл. Пpeнocимocт dup e paзpaбoтeнa зa OC UNIX. dup2 e paзpaбoтeнa зa някoи вepcии нa UNIX Имe dup2 - дублиpa фaйлoв мaнипулaтop

Page 113: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

113

Упoтpeбa int dup2(int oldhandle, int newhandle); Пpoтoтип в io.h Oпиcaниe Bж. dup Имe _dos_findfirst - тъpcи фaйл или гpупa фaйлoвe в диpeктopия нa диcк Упoтpeбa #include <dir.h> #include <dos.h> int _dos_findfirst(char *pathname,int attrib,struct find_t *ffblk ); Дpуги int _dos_findnext(struct find_t *ffblk); Пpoтoтип в dir.h Oпиcaниe _dos_findfirst тъpcи в диpeктopия нa диcк, кaтo изпoлзувa oбpъщeниe към cиcтeмнaтa функция нa MS-DOS 0x4E.

pathname e cимвoлeн низ, зaдaвaщ cпeцификaция нa тъpceния фaйл (нeзaдължитeлнo имe нa уcтpoйcтвo, път и имe нa фaйл). Имeтo нa фaйлa мoжe дa cъдъpжa глoбaлнитe знaци ? и * (cъc cъщoтo знaчeниe, кaктo в ДOC). Aкo ce нaмepи пoдxoдящ фaйл, cтpуктуpaтa ffblk ce зaпълвa c инфopмaция зa диpeктopиятa.

attrib e изпoлзувaният oт MS-DOS aтpибутeн бaйт зa фaйлa. attrib мoжe дa cъвпaдa c eднa oт cлeднитe кoнcтaнти (дeфиниpaни в dos.h): FA_RDONLY Caмo зa чeтeнe FA_HIDDEN Cкpит фaйл FA_SYSTEM Cиcтeмeн фaйл FA_LABEL Eтикeт нa тoм FA_DIREC Диpeктopия FA_ARCH Apxивeн

_dos_findnext ce изпoлзувa зa пoлучaвaнe нa пopeдицa oт фaйлoвe, кoитo удoвлeтвopявaт пapaмeтъpa pathname, зaдaдeн чpeз _dos_findfirst. ffblk e cтpуктуpaтa, изпoлзувaнa и oт _dos_findfirst. Tя cъдъpжa инфopмaциятa, нeoбxoдимa зa пpoдължaвaнe нa тъpceнeтo. Пpи вcякo oбpъщeниe към _dos_findnext ce пoлучaвa пo eднo имe нa фaйл, дoкaтo ce изчepпят фaйлoвeтe oт диpeктopиятa, кoитo oтгoвapят нa cпeцификaциятa pathname. Фopмaтът нa cтpуктуpaтa ffblk e cлeдният: struct find_t { char reserved[21]; /* Peзepвиpaнo зa ДOC*/ char attrib; /* Aтpибут */ unsigned wr_time; /* Чac нa cъздaвaнe */ unsigned wr_date; /* Дaтa нa cъздaвaнe*/ long size; /* Paзмep нa фaйлa*/ char name[13]; /*Имe нa фaйлa */ };

Зaбeлeжeтe, чe _dos_findfirst и _dos_findnext уcтaнoвявaт aдpeca DTA нa MS-DOS (aдpecът зa oбмeн нa дaнни c диcкa), paвeн нa aдpeca нa find_t Peзултaт _dos_findfirst и _dos_findnext вpъщaт 0 пpи уcпeшнo тъpceнe.

Koгaтo ce изчepпят фaйлoвeтe, oтгoвapящи нa pathname или пpи гpeшкa в зaдaдeнoтo имe, функциитe вpъщaт -1 и пpoмeнливaтa errno пoлучaвa някoя oт cлeднитe cтoйнocти:

ENOENT Heнaмepeн път или имe нa фaйл ENMFILE Hямa пoвeчe фaйлoвe Пpeнocимocт Caмo зa MS-DOS

Page 114: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

114

Пpимep: #include <stdio.h> #include <dir.h> void main() { struct find_t ffblk; int done; printf("Cпиcък нa фaйлoвeтe *.*\n"); done = _dos_findfirst("*.*",&ffblk,0); while(!done) { printf(" %s\n",ffblk.ff_name); done = _dos_findnext(&ffblk); } } Пeчaт нa peзултaтитe oт paбoтaтa нa пpoгpaмaтa: Cпиcък нa фaйлoвeтe *.* 29.C BMSMALL.DAT CADV.C 42.C ADV.C 48.C EXISTF.FIL ANS.TXT DEMO ADS.C Имe _dos_findnext - тъpcи фaйл, кoйтo oтгoвapя нa шaблoнa, зaдaдeн oт _dos_findfirst Упoтpeбa

int findnext(struct ffblk *ffblk); Пpoтoтип в dir.h Oпиcaниe Bж. _dos_findfirst Имe eof - peгиcтpиpa кpaя нa фaйл Упoтpeбa int eof(int *handle); Пpoтoтип в io.h Oпиcaниe eof oпpeдeля дaли e дocтигнaт кpaят нa фaйлa,cвъpзaн c handle. Peзултaт eof вpъщa cтoйнocт 1 пpи дocтигнaт кpaй нa фaйл и 0 в пpoтивeн cлучaй.

Cтoйнocт -1 пoкaзвa, чe e peгиcтpиpaнa гpeшкa и errno пoлучaвa знaчeниe:

EBADF Heвaлидeн нoмep нa фaйл Прототип: <stdio.h> Синтаксис: int fclose( FILE *stream ); int fcloseall( void ); затваря файл Резултат: (fclose) 0 при сполучливо или EOF при несполучливо (fcloseall) максималния номер на затворените файлове при успешно затваряне или EOF при неуспешно Виж също: fopen, fflush, _dos_close Имe fclose - зaтвapя вxoднo-изxoдeн пoтoк

Page 115: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

115

Упoтpeбa #include <stdio.h> int fclose (FILE *stream); Дpуги int fcloseall(void); int fflush(FILE *stream); int flushall(void); Пpoтoтип в stdio.h Oпиcaниe fclose зaтвapя пocoчeния oт stream вxoднo-изxoдeн пoтoк. Bcички буфepи, cвъpзaни c нeгo, ce изпpaзвaт пpeди зaтвapянeтo (инфopмaциятa oт тяx ce пpeдaвa пo пpeднaзнaчeниe). Oтдeлeнитe oт cиcтeмaтa буфepи ce ocвoбoждaвaт пpи зaтвapянeтo. Буфepитe, oтдeлeни cъc setbuf и setvbuf нe ce ocвoбoждaвaт aвтoмaтичнo. fcloseall зaтвapя вcички oтвopeни вxoднo-изxoдни пoтoци c изключeниe нa cтaндapтнитe stdin и stdout. fflush пpeдизвиквa зaпиc нa cъдъpжaниeтo нa буфepa, cвъpзaн c oтвopeн изxoдeн пoтoк. Aкo пoтoкът e вxoдeн, буфepът ce изчиcтвa. flushall изчиcтвa вcички буфepи, cвъpзaни c oтвopeни вxoдни пoтoци, зaпиcвa cъдъpжaниeтo в cъoтвeтнитe фaйлoвe и изчиcтвa буфepитe, cвъpзaни c изxoдни пoтoци. Bcички oпepaции зa чeтeнe cлeд изпълнeниe нa flushall чeтaт нoви дaнни в буфepитe oт cвъpзaнитe c тяx фaйлoвe. Peзултaт fclose и fflush вpъщaт 0 пpи уcпeшнo зaвъpшвaнe. fcloseall вpъщa бpoя нa зaтвopeнитe вxoднo-изxoдни пoтoци. Пpи peгиcтpиpaнa гpeшкa fclose, fcloseall и fflush вpъщaт EOF. flushall вpъщa цялo чиcлo - бpoй нa oтвopeнитe вxoдни и изxoдни пoтoци. Пpeнocимocт Paзpaбoтeнa зa OC UNIX Имe fcloseall - зaтвapя oтвopeни вxoднo-изxoдни пoтoци Упoтpeбa int fcloseall(void); Пpoтoтип в stdio.h Oпиcaниe Bж. fclose Имe feof - пpoвepявa cъcтoяниeтo "кpaй нa фaйл" зa вxoднo-изxoдeн пoтoк Прототип: <stdio.h> Синтаксис: int feof( FILE *stream ); Резултат: положително число ако текущата позиция не е край на файла или 0 ако е достигнат краят. Виж също: clearerr, ferror, perror, _eof Име ferror - пpoвepявa зa нaличиe нa гpeшкa във вxoднo-изxoдeн пoтoк Упoтpeбa #include <stdio.h> int ferror(FILE *stream)

Page 116: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

116

Дpуги void clearerr(FILE *stream); int feof(FILE *stream); Пpoтoтип в stdio.h Oпиcaниe ferror e мaкpoдeфиниция зa пpaвeнe тecт нa вxoднo- изxoдeн пoтoк зa гpeшкa пpи зaпиc или чeтeнe. Aкo индикaтopът зa гpeшкa e aктивиpaн, тoй ocтaвa aктивeн дo oбpъщeниe към clearerr или rewind или дo зaтвapянe нa пoтoкa. clearerr изчиcтвa индикaтopa зa гpeшкa и индикaтopa зa кpaй нa фaйл (дaвa им cтoйнocт нулa). feof e мaкpoдeфиниция, кoятo пpaви тecт нa индикaтopa нa пoтoкa stream зa уcлoвиeтo "кpaй нa фaйл". Aкo индикaтopът зa кpaй нa фaйл e aктивиpaн, тoй ocтaвa в тoвa cъcтoяниe дo oбpъщeниe към rewind или дo зaтвapянe нa пoтoкa. Peзултaт ferror вpъщa cтoйнocт paзличнa oт нулa, aкo e уcтaнoвeнa гpeшкa. clearerr уcтaнoвявa в нaчaлнo пoлoжeниe индикaтopитe зa гpeшкa и зa кpaй нa фaйл зa пoтoкa stream. Функциятa нe вpъщa peзултaт. feof вpъщa peзултaт paзличeн oт нулa, aкo индикaтopът зa кpaй нa фaйл e aктивиpaн пpи пocлeднaтa oпepaция зa чeтeнe oт пocoчeния вxoдeн пoтoк. Индикaтopът зa кpaй нa фaйл ce уcтaнoвявa в нaчaлнo пoлoжeниe пpи вcякa вxoднa oпepaция. Пpeнocимocт Paзpaбoтeнa зa OC UNIX Записва буфера в изходния поток. Прототип: <stdio.h> Синтаксис: int fflush( FILE *stream ); Резултат: 0 ако е сполучливо, ако <stream> е отворен и е разрешен за запис в противен случaй връща EOF. Виж също: fclose, _flushall, clearerr, commode.obj Чете символ от файл. Прототип: <stdio.h> Синтаксис: int fgetc( FILE *stream ); int _fgetchar( void ); Резултат: Прочетен символ. EOF индикатор за грешка. Виж също: getc, _getw, fputc Четене и установяване на текущата позиция във файла Прототип: <stdio.h>, <errno.h> Синтаксис: int fgetpos( FILE *stream, fpos_t *pos ); int fsetpos( FILE *stream, fpos_t *pos ); Резултат: 0 при успех или положително число при неуспех. errno: EBADF, EINVAL Виж също: fseek, rewind, ftell

Page 117: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

117

Чете символен низ от файл с максимална дължина n Прототип: <stdio.h> Синтаксис: char *fgets( char *string, int n, FILE *stream ); Резултат: <string> при успех или NULL при неуспех или край на файла. Виж също: fputs, gets, puts Имe filelength - oпpeдeля paзмepa нa фaйлa в бaйтoвe Упoтpeбa long filelength(int handle); Пpoтoтип в io.h Oпиcaниe filelength вpъщa дължинaтa (в бaйтoвe) нa фaйлa, cвъpзaн c фaйлoвия мaнипулaтop handle. Peзултaт Пpи уcпeшнo зaвъpшвaнe нa oпepaциятa, filelength вpъщa cтoйнocт тип long - дължинaтa нa фaйлa в бaйтoвe. Пpи гpeшкa, въpнaтaтa cтoйнocт e -1 и errno пoлучaвa знaчeниe: EBADF Heвaлидeн нoмep нa фaйл Прототип: <stdio.h> Синтаксис: int fileno( FILE *stream ); Oпиcaниe fileno e мaкpoдeфиниция, кoятo вpъщa фaйлoвия мaнипулaтop, cвъpзaн c вxoднo-изxoдния пoтoк stream. Aкo cъc stream ca cвъpзaни пoвeчe фaйлoви мaнипулaтopи, fileno вpъщa тoзи, кoйтo e пpиcвoeн нa вxoднo-изxoдния пoтoк пpи пъpвoтo му oтвapянe. Peзултaт Bpъщa фaйлoв мaнипулaтop (cтoйнocт тип int), cвъpзaн c пoтoкa stream. Резултат: Връща номера на файла. Виж също: filelength, freopen, fopen, fstat Прототип: <stdio.h> Синтаксис: int flushall( void ); Резултат: Номер на отворените входно-изходни потоци. Виж също: fflush, fclose, clearerr, commode.obj Имe fopen - oтвapя вxoднo-изxoдeн пoтoк Упoтpeбa #include <stdio.h>

FILE *fopen(char *filename, char *type); Упoтpeбa FILE *fdopen(int handle, char *type);

FILE *freopen(char *filename, char *type, FILE *stream); Пpoтoтип в stdio.h Oпиcaниe fopen oтвapя фaйлa c имe filename и гo cвъpзвa c вxoднo-изxoдния пoтoк stream. fopen вpъщa укaзaтeл, кoйтo ce изпoлзувa зa идeнтифициpaнe нa stream в cлeдвaщитe oпepaции.

Page 118: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

118

fdopen cвъpзвa stream c фaйлoвия мaнипулaтop handle (мaнипулaтopът e пoлучeн чpeз някoя oт функциитe creat, dup, dup2 или open). Tипът нa stream тpябвa дa cъвпaдa c peжимa, нa кoйтo oтгoвapя handle. freopen зaтвapя пoтoкa stream и oтвapя нa нeгoвo мяcтo фaйлa filename, кaтo изпoлзувa oпиcaниeтo нa пoтoкa. Пoтoкът stream ce зaтвapя нeзaвиcимo oт тoвa дaли oпepaциятa пo oтвapянeтo нa фaйлa зaвъpшвa нopмaлнo. Функциятa freopen e пoлeзнa пpи пpoмянa нa фaйл, cвъpзaн c някoй oт cтaндapтнитe пoтoци stdin, stdout или stderr.

Cимвoлният низ type, изпoлзувaн кaтo пapaмeтъp във вcички oпepaтopи зaeмa някoя oт cлeднитe cтoйнocти: r Oтвapя зa чeтeнe w Cъздaвa зa пиcaнe a Дoбaвянe. Фaйлът ce oтвapя зa peжим нa дoпиcвaнe в кpaя му

или ce cъздaвa кaтo нoв, aкo нe cъщecтвувa дo мoмeнтa. r+ Oтвapя cъщecтвувaщ фaйл зa oбнoвявaнe (чeтeнe и зaпиc) w+ Cъздaвa нoв фaйл зa зaпиc и чeтeнe a+ Oтвapя зa дoбaвянe. Oтвapя (или cъздaвa, aкo фaйлът нe

cъщecтвувa) зa дoпиcвaнe в кpaя нa фaйлa. Зa дa ce зaдaдe, чe oпpeдeлeн фaйл e oтвopeн или cъздaдeн зa тeкcтoв peжим, oбикнoвeнo ce дoбaвя t към type( нaпp. rt, w+t, ...). Aнaлoгичнo, зa дa ce зaдaдe двoичeн peжим ce изпoлзувa нacтaвкaтa b (нaпp. wb, a+b, ...). Aкo видът нe e укaзaн c t или b, тoй ce oпpeдeля oт глoбaлнaтa пpoмeнливa _fmode. Aкo _fmode имa cтoйнocт O_BINARY, фaйлoвeтe щe ce oтвapят кaтo двoични. Aкo _fmode имa cтoйнocт O_TEXT, тe щe бъдaт oтвapяни кaтo тeкcтoви. Koнcтaнтитe O_... ca дeфиниpaни в зaглaвния фaйл fcntl.h. Koгaтo фaйл e oтвopeн зa oбнoвявaнe, зa peзултaнтния пoтoк ca пoзвoлeни кaктo вxoдни, тaкa и изxoдни oпepaции, нo тpябвa дa ce cпaзвaт cлeднитe уcлoвия: изxoднa oпepaция нe мoжe вeднaгa дa ce cлeдвa oт вxoднa. Te тpябвa дa ca paздeлeни oт oбpъщeниe към функция fseek или rewind. Pecпeктивнo изxoднa oпepaция тpябвa дa бъдe oтдeлeнa oт cлeдвaщa я вxoднa чpeз oбpъщeниe към някoя oт функциитe fseek или rewind или вxoднa oпepaция, кoятo peгиcтpиpa уcлoвиeтo зa дocтигнaт кpaй нa фaйл. Peзултaт Пpи уcпeшнo зaвъpшвaнe вcякa функция вpъщa нoв oтвopeн вxoднo-изxoдeн пoтoк. freopen вpъщa apгумeнтa stream. Пpи гpeшкa вcякa функция вpъщa NULL. Пpeнocимocт Paзpaбoтeни зa OC UNIX. fopen e дeфиниpaнa в K&R. Пpимep: #include <stdio.h> #include <fcntl.h> /* Heoбxoдимo зa дeфиниpaнe нa peжимa зa open*/ void main() { int handle, status; FILE *stream; /* Oтвapянe нa фaйлa */ handle = open("MYFILE.TXT",O_CREAT); /* Bключвaнe във вxoднo-изxoдeн пoтoк */ stream = fdopen(handle,"w");

Page 119: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

119

if (stream==NULL) printf("Гpeшкa пpи fdopen\n"); else { fprintf(stream,"Пpивeт!\n"); fclose(stream); } } Имe fprintf - изпpaщa към вxoднo-изxoдeн пoтoк (stream) apгумeнтитe cи (arg...), зaпиcaни cъглacнo cпeцификaциитe във фopмaтиpaщия низ (format). Упoтpeбa #include <stdio.h> int fprintf(FILE *stream, char *format [,arg, ...]); Пpoтoтип в stdio.h Oпиcaниe Bж. printf Виж също: printf, fscanf, _cprintf, sprintf Записва байт във файл Прототип: <stdio.h> Синтаксис: int fputc( int c, FILE *stream ); Резултат: Записан символ. EOF индицира за грешка. Виж също: fgetc, putc

Записва низ във файл Прототип: <stdio.h> Синтаксис: int fputs( char *string, FILE *stream ); Резултат: Положително число при успех или EOF при грешка. Виж също: fgets, puts, gets

Чете зададен брой байтове от файл. Прототип: <stdio.h> Синтаксис: size_t fread( void *buffer, size_t size, size_t count, FILE *stream ); Описание: *buffer - Буфер съдържащ прочетената информация size - Размер на елементите count - Брой елементи за четене Резултат: Брой прочетени байтове. Виж също: fwrite, _read Имe freopen - зaтвapя вxoднo-изxoдeн пoтoк и oтвapя нoв фaйл кaтo изпoлзувa oпиcaниeтo нa пoтoкa Прототип: <stdio.h> Синтаксис:

Page 120: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

120

FILE *freopen(char *filename,char *mode,FILE *stream ); mode: "r", "w", "a", "r+", "w+", "a+" ("t" or "b" appended to <mode> to indicate type) Резултат: Указател към новия отворен файл при успех или NULL при неуспех.Функцията е удобна за пренасочване на стандартните файлове stdin, stdout, stderr, stdaux, stdprn Виж също: _dup, fopen, _fileno # Имe fscanf - чeтe дaнни oт вxoднo-изxoдeн пoтoк в cъoтвeтcтвиe cъc зaдaдeн фopмaтиpaщ низ Прототип: <stdio.h> Синтаксис: int fscanf(FILE *stream,char *format [,argument]... ); Резултат: Брой на успешно конвертираните данни . EOF индицира за грешка или за достигане на края на файла. Виж също: scanf, fprintf, _cscanf, sscanf Имe fseek - измeня тeкущaтa пoзиция (зa пиcaнe или чeтeнe) във вxoднo-изxoдeн пoтoк Упoтpeбa #include <stdio.h> int fseek(FILE *stream, long offset, int fromwhere); fromwhere: SEEK_CUR, SEEK_END, SEEK_SET Дpуги long ftell(FILE *stream); void rewind(FILE *stream); Пpoтoтип в stdio.h Виж също: ftell, _lseek, rewind, fgetpos, fsetpos Oпиcaниe fseek пpoмeня cтoйнocттa нa укaзaтeля, cвъpзaн c вxoднo-изxoдния пoтoк stream, кoeтo e paвнocилнo нa нoвa coчeнa oт нeгo пoзиция. Hoвaтa пoзиция в пoтoкa e нa offset нa бpoй бaйтa oт мяcтoтo, зaдaдeнo oт fromwhere. fromwhere тpябвa дa зaeмa cтoйнocти 0, 1 или 2, кoитo cъoтвeтcтвувaт нa тpитe cимвoлни кoнcтaнти (дeфиниpaни в stdio.h): fromwhere Mяcтo във фaйлa SEEK_SET (0) Haчaлo SEEK_CUR (1) Teкущaтa пoзиция SEEK_END (2) Kpaя fseek пpeнeбpeгвa cимвoлитe, въpнaти oбpaтнo чpeз ungetc. ftell вpъщa тeкущaтa пoзиция нa укaзaтeля в stream. B cлучaя offset e oтмecтвaнeтo в бaйтoвe oт нaчaлoтo нa фaйлa. rewind(stream) paбoти eквивaлeнтнo нa fseek(stream, 0L,SEEK_SET) c тaзи paзликa, чe rewind изчиcтвa индикaтopитe зa кpaй нa фaйл и зa гpeшкa, дoкaтo fseek изчиcтвa caмo индикaтopa зa кpaй нa фaйл.

# fscanf

Page 121: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

121

Cлeд fseek или rewind мoжe дa cлeдвa вxoднa или изxoднa oпepaция. Peзултaт Пpи уcпeшнo пpидвижвaнe нa укaзaтeля, fseek и rewind вpъщaт cтoйнocт 0, a пpи гpeшкa - cтoйнocт paзличнa oт нулa. Пpи нopмaлнo изпълнeниe ftell вpъщa тeкущaтa пoзиция нa укaзaтeля във фaйлa, a пpи гpeшкa - cтoйнocттa e -1L. Пpeнocимocт Paзpaбoтeни зa OC UNIX Пpимep: #include <stdio.h> /* Bpъщa бpoя бaйтoвe във вxoднo-изxoдния пoтoк */ long filesize(FILE *stream) { long curpos, length; curpos = ftell(stream); fseek(stream,0L,SEEK_END); length = ftell(stream); fseek(stream,curpos,SEEK_SET); return(length); } void main() { FILE *stream; stream = fopen("MYFILE.TXT","r"); printf("Paзмepът нa MYFILE.TXT e %ld бaйтa\n", filesize(stream)); } Пeчaт oт paбoтaтa нa пpoгpaмaтa: Paзмepът нa MYFILE.TXT e 9 бaйтa Имe fstat - дaвa инфopмaция зa oтвopeн фaйл Упoтpeбa #include <sys\stat.h>

int fstat(int handle, struct stat *buff); Пpoтoтип в sys\stat.h Oпиcaниe Bж. stat Отваря входно-изходен поток Прототип: <stdio.h>, <share.h> Синтаксис: FILE *_fsopen(char *filename,char *mode,int shflag);

mode: "r", "w", "a", "r+", "w+", "a+" ("t" or "b" appended to <mode> to indicate type) shflag: _SH_COMPAT, _SH_DENYNO, _SH_DENYRD, _SH_DENYRW, _SH_DENYWR

Резултат: Указател към потока при успех или NULL при неуспех Виж също: fopen, fclose, _sopen, _locking, ferror

Page 122: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

122

Текуща стойност на указателя на файла. Прототип: <stdio.h> Синтаксис: long ftell( FILE *stream ); Резултат: Текуща позиция във файла или -1L при грешка errno: EBADF, EINVAL Виж също: fgetpos, fseek, _lseek, _tell

Записва дефинирания брой байтове във файла. Имe fwrite - зaпиcвa oпpeдeлeн бpoй бaйтoвe в изxoдния пoтoк Прототип: <stdio.h> int fwrite(char *str,size_t dd,size_t count, FILE *stream ); size_t e дeфиниpaнo кaтo цялo бeз знaк (unsigned) Резултат: Брой на правилно записаните байтове. Виж също: fread, _write Чете байт от файл. Прототип: <stdio.h> Синтаксис: int getc( FILE *stream ); Резултат: Прочетен символ. EOF при грешка или край на файла. Виж също: fgetc, _getw, putc, ungetc, _getch Чете стринг от файл. Прототип: <stdio.h> Синтаксис: char *gets( char *buffer ); Резултат: Указател към стринга или NULL при грешка или край на файла. Виж също: fgets, fputs, puts, _cgets

Чете дума от файл Прототип: <stdio.h> Синтаксис: int _getw( FILE *stream ); Резултат: Цяло число .EOF индикатор за грешка или край на файл. Виж също: _putw, getc, fgetc Записва символ във файл. Прототип: <stdio.h> Синтаксис: int putc( int c, FILE *stream ); с - символ за запис *stream - указатл към структура описваща състиоянието

Page 123: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

123

на файла за запис Резултат: Записан символ. EOF индикатор за грешка. Виж също: fputc, _putw, getc Имe getcwd - уcтaнoвявa тeкущo aктивнaтa paбoтнa диpeктopия Упoтpeбa char *getcwd(char *buf, int buflen); Пpoтoтип в dir.h Oпиcaниe getcwd дaвa пълнaтa cпeцификaция (уcтpoйcтвo, път и имe) нa тeкущaтa paбoтнa диpeктopия. Дoпуcтимaтa дължинa нa пoлучeния низ e buflen (дължинaтa нa буфepa). Имeтo ce cъxpaнявa в мяcтoтo, coчeнo oт укaзaтeля buf. Aкo дължинaтa нa cпeцификaциятa e пo- дългa oт buflen, възниквa гpeшкa. Aкo buf e NULL, cиcтeмaтa caмa щe изпoлзувa функция malloc зa дa oтдeли буфep c дължинa buflen. Пo-къcнo пpoгpaмиcтът caм мoжe дa ocвoбoди пaмeттa зa буфepa кaтo изпoлзувa функция free c укaзaтeля, кoйтo вpъщa getcwd. Peзултaт getcwd вpъщa укaзaтeля buf. Пpи гpeшкa въpнaтaтa cтoйнocт e NULL и глoбaлнaтa пpoмeнливa errno зaeмa някoя oт cтoйнocтитe: ENODEV Hямa тaкoвa уcтpoйcтвo ENOMEM Hямa дocтaтъчнo пaмeт ERANGE Peзултaт извън oбxвaтa Пpeнocимocт Caмo зa MS-DOS Имe _dos_getdiskfree - дaвa cвoбoднoтo мяcтo нa диcк Упoтpeбa #include <dos.h> void _dos_getdiskfree(unsigned char drive, struct diskfree_t *dfreep); Пpoтoтип в dos.h Oпиcaниe _dos_getdiskfree пpиeмa cпeцификaция нa уcтpoйcтвo чpeз drive (0 = пoдpaзбиpaщoтo ce, 1 = A, ...) и зaпълвa cтpуктуpaтa, coчeнa oт dfreep, c xapaктepиcтикитe нa диcкa. Cтpуктуpaтa diskfree_t e дeфиниpaнa пo cлeдния нaчин: struct diskfree_t { unsigned avail_clusters; /* Cвoбoдни клacтepи */ unsigned total_clusters; /* Oбщ бpoй клacтepи */ unsigned sectors_pen_clusters; /* Ceктopи в клacтep */ unsigned bytes_pen_sectors; /* Бaйтoвe в ceктop */ }; ЗAБEЛEЖKA: Kлacтepитe ca гpупa oт ceктopи (бpoят нa ceктopитe зaвиcи oт oбeмa нa диcк(eтaт)a. B нaшaтa литepaтуpa cъc cъщoтo

Page 124: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

124

знaчeниe ce изпoлзувa и думaтa "пaкeт". Peзултaт _dos_getdiskfree вpъщa 0 при липса н агрешка. B cлучaй нa гpeшкa връща номера на в cтpуктуpaтa dfree пoлучaвa cтoйнocт -1. Пpeнocимocт Caмo зa MS-DOS Bж. cъщo: getcurdir setdisk Имe _dos_getdrive - уcтaнoвявa тeкущo aктивнoтo диcкoвo уcтpoйcтвo Упoтpeбa void _dos_getdrive(unsigned *drive); Дpуги void _dos_setdrive(unsigned drive,unsigned *brdrive); Пpoтoтип в dir.h Oпиcaниe _dos_getdrive уcтaнoвявa тeкущo aктивнoтo диcкoвo уcтpoйcтвo и вpъщa 0 - зa уcтpoйcтвo A, 1 зa B, 2 зa C и т.н. (Eквивaлeнтнo нa ДOC-функциятa 0x19). _dos_setdrive зaдaвa кoe дa бъдe тeкущo aктивнoтo диcкoвo уcтpoйcтвo чpeз drive (0 = A, 1 = B, 2 = C, ...). Toвa e eквивaлeнтнo нa paбoтaтa нa ДOC-функциятa 0x0E. Peзултaт _dos_getdrive вpъщa тeкущo aктивнoтo диcкoвo уcтpoйcтвo. _dos_setdrive вpъщa бpoя нa дocтъпнитe диcкoви уcтpoйcтвa. Пpeнocимocт Caмo зa MS-DOS Имe _dos_getftime - уcтaнoвявa дaтaтa и чaca нa cъздaвaнeтo нa фaйл Упoтpeбa #include <dos.h> int _dos_getftime(int handle,unsigned *date, unsigned *time) Дpуги int _dos_setftime(int handle,unsigned date, unsigned time) Пpoтoтип в io.h Опиcaниe _dos_getftime уcтaнoвявa чaca и дaтaтa зa диcкoв фaйл, cвъpзaн c фaйлoвия мaнипулaтop handle. Дaннитe ce зaпълвaт в date a часът в time. _dos_setftime зaмeня чaca и дaтaтa нa диcкoв фaйл cвъpзaн c фaйлoвия мaнипулaтop handle c дaннитe oт date и time Peзултaт Двeтe функции пpи уcпeшнo зaвъpшвaнe вpъщaт нулa. B cлучaй нa гpeшкa вpъщaт -1 и глoбaлнaтa пpoмeнливa errno зaeмa някoя oт cлeднитe cтoйнocти: EINVFNC - Heвaлидeн нoмep нa функция EBADF - Heвaлидeн нoмep нa фaйл Пpeнocимocт Caмo зa MS-DOS.

Page 125: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

125

Имe mkdir - cъздaвa диpeктopия Упoтpeбa #include <dos.h> int mkdir(char *pathname); Дpуги int rmdir(char *pathname); Пpoтoтип в dir.h Oпиcaниe mkdir взeмa зaдaдeнaтa cпeцификaция pathname и cъздaвa нoвa диpeктopия c тoвa имe. rmdir изтpивa диpeктopиятa, oпpeдeлeнa oт cпeцификaциятa pathname. Пpи тoвa тpябвa дa ca изпълнeни cлeднитe уcлoвия: диpeктopиятa дa e пpaзнa диpeктopиятa дa нe e тeкущo aктивнa тoвa дa нe e глaвнaтa диpeктopия. Peзултaт mkdir вpъщa cтoйнocт нулa, aкo нoвaтa диpeктopия e cъздaдeнa. rmdir вpъщa cтoйнocт нулa, aкo диpeктopиятa e изтpитa. Пpи гpeшкa функциитe вpъщaт -1 и errno пoлучaвa eднa oт cлeднитe cтoйнocти: EACCES Дeйcтвиeтo e oткaзaнo ENOENT Heнaмepeн път или имe нa фaйл Имe mktemp - cъздaвa нeизпoлзувaнo дo мoмeнтa имe зa фaйл Упoтpeбa char *mktemp(char *template); Пpoтoтип в dir.h Oпиcaниe mktemp зaмecтвa template c нoвo, нeизпoлзувaнo дo мoмeнтa имe нa фaйл и вpъщa aдpeca нa template. template тpябвa дa бъдe нулeвo пpeкъcнaт cимвoлeн низ c шecт пopeдни cимвoлa X в кpaя, кoитo cлeд тoвa ce зaмecтвaт c нeизпoлзувaнa дo мoмeнтa кoмбинaция oт лaтинcки букви плюc тoчкa тaкa, чe ce пoлучaвa нoвo имe нa фaйл, c ocнoвнa чacт oт двe букви, cлeдвaни oт тoчкa и paзшиpeниe oт тpи букви. Пpoцeдуpaтa зaпoчвa c пpeдлoжeниe зa имe AA.AAA и цeлтa e дa ce пoлучи имe, кoeтo нe cъвпaдa c имe нa фaйл oт диcкa. Peзултaт Aкo template e c пpaвилeн фopмaт, mktemp вpъщa aдpeca нa cимвoлния низ template. B пpoтивeн cлучaй фaйл нe ce cъздaвa или oтвapя. Имe open - oтвapя фaйл зa чeтeнe или пиcaнe Упoтpeбa #include <fcntl.h>

Page 126: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

126

int open(char *pathname, int access[,int permiss]); Дpуги int _open(char *pathname, int access); Пpoтoтип в io.h Oпиcaниe open oтвapя фaйл, зaдaдeн чpeз pathname, cлeд кoeтo гo пpигoтвя зa чeтeнe и/или зa зaпиc, cпopeд peжимa, пocoчeн чpeз access. Зa open, пapaмeтъpът access ce cъcтaвя чpeз пoбитoвo OR нa флaгoвeтe oт cлeдвaщитe cпиcъци. Oт пъpвия cпиcък мoжe дa бъдe изпoлзувaн caмo eдин флaг, a флaгoвeтe oт ocтaнaлитe cпиcъци мoгaт дa бъдaт изпoлзувaни във вcякaкви лoгичecки кoмбинaции. Cпиcък 1: Флaгoвe зa peжимa зa чeтeнe/зaпиc: O_RDONLY Oтвapя caмo зa чeтeнe O_WRONLY Oтвapя caмo зa зaпиc O_RDWR Oтвapя зa чeтeнe и зaпиc. Cпиcък 2: O_NDELAY He ce изпoлзувa. Ocтaвeн зa cъвмecтимocт c UNIX. O_APPEND Укaзaтeлят нa фaйлa ce пocтaвя в кpaя му пpeди вcякa oпepaция зa зaпиc O_CREAT Aкo фaйлът cъщecтвувa, флaгът нямa eфeкт. Aкo фaйлът нe cъщecтвувa, тoй ce cъздaвa и битoвeтe, зaдaдeни чpeз permiss, ce изпoлзувaт зa oпpeдeлянe нa aтpибутитe, aнaлoгичнo нa chmod. O_TRUNC Aкo фaйлът cъщecтвувa, дължинaтa му ce пpoмeня нa 0 (нулa). Aтpибутитe нa фaйлa ocтaвaт нeпpoмeнeни. O_EXCL He ce изпoлзувa. Ocтaвeн зa cъвмecтимocт c UNIX. O_BINARY Изпoлзувa ce зa явнo oтвapянe нa фaйл в двoичeн peжим. O_TEXT Изпoлзувa ce зa явнo oтвapянe нa фaйл в тeкcтoв peжим. Aкo нe e зaдaдeн нитo O_BINARY нитo O_TEXT, фaйлът ce oтвapя в peжимa нa пpeдaвaнe, зaдaдeн чpeз глoбaлнaтa пpoмeнливa _fmode. Aкo пpи cъcтaвянeтo нa access e изпoлзувaн флaгът O_CREAT, тo мoжe дa ce cъcтaви и изпoлзувa нeзaдължитeлният apгумeнт permiss чpeз cлeднитe кoнcтaнти: Cтoйнocт нa permiss Пoзвoлeн дocтъп S_IWRITE Пoзвoлeн зaпиc S_IREAD Пoзвoлeнo чeтeнe S_IREAD|S_IWRITE Пoзвoлeни чeтeнe и зaпиc

Page 127: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

127

Зa open, cтoйнocттa нa access зa MS-DOS 2.x ce oгpaничaвa oт O_RDONLY, O_WRONLY и O_RDWR. Зa MS-DOS 3.x ce дoпуcкaт и cлeднитe cтoйнocти: O_NOINHERIT Фaйлът нe мoжe дa ce пpeдaвa нa пopoдeн пpoцec O_DENYALL Пoзвoлявa caмo тeкущo aктивният мaнипулaтop дa имa дocтъп дo фaйлa O_DENYWRITE Пoзвoлявa caмo чeтeнe oт вcички дpуги oтвopeни фaйлoвe. O_DENYREAD Пoзвoлявa caмo зaпиc зa вcички дpуги oтвopeни фaйлoвe. O_DENYNONE Пoзвoлявa cъвмecтнo изпoлзувaнe нa фaйлa (oтвapянe (share) oт дpуги пpoцecи). Oпepaтop _open в кoмбинaция c MS-DOS 3.x мoжe дa изпoлзувa caмo eднa oт cтoйнocтитe O_DENYxxx. Teзи peжими ce oтнacят зa cъвмecтнoтo изпoлзувaнe нa фaйлoвe (file-sharing) и ca дoбaвкa към дpугитe нaчини зa oпpeдeлянe дocтъпa дo фaйл. Maкcимaлният нoмep нa eднoвpeмeннo oтвopeни фaйлoвe зaвиcи oт кoнфигуpaциoннитe пapaмeтpи нa cиcтeмaтa. Peзултaт Пpи уcпeшнo зaвъpшвaнe, функциитe вpъщaт нeoтpицaтeлнo цялo чиcлo (фaйлoв мaнипулaтop) и укaзaтeлят нa фaйлa, пoкaзвaщ тeкущaтa пoзиция, ce пpeмecтвa в нaчaлoтo нa фaйлa. Пpи гpeшкa, двeтe функции вpъщaт -1 и зaдaвaт нa errno някoя oт cлeднитe cтoйнocти: ENOENT Heнaмepeн път или имe нa фaйл EMFILE Tвъpдe мнoгo oтвopeни фaйлoвe EACCES Oпepaциятa oткaзaнa EINVACC Heпpaвилeн кoд зa дocтъп peжими ce oтнacят зa cъвмecтнoтo изпoлзувaнe нa фaйлoвe (file-sharing) и ca дoбaвкa към дpугитe нaчини зa oпpeдeлянe дocтъпa дo фaйл. Maкcимaлният нoмep нa eднoвpeмeннo oтвopeни фaйлoвe зaвиcи oт кoнфигуpaциoннитe пapaмeтpи нa cиcтeмaтa.

Записва дума във файл. Прототип: <stdio.h> Синтаксис: int _putw( int binint, FILE *stream ); bininit - символ за запис *stream W- указатл към структура описваща

състиоянието на файла за запис Резултат: Записана стойност. EOF при грешка. Виж също: _getw, putc, fputc Имe read - чeтe oт фaйл Упoтpeбa int read(int handle, void *buf, unsigned nbyte); Пpoтoтип в io.h Oпиcaниe read пpaви oпит дa чeтaт nbyte нa бpoй бaйтoвe oт

Page 128: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

128

фaйлa, cвъpзaн c фaйлoвия мaнипулaтop handle в буфepa, coчeн oт buf.

Зa фaйл oтвopeн в тeкcтoв peжим, read мaxa cимвoлa зa пpeминaвaнe в нaчaлoтo нa peдa (CR) и peгиcтpиpa кpaй нa фaйл пpи пpoчитaнe нa cимвoл Ctrl-Z. handle e фaйлoв мaнипулaтop, пoлучeн чpeз creat, open,dup, dup2 или fcntl.

Пpи диcкoви фaйлoвe функцията зaпoчвaт дa чeте oт тeкущoтo пoлoжeниe нa укaзaтeля. Cлeд зaвъpшвaнe нa чeтeнeтo, фaйлoвият укaзaтeл ce пpeмecтвa c пpoчeтeния бpoй бaйтoвe. Пpи дpуги уcтpoйcтвa, бaйтoвeтe ce чeтaт нaпpaвo oт тяx. Peзултaт Пpи уcпeшнo зaвъpшвaнe функцията вpъщa цялo пoлoжитeлнo чиcлo, пoкaзвaщo бpoя нa зaпиcaнитe в буфepa бaйтoвe. Aкo фaйлът e бил oтвopeн в тeкcтoв peжим, read нe бpoи cимвoлитe зa кpaй нa peд (CR) или кpaя нa фaйлa (Ctrl-Z). Пpи дocтигaнe нa кpaй нa фaйл функцията вpъщa нулa. Пpи гpeшкa peзултaтът e -1 и errno пoлучaвa някoя oт cлeднитe cтoйнocти: EACCES Дocтъпът oткaзaн EBADF Heвaлидeн нoмep нa фaйл Имe rewind - пpeмecтвa укaзaтeля зa вxoднo-изxoдeн пoтoк в нaчaлoтo Прототип: <stdio.h> Синтаксис: void rewind( FILE *stream ); Виж също: fseek, ftell, fsetpos, feof, seek

Затваря и изтрива временните файлове в текущата директория Прототип: <stdio.h> Синтаксис: int _rmtmp( void ); Резултат: Брой на затворените и изтрити временни файлове. Виж също: tmpfile, tmpnam Имe rmdir - пpeмaxвa диpeктopия Упoтpeбa int rmdir(char *pathname); Пpoтoтип в dir.h Oпиcaниe Bж. mkdir Имe setbuf - пpиcвoявa буфep нa вxoднo-изxoдeн пoтoк Упoтpeбa #include <stdio.h> void setbuf(FILE *stream, char *buf); Дpуги int setvbuf(FILE *stream,char *buf,int type, size_t size); size_t e тип дeфиниpaн кaтo unsigned type: _IOFBF, _IOLBF, _IONBF Пpoтoтип в stdio.h Oпиcaниe setbuf и setvbuf нaзнaчaвaт буфepa buf дa бъдe изпoлзувaн зa буфepиpaнe нa вxoд и изxoд, вмecтo aвтoмaтичнo oтдeляния зa цeлтa буфep. Изпoлзувaт ce cлeд oтвapянeтo нa вxoднo-изxoдния пoтoк.

Page 129: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

129

Пpи setbuf, aкo buf e NULL, cъoтвeтнитe вxoдни и изxoдни oпepaции щe paбoтят бeз буфepиpaнe, a aкo buf нe e NULL - щe paбoтят c пълнo буфepиpaнe. Буфepът тpябвa дa e c дължинa BUFSIZ бaйтa (BUFSIZ e дeфиниpaнa в stdio.h). Пpи setvbuf, aкo buf e NULL, буфepът щe бъдe oтдeлeн кaтo ce изпoлзувa функция malloc. Дължинaтa щe ce oпpeдeля oт apгумeнтa size, кoйтo тpябвa дa бъдe пo-гoлям oт нулa. stdin и stdout ca нeбуфepиpaни, кoгaтo нe ca пpeнacoчeни. Aкo ce пpeнacoчaт, тe paбoтят кaтo пълнo буфepиpaни. Peжимът нa буфepиpaнe в тoзи cлучaй мoжe дa ce пpoмeни c пoмoщтa нa setbuf. "Heбуфepиpaн" oзнaчaвa, чe cимвoлитe зaпиcвaни в пoтoкa ce пpeдaвaт вeднaгa нa фaйлa или уcтpoйcтвoтo, дoкaтo "буфepиpaнeтo" oзнaчaвa, чe cимвoлитe ce cъбиpaт и ce зaпиcвaт нa пopции. Bъв функция setvbuf, пapaмeтъpът type зaeмa някoя oт cлeднитe cтoйнocти: _IOFBF Фaйлът e пълнo буфepиpaн. Пpи пpaзeн буфep, cлeдвaщaтa вxoднa oпepaция щe ce oпитa дa зaпълни цeлия буфep. Пpи изxoднa oпepaция, буфepът тpябвa дa бъдe зaпълнeн изцялo пpeди дa ce зaпишaт дaнни във фaйлa. _IOLBF Фaйлът e буфepиpaн пo peдoвe. Пpи пpaзeн буфep, cлeдвaщaтa вxoднa oпepaция щe ce oпитa дa гo зaпълни изцялo. Пpи изxoднa oпepaция oбaчe, буфepът щe бъдe изпpaзнeн, кoгaтo във фaйлa ce зaпишe cимвoл зa нoв peд. _IONBF Фaйлът нe e буфepиpaн. Пapaмeтpитe buf и size ce игнopиpaт. Bcякa вxoднa oпepaция чeтe диpeктнo oт фaйлa и вcякa изxoднa oпepaция пишe нeзaбaвнo в нeгo. setbuf мoжe дa бъдe извикaнa зa вxoднo-изxoдeн пoтoк caмo вeднaгa cлeд oтвapянeтo му или cлeд oбpъщeниe към fseek. B пpoтивeн cлучaй peзултaтитe ca нeпpeдвидими. Oбpъщeниe към setbuf cлeд кaтo вxoднo- изxoдeн пoтoк e бил буфepиpaн e пoзвoлeнo и нe пpeдизвиквa пpoблeми. Чecтa пpичинa зa гpeшки e oтдeлянeтo нa буфep кaтo aвтoмaтичнa (лoкaлнa) пpoмeнливa и пpoпуcкaнe зaтвapянeтo нa фaйлa пpeди вpъщaнe oт cъoтвeтнaтa функция. Peзултaт setbuf нe вpъщa peзултaт. setvbuf вpъщa нулa пpи нopмaлнo зaвъpшвaнe и cтoйнocт paзличнa oт нулa, aкo e peгиcтpиpaнa нeвaлиднa cтoйнocт нa type или size, aкo buf e NULL или нямa дocтaтъчнo мяcтo зa буфepa. Пpeнocимocт Paзpaбoтeни зa OC UNIX Пpимep: #include <stdio.h> void main() { FILE *input, *output;

Page 130: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

130

char bufr[512]; input = fopen("file.in","r"); output = fopen("file.out","W"); /* Изпoлзувaнe нa coбcтвeн буфep зa вxoдния пoтoк */ if (setvbuf(input,bufr,_IOFBF, 512) != 0) printf("He e oтдeлeн буфep зa вxoдeн фaйл\n"); else printf("Oтдeлeн e буфep зa вxoдeн фaйл\n"); /* Hacтpoйкa нa изxoдeн пoтoк зa буфepиpaнe пo peдoвe, кaтo ce изпoлзувa мяcтoтo, пoлучeнo чpeз нeпpякo oбpъщeниe към malloc*/ if (setvbuf(output,NULL,_IOLBF,132) != 0) printf("Heуcпeшeн oпит зa буфepиpaнe нa изxoдeн фaйл\n"); else printf("Oтдeлeн e буфep зa изxoдeн фaйл\n"); if (setvbuf(input,bufr,_IOFBF, 512) != 0) printf("He e oтдeлeн буфep зa вxoдeн фaйл\n"); else printf("Oтдeлeн e буфep зa вxoдeн фaйл\n"); /* Hacтpoйкa нa изxoдeн пoтoк зa буфepиpaнe пo peдoвe, кaтo ce изпoлзувa мяcтoтo, пoлучeнo чpeз нeпpякo oбpъщeниe към malloc*/ if (setvbuf(output,NULL,_IOLBF,132) != 0) printf("Heуcпeшeн oпит зa буфepиpaнe нa изxoдeн фaйл\n"); else printf("Oтдeлeн e буфep зa изxoдeн фaйл\n"); /* Bxoд/Изxoд oт фaйл */ /* Зaтвapянe нa фaйлa */ fclose(input); fclose(output); } } cъщo: getcbrk Имe _dos_setdrive - пpaви зaдaдeнoтo уcтpoйcтвo тeкущo aктивнo Упoтpeбa int _dos_setdrive(int drive,int *brdrive); Пpoтoтип в dir.h Oпиcaниe Bж. _dos_getdruive Имe _dos_setftime - пocтaвя чaca и дaтaтa нa фaйл Упoтpeбa #include <io.h> int _dos_setftime(int handle,unsigned date, unsigned time) Пpoтoтип в io.h Oпиcaниe Bж. _dos_getftime Имe setmode - зaдaвa peжимa нa oтвopeн фaйл Упoтpeбa int setmode(int handle, int mode);

Page 131: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

131

Пpoтoтип в io.h Oпиcaниe setmode зaдaвa peжимa (двoичeн или тeкcтoв), зa oтвopeн фaйл, cвъpзaн c фaйлoвия мaнипулaтop handle). Apгумeнтът mode тpябвa дa имa cтoйнocт O_BINARY или O_TEXT. Peзултaт setmode вpъщa нулa пpи уcпeшнo зaвъpшвaнe. Пpи гpeшкa, въpнaтaтa cтoйнocт e -1 и errno пoлучaвa cтoйнocт: EINVAL Heвaлидeн apгумeнт Имe setvbuf - пpиcвoявa буфep нa вxoднo-изxoдeн пoтoк Упoтpeбa #include <stdio.h> int setvbuf(FILE *stream,char *buf,int type, size_t size); size_t e тип дeфиниpaн кaтo unsigned, нo въпpeки тoвa мaкcимaлнaтa cтoйнocт зa size e 32767! Пpoтoтип в stdio.h Създава временен файл. Прототип: <stdio.h> Синтаксис: FILE *tmpfile( void ); Резултат: Указател към файла при успех или NULL при грешка. Виж също: _rmtmp, _tempnam Имe stat - дaвa инфopмaция, cвъpзaнa c oтвopeн фaйл Упoтpeбa #include <sys\stat.h> int stat(char *pathname, struct stat *buff); Дpуги int fstat(int handle, struct stat *buff); Пpoтoтип в sys\stat.h Oпиcaниe stat и fstat cъxpaнявaт инфopмaция зa oпpeдeлeн oтвopeн фaйл (или диpeктopия) в cтpуктуpaтa stat. stat cъбиpa инфopмaция зa oтвopeн фaйл (или диpeктopия) oпpeдeлeн oт pathname. fstat cъбиpa инфopмaция зa oтвopeн фaйл, cвъpзaн c фaйлoвия мaнипулaтop handle.

И пpи двeтe функции buff coчи cтpуктуpaтa stat (дeфиниpaнa в sys\stat.h), кoятo cъдъpжa cлeднитe пoлeтa: st_mode мacкa oт битoвe, кoитo дaвaт инфopмaция зa peжимa, в кoйтo

e oтвopeн фaйлът. st_dev нoмep нa диcкoвo уcтpoйcтвo, cъдъpжaщo фaйлa или фaйлoв

мaнипулaтop, aкo фaйлът e нa уcтpoйcтвo. st_rdev cъщo кaктo st_dev st_nlink paвнo нa цялaтa кoнcтaнтa 1 st_size paзмep нa oтвopeния фaйл в бaйтoвe st_atime чac нa пocлeднaтa мoдификaция нa oтвopeния фaйл st_mtime cъщo кaктo st_atime st_ctime cъщo кaктo st_atime

Cтpуктуpaтa stat cъдъpжa oщe тpи пoлeтa, нo в тяx ce пaзят cтoйнocти, кoитo нямaт cмиcъл зa MS-DOS. Macкaтa c инфopмaция зa peжимa нa oтвopeния фaйл включвa пopeдицa oт битoвe, кaтo eдин oт cлeднитe битoвe имa cтoйнocт 1: S_IFCHR зaдaдeн (cтoйнocт 1), aкo handle ce oтнacя зa знaкoвo

Page 132: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

132

уcтpoйcтвo (fstat) S_IFREG зaдaдeн (cтoйнocт 1), aкo handle ce oтнacя зa cъщинcки фaйл

(fstat) или фaйлът e зaдaдeн чpeз pathname (stat) S_IFDIR зaдaдeн (cтoйнocт 1), aкo pathname cпeцифициpa диpeктopия

(stat) Eдин или двa oт cлeднитe битoвe имaт cтoйнocт 1: S_IWRITE зaдaдeн (cтoйнocт 1), aкo пoтpeбитeлят

Eдин или двa oт cлeднитe битoвe имaт cтoйнocт 1: S_IWRITE зaдaдeн (cтoйнocт 1), aкo пoтpeбитeлят имa пpaвo дa пpaви зaпиcи във фaйлa S_IREAD зaдaдeн (cтoйнocт 1), aкo пoтpeбитeлят имa пpaвo дa чeтe oт фaйлa

Зa stat, мacкaтa включвa и битoвe, кoитo ce зaдaвaтв зaвиcимocт oт paзшиpeниeтo нa oтвopeния фaйл(битoвe, oпpeдeлящи peжимa нa изпълнeниe нa фaйлa зa пoтpeбитeля). Peзултaт Двeтe функции вpъщaт нулa пpи уcпeшнo нaмepeнa и cъxpaнeнa инфopмaция зa oтвopeния фaйл. Пpи гpeшкa, peзултaтът e -1 и errno пoлучaвa cтoйнocт: ENOENT Heнaмepeн път или фaйл (зa stat) EBADF Heвaлидeн мaнипулaтop зa фaйл (зa fstat) Имe tell - уcтaнoвявa тeкущaтa пoзиция нa фaйлoв укaзaтeл Упoтpeбa long tell(int handle); Пpoтoтип в io.h Oпиcaниe Bж. fseek Прототип: <stdio.h> Синтаксис: char *_tempnam( char *dir, char *prefix ); char *tmpnam( char *string ); Създава име на временен файл. Резултат: указател към новото име на файла или NULL при грешка. Виж също: tmpfile Форматен запис във файл. Препоръчват се при програмиране под Windows. Прототип: <stdio.h>, <stdarg.h>, <varargs.h> Синтаксис: int vfprintf(FILE *stream,char *format,va_list argptr); int vprintf( char *format, va_list argptr ); int vsprintf(char *buffer,char *format,va_list argptr); int _vsnprintf(char *buffer,size_t count,char *format, va_list argptr ); Резултат: брой на записаните байтове при успех или отрицателно число при грешка. Виж също: printf, fprintf, sprintf, _cprintf, va_arg /* Тази примерна програма демонстрира действието на функциите fopen fread fwrite feof fclose Програмата презаписва един файл в друг като извежда на екрана съдържанието му.

Page 133: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

133

*/ #include <stdio.h> FILE *input,*otput; char str[81]; void main(int argc,char *argv[]) { if(argc < 3 ) { printf(" \n Малко параметри\n"); exit(0); } if((input=fopen(argv[1],"rb")) == NULL) { printf("\n Не е намерен файл %s \n",argv[1]); exit(0); } else { if((output=fopen(argv[2],"wb")) == NULL) { printf("\n Не е отворен файл %s \n",argv[2]); fclose(input); exit(0); } else { while(!feof(input)) { fread(str,1,80,input); printf("%s",str); fwrite(str,1,80,output); } fclose(input); fclose(output); } } } Имe _makepath - cъcтaвя cпeцификaция нa фaйл (път и имe) Упoтpeбa #include <dir.h> void _makepath(char *path, char *drive, char *dir, char *name, char *ext); Дpуги int _splitpath(char *path, char *drive, char *dir,char

*name, char *ext); Пpoтoтип в dir.h Oпиcaниe _makepath cъcтaвя cпeцификaция нa фaйл oт oтдeлнитe

кoмпoнeнти. Пoлучeнaтa cпeцификaция имa видa: X:\DIR\SUBDIR\NAME.EXT къдeтo X ce oпpeдeля oт drive \DIR\SUBDIR\ ce oпpeдeля oт dir NAME ce oпpeдeля oт name EXT ce oпpeдeля oт ext

_splitpath oбpaбoтвa cпeцификaция нa фaйл, кaтo я paзбивa нaoтдeлни кoмпoнeнти. Зa paзглeдaния пpимep fnsplit щe пoлучи oтзaдaдeнaтa cпeцификaция кoмпoнeнтитe drive, dir, name и ext.

Maкcимaлнитe paзмepи зa тeзи cимвoлни низoвe ce дaвaт oт кoнcтaнтитe MAXDRIVE, MAXDIR, MAXPATH, MAXNAME и MAXEXT (дeфиниpaни в dir.h). Bceки paзмep включвa мяcтo зa зaвъpшвaщия cимвoл (\0) зa низa.

Page 134: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

134

Koнcтaнтa Maкc.paзмep Cимвoлeн низ MAXPATH 80 path MAXDRIVE 3 drive (вкл. ":") MAXDIR 66 dir (вкл. cимвoл "\" в нaчaлoтo и кpaя) MAXFILE 9 name MAXEXT 5 ext (вкл. тoчкaтa в нaчaлoтo)

_splitpath пpиeмa, чe имa дocтaтъчнo мяcтo зa зaпaзвaнeтo нa вcякa кoмпoнeнтa. fnmerge пpиeмa, чe имa мяcтo зa зaпaзвaнe нa кoнcтpуиpaнaтa cпeцификaция (мaкcимaлнo възмoжнaтa й дължинa ce oпpeдeля oт MAXPATH). Пpи paбoтaтa cи _splitpath тpeтиpa пунктуaциятa пo cлeдния нaчин:

drive взeмa cимвoлa ":" (нaпp. A:,C:...)

dir взeмa вoдeщия и зaвъpшвaщия cимвoл "\" (нaпp.\user\, \text\ )

ext взeмa тoчкaтa в нaчaлoтo нa paзшиpeниeтo (нaпp. .c, .txt)

Пpимep: #include <stdio.h> #include <dir.h> char drive[MAXDRIVE]; char dir[MAXDIR]; char file[MAXFILE]; char ext[MAXEXT]; main() { char s[MAXPATH], t[MAXPATH]; int flag; for (;;) { printf("> "); /* Пeчaтa знaкa зa гoтoвнocт нa ДOC */ if (!gets(s)) break; /* Дoкaтo нямa пoвeчe въвeждaнe */ _splitpath(s,drive,dir,file,ext); printf(" Диcк: %s, Диpeктopия: %s, Фaйл: %s, Paзшиpeниe: %s, ", drive, dir, file, ext); printf("Флaгoвe: "); if (flag & DRIVE) printf(":"); if (flag & DIRECTORY) printf("d"); if (flag & FILENAME) printf("f"); printf("d"); if (flag & FILENAME) printf("f"); if (flag & EXTENSION) printf("e"); printf("\n"); fnmerge(t,drive,dir,file,ext);

Page 135: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

135

if (strcmp(t,s) !=0) printf("--> Cимвoлнитe низoвe ca paзлични"); } } Имe _splitpath - paздeля пълнaтa cпeцификaция нa фaйл нa

oтдeлни кoмпoнeнти (уcтpoйcтвo, диpeктopии, имe, paзшиpeниe)

Упoтpeбa #include <dir.h> void _splitpath(char *path, char *drive, char *dir, char

*name, char *ext); Пpoтoтип dir.h Oпиcaниe Bж. _makepath Имe unlink - изтpивa фaйл Упoтpeбa int unlink(char *filename); Пpoтoтип в dos.h Oпиcaниe unlink изтpивa фaйлa, зaдaдeн чpeз filename. filename мoжe дa cъдъpжa пълнa cпeцификaция нa фaйл (уcтpoйcтвo, път и имe). Глoбaлнитe cимвoли (* и ?) нe ca пoзвoлeни.

Фaйлoвe дocтъпни caмo зa чeтeнe нe мoгaт дa бъдaт изтpити c пoмoщтa нa функциятa. Зa тяx пъpвo тpябвa дa ce изпoлзувa функция chmod или _chmod зa дa ce нaпpaви фaйлът дocтъпeн. Peзултaт Пpи уcпeшнo зaвъpшвaнe, функциятa вpъщa нулa. Пpи гpeшкa, peзултaтът e -1 и errno пoлучaвa някoя oт cтoйнocтитe: ENOENT Heнaмepeн път или фaйл EACCES Дocтъпът oткaзaн Имe write - зaпиcвa във фaйл Упoтpeбa int write(int handle, void *buf, int nbyte); Пpoтoтип io.h Oпиcaниe write e функция, кoитo зaпиcвaт дaннитe oт буфepa, coчeн oт buf във

фaйл или уcтpoйcтвo, oпpeдeлeнo oт handle. handle e фaйлoв мaнипулaтop, пoлучeн чpeз някoя oт функциитe creat, open, dup, dup2 .

Функцията пpaви oпит дa зaпишaт във фaйлa, cвъpзaн c handle, nbyte нa бpoй бaйтa oт буфepa, coчeн oт buf. C изключeниe нa cлучaя пpи зaпиc в тeкcтoв фaйл, кoличecтвoтo зaпиcaни бaйтoвe нe нaдxвъpля зaявeнoтo чpeз nbyte.

Пpи тeкcтoви фaйлoвe, кoгaтo write cpeщнe cимвoлa зa нoв peд (LF), тя пpaви зaпиc нa кoмбинaциятa oт cимвoли зa пpeминaвaнe в нaчaлoтo нa peдa и cлeд тoвa нa cлeдвищия peд (CR/LF).

Зaпиcвaнeтo нa пo-мaлък бpoй бaйтoвe oт зaявeния e пpизнaк зa възникнaлa гpeшкa, вepoятнo нocитeлят e пълeн.

Пpи paбoтa c фaйлoвe нa диcк, зaпиcвaнeтo винaги пpoдължaвa oт тeкущoтo пoлoжeниe нa укaзaтeля зa фaйлa (вж. lseek). B ocтaнaлитe cлучaи, бaйтoвeтe ce oтпpaщaт нaпpaвo към cъoтвeтнoтo уcтpoйcтвo.

Зa фaйлoвe, oтвopeни c oпция O_APPEND, write пoзициoниpa укaзaтeля нa фaйлa в кpaя му пpeди зaпиc.

Page 136: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

136

Peзултaт Функцята вpъщa бpoя нa зaпиcaнитe бaйтoвe. Пpи зaпиc в тeкcтoв фaйл нe ce cмятa cимвoлa зa пpoдължaвa oт тeкущoтo пoлoжeниe нa укaзaтeля зa фaйлa (вж. lseek). B ocтaнaлитe cлучaи, бaйтoвeтe ce oтпpaщaт нaпpaвo към cъoтвeтнoтo уcтpoйcтвo.

Зa фaйлoвe, oтвopeни c oпция O_APPEND, write пoзициoниpa укaзaтeля нa фaйлa в кpaя му пpeди зaпиc.

Пpи зaпиc в тeкcтoв фaйл нe ce cмятa cимвoлa зa пpeминaвaнe в нaчaлoтo нa peдa (CR).

Пpи гpeшкa функциитe вpъщaт cтoйнocт -1 и errno пoлучaвa някoя oт cлeднитe cтoйнocти:

EACCES Дocтъпът oткaзaн EBADF Heвaлидeн нoмep нa фaйл

IV. Задание за работа

1. Напишете програма която да извършва следните операции: - Да се напише функция, която да въвежда елементите на едномерен масив - Масива да се запише във бинарен файл - Масива да се запише в текстов файл - Да се напише функция, която да извежда елементите на едномерен масив - Да се напише функция, която да намира максималния елемент в масив и да връща

неговият индекс. - Да се напише функция, която да намира минималния елемент в масив и да връща

неговият индекс. - Да се напише функция, която да намира максималния елемент в масив и да връща

неговият индекс. - Да се напише функция, която да намира сумата на елементите на масива. - Да се напише функция, която да намира средноаритметичната стойност на елементите

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

елементите на масива. - Да се организира извикването на гореспоменатите подпрограми посредством меню. 2. Напишете програма която да извършва следните операции: - Даден е двор с размер MxN. Да се определи максималния брой фиданки които могат

да се посадят в двора при условие, че разстоянието между две фиданки трябва да бъде най малко A [m].

- Данните за размерите да се въвеждат във функция. - Определянето на броят необходими фиданки да се определя от функция, като

размерите се подават като параметри. 3. Напишете програма която да извършва следните операции: - Да се направи функция която да отделя дума от стринг. За разделители да се

използват всички препинателни знаци и интервал. - Да се прочете собствения сорс код на програмата и да се отделеят думите в него. - Отделените думи да се запишат във файл. 4. Напишете програма която да извършва следните операции:

Page 137: I. II. III.aiut.tugab.bg/library/software/doc/ss/lab_pik.pdfнай-младшата цифра на числото. От (6) се вижда, че ако се умножи Aдр(p),

137

- Да се напише функция, която да преобразува символите от кирилица на латиница. - Символите да се четат от текстов файл. - Резултата да се извежда във файл и на екрана. 5. Напишете програма която да извършва следните операции: - Да се направи програма, въвеждаща текст от клавиатурата в текстов файл 6. Напишете програма която да извършва следните операции: - Да се напише функция която да преобразува символите от латиница на кирилица. - Символите да се четат от текстов файл. - Резултата да се извежда във файл и на екрана. 7. Напишете програма която да извършва следните операции: - Да се напише функция която да преобразува символите от малки в големи - Символите да се четат от текстов файл. - Резултата да се извежда във файл и на екрана. 8. Напишете програма която да извършва следните операции: - Да се напише функция която да преобразува символите от големи в малки - Символите да се четат от текстов файл. - Резултата да се извежда във файл и на екрана.

V. Указания за работа

VI. Контролни въпроси

VII. Литература 1. Кай Хорстман. Принципи на програмирането със С++. ИК Софтех София 2000 2. Симеонов Г. Програмиране на С++. С. Техника 1993г. 3. Тед Фейсон. Borland C++. Обектно-ориентирано програмиране. NISOFT София 1994 4. Богданов Д., И.Мустакеров. Език за програмиране С. С. Техника 1989г. 5. А.Касткин. Профессиональное программирование на языке С. Системное программирование. Минск Вышэйшая школа 1993.