C programozás/Vezérlési szerkezetek

A Wikikönyvekből, a szabad elektronikus könyvtárból.


Gyakran szükség van arra, hogy a felhasználó által megadott adatok függvényében egy vagy másféle módon viselkedjen a program, vagy arra, hogy néhány lépést többször megismételjünk néhány sor kóddal. A C-ben a feltételes elágazásnak és a ciklusoknak nevezett vezérlési szerkezetek teszik lehetővé ezt.

Ebben a részben érteni kell, hogy mit jelent a blokk szó. Ezt az Alapfogalmak fejezetben tárgyaltuk. A blokk az utasításoknak olyan csoportja, amelyekkel az a szándékunk, hogy egy egységként hajtódjanak végre. A C-ben a blokkot kapcsos zárójelek { és } határolják. Nem szükséges, hogy a blokkok végére pontosvesszőt rakjunk. A blokkok üresek is lehetnek, mint a {}. A blokkokat egymásba is ágyazhatjuk, azaz lehetnek blokkok egy nagyobb blokkban.

Feltételes elágazások[szerkesztés]

Ritka az az értelmes program, amelynek nem szükséges döntést hoznia. A feltételes elágazás az az utasítás, amely arra utasítja a számítógépet, hogy csak akkor hajtson végre egy blokkot, vagy akkor változtasson meg bizonyos adatokat, ha bizonyos feltételek teljesülnek.

A leggyakoribb feltételes elágazások az if-else utasítások. Bizonyos esetekben az elágazásokat tömörebben lehet switch-case utasításokkal leírni.

Mielőtt valaki megsimeri a feltételes elágazások utasításait, szükséges megismerkednie hogyan fejezzük ki a C-ben a logikai relációkat. A C a logikai relációkat az matematikai műveletek mintájára kezeli. A 0 (nulla) érték jelenti a hamisat, és minden más érték igazat jelent.

Mivel a logika a C-ben tulajdonképpen aritmetika, az aritmetikai és a logikai operátorok megegyeznek. Mégis vannak olyan operátorok, amelyek általában a logikához kapcsolódnak:

Relációs és azonosság kifejezések:[szerkesztés]

a < b: 1 ha a kisebb mint b, különben 0.
a > b: 1 ha a nagyobb mint b, különben 0.
a <= b: 1 ha a kisebb, vagy egyenlő mint b, különben 0.
a >= b: 1 ha a nagyobb, vagy egyenlő mint b, különben 0.
a == b: 1 ha a egyenlő b-vel, különben 0.
a != b: 1 ha a nem egyenlő b-vel, különben 0.

Kezdő programozóknak ki kell hangsúlyozni, hogy az "egyenlőek" operátor az ==, nem az =. Ez számos kódolási hiba oka lehet, és gyakran nehezen megtalálható hibáké, mivel az (a = b) kifejezés a értékét egyenlővé teszi b értékével és b-re értékelődik ki; ellenben az (a == b) kifejezés, ellenőrzi, hogy a egyenlő-e b-vel. Muszáj észben tartani, hogy az = és az == összekeverése gyakran nem derül ki a fordítás során. Az if (c = 20) {} kód teljesen érvényes C-programkód, de c változónak a 20 értéket adja, és igaznak értékelődik ki.

Vegyük észre, hogy a C-ben nincsen külön logikai típus, mint sok más nyelvben. A 0 hamis, a többi érték igaz, emiatt a következők ugyanazt jelentik:

 if (foo()) {
   // valamit csinálj
 }

és

 if (foo() != 0) {
   // valamit csinálj
 }

Több feltétel egyszerre teljesülését (az és logikai műveletet) az && művelettel vizsgálhatjuk. Ha egy értéknek 0 és 10 közé kell esnie, azt a következőképpen adhatom meg:

 int value = 20;
 if ( 0 < value && value < 10) {   // a && jelentése "és"
  /* csinálj valamit */
 }

A zárójelben található kifejezés helyett a (0 < value < 10) kifejezés hibás.

Logikai kifejezések[szerkesztés]

a || b
ha a VAGY b igaz (vagy mindkettő), akkor 1-et ad, különben 0-át.
a && b
ha a ÉS b mindkettő igaz, akkor 1-et ad, különben 0-át.
!a
ha a igaz, akkor 0 az eredmény, ha a 0, az eredmény 1.

