Коммерциялық емес акционерлік қоғам

АЛМАТЫ ЭНЕРГЕТИКА ЖӘНЕ БАЙЛАНЫС УНИВЕРСИТЕТІ

Компьютерлік технологиялар кафедрасы

                                                

 

АЛГОРИТМДІК ТІЛДЕРДЕ ПРОГРАММАЛАУ

2--бөлім

5В070400 - «Есептеу техникасы және бағдарламалық қамтамасыз ету» мамандығының

барлық оқу  түрінің студенттері үшін зертханалық жұмыстарды  орындауға арналған әдістемелік нұсқаулар

  

 

 

Алматы, 2012

ҚҰРАСТЫРУШЫЛАР: З.Қ.Құралбаев, З.А.Акижанова. «Алгоритмдік тілдерде  программалау». 2-бөлім.  5В070400 - «Есептеу техникасы және бағдарламалық қамтамасыз ету» мамандығының барлық  түрінің  студенттері үшін зертханалық жұмыстарды  орындауға арналған әдістемелік нұсқаулар. – Алматы: АЭжБУ, 2012. – 33 б.

 

Әдістемелік  нұсқаулар компьютерлік сыныпта «Алгоритмдік тілдерде программалау»  пәні бойынша  зертханалық жұмыстарды орындауға арналған.  Әрбір жұмыстың тақырыбына байланысты  студент алгоритм құрастыру мен Си алгоритмдік тілінде  программа жазуға дағдылануға арналған тапсырмаларды орындауына көмек болатын нұсқаулар келтірілген.

Әдістемелік нұсқаулар 5В070400 – «Есептеу техникасы және бағдарламалық қамтамасыз ету» мамандығының  барлық оқу түрінің студенттеріне арналған.

Әдеб. көрсеткіші – 11 атау.

 

Пікір беруші: физ.-мат. ғыл.канд. А.А Аманбаев

 

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

 

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

Мазмұны 

Кіріспе          

4

№ 1 зертханалық жұмыс. Си тілінде қарапайым алгоритмдерді       программалаудың  негіздері   

4

 

№ 2 зертханалық жұмыс. Циклдық алгоритмдерді программалау    

7  

№ 3 зертханалық жұмыс. Программа мәтініндегі ауыстырулар   

11

№ 4 зертханалық жұмыс. Көрсеткіштер және оларға қолданылатын амалдар     

15 

№ 5 зертханалық жұмыс. Си тілінде бір өлшемді массивтерге байланысты алгоритмдерді программалау                                                

20 

№ 6 зертханалық жұмыс. Массив элементтерінің  көрсеткіштері

24

№7 зертханалық жұмыс.   Көп өлшемді массивтерді  программалау   

27 

№8 зертханалық жұмыс.     Си тілінде мәтіндерді өңдеу

30 

Әдебиеттер тізімі      

33 

 

Кіріспе 

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

Әрбір зертханалық жұмысты орындаудан бұрын студенттер дәрістерде алған теориялық мәліметтермен бірге оқулықтар мен оқу құралдарын оқып, тақырып бойынша қажетті материалдармен танысуы тиіс. Компьютерлік сыныпта жұмысты орындауға кірісерде студент оқытушыдан рұқсат алуы тиіс. Рұқсат алу үшін студент  теориялық білімін көрсетіп, оқытушы берген  бақылау сұрақтарына жауап береді.

Әдістемелік нұсқауларда келтірілген мысалдарды орындағаннан кейін студент оқытушы берген жеке тапсырманы орындап, жұмыс бойынша жазбаша есеп беруі және ол есепті қорғауы тиіс. Зертханалық жұмыс бойынша есеп келесі бөлімдерден тұрады:

1) Зертханалық жұмыстың аты және тапсырманың қойылуы.

2) Есептің алгоритмі.

3) Негізгі белгілеулер.

4) Программаның мәтіні (листингі).

5) Есептің шешімі (нәтижесі).

6) Нәтижені талдау және қорытынды.

 

№1 зертханалық жұмыс. Си тілінде қарапайым алгоритмдерді  программалаудың  негіздері

 

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

 

Бақылау сұрақтары:

1) Қандай препроцессорлық команда программаның мәтініне енгізу-шығару кітапханалық функцияларын енгізеді?

2) Си тіліндегі программа қандай бөлімдерден тұрады?

3)  Оператор алдына белгі не үшін қойылады?

4)  Қандай жағдайда шартсыз өту операторы пайдаланылады?

5)  Бос оператор не үшін қажет?

6) Си  тіліндегі программаның бас функциясы қалай аталады?

7) Функция денесі дегеніміз не?

8) Си тіліндегі программаның орындалу нәтижесін алу үшін қандай функцияны пайдалануға болады?  

9) #include директивасы не үшін қолданылады?        

10) Деректерді клавиатурадан енгізу үшін Си тіліндегі программада қандай функция қолданылады?    

 

Жаттығулар және олардың орындалу үлгілері

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

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

#include<stdio.h>

#include< math.h >

void main()

{    double a,b,c,p,s;

      printf(“\n a=”);   scanf(“%lf”, &a”);

      printf(“\n b=”);   scanf(“%lf”,&b);

      printf(“\n c=”);   scanf(“%lf”, &c;

      p=(a+b+c)/2;   s= sqrt (p*(p-a)*(p-b)*(p-c));

      printf(“\n үшбұрыштың ауданы: s= %f”, s);

}

Программада a,b,c параметрлерінің мәндерін енгізгенде l модификаторы қолданылды. Клавиатурадан осы параметрлердің сан мәндерін енгізіп,  программаның орындалу нәтижесі алынады. Ескеретін жағдай,  енгізілетін үшбұрыштың қабырғаларының ұзындықтарының сан мәндері үшбұрыштың қасиетіне сәйкес болуы тиіс немесе оның екі қабырғасының қосындысы үшіншісінен үлкен болуы керек.

Бұл программада #include<stdio.h> директивасымен қатар препроцессордың #include<math.h> директивасы пайдаланылған. Оның себебі программада математикалық sqrt() функциясының болғандығы. Программада математикалық функция қолданылатын болса, онда стандартты кітапханадағы math.h файлы көрсетілуі тиіс.

2 мысал. Квадрат теңдеуді шешу. Тармақталған алгоритмді жүзеге асырушы программаның мысалы ретінде квадрат теңдеуді шешудің программасы қарастырылсын:

 #include<stdio.h>

#include<math.h>

void main()

{

       double a,b,c,d,x1,x2;

       printf("\n a=");   scanf("%lf",&a);  printf("\n b=");   scanf("%lf",&b);

       printf("\n c=");    scanf("%lf",&c);    d=b*b-4*a*c;

  if(d>=0.0)

       {    x1=(-b+sqrt(d))/(2*a);   x2=(-b-sqrt(d))/(2*a);

             printf("\n Түбірлер: x1=%e,x2=%e",x1,x2);    }

    else    printf("\n Түбірлер жоқ.");    }

Тағы бір ескеретін жай – нақты айнымалыларды сипаттауда float типінің орнына double типін пайдаланған жөн.

 

Өз бетімен орындауға арналған тапсырмалар:

№1 Аргументтің x өзгеру қадамы 0,4  болғанда оның -2 мен 2 аралығындағы мәндеріне сәйкес берілген y=2,4x2+3x-4 функциясының мәндерінің кестесін құрастырыңыз.

№ 2  Келесі мәндер үшін теру санын   есептеу керек: n=12, k=7.

№ 3 Мәндері 200-ден аспайтын, 3-ке бөлінетін натурал сандардың санын анықтау.

№ 4 Келесі қосындыны есептеудің программасын құрастыру керек:

                              S = 1 +  +  +  + . . .                           

Есептеу үрдісін келесі қосылғыш ε = 0.0001 санынан кіші болғанда  ғана тоқтату керек.

         № 5.  Келесі қосындыны есептеу керек:   S = , мұндағы  x=0,125.

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

          № 7 Берілген a және b сандары мен k кодын енгізу керек. Егер  k=1 болса, қатеттері a және b болатын тікбұрыштың ауданын,  k=2 болғанда, қабырғалары a және b болатын тіктөртбұрыштың ауданын,  ал k=3 болғанда, координатасы  x=a, y=b  болатын  нүктеден  координаттар осінің басына дейінгі ара қашықтықты табу керек, ал басқа жағдайларда «код дұрыс емес» деген мәлімдеме шығару керек. 

 № 8. Енгізілген санның және кодтың мәні бойынша дөңгелектің ауданын (код=1), дөңгелектің ұзындығын (код=2), шардың көлемін (код=3), квадраттың ауданын (код=4) есептейтін программа жазу керек. Берілген а саны фигураның радиусы немесе қабырға ұзындығы болып табылады. 

  № 9. Берілген a және b үшін формуланың енгізілген номері бойынша есептейтін программа жазу керек:

   

  № 10. 20-дан 99-ға дейінгі сандарды енгізгенде, сандарды мәтін ретінде шығаратын программа жазу керек. Мысалы, а=51 мәнді мына түрде шығарады «елу бір».

 

№2 зертханалық жұмыс.  Циклдық алгоритмдерді программалау

 

Си тіліндегі циклдық операторлардың  Паскаль тіліндегі операторларға ұқсастығы бар болғанымен, кейбір ерекшеліктері де бар.  Си тілінде  while, for, do қызметтік сөздермен белгіленген цикл операторлары қолданылады.

 

Жұмыс мақсаты:  циклдық алгоритмдерді ұйымдастыру үшін  цикл операторларын пайдаланып, программа құрастыруға машықтану.

 

Бақылау сұрақтары:

1) Си тіліндегі цикл құрастыруға арналған операторлардың жазылу тәртіптерін  атаңыз.

