Programmeringsspråket C

Funktioner

Hur man använder funktioner

En funktion brukar ofta liknas vid en "svart låda", där man stoppar in ett eller flera värden, och ut får man nya värden. Funktioner är mycket viktigt för ett överskådligare och mer lättprogrammerat program.

Vi har redan använt standardfunktionerna printf() och scanf(), och du har sett en tredje standardfunktion, putchar(). Funktioner i C tar emot värden inom parenteser (jfr. printf("hejdu")), och antar själva värdet de returnerar. Den sista satsen kan låta klurig, men vi ska försöka förklara så bra vi kan.

Tänk dig att vi har en funktion som heter summera(). (Om namngivningen på funktioner: samma regler gäller som för variabler.) Den tar emot två värden, två heltal. Våran funktion adderar dessa två heltal, och returnerar summan. Så här skulle anropet till denna funktion se ut:

int summa;
 
summa = summera(4, 8);
 
//nu har variabeln summa värdet 12

Eftersom du redan visste hur funktionsanrop sker, då du har använt både printf() och scanf() sen tidigare, så var den biten inget nytt. Nytt är däremot hur vi använde funktionen som ett värde, när vi tilldelade variabeln summa ett värde. Detta innebär en stor flexibilitet med funktioner, att de kan användas precis som ett värde. Betänk följande exempel.

if (summera(tal1, tal2) < 10) {
    printf("Summan av talen är %i\n", summera(tal1, tal2));
}

Här kollar vi först, returnerar funktionen ett värde under tio? Om den gör det, så skriver vi ut vad för värde funktionen returnerade. Eftersom funktionen returnerar ett heltal, använder vi %i med printf() för att skriva ut värdet. Återigen visar vi här hur funktioner kan användas som de värden de returnerar.

Hur man skapar funktioner

Detta kanske hittills har varit en tråkig del att läsa, eftersom du inte kunnat testa. Du har ju trots allt inte funktionen vi använder. Nu är det dags att visa hur man gör funktioner. Här kommer ett exempel med föregående funktion, fast med koden i sin helhet.

#include <stdio.h>
 
int summera(int a, int b) {
    return a + b;
}
 
 
int main() {
    int talett, int taltva;
 
    printf("Skriv in tva heltal, sa skriver jag ut summan!\n");
    printf("Tal 1: ");
    scanf("%i", &talett);
    printf("Tal 2: ");
    scanf("%i", &taltva);
 
    printf("Summan ar: %i\n", summera(talett, taltva));
 
    return 0;
}

I main() är det faktiskt inte så mycket nytt, vi initierar två heltalsvariabler, vi skriver ut lite text, vi läser in värden till de båda variablerna. Sen kommer det nya: när vi skriver ut sista texten anropar vi också funktionen summera(). Anropet ser ut precis som vi lärt oss.