Itt egy összetettebb példa. Az

  e = ((a && b) || (c > d));

kifejezésben e értéke 1 lesz, ha a és b nem nulla, vagy c nagyobb, mint d. Egyébként az e értéke nulla lesz.

C uses short circuit evaluation of logical expressions. That is to say, once it is able to determine the truth of a logical expression, it does no further evaluation. This is often useful as in the following:

int myArray[12];
....
if ( i < 12 && myArray[i] > 3) { 
....

In the snippet of code, the comparison of i with 12 is done first. If it evaluates to 0 (false), i would be out of bounds as an index to myArray. In this case, the program never attempts to access myArray[i] since the truth of the expression is known to be false. Hence we need not worry here about trying to access an out-of-bounds array element if it is already known that i is greater than or equal to zero. A similar thing happens with expressions involving the or || operator.

while( doThis() || doThat()) ...

doThat() is never called if doThis() returns a non-zero (true) value.

Bitwise Boolean Expressions[szerkesztés]

The bitwise operators work bit by bit on the operands. The operands must be of integral type (one of the types used for integers). The six bitwise operators are & (AND), | (OR), ^ (exclusive OR, commonly called XOR), ~ (NOT, which changes 1 to 0 and 0 to 1), << (shift left), and >> (shift right). The negation operator is a unary operator which precedes the operand. The others are binary operators which lie between the two operands. The precedence of these operators is lower than that of the relational and equivalence operators; it is often required to parenthesize expressions involving bitwise operators.

For this section, recall that a number starting with 0x is hexadecimal, or hex for short. Unlike the normal decimal system using powers of 10 and digits 0123456789, hex uses powers of 16 and digits 0123456789abcdef. Hexadecimal is commonly used in C programs because a programmer can quickly convert it to or from binary (powers of 2 and digits 01). C does not directly support binary notation, which would be really verbose anyway.

a & b
bitwise boolean and of a and b
0xc & 0xa produces the value 0x8 (in binary, 1100 & 1010 produces 1000)
a | b
bitwise boolean or of a and b
0xc | 0xa produces the value 0xe (in binary, 1100 | 1010 produces 1110)
a ^ b
bitwise xor of a and b
0xc ^ 0xa produces the value 0x6 (in binary, 1100 ^ 1010 produces 0110)
~a
bitwise complement of a.
~0xc produces the value -1-0xc (in binary, ~1100 produces ...11110011 where "..." may be many more 1 bits)
a << b
shift a left by b (multiply a by )
0xc << 1 produces the value 0x18 (in binary, 1100 << 1 produces the value 11000)
a >> b
shift a right by b (divide a by )
0xc >> 1 produces the value 0x6 (in binary, 1100 >> 1 produces the value 110)

Az if-else utasítás[szerkesztés]

Az if-else utasítás lehetővé teszi, hogy bizonyos kódrészlet csak akkor fusson le, ha valamilyen feltétel teljesül:

   if (/* ide jön a feltétel */) {
      /* ha a feltétel igaz, ez a kód fut le */
   } else {
      /* ha a feltétel 0 (hamis), ez a kód fut le */
   }

Az else és az azt követő kódblokk elhagyható. Ha nem szükséges a feltétel hamis értéke esetén végrehajtani egy kódot, akkor hagyd el.

Ciklusok[szerkesztés]

Programozás során gyakran van szükség arra, hogy valamit adott számszor hajtsunk végre, vagy addig, amíg egy feltétel teljesül. Haszontalan és unalmas annyiszor leírni a utasítást vagy utasításblokkot, ahányszor végre kell hajtani, ezenfelül nehezen módosítható.

While ciklus[szerkesztés]

A while-ciklus a legalapvetőbb ciklusfajta. Addig fut, amíg a fetétel nem nulla (igaz). Például, ha a következőt próbálod ki, a akkor a program végtelen ciklusba kerül, és kézzel kell bezárni a programot.

 int a=1;
 while(42) {
    a = a*2;
 }

Itt egy másik példa a while-ciklusra. Ez kiírja a kettő összes hatványát, ami kisebb 100-nál.

 int n=1;
 while(n<100) {
    printf("n értéke %d \n", n);
    n = n*2;
 }

Minden ciklus futását a break és continue utasításokkal is befolyásolhatjuk. A break utasítás rögtön kilép az azt tartlamazó legbelső ciklusból. A continue utasítás kihagyja a blokk maradék részét, és a vezérlő feltételes utasításnál folytatja újra. Például:

 int n=1;
 while (42) { // ciklus amíg a break ki nem lép
    printf("n értéke %d ",n);
    n = n*2;
    if(n>100) {
        break;
    } else if(n==64) {
        continue;  // Rögtön visszamegy a while utasításhoz, kihagyja a következő utasítást
    }
    printf("n értéke nem 64\n");
 }

Ebben a példában a számítógép kiírja az n értéket, mint a korábbi, és kiírja azt is, hogy n nem 64 (hacsak nem ugorja át a continue utasítás).

Hasonlóan a fenti if-hez, a while kapcsos zárójelei elhagyhatóak, ha csak egy utasításból áll a kód:

 int n=1;
 while(a < 100) n = n*2;

Ez addig növeli az n értékét, amíg n nem kisebb, mint 100.

When the computer reaches the end of the while loop, it always goes back to the while statement at the top of the loop, where it re-evaluates the controlling condition. If that condition is "true" at that instant -- even if it was temporarily 0 for a few statements inside the loop -- then the computer begins executing the statements inside the loop again; otherwise the computer exits the loop. The computer does not "continuously check" the controlling condition of a while loop during the execution of that loop. It only "peeks" at the controlling condition each time it reaches the while at the top of the loop.

It is very important to note, once the controlling condition of a While loop becomes 0 (false), the loop will not terminate until the block of code is finished and it is time to reevaluate the conditional. If you need to terminate a While loop immediately upon reaching a certain condition, consider using break.

Gyakran használt forma az alábbi:

 int i = 5;
 while(i--) {
    printf("A Java és a C# nem képes erre\n");
 }

Ez végrehajtja a while-ciklus magját 5-ször, miközben végigmegy 4-től 0-ig az i értéke. Ezek azok az értékek, amelyek ahhoz kellenek, hogy egy 5 elemű tömb minden elemét elérjük.

for-ciklusok[szerkesztés]

A for-ciklusok valahogy így néznek ki:

for(initializáció; teszt; növelés)
{
   /* code */
}

Az initializáló utasítás egyetlenegyszer hajtódik végre - a teszt feltétel első vizsgálata előtt. Általában valamilyen változó kezdeti értékének beállítására használják, bár ez nem kötelező.

A teszt kifejezés minden esetben kiértékelődik, mielőtt a ciklusmag végrehajtódik. Ha 0-t (hamis) ad eredményül, a ciklusmag nem fog újra végrehajtódni, és a futás a for-ciklust követő kódnál folytatódik. Ha a kifejezés nem nulla (igaz), akkor a ciklusmag végrehajtódik.

Minden egyes ciklus után a növelő utasítás végrehajtódik. Ez gyakran arra való, hogy a ciklus indexét megnöveljük, azt a változót, amelyet inicializáltunk az inicializáló kifejezéssel, és teszteltünk a tesztékifejezéssel. Ennek a végrehajtása után a vezérlés a ciklus tetejére ugrik, ahol a tesztelés újra lefut. Ha egy continue utasítás hajtódik végre a for-ciklusban, azután a növelő utasítás hajtódik végre.

Gyakran egy for-ciklust használunk arra hogy egy tömb elemein végighaladjunk, minden egyes alkalommal egy elemet feldolgozva.

 int  myArray[12];
 int ix;
 for (ix = 0; ix<12; ix++) {
    myArray[ix] = 5 * ix + 3;
 }

A fenti ciklus inicializálja a myArray tömb mind a 12 elemét.

A ciklusváltozó bármelyik értékkel indulhat. A következő esetben 1-gyel kezdődik.

 for(ix = 1; ix <= 10; ix++) {
    printf("%d ", ix);
 }

Amely ezt írja ki:

1 2 3 4 5 6 7 8 9 10 

Leggyakrabban 0-tól fogod indítani a ciklusváltozót, mival a tömbök indexelése nullával kezdődik, de néha mást fogsz használni a ciklusváltozó inicializálására.

A növelő rész más dolgot is csinálhat, például csökkenthet is. Az alábbi típusú ciklusok gyakoriak:

 for (i = 5; i > 0; i--) {
    printf("%d ",i);
 }

amely ezt írja ki:

5 4 3 2 1