2) Бас функция не үшін қажет?

3)  Шарт өрнегінде қандай жазу көрсетіледі?

4)  Цикл есептегіші ретінде қандай типтегі айнымалыны пайдалануға болады?

5) Қандай операция унарлық  деп аталады?

 

Жаттығулар мен оларды орындаудың мысалдары

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

while (шарт өрнегі)

цикл денесі

1 мысал. Шексіз қосынды. Келесі  шексіз қосындыны берілген дәлдікпен (ε>0) есептеу қарастырылсын:

Белгілеулер туралы  қысқаша түсінік берілсін. Мұндағы   r - қосымша енгізілген идентификатор, ол әрбір қосылғыштың  ағымдағы мәнін келесі формула бойынша анықтайды r=r*x/k; оның алғашқы k=0 үшін мәні 1-ге тең, сондықтан циклдың басына  дейін қосындының  b  мәні 1-ге тең, ал r=x деп алынған. while операторындағы жақшаның ішіне қосындыны есептеудің дәлдігі немесе әрбір ағымдағы қосылғыштың абсолют шамасы  берілген дәлдікті қанағаттандыруы туралы шарт жазылған. Ол шарт екі шарт ретінде жазылған.

Программаның мәтіні келесі түрде жазылады:

#include <stdio.h>

void main()

{

       int k;  double eps, b=1.0, r,x;

       printf("\n Еңгізініз  x=");  scanf(" %lf", &x);

     {   printf("\n берілген дәлдік eps=");   scanf("%lf", &eps);   }

       k=2;   r=x;    while (r > eps || r < -eps)

     {     b=b+r;   r=r*x/k;   k++;   }

   printf("қосындының  b  мәні : %f\n",b);     printf("Қате: %f\n", r);

   printf("Есептелген тізімнің элементтерінің саны : %d\n",k);

   }

Циклдық do операторының ерекшелігі – мұнда шарт өрнегі цикл денесінен кейін жазылады. Сонымен бірге, шарт өрнегін жазғанда  while қызметтік сөзі  қолданылады. Сонымен, циклдық do операторы келесі түрде  жазылады:

do

цикл денесі

while (шарт өрнегі);

2 мысал. Жоғарыда келтірілген программаның мәтіні осы do операторын пайдаланғанда  келесі түрде жазылады:

#include <stdio.h>

void main()

{       int k;  double eps,  b=1.0, r,x;

         printf("\n Еңгізініз  x=");  scanf(" %lf", &x);

         printf("\n берілген дәлдік eps=");  scanf("%lf", &eps);

         k=1;    r=1.0;   

do  {   r=r*x/k;   b=b+r;   k++;   }  while(r>=eps || r<=-eps);

printf("қосындының мәні: %f\n",b); printf("Қате: %f\n", r);  

printf("Есептелген тізімнің элементтерінің саны: %d\n",k);

}

Осы программаның орындалу нәтижесін алдыңғы программаның нәтижесімен салыстыру керек.

 Циклдық оператор for келесі түрде жазылады:

for (өрнек 1; шарт өрнегі2; өрнек 3)

цикл денесі

3 мысал. Жоғарыдағы программаның мәтіні for циклдық операторын пайдаланғанда келесі түрде жазылады:

#include <stdio.h>

void main()

{  int k;  double eps,  b=1.0,  r, x;

    printf("\n Еңгізініз x="); scanf(" %lf", &x);

    printf("\n берілген дәлдік eps="); scanf("%lf", &eps);

        for (k=1, r=1.0; r>eps || r<-eps; k++)   {   r=r*x/k;   b=b+r;  }

     printf("қосындының мәні: %f\n",b);   printf("Қате: %f\n", r);

     printf("Есептелген тізімнің элементтерінің саны: %d\n",k);

}

4 мысал.  Берілгені  нақты сан x. Келесі шектелген қосындыны есептеу керек:

Программаны құрастырудан бұрын келесі белгілеулер енгізілсін:  k – цикл есептегіші, n – есептегіштің ең үлкен мәні, S – есептелінуге тиіс қосынды,  p – кезектегі қосылғыш. Бұл программаның мәтіні келесі түрде жазылады:

#include<stdio.h>

void main()

{

      float x,s,p;  int k,n;

      printf("\n Еңгізініз n=");    scanf("%d",&n);

      printf("\n Еңгізініз x=");   scanf("%f", &x);

      p=x;  s=x;

      for (k=1; k<=n; k++)   {    p=(-p)*x*x/(2*k*(2*k+1));  s+=p;   }

      printf("\ns=%f",s);

  }

Өз бетімен орындауға арналған тапсырмалар:

№1-4 Берілген [a,b] интервалында 10 әртүрлі  х (мұндағы |x|<1) мәндері үшін функцияның мәндерін есептеу және функцияны дәрежелік қатарға жіктеп есептеу керек. Дәрежелік қатар қосындысын екі әдіспен табу: берілген n (10<=n<=20)  қатарындағы элементтер саны үшін және берілген ε дәлдік үшін, шексіз қатар үшін. Келесі түрдегі кесте құру керек:

х

Функцияның дәл мәні

n элементтер қатары үшін функцияның жуық мәні.

ε дәл мәні үшін функцияның жуық мәні. 

 

 

 

 

 

1)

2)

3)

4)

№5  Берілген n үшін тіктөртбұрыштар формуласы бойынша анықталған  интегралды есептеу. Аргумент пент функция мәндерін  (xi, f(xi)) кестесі түрінде шығару. Берілгені келесі интеграл:   (массивтарды қолданбау керек):

  

№6  Нақты c, d, x сандары  және бүтін сан n (c< d) берілген. n  мәні үшін трапеция формуласы арқылы келесі интегралды есептеу керек:  Ол үшін пайдаланылатын трапециялар формуласы  келесі түрде жазылады:

где 

 №7 (a,b) интервалында h қадамымен x аргументі үшін төмендегі функция мәндерін есептеу және экранға шығару  керек:  

.

№8 Бүтін тізбектелген оң сандарды енгізген кезде қосындыны есептеу орындалады. Кез келген теріс сандарды енгізгенде енгізу тоқтатылады.

№9-10 Берілген [a,b] интервалында әртүрлі 10 х (мұндағы |x|<1) мәндері үшін функцияның мәндерін есептеу және функцияны дәрежелік қатарға ыдыратып есептеу. Дәрежелік қатар қосындысын екі әдіспен табу: берілген n (10<=n<=20)  қатарындағы элементтер саны үшін және берілген ε дәлдік үшін, шексіз қатар үшін. Кесте құру:

х

n элементтер қатары үшін функцияның жуық мәні.

ε дәл мәні үшін функцияның жуық мәні. 

 

 

 

 

Берілген функциялар:                      

 

№3 зертханалық жұмыс.  Программа  мәтініндегі ауыстырулар

 

Жұмыс мақсаты: Си тілінде жиі қолданылатын программа мәтініндегі ауыстыруларды орындауға машықтану.

 

Бақылау сұрақтары:

1) Препроцессор дегеніміз не?

2) Препроцессордың негізгі қызметін атаңыз.

3) Мәтінді ауыстыруды қандай препроцессор директивасы орындайды?

4) Идентификаторларды басқа символдармен ауыстырудың қажеті қандай?

5) Директиваның әсері мәтіннің қанша жерімен шектелген?

6) Ауыстыру қандай жағдайларда орындалмайды?

7) Стандартты емес бас тақырыптық файлдарды кім құрастырады?

8) Қандай препроцессорлық директива арқылы мәтін файлдан енгізіледі?

9) #undef С директивасы орындалғанда С идентификаторы қандай мән қабылдайды?

10) Егер математикалық функция қолданылатын болса, программада қандай директива болуы тиіс?

 

Жаттығулар және олардың орындалу үлгілері

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

#define идентификатор ауыстыру_жолдары.

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

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

 Ауыстыру кезінде кейбір шектеулер бар. Ауыстыру келесі  жағдайларда орындалмайды:

-  комментарийлер ішінде;

- жол  константалары ішінде;

-  символдық константалар мен идентификаторлар ішінде.

Басқаша айтқанда, препроцессорлық ауыстырулар тырнақшалармен (“ мен ”), апострофтармен  (‘ мен ’)  және бөлгіштермен  (/* мен */) шектелген мәтіндерге қатысты емес.

Ауыстыруларды жүзеге асыруды көрсету үшін келесі мысал қарастырылсын. Мұнда төрт түрлі ауыстыру қолданылған.

1 мысал. Натурал сан n және нақты сан x берілген. Келесі қосындыны есептеу керек:   

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

#include<stdio.h>

#include<math.h>

#define begin {  #define   end }

#define n 10

#define x 0.5

void main()

        begin   double s,p;  int k;

               for(s =0.0, p =1.0, k=1; k<=n; k++)

                   begin   p*=sin(x);  s+=p;   end

               printf("\n қосындының мәні s=%lf",s);      end

Бұл программада #define арқылы қосылғыштардың санын n немесе нақты сан x-тің мәнін бір жерден өзгертіп, қосындының әртүрлі жағдайдағы мәндерін есептеуге болады.

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

2 мысал. Осы мүмкіншілікті көрсету үшін келесі қарапайым қосындыны табудың программасы қарастырылсын:

Мұндағы  n  және  x  берілген; әрбір келесі қосылғыш p=xk деп белгіленеді.  Әрбір p келесі қосылғышының мәні алдыңғы қосылғыштың мәнін  x-қа көбейту арқылы анықталады. 

Бұл жерде  #define BAS  printf("\n қосындының мәні =%lf",s); директивасы  printf("\n қосындының мәні =%lf",s); функциясының орнына BAS-ты пайдалануға мүмкіншілік береді.  Программаның мәтінін келесі түрде жазуға болады:

#include<stdio.h>

#define BAS  printf("\n қосындының мәні =%lf",s);

void main()

{    int n,k;   double x,s,p;

      printf("\n Еңгізу n=");   scanf("%d",&n);

      printf("\n Еңгізу x=");    scanf("%lf",&x);

      s=0.0; k=1; p=1.0;

        do   {   p=p*x;   s=s+p;   k=k+1;    }       while(k<=n);    BAS;

  }

Программа орындалғаннан кейін қосындының s мәні BAS; арқылы экранға шығарылады. Программаны орындап, нәтижесін алу керек.

Мәтіндегі ауыстыруды болдырмау үшін келесі команданы қолдануға болады:    #undef идентификатор.

Деректердің типтерін енгізу. #define директивасының көмегімен программашы өзінің жеке базалық (негізгі) немесе туынды белгілеулерінің типтерін  енгізе алады. Мысалы,  келесі директива

#define REAL Long double.

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

Өрнектерді ауыстыруКелесі параметрлі макроанықтаманың

#define ат(параметрлер_тізімі)ауыстыру_жолы

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

Макроанықтаманың классикалық мысалы:

#define ABS(X) ( X<0?–(X):X)

бойынша, егер X<0 болса немесе X теріс мән қабылдаса, онда ABS(X)=-X, ал керісінші жағдайда ABS(X)=X. Осындай макроанықтама арқылы программаға айнымалының абсолют шамасын анықтайтын өрнекті енгізуге болады. Мысалы,    ABS(E-Z) конструкциясы келесі өрнекпен ауыстырылады: (E-Z<0?–(E-Z):E-Z). Осының нәтижесінде E-Z өрнегінің абсолюттік мәні анықталады.

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

3 мысал.  Келесі шексіз қосындыны  берілген ε>0 дәлдігімен табу керек болсын:     

Қосындының мәнін анықтау кезектегі қосылғыштың p= абсолют шамасы  берілген ε кішігірім санынан кіші болғанша орындалады немесе келесі теңсіздік орындалғанға дейін:    ‌‌‌‌‌ < ε.

Бұл есептің программасының мәтінін келесі түрде жазуға болады:

#include<stdio.h>

#define eps 0.001

#define REAL  double

#define ABS(x) (x<0?-(x):x)

#define BAS printf("\nҚосындының мәні s=%lf,кезектегі қосылғыш p=%lf",s,p)

void main()

{int k=2;    REAL s, p;     s=0.0;   p=0.5; printf("\n \nРезультат: \n");

while(ABS(p)>eps)

{  s=s+p;  BAS;   p=1.0/k/(k+1.0);    k++;     } }

 

Өз бетімен орындауға арналған тапсырмалар:

Берілген №2 зертханалық жұмыстың тапсыпмаларды келесі өзгертістермен орындау керек:  константаларды еңгізу және результаттарды шығару үшін препроцессордың директиваларды пайдалану.

 

№4 зертханалық жұмыс.  Көрсеткіштер және оларға қолданылатын амалдар

 

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

 

Бақылау сұрақтары:

1) Операция коды дегенді қалай түсінесіз?

2) Машина тіліндегі команда дегеніміз не?

3) Объектінің адресі дегеніміз не?

4) Объектінің адресі қалай бейнеленеді?

5) «Нульдік» көрсеткіш NULL қандай адресті  көрсетеді?

6) Көрсеткішті қалай сипаттауға болады?

7) Көрсеткіш сілтеме жасаған объектінің мәнін қалай алуға болады?

8) Көрсеткіштің адресі қалай анықталады?

9) Бір типке жататын көрсеткіштерді қосуға болады ма?

