Z Javy migrujuvší céčkar 4: funkcie

Dnes pokračujme radostným seriálom o migrácii z Javy na C. Porozprávajme sa o funkciách a dajme si rovno príklad:

Zistite, či tri úsečky dokážu byť stranami trojuholníka.

Kuk do základoškolského učiva! Má platiť trojuholníková nerovnosť:

a + b > c
b + c > a
c + a > b

Urobme si implementáciu v Jave:

public class Trojuholnik {
    public static boolean tvoriaTrojuholnik(double a, double b, double c) {
        return (a + b > c) && (b + c > a) && (c + a > b);
    }

    public static void main(String[] args) {
        return Trojuholnik.tvoriaTrojuholnik(3, 4, 5);
    }
}

Funkcie v C

Každý program v C má minimálne jednu funkciu: main() — bezparametrovú a vracajúci int. (Bokom: main() môže mať aj viacero parametrov, ale o tom potom).

Pokojne si môžeme dodať aj ďalšie funkcie: stačí sa dohodnúť na dátových typoch a návratových hodnotách.

Java funkcia vracala boolean. Pamätajme si toto:

boolean v C nie je. Namiesto toho 0 znamená false a iné číslo znamená true˛.

Funkcia pre strany trojuholníka môže vyzerať takto:

int tvoria_trojuholnik(double a, double b, double c) {
    return (a + b > c) && (b + c > a) && (c + a > b);
}

Funkcia je zároveň pekný príklad použitia logických operátorov: napr. výraz a + b > c sa vyhodnotí buď na nulu (= nepravda) alebo na nejaké kladné či záporné číslo (= pravda). Napr.

printf("%d", 3 < 4);

vráti nejaké nenulové číslo (napr. 1).

Logické operátory tiež pracujú s číslami v podobnom duchu.

Použitie môže vyzerať takto:

int main(void)
{
    if(tvoria_trojuholnik(3, 4, 5)) {
        printf("Strany tvoria trojuholnik");
    } else {
        printf("Strany NETVORIA trojuholnik");
    }

    return EXIT_SUCCESS;
}

Aha, sám if, ktorý v Jave berie booleovský výraz, pracuje na číselnej filozofii.

Celý zdroják bude teda takýto:

#include <stdio.h>
#include <stdlib.h>

int tvoria_trojuholnik(double a, double b, double c) {
    return (a + b > c) && (b + c > a) && (c + a > b);
}

int main(void)
{
    if(tvoria_trojuholnik(3, 4, 5)) {
        printf("Strany tvoria trojuholnik");
    } else {
        printf("Strany NETVORIA trojuholnik");
    }

    return EXIT_SUCCESS;
}

Poradie funkcií

V Jave platí fintivá vlastnosť, že metódy môžu byť v triede uvádzané v ľubovoľnom poradí. Nie tak v C. Spravme zámenu metód!

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    if(tvoria_trojuholnik(3, 4, 5)) {
        printf("Strany tvoria trojuholnik");
    } else {
        printf("Strany NETVORIA trojuholnik");
    }

    return EXIT_SUCCESS;
}

int tvoria_trojuholnik(double a, double b, double c) {
    return (a + b > c) && (b + c > a) && (c + a > b);
}

Kompilácia zlyhá:

error: implicit declaration of function 'tvoria_trojuholnik'

(Ale ak nemáte nastavené žiadne -Wall, -Wextra a podobne, tak dostanete len varovanie a program sa skompiluje a zrejme aj zbehne.)

Funkcia main() čaká, že v zdrojáku pred ňou sa nájde funkcia tvoria_trojuholnik, aby mohol overiť počet parametrov, ich typy a návratovú hodnotu. Ak funkciu nenájde, bude predpokladať, že tvoria_trojuholnik zožerie ľubovoľný počet parametrov a bude vracať int. To je síce v tomto prípade schváliteľné, ale v prípade zložitejších funkcií to môže viesť ku kadejakým divných chybám.

Platí zásada:

Funkcie, ktoré majú telo za prvým použitím, musia mať pred použitím deklarovanú hlavičku.

Takto:

#include <stdio.h>
#include <stdlib.h>

int tvoria_trojuholnik(double a, double b, double c);

/*´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´*/

int main(void)
{
    if(tvoria_trojuholnik(3, 4, 5)) {
        printf("Strany tvoria trojuholnik");
    } else {
        printf("Strany NETVORIA trojuholnik");
    }

    return EXIT_SUCCESS;
}

int tvoria_trojuholnik(double a, double b, double c) {
    return (a + b > c) && (b + c > a) && (c + a > b);
}

Hlavička je teda dvakrát: raz na začiatku a raz na konci. Toto správanie jepredobraz filozofie interfaceov z Javy: najprv uvediete všetky hlavičky funkciá a potom ich implementácie.

Parametre funkcií

Ak funkcia neberie žiadne parametre, nezabudnite uviesť do zátvoriek void. Ak funkcia nevracia nič, návratová hodnota je void:

void pozdrav(void) {
    printf("Huh?");
}

Parametre, ktoré sa nepoužívajú

Niekedy sa stane, že máte vo funkcii viacero parametrov, ale nie všetky chcete použiť. Ak ste si pozapínali všetky varovania ako chyby, máte problém:

void pozdrav(int pocet_pozdraveni) {
    printf("Huh?");
}

To znamená:

main.c:4: error: unused parameter 'pocet_pozdraveni'

Riešenie spočíva v šialenom triku:

void pozdrav(int pocet_pozdraveni) {
    (void) pocet_pozdraveni;

    printf("Huh?");
}

Nepoužitý parameter pretypujeme na void, čím zahlásime, že ho budeme ignorovať a kompilátor bude spokojný.

Odovzdávanie parametrov

V C sa všetky parametre odovzdávajú hodnotou. Ak máme našu funkciu:

int tvoria_trojuholnik(double a, double b, double c) {
    /*....*/
}

a zavoláme ju s parametrami 3, 4, 5, tak v skutočnosti sa stane toto:

  1. V programe sa za behu po zavolaní funkcie uchmatne z pamäte (na zásobníku) priestor pre tri premenné typu double: a, b, c.
  2. Do nich sa nakopírujú hodnoty 3, 4, 5.
  3. Prebehne výpočet, funkcia skončí. Pamäť s premennými a, b, c sa automaticky uvoľní.

Klasický školský príklad hovorí:

#include <stdio.h>
#include <stdlib.h>

void vymen(int a, int b) {
    int c = a;
    a = b;
    b = c;
}

int main(void)
{
    int hodnota1 = 1;
    int hodnota2 = 42;
    vymen(hodnota1, hodnota2);

    printf("%d %d", hodnota1, hodnota2);

    return EXIT_SUCCESS;
}

Vypíše to…

1 42

Beh je totiž jasný: po zavolaní vymen() sa vyhradí pamäť pre dva inty: do prvého sa nakopíruje obsah premennej hodnota1 a do druhého hodnota2, čiže

a <- 1
b <- 42

Funkcia dobehne a skončí v takomto stave:

a = 42
b = 1
c = 42

V tej chvíli sa pamäť uvoľní, obsahy premenných sa zahodia a obsahy v hodnota1 a hodnota2 ostanú nezmenené.

Toto sa samozrejme dá zmeniť, ale na to potrebujeme smerníky/pointery, o ktorých neskôr.

Návratové hodnoty funkcií

Kúzelné je takéto správanie

int vrat_daco(void) {
    /* nerobíme nič */
}

V Jave by už kompilátor frflal, že funkcia vracia int… ale žiadny nevracia a teda, že musí mať nejaký return. V C to nie je chyba, akurát funkcia bude mať nedefinovanú návratovú hodnotu. Ak zapnete pri kompilácii štandardné varovanie -Wall, budete na to upozornení.

Záver

Rozobrali sme si všetko, čo bolo treba, nabudúce polia!