Զրույցներ C ծրագրավորման լեզվի մասին

Զրույց չորրորդ

Նախորդ զրույցներում իմ բերած օրինակները շատ պարզ էին․ ծրագիրը սկսվում էր main ֆունկցիայից, կանչվում էր այս կամ այն ֆունկցիան, կատարվում էին հաջորդական գործողություններ ու ծրագրի կատարումն ավարտվում էր։ Իսկ ի՞նչ անել, երբ պետք է ծրագրի տրամաբանությունը ղեկավարել ներմուծված կամ ձևավորված տվյալներից կախված։ Օրինակ, ինչպե՞ս սահմանեմ թվի նշանը որոշող sign ֆունկցիան։ Այն պետք է արգումենտում ստացած դրականների թվի համար վերադարձնի՝ 1, բացասականի համար՝ -1, իսկ զրոյի համար՝ 0։ Պարզ է, որ ֆունկցիայի այդպիսի վարքը մոդելավորելու համար հարկավոր է ճյուղավորման հրաման։

C լեզվում ճյուղավորումները կազմակերպվում են if հրամանով.

if( ⟨condition⟩ ) ⟨then-block⟩ else ⟨else-block⟩

Եթե ճշմարիտ է ⟨condition⟩ պայմանը, ապա կատարվում են ⟨then-block⟩ հրամանները, հակառակ դեպքում կատարվում են ⟨else-block⟩ հրամանները։ Եթե պայմանի կեղծ լինելու դեպքում ոչինչ անել պետք չէ, ապա else ծառայողական բառը և նրան հաջորդող ⟨else-block⟩ հրամանները կարելի է չգրել։ Օրինակ, այսպես.

if( argc == 1 )
  usage();

usage ֆունկցիան կանչվում է միայն այն դեպքում, երբ argc փոփոխականը հավասար է մեկի։

Արդեն տեղին է հիշատակել C լեզվի համեմատման գործողությունների մասին։ Դրանք վեցն են և նախատեսված են թվերի ու նիշերի համեմատման համար, իսկ == և != գործողությունները օգտագործվում են նաև ցուցիչների հավասարությունն ու անհավասարությունը ստուգելու համար։

Գործողություն Նշանակություն
== հավասար է
!= հավասար չէ
> մեծ է
>= մեծ է կամ հավասար
< փոքր է
<= փոքր է կամ հավասար

Հիմա վերը հիշատակված sign ֆունկցիայի մասին։ Այն կարող է ունենալ հետևյալ տեսքը.

int sign( double num )
{
  int res = 0; /* զրոյի դեպքը */
  if( num < 0 ) /* եթե բացասական է */
    res = -1;
  else if( num > 0 ) /* այլապես եթե դրական է */
    res = 1;
  return res;
}

Բայց, քանի որ return հրամանն իր կիրառման կետում ավարտում է ֆունկցայի աշխատանքը, sign ֆունկցիան կարելի է գրել ավելի պարզ ձևով։

int sign( double num )
{
  if( num < 0 ) return -1; /* բացասական */
  if( num > 0 ) return 1;  /* դրական */
  return 0; /* զրո */
}

Ճյուղավորման հրամանը հնարավորություն է տալիս ծրագրում կազմակերպել նաև կրկնություններ։ Օրինակ, տրված x թվի y աստիճանը (երկուսն էլ ամբողջ թվեր են) հաշվող ֆունկցիան կարող եմ սահմանել հետևյալ կերպ.

int power( int x, int y )
{
  if( y == 0 )  /* թվի զրո աստիճանը */
    return 1;   /* 1 է */
  return x * power( x, y - 1 ); /* x^y = x * x^(y - 1) */
}

Այս ֆունկցիայում կրկնությունը մոդելավորված է ռեկուրսիայի օգնությամբ. x թվի հերթական y աստիճանը հաշվելու համար x-ը բազմապատկվում է իր y-1 աստիճանի արժեքի հետ։ Եթե որպես աստիճան տրված է 0, ապա վերադարձնում է 1։

Մեկ այլ օրինակ՝ առանց ռեկուրսիայի օգտագործման։ powers_of_two ֆունկցիան արգումենտում ստանում է N ամբողջ թիվը և ստանդարտ արտածման հոսքին է արտածում 2[0..N] աստիճանների աղյուսակը.

void powers_of_two( int nm )
{
  int pw = 0;
cycle:
  printf( "2^%d = %d\n", pw, 1 << pw );
  pw = pw + 1;
  if( pw <= nm ) goto cycle;
}

Այստեղ ցիկլի կազմակերպման համար ես օգտագործել եմ goto հրամանը և մի նշիչ (label)։ C լեզվի նշիչները իդենտիֆիկատորներ են, որոնցից հետո դրվում է :։ Դրանք կարող են գրվել ցանկացած հրամանից առաջ և օգտագործում են այդ հրամանին ուղղակի անցում կատարելու համար։ powers_of_two ֆունկցիայի չորրորդ տողում գրված է cycle նշիչը, որին յոթերորդ տողում գրված goto հրամանով անցում է կատարվում այն դեպքում, երբ pw-ի արժեքը դեռևս փոքր է nm-ի արժեքից։