10) Бүтін мәнді айнымалы орналасқан адрес қанша ұяшықтан тұрады?

 

Жаттығулар және олардың орындалу үлгілері

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

 Си тілінде көрсеткіштерге қолданылатын амалдардың (операциялардың) негізгілері қарастырылсын.

 Меншіктеу операциясы. Меншіктеу операциясының жазылу тәртібі белгілі: операция таңбасының  = сол жағында көрсеткіш аты, ал оң жағында -  көрсеткіш, немесе NULL константасы, немесе сол типке жататын кез-келген объектінің аты орналасқан.  Операцияның орындалу нәтижесінде оң жақтағы  көрсеткіш атына оң жақтағы мән беріледі.

Көрсеткіш сілтеме жасаған объектінің мәнін алу.  Келесі өрнек  *көрсеткіш_аты көрсеткіш анықтаған адрес бойынша орналасқан мәнді алуды қамтамасыз етеді. Егер date 2007 болса, одан кейін ол адрес m және   k көрсеткіштеріне берілсе, онда  *k немесе *m мәні 2007 болады. Осыдан мынадай нәтиже шығады: date айнымалысының аты, k,m   көрсеткіштерінің  адресін анықтаушылар *k,*m  бір ғана адресті көрсетеді, ол  date  айнымалысының мәніне бөлінген адрес. Ал келесі операциялардың кез-келгені *k=өрнек, *m =өрнек, date=өрнек ЭЕМ-нің жадысының белгілі бір ғана бөлігіндегі мәнді өзгертеді.

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

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

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

1 мысал. Келесі программада көрсеткіштерге меншіктеу операциясын қолдану, көрсеткіштер мен объектілердің date, k, m орналасқан адрестерін табу қарастырылған:

#include<stdio.h>

 void main()

{

     printf("\n Rezultaty: ");

     char *c;   c="Almaty";  int date=2007;

     int  *k, *m; /* operator opisanij ukasatelei */

     m=&date; /* operator prisvaivanij */

     k=m;     /* operator prisvaivanij */

      c=NULL;  /* operator prisvaivanij */

printf("\n Znachenij objektov ukasatelei: *k=%d,*m=%d",*k,*m);

   /* operator vyvoda */

printf("\n Adresa ukasatelei: &*k=%p, &*m=%p, &*c=%p", *k,*m,*c);         /* operator vyvoda */

printf("\n Adresa objektov: &date=%p, &k=%p, &m=%p", &date, &k, &m);         /* operator vyvoda */

}

Программаның орындалу нәтижесін талдау керек.

Көрсеткіштің мәнін өзгертетін унарлық операциялар.  Көрсеткіштерге басқа деректердің түрлері сияқты унарлық операцияларды  орындауға болады. Адресті анықтайтын &, көрсеткішті сипаттайтын * операциялардан бөлек, келесі унарлық операциялардың ‘++’ және ‘--’ көмегімен  көрсеткіш типіне жататын  айнымалылардың мәндері олармен байланысты деректердің типіне байланысты өзгертіледі.  Ол объектінің алатын жад көлеміне байланысты. Егер көрсеткіш char (1 байт) типімен байланысқан болса,   онда  ‘++’ және ‘--’  унарлық операцияларының орындалу нәтижесінде оның мәні 1-ге өзгереді.  Егер көрсеткіш  int типімен байланысты болса, онда 2-ге, ал егер   float немесе long болса, онда 4-ке өзгереді. Сонымен, көрсеткіш мәні 1-ге өзгергенде ол алдыңғы (кейінгі) жолдың басына шығарылады.

Аддитивтік  операциялар. Аддитивтік операциялар көрсеткіштерде әртүрлі қолданылады. Көрсеткіш типіндегі екі айнымалыны қосуға болмайды. Көрсеткішке бүтін санды қосуға болады. Бұл жерде есептелетін мән қосылатын саннан ғана тәуелді емес, сонымен бірге көрсеткішпен байланысты объектінің типіне де байланысты. Егер көрсеткіш long типіне жатса, онда оған қосылатын 1 оның нақты мәнін 4-ке үлкейтеді,  немесе  long типінің объектісінің жадтағы келесі адресіне өтеді.  Қосу операциясыныан алу операциясының өзгешілігі – оның типтері бірдей көрсеткіштерге орындалатындығы.  Оның көмегімен жадтағы екі бірдей типтегі объектілердің ара қашықтығы анықталады. Бұл жерде ара қашықтық жеке элементтің ұзындығымен есептеледі.

Жалпы көрсеткіштердің айырмасын есептегенде нәтиже компиляторға байланысты болады.  Си тіліндегі программа компиляторға тәуелді болмас үшін мынадай мүмкіншілік енгізілген: бас тақырыптық stddef.h файлында  ptrdiff_t деген ат анықталған; оның көмегімен көрсеткіштердің нақты жүзеге асу кезіндегі көрсеткіштердің айырмасының типі белгіленеді.

Көрсеткіштердің айырмаларын есептеуді көрсететін мысал ретінде келесі программа қарастырылсын.

2 мысал.  Мұнда бүтін типке жататын үш айнымалының x,y,a мәндері енгізіледі. Олардың көрсеткіштерінің адрестері мен көрсеткіштердің айырмалары анықталып, экранға шығарылады. Программаның мәтіні келесі түрде жазылады:

#include<stdio.h>

#include<stddef.h>

void main()/*prostaj programma*/

{

int x,y,a;   int *i,*k,*m;     ptrdiff_t j,n,p;

printf("\n Vvedite x=");   scanf("%d",&x);

printf("\n Vvedite y=");   scanf("%d",&y);

printf("\n Vvedite a=");    scanf("%d",&a);

i=&x;  k=&y;  m=&a;

printf("\n Adresa ukasatelei: &i=%p &k=%p &m=%p",&i,&k,&m);

j=i-k;  n=i-m;   p=m-k;

printf("\n j=%d",(int)j);  printf("\n n=%d",(int)n);   printf("\n p=%d",(int)p);

  }

Кез-келген үш санды енгізіп, программаның орындалу нәтижесін талдау керек.

 

Өз бетімен орындауға арналған тапсырмалар:

Жұмысты  орындауға арналған  нұсқаулар:

1)                var1 айнымалының адресін анықтау үшін  адресті анықтайтын унарлық & операция қолданылады. Мысалы,  &var1 - компьютер жадындағы айнымалының адресі.

2)                айнымалының көрсеткішінің мәнін де адресті анықтайтын & операция арқылы анықтауға боладыukvar1=&var1.

3)                айнымалымен көрсеткіш арқылы жұмысты орындау үшін  көрсеткішті сипаттайтын * операция пайдаланылады. var1 айнымалының мәнін өзгерту үшін  келесі амал орындалады: var1= var1+5; не болмаса көрсеткіш арқылы:   *ukvar1=*ukvar1+5;

Мысал:

int  var1;

int   *ukvar1;   //Көрсеткіш,  тип  int

ukvar1=&var1;// Айнымалының адресі

cout<<” Көрсеткіш =”<< ukvar1<<   // Айнымалының адресі

                ”\n  var1 айнымалының мәні  =”<< *ukvar1;

 

 

 

 

 

 

 

Сілтеме  - көрсеткіштің түрі, ол айнымалының псевдонимы ретінде пайдаланады.

Мысал:

# include  <iostream.h>

int main()

{   int  var1;

int  &ssylkavar1 ;   // Сілтеме,  тип  int

var1=5;

&ssylkavar1=var1;//  Сілтеменің алғашқы мәні,

                  //енді сілтеме -  айнымалының псевдонимі

cout<<” айнымалының мәні=”<< var1<<  

 ssylkavar1= ssylkavar1+4;

cout<<” айнымалының жаңа мәні =”<< var1;

cout<<” айнымалының жаңа мәні сілтеме арқылы =” <<  ssylkavar1;

return 0;     }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4)                Параметрді функцияға параметр - сілтеме арқылы беру:

# include  <iostream.h>

void func1(int &svar1);   // функцияның  прототипі

int main() {int  var1=5;    // айнымалының  алғашқы мәні

cout<<” айнымалының мәні =”<< var1<< 

func1(var1);  

cout<<” айнымалының жаңа мәні =”<< var1;

return 0;     }

void func1(int &svar1)  // функция

{      svar1= svar1+4;      }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

№ 1 зертханалық жұмыстағы есепті  үш түрлі жолмен шешу керек:

- көрсеткіш арқылы;

- сілтеме арқылы;

- функцияның параметрі- сілтеме.

Мысал.  Келесі қосындыны  әртүрлі жолмен есептеу керек:       

1)     қарапайым  түрде:

 #include <cstdlib>

#include <iostream>

using namespace std;

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

{    int i,n=10;         float s=0;    cout<<"n=";   cin>>n;

      for (i=0,s=0;i<=n;i++)   {s=s+1.0*(i+1)/(i+2); cout<<"\ns="<<s;}

     system("PAUSE");    return  EXIT_SUCCESS; }

2) Көрсеткіш арқылы:

#include <cstdlib>

#include <iostream>

using namespace std;

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

{  int i,n=5;        float s=0, *uk=&s;

for (i=0,s=0;i<=n;i++)    {s=s+1.0*(i+1)/(i+2); cout<<"\ns="<<s;}      *uk=0;

for (i=0;i<=n;i++)   {*uk=*uk+1.0*(i+1)/(i+2); cout<<"\n*uk="<<*uk;}

    system("PAUSE");    return EXIT_SUCCESS;

}

3) Сілтеме арқылы:

 #include <cstdlib>

#include <iostream>

using namespace std;

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

{  int i,n=5;   float s, &ssyl_s=s;

 for (i=0,s=0;i<=n;i++)    {s=s+1.0*(i+1)/(i+2); cout<<"\ns="<<s;}   ssyl_s=0;

 for (i=0;i<=n;i++)    {ssyl_s=ssyl_s+1.0*(i+1)/(i+2); cout<<"\nssyl_s="<<ssyl_s;}

    system("PAUSE");

    return EXIT_SUCCESS;        }

4) функцияның параметрі- сілтеме.

     #include <cstdlib>

#include <iostream>

using namespace std;

void func1(int k,float &ssyl_s);

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

{  int i,n=5;  float s, &ssyl_s=s;

for (i=0,s=0;i<=n;i++)    {s=s+1.0*(i+1)/(i+2); cout<<"\ns="<<s;}

func1(n,s);   cout <<"\ns="<<s;

    system("PAUSE");  return EXIT_SUCCESS;        }

void func1(int k,float &ssyl_s)

{int j;    ssyl_s=0;

for (j=0;j<=k;j++)  {ssyl_s=ssyl_s+1.0*(j+1)/(j+2); cout<<"\nssyl_s="<<ssyl_s;}

}

 

№5 зертханалық жұмыс.    Си тілінде бір өлшемді массивтерге байланысты алгоритмдерді программалау

 

Жұмыс мақсаты:  бір өлшемді массивтерге  амалдар қолдануға  арналған Си тілінде  программалауға машықтану.

 

Бақылау сұрақтары:

1) Массивтерді сипаттауда Си тілінің Паскаль тілінен айырмашылықтары қандай?

2) Массивтегі бірінші индекстің номерін нольден басқа санға ауыстыруға болады ма?

3)  Массивтің элементін белгілеуде Паскаль тілімен салыстырғанда қандай ерекшелік бар?

4) Массивтің массиві не болады?

5) Массив элементтерінің саны қалай беріледі?

 

Жаттығулар және олардың орындалу үлгілері

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

1 мысал. Берілген бір өлшемді массив элементтерінің қосындысы мен орташа мәнін есептеу.

Массив элементтері  x[i], i=0,1,2,..., n-1, олардың қосындысы s және орташа мәні  d деп белгіленген. Осы есептің программасының мәтіні келесі түрде жазылады:

#include<stdio.h>

void main()

/*Массив элементерінің қосындысы мен орташасын табу*/

{    int n,i,j;      float s, d ,x[100];

while(1)

{ printf("\n Элементтерінің саны n=");   scanf("%d", &n);

if(n>0 && n<=100) break;

printf("\n Қате! 0<n<101 болуы керек");    

  printf("\n массив элементтерін еңгізу :\n  ");

  for(b=0.0,i=0; i<n; i++)

  {           printf("x[%d] =", i);      scanf("%f",&x[i]);

  s+= x[i];  /* қосындысын есептеу */          }

  d=s/n;  /* орташасын табу */

  printf("\n қосындысы = %f, орташасы = %f",s,d);       }

Мұндағы while(1) операторында алдын-ала  1 жазылған; ол дегеніміз  break операторы арқылы циклдан n-ге тек дұрыс мән (0<n<101) берілгенде ғана шығуға болатынын көрсетеді.

2 мысал. Берілген бір өлшемді массивтің ең кіші элементі мен оның нөмірін анықтау.

Программада келесі белгілеулер енгіілген: a[i]- массив элементі, x-массивтің ең кіші элементінің мәні, k-ең кіші элементтің нөмірі.  Программаның мәтінін келесі түрде жазуға болады:

#include<stdio.h>

void main()

/* массивтің ең кіші элементі мен оның нөмірін анықтау*/

{    int i, k ,n;  float a[100], x;

while(1)

{     printf("\n Еңгізініз n=");  scanf("%d", &n);

if(n>0 && n<100)break;   printf("\n Қате! 0<n<100 болуы керек!");

}

printf("\n массив элементтерін еңгізу:\n");

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

{     printf("a[%d]=",i);  scanf("%f", & a[i]);    }

x=a[0];     for( i=0; i<n; i++)

if (x>a[i])      {   x=a[i]; k=i;    }

printf("ең кіші элементі x=%f\ nоның нөмірі k= %d\n", x,k);        }

Осы жерде мынадай сұрақ туады: егер массивтің ең үлкен элементі мен оның нөмірін табу керек болса, осы программаға қандай өзгерістер енгізу қажет?

3 мысал. Енді массивті сорттау туралы есептің қарапайым түрі – массивті элементтерінің мәндерінің өсуі бойынша орналастыру. Берілгені келесі  n элементтен тұратын массив:       a[0],a[1],a[2], . . . , a[n-1]

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

#include<stdio.h>

main()

/* массивті элементтерінің мәндерінің өсуі бойынша орналастыру */

{ 

 int n,i,j;   double a[100],b;     while(1)

{   printf("\nмассив элементтерін саны n=");   scanf("%d",&n);

        if(n>1 && n<100) break;

printf("\n Қате! 0<n<100 болуы керек!"); 

}

 printf("\n массив элементтерін еңгізу:\n");

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

 {      printf("a[%d]=",j+1);     scanf("%lf", &a[j]);      }

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

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

           {     b=a[i];     a[i]=a[j];   a[j]=b;      }

     printf("\nРеттелінген массив: \n");

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

      printf("a[%d]=%5.1f\n",j+1,a[j]);

    }

Массив элементтерінің нөмірлері нөлден емес бірден басталуы үшін printf("a[%d]=%f\n",j+1,a[j]); функциясында j-дың орнына j+1 жазылған.

Бұл  жерде де мынадай сұрақ туады:  массив элементтерінің кемуі бойынша орналастыру үшін осы программаға қандай өзгеріс енгізу қажет? Ол сұраққа жауап беріп,  программаның компьютерде орындалуын тексерген жөн.  Массивтермен жұмыс істегенде, олардың элементтерінің алғашқы мәндерін алдын-ала беруге болатын мүмкіндік бар; оны инициализация жасау деп  атайды. Ондай жағдайда массив элементтерінің санын көрсетудің қажеті  болмайды. Егер массив элементтерінің алғашқы мәндері берілсе, компилятор алғашқы мәндердің саны бойынша массивтің элементтерінің санын өзі анықтайды.

 

Өз бетімен орындауға арналған тапсырмалар:

№1   Берілгені n  бүтін саны (n >1) және  сандары берілген.  Осы сандар тізбегінің  математикалық үмітін     және    дисперсиясын   есептеу керек.

№2  Компьютер жадына  сандар тізбегі ,  енгізіледі; осы тізбектегі бірінші теріс санға дейінгі оң сандардың саны анықталып,  n деп белгіленеді. Келесі қосындыны есептеу керек:   

3. Келесі формула бойынша массивтің элементтерін экранға шығару керек:         Ak = ,  k =1,2,3, . . ., n.

4. Берілген матрицаның әрбір  бағанасының (тік жолының) қосындысы компоненті болатын векторды табу.

5. Матрицаның әрбір жолының элементтерін мәндерінің өсуі бойынша орналастыру керек.

6. Матрицаның ең кіші элементін,  оның мәнін, орналасқан тік және жатық жолдардың номерлерін табу керек.

7. Массив элементтері келесі формула бойынша анықталсын:

                             yk = 2sin(πk)+3sin(πk/3), k=1,2,3,. . . 15.

Осы массивтің [0,1]аралығында орналасқан элементтерінің санын және нөмірлерін табу керек.

8. Массив элементтері келесі формула бойынша анықталсын:

                              xi = e-i(5i +3), i = 1,2,3,. . .,20.

Осы массив элементтерінің келесі интервалдарда: [0,1), [1,2), [2,3), [3,4), [4,5), [5,6),  [6,7]  орналасқан мәндерінің сандарын  pk, k=1,2,3,...,7, табу керек. Осы сандардан pk, k=1,2,3,...,7  тұратын массивті  элементтерінің өсуі бойынша сұрыптау керек.

 

№6 зертханалық жұмыс.     Массив элементтерінің  көрсеткіштері

 

Жұмыс мақсаты: массивтермен жұмыс істегенде көрсеткіштерді пайдалануға дағдылану.

 

Бақылау сұрақтары:

1) Массивтің типі әрбір элементке бөлінетін жад көлемін қалай анықтайды?

