Laboratorium Podstaw Informatyki Strona 1 Laboratorium Podstaw Informatyki Kierunek Elektrotechnika Ćwiczenie 2 Funkcje, operatory i wyrażenia. Kraków 2010
Laboratorium Podstaw Informatyki Strona 2 Funkcje Każda funkcja języka C ma następującą składnię (format): typ_zwracanej_wartości nazwa_funkcji( lista_deklaracji_argumentów ) deklaracje i instrukcje, jeśli występują Typ zwracanej wartości funkcji w języku C jest opcjonalny, jeśli zostanie pominięty to przyjmuje się, że funkcja zwraca wartość typu int. Jeśli chcemy, aby funkcja nie zwracała żadnej wartości to deklarujemy, że zwraca void. Typ void jest typem specjalnym i oznacza nic. Funkcja, która powinna zwrócić wartość wyrażenia robi to przy użyciu instrukcji: return wyrażenie; Instrukcja return jest ostatnią instrukcją wykonywaną przez funkcję. Oznacza to, że program, gdy wykona instrukcję return opuszcza funkcję, nawet wtedy gdy po tej instrukcji są jakieś inne. Przykład. Napisać funkcję, która sprawdzi czy w podanej tablicy znakowej, zawierającej jakiś tekst, występuje podany znak. Jeśli, tak to funkcja ma zwróci indeks do tablicy (czyli pozycję na której wystąpił ten znak, licząc od 0), jeśli nie to ma zwrócić liczbę -1; int CzyWystepuje(char tab[ ], char c) int i; for(i = 0; i < strlen(tab); i = i+1) if(tab[i] == c) /*pierwszy parametr to tablica znaków o nazwie tab, drugi o nazwie c to znak*/ /*podwojny znak = to operator porównania*/ return i; /* jesli znalazłeś szukany znak to zwróć jego pozycje i wyjdz z funkcji */ return -1; /* jesli nie znalazleś to zwróć -1, jako informacje o niepowodzeniu */ main( ) /*glowna funkcja programu*/ char t[] = "przykladowy tekst"; /*deklaracja tablicy z równoczesną jej inicjalizacją*/ int retval; retval = CzyWystepuje(t, 'z'); /*wywolanie funkcji*/ printf("wynik = %d\n", retval); /*wypisanie wartości zwróconej*/ printf("wynik = %d\n", CzyWystępuje(t, 'b')); /*wywołanie funkcji, wartość zwrócona przez nią będzie argumentem wywołania printf*/ w deklaracji funkcji CzyWystępuje argumenty: tab i c nazywane są argumentami formalnymi. W funkcji main występuje takie oto wywołanie funkcji: CzyWystępuje(t, z ). Jak widzimy w nawiasach podano argumenty t i 'z', które nazywane są argumentami aktualnymi. W trakcie wywołania argumenty te podstawiane są za argumenty formalne, tj. tablica t występująca w funkcji main widziana jest przez funkcje CzyWystępuje pod nazwą tab, a litera z podstawiana jest za znak c.
Laboratorium Podstaw Informatyki Strona 3 Zauważ, że w warunku na zakończenie pętli for użyta jest funkcja strlen. Funkcja ta jako argument dostaje tab (stała tekstowa, lub tablica tekstowa), a zwraca jej długość (liczbę znaków). Aby skorzystać z funkcji strlen musisz włączyć do programu plik nagłówkowy string.h (poprzez użycie dyrektywy preprocesora #include <string.h>). Zauważ też, że tablice w jezyku C indeksowane są od ZERA. Nieco więcej o argumentach funkcji W języku C argumenty do funkcji mogą być przekazywane na dwa sposoby: poprzez wartość i poprzez adres. Jeśli argument jest przekazywany poprzez wartość, to funkcja otrzymuje prywatną, tymczasową kopię danego argumentu. Ponadto funkcja ta nie ma dostępu do rzeczywistego argumentu w funkcji wywołującej. Wewnątrz funkcji każdy taki argument jest zmienną lokalną o wartości początkowej równej wartości, z którą funkcja została wywołana. Funkcja może zmieniać wartość tej zmiennej, ale te zmiany nie są widoczne na zewnątrz, bo przeprowadzane są na kopii, która po zakończeniu wykonywania funkcji przestaje istnieć. Inaczej przedstawia się sytuacja dla zmiennych, które są przekazywane poprzez adres. W takim wypadku funkcja nie dostaje kopii argumentu tylko jego adres w pamięci komputera. Mając adres danego obiektu można ten obiekt zmodyfikować. Więcej o tym sposobie przekazywania parametrów później. W sposób specjalny traktowane są tablice będące argumentem funkcji. Do funkcji przekazuje się informacje o położeniu (tj. adres) pierwszego elementu tablicy. Nie tworzy się kopii tablicy, tj. elementy tablicy nie są kopiowane! Jest to podyktowane wymaganiami na efektywność. Gdyby tablice były przekazywane przez wartość to należało by za każdym razem kiedy wywołujemy funkcję, której argumentem jest tablica wykonać kopię tej tablicy. Pomijając czas potrzebny na wykonanie tej operacji to pewnie wkrótce zabrakłoby pamięci na te kopie. Ponieważ tablice przekazywane są przez adres, funkcja może zmienić elementy tablicy, której adres otrzymała. Przykład. Poniższy przykład jest ilustracją przekazywania argumentów przez wartość. Przekonaj się, że funkcja zamien działa na kopi przekazanych jej parametrów aktualnych i po opuszczeniu funkcji przekazane jej argumenty mają taką samą wartość, jak przed wejściem do funkcji. Tak więc w rzeczywistości funkcja zamien niczego nie zamienia. int zamien(int a, int b); /*deklaracja zapowiadająca*/ main ( ) int a=1, b=2; printf("a=%d,b=%d", a, b); zamien(a, b); printf("po powrocie z zamien: a=%d,b=%d", a, b); zamien (int a, int b) int t=a; a=b; b=t; printf("wewnatrz zamien: a=%d,b=%d", a, b);
Laboratorium Podstaw Informatyki Strona 4 Operatory zwiększania i zmniejszania wartości zmiennych: arg++, ++arg oraz arg--, --arg Operatory te służą do zwiększania lub zmniejszania o 1 argumentu arg. Przykład: Wpisz poniższy program i zobacz co zostanie wyświetlone. main( ) int x, y; printf("start! \n\n\n"); x=1; printf("++x = %d", ++x); //operator przedrostkowy x=1; printf("x++ = %d", x++); //operator przyrostkowy x=1; ++x; printf("x = %d", x); x=1; x++; printf("x = %d", x); x=1; y = ++x; printf("y = %d", y); x=1; y = x++; printf("y = %d", y); Tablice znakowe Naszą znajomość z tablicami rozpoczniemy od tablic znakowych. Przykładami deklaracji tablic znakowych są: char tekst[32]; char txt[ ] = "to jest tekst"; W przypadku pierwszym pokazana jest deklaracja tablicy o rozmiarze 32 elementów typu char i nazwie tekst. Jak widać tablica ta niej jest zainicjowana, wiec ogólnie zawiera wartości przypadkowe. W drugim przypadku deklarujemy i inicjalizujemy tablicę o nazwie txt. Jak widać nie podaliśmy rozmiaru tablicy. Kompilator ustawi automatycznie rozmiar tej tablicy na liczbę znaków w tekście inicjalizującym + 1, tj. 13 + 1. Rozmiar tablicy ustawiany jest na ilość liter + 1, dlatego że w języku C każdy tekst (w naszym wypadku stała tekstowa) zakończony jest znakiem '\0' oznaczającym koniec łańcucha tekstowego. Dzięki temu np. funkcja printf wie, gdzie tekst się kończy, a funkcja strlen moze obliczyć jego długość. Tablice w języku C są indeksowane od 0. W poniższym przykładzie wypiszemy co znajduje się w tablicy txt. Zrobimy to na dwa sposoby. char txt[] = "to jest tekst"; main( ) int i;
Laboratorium Podstaw Informatyki Strona 5 printf("%s\n", txt); /* sposób pierwszy */ i = 0; while(txt[i]) /* sposób drugi - znak po znaku */ printf("%c", txt[i++]); Zwróć uwagę na warunek w pętli while, ponieważ tekst jest zakończony znakiem '\0' to pętla skończy się we właściwy sposób i znak '\0' (odpowiada mu kod o wartości ZERO) nie będzie już wypisywany. Uwaga: pętla while kręci się tak długo jak długo warunek ma wartość prawdy, tzn. jest różny od ZERA. Zadanie 1: Napisz funkcję reverse(char s[ ]), odwracającą znaki ze strumienia wejściowego. Funkcja ma zbierać znaki, aż do napotkania znaku odpowiadającego klawiszowi ESC. Po napotkaniu jakiegoś z wymienionych znaków funkcja wypisuje to co zebrała, ale w odwrotnej kolejności. Do wczytywania znaków z klawiatury (standardowego strumienia wejściowego) użyj funkcji getch (lub getche jeśli chcesz by wprowadzane znaki pojawiały się na ekranie, tzw. echo). Kod ASCII klawisza ESC to 27. Zadanie 2: Napisać funkcję doklej, której parametrami są dwa teksty s i t. Funkcja ma dopisać do tekstu znajdującego się w tablicy s tekst t. Funkcja nie może utworzyć trzeciego tekstu - ma dokleić drugi do pierwszego. Uwaga, tablica zawierająca tekst pierwszy musi być wystarczająco duża, by pomieściła dodatkowo znaki tekstu drugiego. W zadaniu użyj poniższego szablonu: doklej(char s[ ], char t[ ]) /* ciało funkcji...*/ main() char s [128] = "pierwszy "; /*alokacja tablicy mogącej pomieścić tekst o 127 znakach */ doklej(s, "drugi"); printf("%s", s); /*wypisz wynik*/ Zadanie 3: Napisz funkcję znajdz(char s[ ], char x[ ]), której zadaniem będzie sprawdzenie, czy w podanym tekście s występuje podtekst x. Jeśli występuje, to funkcja ma zwrócić pozycję pierwszej litery wystąpienia podtekstu x w tekscie s. Przykład: wywołanie znajdz( napotkaniu, otk ) ma zwrócić wartość 3, a wywołanie znajdz( napotkaniu, otki ) ma zwrócić wartość -1, bo ciąg otki nie występuje w napotkaniu. Uwaga: w zadaniu tym pomocna może okazać się instrukcja break, która powoduje przerwanie wykonywania pętli.
Laboratorium Podstaw Informatyki Strona 6 Zadanie 4: Napisz funkcję małenaduze(char org[ ], char wyn[ ]), która zmieni wszystkie małe litery występujące w tekście org na duże, a rezultat zapisze do tablicy wyn. Przykład: wywołanie małenaduze( 0 1 ala X, tab2) umieści w tablicy tab2 tekst 0 1 ALA X. Uwaga: program powinien zapewnić tablicę tab2 o odpowiedniej wielkości. W najprostszym przypadku może to wyglądać tak: małenaduze(char org[ ], char wyn[ ]) /* ciało funkcji...*/ main() char t [128]; /*alokacja tablicy mogącej pomieścić tekst o 127 znakach */ małenaduze("0 1 ala X", t); printf("%s", t);/*wypisz wynik*/ Uwaga 2: W języku C (jak również w innych językach) znakom odpowiadają pewne liczby nazywane kodami tych znaków (kod ASCII precyzuje jakiemu znakowi, jest ich 256 odpowiada jaki kod liczbowy) np.: litera A ma kod 65 litera B ma kod większy o jeden od litery A, a więc 66 Można więc wypisać duże litery wraz z ich kodami używając takiej pętli: for(i='a'; i < 'Z'; i++) printf("litera:%c ma kod liczbowy:%d", i, i); lub takiej for(i=66; i < 90; i++) printf("litera:%c ma kod liczbowy:%d", i, i); Przypominam, że znak sterujący %c informuje funkcję printf, że ma wypisać znak, nie liczbę, a znak sterujący %d informuje, że należy wypisać wartość liczbową wyrażenia. Jak widać w języku C istnieje dualność pomiędzy znakami, a odpowiadającymi im kodami liczbowymi. W procesie kompilacji instrukcja printf("%c", A ) tłumaczona jest na printf("%c",65). My jednak chcąc zachować czytelność programu powinniśmy stosować formę pierwszą. Operatory relacji i logiczne: > >= < <= większe, większe lub równe, mniejsze, mniejsze lub równe == przyrównanie!= nierówne && logiczne AND logiczne OR
Laboratorium Podstaw Informatyki Strona 7 Bitowe operatory logiczne & bitowa koniunkcja AND bitowa alternatywa OR ^ bitowa różnica symetryczna XOR << bitowe przesunięcie w lewo >> bitowe przesunięcie w prawo ~ bitowe uzupełnienie jedynkowe Zadanie 5: Napisz funkcję WordLength( ), która przy użyciu operatorów bitowych obliczy liczbę bitów typu int (liczba bitów typu int nazywana jest też długością słowa maszyny). Uwaga, jest to zadanie podobne do realizowanego na poprzednich ćwiczeniach z użyciem operatora sizeof teraz chcąc lepiej poznać operatory bitowe chcemy zrealizować je bez użycia tego operatora. Zadanie 6: Napisz funkcję invert(int x, char p, char n), która zamieni n bitów argumentu x z 1 na 0 i odwrotnie, rozpoczynając od pozycji p. Pozostałe bity nie powinny ulec zmianie. Użyj do tego operatora XOR. Przykład: wywołanie invert(15, 3, 2), w zapisie binarnym: 15 = 00001111b, zamieniając na przeciwne bity 3 i 4 (licząc od zera) otrzymujemy: 00010111b. Podpowiedź: należy obliczyć odpowiednią maskę, maska ta powinna mieć 1 na pozycjach, które mają być zmienione na przeciwne i zera na pozostałych. W naszym przykładzie: maska = 00011000, obliczając: 00001111b XOR 00011000 otrzymujemy 00010111b. Operatory i wyrażenia przypisania i = i + 2 jest równoważne i += 2 i = i / 3 jest równoważne i /= 3 Dla większości operatorów dwuargumentowych występuje odpowiedni operator przypisania op=, gdzie op jest jednym z operatorów: + - * / % << >> & ^ Wyrażenia warunkowe Konstrukcja: if(a > b) z = a; else z = b jest równoważna: z = (a > b)? a : b;