Laboratorium Podstaw Informatyki
Kierunek Elektrotechnika
Ćwiczenie 1.3
Funkcje, operatory i wyrażenia.
Wskaźniki i tablice
Zakład Metrologii AGH
Kraków 2000
Funkcje
Każda funkcja ma następujący format:
typ_zwracanej_wartości nazwa_funkcji( lista_deklaracji_argumentów )
{
deklaracje i instrukcje, jeśli występują
}
Typ zwracanej wartości 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 wychodzi z funkcji, 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 CzyWystępuje(char tab[ ], char c) /*pierwszy parametr to tablica znaków o nazwie
tab, drugi o nazwie c to znak*/
{
int i;
for(i = 0; i < strlen(tab); i = i+1)
if(tab[i] == c) /*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 = CzyWystępuje(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 parametry char tab[ ] i char c nazywane są parametrami formalnymi. Przy wywołaniu funkcji takim jak np. CzyWystępuje(t, `z'); umieszczone są parametry aktualne, które są podstawiane za parametry formalne. Np 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.
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). 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 parametru tylko jego adres. Mając adres danego obiektu można go zmodyfikować. Więcej o tym sposobie przekazywania parametrów później.
W sposób specjalny traktowane są tablice będące argumentem funkcji. Wtedy to do funkcji przekazuje się wartość będącą położeniem (adres) początku tablicy; jej elementy 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 jakaś tablica wykonać kopie tej tablicy. Pomijając czas potrzebny na wykonanie tej operacji to pewnie wkrótce zabrakłoby pamięci na te kopie. Funkcja może zmienić elementy tablicy poprzez indeksowanie od tego położenia. Tablice są więc przekazywane przez adres.
Zadanie 1:
Napisz program z jakąś dowolną własną funkcją ilustrujący przekazywanie argumentów przez wartość. Niech Twoja funkcja najpierw wypisze wartość przekazanego jej argumentu, potem dokona pewnej jego modyfikacji (np. podstaw coś) i wypisze go ponownie. Po powrocie z funkcji wypisz ponownie wartość zmiennej, która była przekazana przykładowej funkcji. Przekonaj się, że taka funkcja działa na kopi przekazanych jej parametrów aktualnych i po opuszczeniu funkcji przekazane jej parametry mają taką samą wartość, jak przed wejściem do funkcji.
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.
Zadanie 2:
Napisać program, który pokaże różnice pomiędzy operatorami przedrostkowymi (np. ++arg) i przyrostkowymi (np. arg++).
Operatory relacji i logiczne:
> >= < <= |
większe, większe lub równe, mniejsze, mniejsze lub równe |
== |
przyrównanie |
!= |
nierówne |
&& |
logiczne AND |
|| |
logiczne OR |
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 3:
Napisz funkcję WordLength(), która przy użyciu operatorów bitowych obliczy długość słowa dostępnej maszyny, to znaczy określi liczbę bitów wartości typu int.
Zadanie 4:
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 się zmienić. 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. Jak to zrobić: 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;
Zadanie 5:
Napisz funkcje sprawdzającą podzielność liczby przez 2 przy wykorzystaniu wyrażenia warunkowego, a nie instrukcji if-else. Uwaga: ponieważ tekst jest tablicą znaków, to do funkcji go wypisującej przekazywany jest adres pierwszej literki. W związku z tym tekst (adres początku tekstu) może być wartością wyrażena warunkowego - wykorzystaj ten fakt.
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 deklarujemy tablicę o rozmiarze 32 elementów typu char i nazwie tekst. Jak widać tablica ta jest nie 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 długość tekstu plus 1, tj. 13 + 1. Rozmiar tablicy ustawiany jest na ilość liter plus jeden, dlatego że 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. Spróbujmy wypisać co znajduje się w tablicy txt. Zrobimy to na dwa sposoby.
#include <stdio.h>
char txt[] = "to jest tekst";
main( )
{
int i;
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 6:
Napisz funkcję reverse(char s[ ]), odwracającą znaki ze strumienia wejściowego. Funkcja ma zbierać znaki, aż do napotkania znaku odpowiadajacego 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. Kod klawisza ESC to 27.
Zadanie 7:
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 tak 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”.
Zadanie 8:
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 w 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:
#include <stdio.h>
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*/
}
Laboratorium Podstaw Informatyki Strona 1
Zakład Metrologii AGH