2) Массвитің бірінші элементінің адресі қалай анықталады?

3) Массив элементерінің көрсеткіштерін қалай анықтауға болады?

4) Массивтің кез келген элементінің адресі қалай анықталады?

5) Массив элементінің адресін анықтауда оның типінің қандай ролі бар?

Си тілінің стандарты бойынша, массив дегеніміз бір типке жататын элементтердің реттелінген жиыны.  Оның барлық элементтері жадтың белгілі бір облысында, бір қатарға орналасады. Программада массив қолданылатын болса, онда ол келесі түрде  сипаталынуға тиіс екендігі белгілі:

тип массив_аты [элементтер саны].

Мұндағы тип әрбір элементке бөлінетін жад көлемін анықтайды, ал массив_аты осы массивтің элементтерін орналастыруға арналған жад облысының көрсеткіші болады.  Басқаша айтқанда, массивтің бірінші элементінің (индексі 0-ге тең)  адресі массивтің атымен анықталады:

&массив_аты

Қалған элементтердің адрестерін осы адреске тұрақты бүтін сандарды қосу арқылы табуға болады. Қосылатын бүтін санның мәні объектінің типіне байланысты болады.

1 мысал. Ұзындығы немесе элементтер саны 7-ге тең char типіне жататын z[] массиві берілген. Char типіне жататын әрбір таңба үшін 1 байттық көлемдегі жад бөлінетіні белгілі. Осы жағдайды ескере отырып,  көрсеткіштің массив элементінің бірінен екіншісіне өткенде өзгеруін көрсететін  келесі программа фрагменті қарастырылсын:

#include<stdio.h>

 void main()

{      char z[7],*c;

        printf("\n массивтын элементтерінің адрестері:\n");

       for(c=z; c<=&z[7]; c++)

       printf("%6p",c);       }

Мұнда көрсетілген массив элементтерінің адрестерінің ара қашықтығы бірге тең қатар орналасқан номерлер болатынына көз жеткізу керек. Циклдық есептегіштің алғашқа мәні  үшін c=z. Ал қалған мәндерін табу үшін 1-ді қосу керек.

Енді элементтері int типіне жататын бүтін мәнді  бір өлшемді массив қарастырылсын a[]. Бұл жағдайда жоғарыда келтірілген программа мынадай түрде жазылады:

#include<stdio.h>

    void main()

    {       int a[7],*b;

             printf("\n массивтын элементтерінің адрестері:\n");

             for(b=a; b<=&a[7];b++)       printf("%6p", b);      }

Мұнда көрсетілген массив элементтерінің адрестерінің ара қашықтығы екіге тең қатар орналасқан номерлер болатынын көз жеткізу керек. Циклдық есептегіштің алғашқа мәні  үшін b=a. Ал қалған мәндерін табу үшін 2-ні қосып отыру  керек.

Сонымен, Си тілінің синтаксисі бойынша, массивтің аты оның бірінші (индексі нольге тең) элементінің адресін көрсетеді, ал  массивтің қажетті элементінің адресін анықтау ұшін оның атына бүтін санды қосу керек екен. Сондықтан символдардан (char) тұратын массивтің z[i] элементінің адресін келесі екі формада жазуға болады: &z[i] және z+i. Себебі әрбір символ жадтың 1 байттық орнын алады. Ал егер массив элементтері бүтін сандардан (int) тұратын болса, онда екі көрші элементтің адрестерінің айырмасы 2-ге, ал нақты сандардан (float) тұрса, онда 4-ке тең болады. Осы айтылған тұжырымдарды көрсету үшін тағы бір  мысал қарастырылсын.

2 мысал. Символдық, бүтін және нақты сандардың массивтерінің элементтерінің адрестерін шығару. Программа мәтінін келесі түрде жазуға болады:

#include<stdio.h>

void main()

{

char z[5]; int m[5]; float a[5];   char *uz; int *um; float *ua;

printf("\nсимволды массив элементтерінің адрестері: \n");

for(uz = z; uz<=&z[4]; uz++)  printf("%10p", uz); 

printf("\n Бүтін сандық массив элементтерінің адрестері:\n");

for(um=m; um<=&m[4]; um++) printf("%10p",um); 

printf("\n Нақты сандық массив элементтерінің адрестері:\n");

for(ua=a; ua<=&a[4]; ua++) printf("%10p", ua);

  }

Осы программаға  түсініктер берілсін. Көрсеткіштің  uz алғашқы мәні индекссіз массив аты z болса, ал соңғы мәні массивтің соңғы элементінің адресі  &z[4] болады. Басқа массивтер үшін де осындай жағдай орын алады.

Программаның орындалу нәтижесінен мынадай қорытынды жасауға болады: көрсеткіш  1-ге өзгергенде, объектілердің типтеріне байланысты әртүрлі нәтиже алынады. Көрсеткіштің мәні элементке бөлінетін жад ұзындығына өзгереді. Егер символдық массив қарастырылса, онда 1-ге, бүтін сандардың массиві үшін 2-ге, ал нақты сандардың массиві үшін 4-ке өзгереді.