Երկուսի հերթական pw աստիճանը հաշվարկվում է 1 << pw արտահայտությամբ (printf ֆունկցիայի երրորդ արգումենտում)։ Այս << գործողությունը իր ձախ արգումենտի բիթերը դեպի ձախ է տեղաշարժում աջ արգումենում տրված քանակով՝ ազատված դիրքերը լրացնելով զրոներով։

Բացի if-else ճյուղավորման հրամանից, C լեզուն ունի նաև switch ընտրության հրամանը, որը թույլ է տալիս ընտրություն կատարել արտահայտության արժեքներ մեջ և ամեն մի արժեքին համապատասխանեցնել հրամանների առանձին հաջորդականություն։ Նրա ընդհանուր տեսքն այսպիսինն է.

switch( ⟨expression⟩ ) {
  case ⟨value_1⟩:
    ⟨block_1⟩
  case ⟨value_2⟩:
    ⟨block_2⟩
  ...
  default:
    ⟨block_!⟩
}

switch հրամանի արգումենտի ⟨expression⟩ արտահայտությունը պետք է լինի ամբողջաթիվ կամ նիշային, իսկ case հրամանի արգումենտները՝ նույն տիպի հաստատուններ։ Եթե ⟨expression⟩-ի արժեքը ⟨value_1⟩ է, ապա կատարվում են ⟨block_1⟩ խմբի հրամանները, եթե ⟨value_2⟩ է, ապա՝ ⟨block_2⟩ հրամանները և այդպես շարունակ։ Եթե ⟨expression⟩-ի արժեքը ոչ մի case տարբերակի չի համընկնում, ապա կատարվում է default ծառայողական բառից հետո գրված ⟨block_!⟩ հրամանները։

Օրինակ, սահմանեմ day_of_week (շաբաթվա օր) ֆունկցիան, որն արգումենտում ստանում է ամբողջ թիվ և արտածում է համապատասխան շաբաթվա օրվա անունը։

void day_of_week( int day )
{
  switch( day ) {
    case 1:
      puts( "երկուշաբթի" );
      break;
    case 2:
      puts( "երեքշաբթի" );
      break;
    case 3:
      puts( "չորեքշաբթի" );
      break;
    case 4:
      puts( "հինգշաբթի" );
      break;
    case 5:
      puts( "ուրբաթ" );
      break;
    case 6:
      puts( "շաբաթ" );
      break;
    case 7:
      puts( "կիրակի" );
      break;
    default:
      puts( "այդպիսի համարով օր չկա" );
  }
}

Պետք է ուշադրություն դարձնել, որ բոլոր case բլոկներն ավարտվում են break հրամանով։ Բանն այն է, որ switch հրամանի մարմինը հրամանների մի ընդհանուր հաջորդականություն է, իսկ case և default հրամաններն այդ հաջորդականության մեջ ձևավորում են նշիչներ։ Երբ սկսվում է կատարվել բլոկներից որևէ մեկը, ապա կատարվում են switch հրամանի մարմնի հաջորդ բոլոր հրամանները։ break հրամանը հնարավորություն է տալիս կատարումն ընդհատել հարկավոր տեղում։

Օրինակ, սահմանեմ working_day (աշխատանքային օր) ֆունկցիան, որը [1..5] օրերի համար արտածում է «աշխատանքային օր է» արտահայտությունը, իսկ 6 և 7 օրերի համար՝ «հանգստյան օր է» արտահայտությունը։

void working_day( int day )
{
  switch( day ) {
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
      puts( "աշխատանքային օր է" );
      break;
    case 6:
    case 7:
      puts( "հանգստյան օր է" );
      break;
    default:
      puts( "այդպիսի համարով օր չկա" );
  }
}

Հետաքրքրության համար կարելի է հեռացնել break հրամանները և տեսնել, թե ինչ կկատարվի։

day_of_week ֆունկցիան սահմանեմ նաև if-else հրամանի օգնությամբ.

void day_of_week( int day )
{
  if( day == 1 )
    puts( "երկուշաբթի" );
  else if( day == 2 )
    puts( "երեքշաբթի" );
  else if( day == 3 )
    puts( "չորեքշաբթի" );
  else if( day == 4 )
    puts( "հինգշաբթի" );
  else if( day == 5 )
    puts( "ուրբաթ" );
  else if( day == 6 )
    puts( "շաբաթ" );
  else if( day == 7 )
    puts( "կիրակի" );
  else
    puts( "այդպիսի համարով օր չկա" );
}

Հերթով դիտարկվում են day արգումենտի արժեքները և ամեն մեկի համար արտածվում է համապատասխան բառը։ Ամենավերջին else ճյուղը կատարվում է այն դեպքում, երբ day փոփոխականի արժեքը [1..7] միջակայքից չէ։