Det intressanta i detta kodexempel är förstås själva funktionen summera(), och inte anropet till den. Definitionen av funktioner ser ut som i detta exempel, och du känner nog igen flera delar av det från våran main()-funktion. Första raden, int summera(int a, int b) { säger flera saker. Först och främst, funktionen är av heltalstyp (int) och den heter summera. Sedan inom parentes anges hur många argument den tar, samt av vilken sort, och vad de ska heta i funktionen. I vårt fall tar den två argument, båda av heltalstyp, och de ska heta a och b.

Nästa rad, som också är enda raden i funktionen, returnerar ett värde. Det värde som returneras blir "funktionens värde". I detta fall är det av heltalstyp, eftersom funktionen är av heltalstyp. Värdet som returneras är summan av våra två argument, a och b. När en return-instruktion körs inom en funktion, avslutas körningen av funktionen. Ska något utföras inuti en funktion ska det ske innan return sker.

Detta är alltså all magi bakom en simpel funktion! Som du nu även kan utläsa, är alltså main() av heltalstyp, tar inte emot några argument (än i alla fall, den kan göra det om man vill), och den returnerar 0 dit den anropades. Som vi berättade i del 1 är det systemet som anropar main(), och systemet tolkar nollan som att programmet avslutades utan fel.

Varning: variabler tillsammans med funktioner kan vara krångligt, om man inte känner till vad som kallas scope på engelska. Varje variabel har alltså ett scope, inom vilket den existerar. När körningen av programmet går utanför detta scope kommer variabeln att tas bort. Detta är viktigt att hålla koll på. En variabels scope är oftast funktionen den är initialiserad i. Det innebär att, i förra exemplet, känner inte våran summera()-funktion till variablerna talett och taltva. Skulle man försöka komma åt dem inom summera()-funktionen skulle man få ett fel. På samma sätt går det inte att komma åt variablerna a eller b i main()-funktionen, för så fort summera() har körts klart är dessa variabler borttagna. Detta var alltså anledningen att summera inte kunde summera talett och taltva själv, utan vi var tvugna att gå via a och b.

Funktioner som inte returnerar någonting

Alla funktioner som är av typen int, char, double eller någon av de andra typerna vi listar i del 2 ska returnera ett värde. Det finns däremot ett undantag, den mystiska typen void. Funktioner av denna typ får inte returnera värden, och om funktionen är av denna typ behöver man inte ens ha med en return-rad. Man kan om man behöver ha en tom return, utan något värde, men man måste inte. Till vilken nytta är då dessa funktioner? kan man fråga sig. Jo, såklart en hel del, om man har fantasi. Men desto viktigare kommer det bli sen, när vi kommer in på pekare. Dock vill vi gå in på dessa funktioner ändå nu, för att ha det gjort.

#include <stdio.h>
 
void skriv_ut_tal(int a) {
    printf("Argumentet jag fick var: %i\n", a);
}
 
int main() {
    skriv_ut_tal(34);
    return 0;
}

Detta måste vara våran kortaste main()-funktion någonsin! Allt den gör är att anropa vår alldeles egna utskriftsfunktion, och sen avsluta. Vår utskriftsfunktion gör inget annat än att skriva ut talet den får som argument.

Här kommer ett mer avancerat exempel på void-funktion. Den tar emot en sträng som argument, och skriver ut den. Notera att detta program aldrig anropar printf(). Här kommer eventuellt lite nytt, så håll i dig!

#include <stdio.h>
 
void skriv_ut_strang(char str[]) {
    int i;
 
    i = 0;
    while (str[i] != '\0') {
        putchar(str[i]);
        i++;
    }
    putchar('\n');
}
 
 
int main() {
    char namn[30];
 
    printf("Vad heter du? ");
    gets(namn);
 
    skriv_ut_strang(namn);
 
    return 0;
}

Och, som vanligt kommer jag börja förklara main()-funktionen, eftersom det också är där exekveringen av koden börjar. Initialiseringen av vår sträng ska inte vara främmande, om du har läst del 8. Inte heller ska printf() vara något nytt vid det här laget. Följande rad är däremot intressant. Den läser in en sträng till våran array. Här finns en liten sak att påpeka, skriv inte in ett värde längre än 29 tecken (arrayen är 30 poster lång, den sista behövs för nulltecknet som avslutar en sträng). gets() står för get string, alltså hämta en sträng (från användaren). Denna lagras sedan i arrayen som är argument till funktionen.

Varning: Notera utebliven användning av scanf() för att hämta strängen! Självklart går det att använda scanf() för detta, men den klipper av strängen vid första mellanrum, vilket inte alltid är vad man vill. Därför föredrar jag att skriva egna funktioner för att läsa in strängar, för full kontroll. Tills vidare duger gets(). Är du den experimenterande typen som vill, efter detta kapitel, prova att skriva en funktion för att läsa in strängar kan jag ge ledtråden att getc() läser in ett tecken. Att skriva en stränginläsande funktion anser dock jag är lite överkurs på den här nivån, så bli inte förkrossad om du inte får till det. :)

Sedan sker ett anrop på våran utskrivningsfunktion, och efter det avslutas programmet.

Vår utskrivningsfunktion kräver lite extra tilltanke för att förstå. Först och främst tar den ett argument som är en array av char-värden. Notera de tomma hakparenteserna. Att detta går att göra beror på att vi skickar en array som argument, och vår nya array kommer automatiskt att bli lika stor som vår gamla. (Faktum är att våran nya array egentligen inte ens är en ny array, utan samma array! Bry dig inte för mycket om det än, mer om detta kommer när vi går in på pekare.)

Sedan deklarerar vi heltalet i, och tilldelar det värde 0, inget konstigt med det. Följande loop drar nytta av hur strängar fungerar i C. Så länge bokstaven med nummer i, inte är lika med nulltecknet, så ska den skriva ut bokstaven och öka på i. På så sätt kommer vi att skriva ut alla bokstäver i strängen tills vi stöter på nulltecknet (som betyder att strängen har tagit slut). När nulltecknet är upptäckt avslutas loopen, och en radbrytning skrivs ut.

Varning: återigen vill vi påpeka: det är viktigt att null-tecknet kommer med i strängen! I detta senaste program visas det tydligt hur viktigt det är, för vad händer om det inte finns med något nulltecken? Då kommer i kunna få så höga värden som typ 1627378... och bokstav nr 1627378 finns inte. Programmet kommer leta på något ställe i minnet det absolut inte får gräva i.

Detta var troligtvis den längsta delen hittills, men du har också fått veta nästan allt om funktioner. Dessutom har du fått en extralektion om strängar. Ja, strängar i C är svårt.

← Strängar

Switch-sats →

Copyleft kqr & slaeshjag 2009, 2012 some rights reserved