Ескертетін жай, массив аты көрсеткіш типіне жататын айнымалы емес. Ол массивтің басының адресі болатын бүтін константа. Массив атына үлкейту ‘++’ және азайту ‘--’ амалдарын қолдануға болмайды. Ол меншіктеу операторының сол жағына жазылмайды. Жоғарыда келтірілген массив элементтерінің көрсеткіштері туралы мәліметтерді іс жүзінде жүзеге асыру мақсатымен келесі мысал қарастырылсын.

3 мысал.   Бұл мысалда массив пен екі көрсеткіш қарастырылған. Көрсеткіштер адрестерін, массив элементтерінің адрестерін және массив элементтерінң мәндерін шығару керек. Программа мәтіні келесі түрде жазылады:

#include<stdio.h>

float x[]={10.0, 20.0, 30.0, 40.0, 50.0};

void main()

{       float *u1,*u2;   int i;

         printf("\nКөрсеткіштер адрестері: &u1=%p &u2=%p",  &u1, &u2);

         printf("\n массив элементтерінің адрестері: \n");

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

  {     if(i==3) printf("\n");   printf("&x[%d] = %p", i, &x[i]);    }

         printf("\nмассив элементтерінң мәндері: \n");

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

   {   if(i==3) printf("\n");    printf("x[%d] = %5.1f",i,x[i]);   }

 }

Жоғарыда келтірілген программаның орындалу нәтижесінде алынған жауаптың әртүрлі компьютерде әртүрлі болатындығын тексеру керек.

Әрбір студент жеке тапсырманы оқытушыдан алады.

 

 

Өз бетімен орындауға арналған тапсырмалар:

№5 зертханалық жұмыстағы тапсырмаларды бірөлшемді массивтерге көрсеткіш арқылы орындау.

 

№7 зертханалық жұмыс.    Көп өлшемді массивтерді программалау

 

Жұмыс мақсаты: Си тілінде көп өлшемді массивтерді программалау ерекшеліктерімен танысып, машықтану.

 

Бақылау сұрақтары:

1) Екі өлшемді массивтің элементтерін бір өлшемді элемент күйінде қалай жазуға болады?

2) Көрсеткіштердің мәндерін  шығарғанда printf() функциясының форматтық жолына қандай спецификатор жазылады?

3) Массив аты көрсеткіш болады ма?

4) Массив атына ‘++’  және ‘--’ амалдарын қолдануға болады ма?

5) Программаның орындалу кезінде жадтың кейбір бөліктерін не үшін босату керек?

 

Жаттығулар және олардың орындалу үлгілері

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

 Мысалы,  егер үш жатық жолдан және екі тік жолдан тұратын  екі өлшемді массив қарастырылса, онда

double A[3][2]={{5,4},{8,2},{10,5}};

деп сипаттауға болады. Мұндағы:

A[0][0]=5; A[0][1]=4; A[1][0]=8; A[1][1]=2; A[2][0]=10;  A[2][1]=5.

Осындай нәтижені келесі жазу арқылы да алуға болады:

double A[3][2]={5,4,8,2,10,5};

Массивтердің өлшемі туралы Си тілінде ешқандай шек қойылмайды; оның ең үлкен мәнін  стандартты кітапхананың бас тақырыптық файлында анықталған препроцессорлық константа береді. Іс жүзінде, инженерлік және экономикалық есептерде, көбінесе бір және  екі өлшемді массивтермен шектеледі. Үш және одан көп өлшемді массивтер сирек кездеседі әрі оларды пайдалану тәртіптері екі өлшемді массивтерге ұқсас. Сондықтан екі өлшемді массивтерге немесе матрицаларға қолданылатын амалдармен шектелуге болады.

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

Көрсеткіштер массиві келесі үш түрлі анықтамалардың бірі арқылы енгізіледі:

1)                тип*массив_аты [размер];

2)                тип*массив_аты;

3)                тип*массив_аты[размер]= инициализатор.

Мұндағы:

а)                 тип базалық немесе туынды типтің бірі болуы мүмкін;

б)                массив_аты – программашы таңдаған идентификатор;

в)                размер – трансляция кезінде анықталатын тұрақты шама;

г)                 инициализатор – фигуралық жақшалардың ішіне жазылған тип* типіндегі мәндер.

Мысалы,       int *uk[5];    int *ua[]={a[0], a[3],  a[2], a[5]};

                       int *ub[5]={b[0], b[] ,b[2], b[3], b[4]};

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

Осы айтылған  тұжырымдарды көрсету үшін келесі жатық жолдарындағы элементтер саны әртүрлі матрица қарастырылсын.

1 мысал. Берілгені   a екі өлшемді  матрица; оның жатық жолдарының саны  n, ал ондағы элементтер саны m әртүрлі.  Матрица элементтері «жатық жол» бойынша енгізіледі. Оcы  матрицаның  жатық жолдарының элементтерін мәндерінің өсуі бойынша реттеу керек.

Келесі белгілеулер енгізілсін:

a – массив көрсеткіші; 

m – массив ұзындығының көрсеткіші;

z[ ]- көмекші  массив.

Бұл есепті шешудің программасының мәтіні келесі түрде жазылады:

#include<stdio.h>

#include<alloc.h>

void main()

{ double ** a;   int n,i,j,k;   double y,x;   int * m;

   printf("\n\n жатық жолдарының саны n=");    scanf("%d",&n);

   a=(double**)calloc(n,sizeof(double*));  m=(int*)malloc(sizeof(int)*n);

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

{ printf("\n жатық жолдарының элементтерін саны m[%d]=",i);    

   scanf("%d",&m[i]);        a[i]=(double*)calloc(m[i], sizeof(double));

   for(j=0; j<m[i]; j++)

    {printf("a[%d][%d]=",i,j);   scanf("%le", &y);   a[i][j]=y; }      }

 double z[10];

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

 { for(j=0; j<m[i]; j++)      z[j]=a[i][j];

    for (j=0; j<m[i]-1; j++)   

{x=z[j]; for (k=j+1;k<m[i];k++) if (x>z[k]) {x=z[k]; z[k]=z[j];  z[j]=x;}   }              

for(j=0; j<m[i]; j++)       a[i][j]=z[j];

     }         printf("\n Результат:");

for(i=0; i<n; i++)  { printf("\n Жол %d, элемент саны %d:\n",i,m[i]);

 for(j=0; j<m[i]; j++) printf("\t%4.1f",a[i][j]);     free(a[i]);

    }    free(a);       free(m);    }

 

Өз бетімен орындауға арналған тапсырмалар:

№1 Берілгені: A  матрицасы мен B векторы. Осылардың көбейтіндісі болатын C векторын табу керек. Ол үшін келесі формуланы пайдалануға болады: Ci= =Ai1B1+Ai2B2+Ai3B3+...+AimBm, мұндағы Aij– A матрицасының элементі,  Bj –  B векторының компоненті,  i=1,2,3,...,n.

№2  Берілгені квадрат матрица. Бұл матрицаның i–ші жатық жолындағы және j–ші бағанасындағы элементтерді алып тастау керек.

№3 Берілген квадрат матрицаның элементтерінің қосындысы ең үлкен болатын  жолдың номерін табу керек.

№4 Берілген матрицаның ең үлкен элементі орналасқан жолдың элементтерін өсуі бойынша реттеу керек.

№5 Берілген квадрат матрицаның ең кіші элементі орналасқан жолды элементтерінің кемуі бойынша реттеу керек.

 

№8 зертханалық жұмыс.     Си тілінде мәтіндерді өңдеу 

 

Жұмыс мақсаты: мәтіндерді  Си тілінде өңдеуге  арналған программалар құрастыруға машықтану.

 

Бақылау сұрақтары:

1) Символдар массивін қалай құрастыруға болады?

2) Си тіліндегі символдар массивінің жолдан айырмашылығы қандай?

3) sizeof() функциясы қандай қызмет атқарады?

4) Массивті сипаттаудың Си тілі мен Паскаль тіліндегі айырмашылықтарды атаңыз.

5) Массив элементтері компьютер жадында қалай орналасады?

 6) Массивтің соңғы элементінің адресін анықтау үшін қандай амалды орындау керек?

7) Си тілінде массивтің бірінші элементінің индексі бірге тең болуы үшін  қандай амалдарды қолдану керек?

8) Массивті инициализациялау дегеніміз не?

 

Жаттығулар және олардың орындалу үлгілері

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

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

Символдық деректерді сипаттау үшін базалық типі енгізілген.  Символдық айнымалыларды сипаттау келесі түрде жазылады:

char айнымалы_аттарының_тізбегі;

Символдың коды қажет болса, оны  алу үшін printf() функциясын пайдалануға болады. Программа құрастырушы үшін символдың кодын білудің қажеті жоқ. Символды пайдалану үшін символдық константаны пайдаланған дұрыс немесе символды апострофқа (жеке тырнақшаға) алса жеткілікті. Мысалы, ‘a’, ‘b’, ‘c’ және т.б.

Символдық деректерді енгізу және шығару үшін кітапханалық printf() және scanf() функциялары қолданылады. Бұл функцияның спецификациясы %c  түрінде болады. Мысалы,  scanf(“%c”, &a); функциясы арқылы &a адресіндегі a символының мәні енгізіледі.

Аталынған осы екі  функциядан бөлек, символдық деректерді шығару мен енгізу үшін келесі арнайы getchar() және  putchar(X) функцияларын да пайдалануға болады.:  getchar() функциясы сырттан клавиатура арқылы енгізілетін информацияны жеке бір символдан оқуды қамтамасыз етеді. Енгізілетін деректер орындау клавишасын (<Enter>) басқаннан кейін ғана оқыла бастайды. Бұл функцияда аргумент немесе параметр болмайды. putchar(X) функциясы X-тің символдық мәнін экранға шығарады.

Символдар  компьютердің ішкі кодында білгілі тәртіппен орналасқан. Сондықтан оларды бүтін сандар сияқты қарастыруға болады.

Символдардан (әріптерден, таңбалардан және т.б.) сөздерді немесе басқа конструкцияларды құрастырғанда оларды массивтер ретінде қарастырған ыңғайлы. Сондықтан жолдар мен жолдық константалар программада символдардың тізбегі ретінде қарастырылады немесе жол символдар массиві ретінде қарастырылып, char[] типіне жатқызылады. Енгізу scanf() және шығару printf() функцияларында символдық жол үшін %s спецификациясы қолданылады. Сондықтан оларды қарастырғанда char типіндегі массивтерге амалдар қолдану керек болады.

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

1 мысал. Мысалы,  «Almaty-Astana» деген сөздерді құрастыру қарастырылсын. Бұл амалды келесі программаның көмегімен орындауға болады:

#include<stdio.h>

main()

{      int uzyn;    char aripter[]={'A','l','m','a','t','y','-','A','s','t','a','n','a'};

        printf("\n Нәтиже:");  uzyn=sizeof(aripter)/sizeof(char);

        printf("\n %s", aripter); }

Мұндағы sizeof() функциясы аргументтің ұзындығын анықтайды. Сонда  uzyn=sizeof(aripter)/sizeof(char); меншіктеу  операторы массивтегі элементтер санын немесе циклдың қайталану санын анықтайды. Ал  printf("%c",aripter[k]); функциясының %c спецификаторы шығарылатын деректің char типіне жататынын көрсетеді. Символдардың массивін сипаттау кезінде оның индексінің өзгеру шектері көрсетілмейді немесе квадрат жақшаның іші бос болады (char aripter[]).

Символдардың массиві жолды құрайды. Тырнақшаның ішіне жазылған мәтінді жол константалары деп атайды. Мысалы, “Almaty-Astana” немесе “Almaty-bas chala, Astana-jas chala” және т.б.

Жол константаларын символдардың массиві ретінде анықтауға болады, мысалы:   char aripter[]=“Almaty-Astana”;

Бұл жерде компилятор осы  символдарды (барлығы 13 символ) орналастыру үшін 14 байт бөледі. Себебі соңғы 14-ші байтқа компилятор терминалдық нульді (нульдік байтты) орналастырады. Терминалдық нуль символдар массивінің соңын көрсетеді. Терминалдық нульмен аяқталатын символдар массивін Си тілінің жолы деп атайды. Егер осы мәтіннің компилятор  арқылы орналастырылған жад картасын қарастыратын болса, онда алғашқы 13 байтқа осындағы символдардың ASCII стандартына сәйкес кодтары орналасады, ал 14-ші байтқа терминалдық нуль орналасады.

Жолдар массивтер ретінде қарастырылатын болғандықтан, олар үшін көрсеткіштерді пайдалану ешбір қиындық туғызбайды. Сондықтан келесі  екі анықтама қарастырылсын.

1) Массивке жолды жазу үшін  келесі сипаттауды қолдануға болады:

char A[30];

Осыдан кейін A[30] массиві автоматты түрде қажетті жад алады. Сондықтан келесі енгізу функциясын жазуға болады:  scanf(“%s”,A);

2)  Жолдың көрсеткішін  сипаттау: char* B;

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

 Мысалы,        B=(char*)malloc(80);

операторы 80 байт жад  көлемін  B көрсеткішімен байланыстырады. Осыдан кейін енгізу функциясының көмегімен жол деректерін енгізуге болады. 

2 мысал. Келесі  программада char* типіне жататын көрсеткіштердің бір өлшемді массиві анықталған әрі ол инициализация арқылы жолдар жиынымен байланысқан. Шығару функциясы  printf() пен %s спецификациясы жол көрсеткішін параметр ретінде пайдалануға мүмкіншілік береді.

#include<stdio.h>

void main()

{char*abai[]={"Білім","таппай","мақтанба,","Орын","таппай", "баптанба." };

int k,n;    n=sizeof(abai)/sizeof(abai[0]);

printf("\n Элементтер саны n=%d.", n);   printf("\n");

printf("\n Программаның орындалу нәтижесі:");

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

   printf("\n%s ",abai[k]);}

Әрбір студент жеке тапсырманы оқытушыдан алады.

 

Әдебиеттер тізімі 

1. Абрамов С.А., Гнездилова Г.Г., Капустина Е.Н., Селюн М.И. Задачи по программированию. – М.: Наука. Гл. ред. физ.-мат. лит, 1988.  - 224 с.

2. Аляев Ю.А., Козлов д.А. Алгоритмизация и языки программирования Pascal, C++, Visual Basic: Учебно-справочное пособие.- М.: Финансы и статистика, 2004.-320 с.

3. Культин Н. C/C++  в задачах и примерах. – СПб.: БХВ-Петербург, 2008. – 288 с.: ил.

4. Құралбаев З. Алгоритмдеу және программалау тілдері. Алматы:  2008.- 353 бет.

5. Мартынов Н.Н. Информатика: С для начинающих. – М.: КУДИЦ-ОБРАЗ, 2006.-304 с.

6. Никлаус Вирт. Алгоритмы и стуктуры данных.- Досса: Хамарайан, 1997.- 360 с.

7. Подбельский В.В., Фомин С.С. Программирование на языке Си: учеб. пособие. – 2-е доп. изд. – М.: Финансы  и статистика, 2007.- 600 с.: ил.

8. Уэйт М., Прата С., Мартин Д. Язык Си. Руководство для начинающих: Пер. с англ. – М.: Мир, 1988. – 512 с., ил. 

2011 ж. жиынтық жоспары, реті. 259