profil

C++ - wykład 1

poleca 85% 108 głosów

Treść
Grafika
Filmy
Komentarze

Programowanie w języku C/C++

Wykłady - 30 godz. semestr I - studia zaoczne
Wykład 1 - 2 godz.


Zakres tematyczny:

1. Wprowadzenie - historia języka C/C++
2. Pierwszy program :struktura programu jednosegmentowego, operacje wejścia /wyjścia - porównanie języka C i C++.

1.Wprowadzenie.

Na dzisiejszym wykładzie rozpoczynamy omawianie jednego z wielu języków programowania, a mianowicie języka C i jego potomka języka C++. Celowo nie ograniczymy się jedynie do bardzo modnego ostatnio języka C++, albowiem początkującym zwłaszcza programistom programowanie samo w sobie sprawia wiele problemów, nie mówiąc już o programowaniu obiektowo zorientowanym. Poza tym, spotkacie się Państwo w literaturze z programami napisanymi w języku C i ich analiza mogłaby sprawić trudności.

Na wykładach będziemy kolejno omawiać poszczególne elementy języka C i ich ewentualne uzupełnienia w języku C++. Stosunkowo niewiele miejsca poświęcimy programowaniu obiektowemu, ponieważ zagadnieniu temu poświęcony będzie odrębny przedmiot w późniejszych semestrach. Na początek kilka słów o historii języka C.

Język C opracowany został ponad 20 lat temu przez Kernighana i Ritchiego, którzy są jednocześnie autorami najpopularniejszego opracowania na temat standardu tego języka. Pierwowzorem tego języka był język B (wywodzący sie z języka BCPL) stworzony dla pierwszego systemu UNIX. W toku rozwoju powstało kilka standardów języka, z których obecnie najpopularniejszy to ANSII C. Jest to jednak standard umożliwiający tylko programowanie strukturalne. Wraz z rozwojem obiektowych elementów języka powstał standard AT&T Bell Laboratories, obejmujący oba elementy języka.

Język C jest językiem ogólnego stosowania. Był on ściśle związany z systemem UNIX dla którego został opracowany, ponieważ zarówno system operacyjny jak i większość programów działających pod jego kontrolą napisano w C.

Ideą, która przyświecała twórcom języka C, było stworzenie takiego języka, który skupiałby w sobie cechy języków niskiego poziomu, a więc: szybkość i efektywność kodu wynikowego programów, jak i wysokiego poziomu: prostszy zapis algorytmów, większa przenośność programów między różnymi typami komputerów. Tak powstał język C dziedziczący część cech języków asemblerowych jak i języków wysokiego poziomu.

W istocie, rdzeń języka C można określić jako stosunkowo niskiego poziomu, ponieważ:

a) posługuje sie tymi samymi typami obiektów co większość komputerów: a więc znakami, liczbami, adresami. Obiekty te mogą być łączone lub przemieszczane za pomocą zwykłych operacji arytmetycznych i logicznych dostępnych na istniejących maszynach. Nie istnieją tu operacje na obiektach złożonych jak ciągi znaków, zbiory, listy, tablice;
b) nie zawiera w przeciwieństwie do np.Pascala (który z pewnością większość z Państwa zna), instrukcji pozwalających wykonywać operacje wejścia/wyjścia i innych specjalistycznych funkcji, będących standardowymi elementami języków wysokiego poziomu. W zamian za to, otrzymujemy niewielką liczbę instrukcji, znaczną liczbę różnorodnych typów danych i operatorów, umożliwiajacych efektywny zapis algorytmów.

Takie postępowanie można wytłumaczyć chęcią stworzenia języka zapewniającego lepszą przenośność programów pomiędzy komputerami różnych typów. Wiadomo bowiem, że sposób przeprowadzania np. operacji we/wy zależy od danego typu systemów komputerowych i zwykle bywa zaprogramowany z uwzględnieniem specyfiki konkretnego systemu.

Problem braku instrukcji (wydawałoby się niezbędnych) do realizacji tych operacji rozwiązano dostarczając odpowiednie funkcje w standardowych bibliotekach języka. Stanowią one integralną część języka bez której nie można się obejść przy programowaniu. Niestety Ci z państwa, którzy programowali wcześniej w innych językach, szczególnie w Pascalu, mogą napotkać pewne problemy wynikające ze znacznie gorszej niż w tym języku diagnostyki błędów. Tak więc trzeba będzie zwrócić baczniejszą uwagę na sprawy składni i analizy błędów logicznych wynikających z błędnego wykorzystania elementów języka do zapisu algorytmu.

2. Pierwszy program

Rozpoczynamy teraz praktyczną naukę programowania w języku C/C++. Na początek powiemy sobie króciutko o kodzie źródłowym programu. W C/C++ tworzenie programu odbywa sie w dwóch etapach:

1. opracowanie kodu źródłowego
2. generowanie kodu wynikowego

Pierwszy etap polega na zapisaniu algorytmu za pomocą instrukcji języka. W efekcie programista otrzymuje kod źródłowy programu, który jest plikiem tekstowym zawierającym zapis algorytmu.

Drugi etap polega na przetłumaczeniu konstrukcji właściwych językowi wyższego poziomu na rozkazy procesora i doprowadzenie programu do postaci wykonywanej przez komputer. Operacja ta składa się z dwóch faz: kompilacji i linkowania: czyli łączenia. Nas interesuje etap pierwszy, czyli generowanie kodu źródłowego. Ponieważ każdy programista wyznaje zasadę : ABY PISAĆ TRZEBA PISAĆ , zaczynamy naukę programowania od najprostrzego programu:

Program 1

#include
main()
{
printf("Witamy w krainie języka C");
}

Program 2
#include
main()
{
cout<<"Witamy w krainie języka C++";
}

Wykonanie tych programów spowoduje wyświetlenie na ekranie monitora napisu: "Witamy w krainie języka C" dla programu 1 i "Witamy w krainie języka C++" dla programu 2.

Jak widać, oba programy mają takie samo zadanie: wypisanie tekstu na ekranie, ale różnią się nieznacznie: pierwszy napisany został zgodnie ze standardem języka C (dalej mówić będziemy napisany w C), drugi napisany został w języku C++. O różnicach będziemy mówić za chwilę, teraz omówimy wspólne cechy, a mianowicie strukturę programu w języku C/C++, która jest taka sama.

Każdy program C/C++ jest zbudowany z funkcji i zmiennych. Funkcja zawiera instrukcje określające jakie operacje obliczeniowe należy wykonać, zmienne natomiast przechowują wartości używane podczas tego procesu. Program musi zawierać funkcję zwaną main. Funkcjom języka C/C++ można nadawać dowolne nazwy lecz main jest nazwą specjalną. Od niej rozpoczyna się wykonywanie programu. Treść tej funkcji (zwana ciałem funkcji), czyli innymi słowy instrukcje wykonywane w ramach tej funkcji, zawarte są między dwoma nawiasami klamrowymi {...} (tak jak w każdej innej funkcji). W naszych programach funkcja main ma tylko jedną instrukcję:

1. printf("Witamy w krainie jezyka C");
2. cout<<"Witamy w krainie jezyka C++";

która sprawia, że na standardowym urządzeniu wyściowym czyli po prostu na ekranie zostanie wypisany napis ujęty w znaki cudzysłowia. Umieszczony na końcu średnik jest znakiem końca instrukcji. Każda instrukcja musi być zakończona średnikem.

Język C/C++ jest językiem o tzw. wolnym formacie. Wszystko może znaleźć się w każdym miejscu linii, a nawet rozpisane na kilka linii. Poza nielicznymi sytuacjami, w dowolnym miejscu instrukcji można przejść do innej linii i tam kontynuować pisanie, ponieważ koniec instrukcji określa nie koniec linii, ale średnik.

Dla wygody programisty lepiej jest każdą instrukcję pisać w osobnej linii, co po pierwsze zwiększa czytelność programów, a po drugie, korzystając z takich narzędzi jak debugger mamy możliwość śledzenia programu linia po linii.

Wracając do naszego programu. Na ogół main, aby wykonać działanie woła na pomoc (inaczej wywołuje) inne funkcje napisane przez programistę lub pochodzące z bibliotek dostarczonych wraz z kompilatorem. Przyjżyjmy się pierwszym linijkom naszych programów. Zlecają one kompilatorowi dołączenie do programu informacji o standardowej bibliotece we/wy. Jak pamietamy, operacje te nie są częścią definicji języka. Dla języka C będzie to biblioteka stdio, a dla C++ iostream. Dokładniej operacje we/wy będziemy omawiać w dalszej części wykładu.

Jedną z metod komunikacji miedzy funkcjami jest przekazywanie danych. Funkcja wywołująca tworzy listę wartości czyli tzw. argumentów i dostarcza je funkcji wywoływanej.
Jest ona umieszczana w nawiasach okrągłych, bezpośrednio po nazwie funkcji. W naszym przypadku main jest funkcją bezparametrową, o czym informuje nas pusty nawias po nazwie main. W szczególnych przypadkach funkcja main może posiadać parametry. Przykładem funkcji z jednym argumentem jest funkcja printf. Argumentem jest tutaj stała napisowa ujęta w znaki cudzysłowia.

Zarówna w 1-szym jak i 2-gim programie w stałych napisowych mogą występowac pewne znaki umożliwiające sterowanie wypisywaniem na ekran. Zmodyfikujmy nasze programy w instrukcjach odpowiadających za wyświetlenie napisu:

1. printf("Witamy
w krainie jezyka C");

2.cout<<"Witamy
w krainie jezyka C++";

Wstawienie w środek tekstu znaku
powoduje przerwanie wypisywania w bieżącym wierszu i jego kontynuację od poczatku w nowym wierszu:

Witamy w krainie języka C(C++)

Miejsce wstawienia tego znaku zależy od sposobu w jakim chcemy wypisac komunikat. Oprócz znaku
dostępne są inne znaki formatujące np.: - wstawienie zanku tabulacji, - wstawienie znaku cofania (backspace) i inne.

Poprzednie programy wykonywały tylko podstawowe operacje wyjściowe - wypisywanie komunikatu. W naszym następnym programie spotkamy się z inną operacją we/wy - wczytaniem danych z klawiatury. Wprowadzimy też kilka nowych pojęć takich jak: komentarz, deklaracja, zmienne, formatowane wypisywanie danych.

Program 3

#include

/* Program do wyznaczania pola powierzchni kuli o promieniu R */
main() { int r, p; // p-pole kuli, r-promien kuli
printf("Wprowadz promien kuli r = ");
scanf("%i",&r);
p = 4 * pi * r * r;
printf("pole kuli o promieniu r = %i jest rowne p = %i",r,p);
}

Program 4
#include
/* Program do wyznaczania pola powierzchni kuli o promieniu R */
main() {int r, p; // p-pole kuli, r-promien kuli
cout<<"Wprowadz promien kuli r = ";
cin>>r;
p = 4 * pi * r * r;
cout <<"pole kuli o promieniu r = <
2. cin>>r;

Są one instrukcjami odpowiadającymi za operacje związane z klawiaturą, czyli standardowym urządzeniem wejściowym (C-onsole IN-put). Umożliwiają one wczytanie z klawiatury liczby, której wartość zostaje przypisana zmiennej r. Pierwsza instrukcja jest charakterystyczna dla C, druga dla C++. Już na pierwszy rzut oka widać, że druga instrukcja jest wyraźnie prostsza. W funkcji scanf musimy pamietać o typie wczytywanej liczby, i o tym czy mamy wpisac znak & czy też nie. Po wczytaniu danych następuje część obliczeniowa. Następnie instrukcje odpowiadające za wydrukowanie wartości wyniku. Używamy do tego celu znanych już nam konstrukcji cout i printf.

1.printf("pole kuli o promieniu r = %i jest rowne p = %i",r,p); 2.cout <<"pole kuli o promieniu r = <
for(i=0;(i<80) && ((ch=getchar()) != EOF) && (ch !='
');i++)
buffer[i] = ch;
buffer[i] = '';
printf("%s
",buffer);

}

Program ten używa funkcji getchar() do wprowadzania z klawiatury jednej linii i gromadzenia jej w tablicy znaków buffer i kończy string znakiem specjalnym zanim wyświetli go na ekranie monitora.

Do wyprowadzania na ekran jednego znaku o określonym kodzie służy funkcja putchar():

int putchar(int)

Funkcja zwraca kod wypisanego znaku lub EOF gdy powstal błąd. Przykład zastosowania funkcji :



#include
#include
main()
{
int ch;
while((ch = getchar()) != EOF)
putchar(tolower(ch));
return 0;
}

Program zamienia małe litery tekstu wejściowego na duże.

Na ćwiczeniach napiszemy kilka programów wykorzystujących te funkcje, które później będziecie państwo sprawdziać na laboratorium. Przejdziemy teraz do następnej grupy funkcji obsługujących tzw.stringi czyli ciagi znaków. Są to funkcje:gets i puts. Służą one do pobierania łańcucha znaków z klawiatury i wysłania na ekran zapamiętanego wcześniej w tablicy znaków.

Funkcja gets(string) czyta łańcuch znaków z klawiatury i zapisuje go do zmiennej buffer:

char *gets(buffer);

char * buffer;

Pierwszy napotkany znak nowej linii '
' zamienia na NULL przed zwróceniem stringu.

Jeśli odczyt przeprowadzony był bez błędu, funkcja zwraca adres tablicy znaków (swój argument), jeśli nie zwraca null pointer



Przykład 6

#include
int main()
{
char imie[20];
char *res;
printf("Cześć jestem IBM .Podaj mi swoje imie");
res = gets(imie); /* może być tylko gets(imie); */
printf("Milo mi Cie poznac %s",res);
return 0;
}

Program ten jak widać prosi o podanie imienia osoby uruchamiającej program, zapisując to imię przy pomocy funkcji gets do tablicy znakow imie i odpowiada jej imieniem wypisując zawartość wskażnika res . Wprowadziliśmy tu kilka nowych konstrukcji. Pierwsza to linia :

int main()

Poprzednio nie wstawialiśmy słowa kluczowego int przed nazwa main. W standardzie przyjęło się, że każda funkcja jeśli nie zadeklarowaliśmy inaczej jest typu int. Można wtedy typ funkcji opóścić, jeśli natomiast napiszemy np. int wtedy w przedostatniej linii musimy napisać instrukcję określającą wartość jaką zwraca funkcja tu: return 0. Gdy funkcja nie zwraca wartości wtedy zamiast int main() piszemy void main() i pomijamy linię return 0; Drugą nowością jest linia

char imie[20].

Jest to deklaracja 20-elementowej tablicy typu char (znak). Jak mówiliśmy string to po prostu tablica znaków.

Funkcja puts(string) zapisuje string na ekran:
int puts(string);
const char *string;

Zamienia znak końca stringu na znak nowej linii
. Zwraca 0 jesli operacja przeszła bez błędu, i wartość niezerową gdy wystąpił błąd.

#include
int result;
main()
{

/* write a prompt to stdout */
result = puts("Insert data disk and strike any key");
}

Do realizacji tzw. formatowanego we/wy służy grupa funkcji scanf i printf. Pamiętamy z poprzednich programów, że funkcja printf() służy do wypisywania na ekranie monitora ciągu znaków i wartości zmiennych, przy czym obie te funkcje można łączyć razem np.:

printf("Wartość zmiennej a = % 2i zmiennej b = %4.2f
zmiennej c = %c zmiennej d = %s
",a,b,c,d);

Składnia tej funkcji jest następująca:

int printf(format [,argument]...);
const char *format;

Zmienna format zawiera zwykłe znaki kopiowane do strumienia wyjściowego oraz specyfikacje przekształceń, z których każde wskazuje sposób przekształcenia i wypisania kolejnego argumentu funkcji printf. Każdą specyfikację przekształcenia rozpoczyna znak % a kończy znak charakterystyczny dla tego przekształcenia. Między % i znakiem przekształcenia mogą w następującej kolejności wystepować:

- minus zlecający dosunięcie argumentu do lewego krańca jego pola,

- liczba określająca minimalny rozmiar pola. Jeśli trzeba pole zostanie uzupełnione do pełnego rozmiaru z lewej strony(lub z prawej gdy wczesniej wystapił minus),

- kropka rozdzielająca rozmiar pola od precyzji,

- liczba okreslająca precyzję tj. max. liczbe znaków dla tekstu, liczbe cyfr po kropce dla liczb zmiennoprzecinkowej lub min. liczbe cyfr dla wartosci całkowitej,

- jedna z liter: h - argument całkowity należy wypisac jako short, l - jesli jako long.

Podstawowe znaki przekształcenia:

d ,i - liczba dziesiętna
o - liczba ósemkkowa bez znaku
x,X - liczba 16-tkowa bez znaku
u - liczba dziesiętna bez znaku
c - znak
s - ciąg znaków wypisywany do wystapienia lub wyczerpania liczby znaków okreslonych przez precyzję.
f - liczba rzeczywista [-]m.ddd gdzie liczbe cyfr d określa precyzja( domyślnie 6)
e,E - [-]m.dde+-xx

W miejsce formatu wpisywane są wartości zmiennych w kolejności wskazanej w liście zmiennych występującej po formacie. Aby móc wydrukować znak % trzeba napisać: %%

Funkcja scanf służy do realizacji formatowanego wejścia. Podobnie jak w funkcji printf funkcja scanf zawiera łańcuch formatujący określajacy typ wprowadzanych zmiennych. Składnia tej funkcji jest następująca:

int scanfformat[,argument]...);
const char* format;

Funkcja umożliwia większość tych samych przekształceń co funkcja printf lecz w przeciwnym kierunku. Wczytuje znaki ze standardowego wejścia, interpretuje je zgodnie ze specyfikacjami zawartymi w formacie i zapamietuje wyniki w miejscach okreslonych przez pozostale argumenty. Argumenty muszą być wskaźnikami (znak &) i określaja, gdzie dane mają być przekazane.

int i,j;
scanf("%d%d",&i,&j);

Format zawiera specyfikatory przeksztalceń . Mogą tu wystapić:

- odstepy i znaki tabulacji ( są ignorowane),

- zwykłe czarne znaki takie jakie przewidujemy zastać w strumieniu wejściowym(muszą one idealnie pasować do znaków które będą wystepować w strumieniu wejściowym np: scanf("%d/%d/%d",&d,&m,&r); Znaki / będa wystepowac w strumieniu oddzielając zmienne d,m,r;

- specyfikacje przekształceń złożone ze znaku % , opcjonalnego znaku * wstrzymującego przypisanie, opcjonalnej liczby okreslajacej max.rozmiar pola, jednego zopcjonalnych znaików h, l, L ustalajacych rozmiar wyniku oraz ze znaku przekształcenia (podobnych jak w funkcji printf).



Wynik przekształcenia wstawiany jest do zmiennej wskazanej odpowiednim argumentem. Jesli przypisanie ma być wstrzymane (znak *) to pole wejściowe pomija się. Polem wejściowym jest cia czarnych znaków rozciągający sie do najbliższego ciągu białych znaków.

Dokładniej o tych funkcjach musicie państwo przeczytac w literaturze.

To były podstawowe funkcje we/wy w ujęciu proceduralnym, charakterystycznym dla jezyka C. Obecnie omówimy metody realizacji operacji we/wy na standardowych urządzeniach we/wy w języku C++. Zanim do tego przejdziemy musimy na chwilę zatrzymać się przy pojęciu strumienia. Wprowadzanie i wyprowadzanie informacji można potraktować jako strumień bajtów płynący od źródła do ujścia. Np:jeśli chcemy do zmiennej x wczytać z klawiatury jakąś liczbę, wtedy strumień bajtów płynie od urządzenia zewnętrznego - klawiatura do tego miejsca w pamięci gdzie mieści się zmienna x. Dla operacji we/wy kompilator predefiniuje kilka strumieni (predefiniuje czyli zakłada, otwiera strumień i po zakończeniu programu zamyka go) : cout, cin, cerr. Aby skorzystać z tych strumieni w programie trzeba umieścić dyrektywę

#include

podobnie jak dla C .

Ze strumieniem wyjściowym cout został stowarzyszony operator << (z ang. insertion operator), który wskazuje skąd dokąd zostanie wysłana informacja (odpowiada za wysłanie informacji do strumienia), czyli program:

Przykład 7

#include
void main()
{
int x = 123;
cout<
}

powoduje wysłanie do strumienia wyjściowego (który kończy się na ekranie) liczby x = 123.

Można wysłać różne typy zmiennych i stałych przy pomocy jednej instrukcji np.

int x=123;
cout<<"Wartość x = "<< x <<'.'

W przykładzie tym, za pomocą jednej instrukcji przesłano na ekran komunikat, wartość zmiennej i pojedynczy znak. Na ekranie pojawi się napis: Wartość x = 123.

Linijka int x=123 odpowiada za deklaracje zmiennych połączoną z jej inicjacją, czyli nadaniem wartści początkowej. Zwróćmy uwagę, że poszczególne typy zmiennych oddzielone są operatorami <<.

Jak dotąd przykłady podane przez nas nie pokazywały jak przesłać formatowane dane do cout. Przypuśćmy, że chcemy wyświetlić wartość całkowitą używając notacji 16-tkowej. Funkcja printf umożliwiała taką operację. Ale jak to zrobić w C++ za pomocą strumienia cout? Na marginesie pamiętajmy, że C jest częścią C++. Jeśli nie można czegoś zrobić w C++ lub jest to bardziej skomplikowane to w każdej chwili możemy odwołać się do C (w naszym przypadku wystarczy dołączyć , aby użyć funkcji printf). W C++ ze strumieniem cout współpracuje zestaw manipulatorów. Zmieniają one bieżący format dla całkowitego argumentu. Są to: dec, oct, hex.

Przykład 8

#include
main()
{
int x=123;
cout << dec << x <<' '
<< oct << x <<' '
<< hex << x;
}

Przykład pokazuje jak można wyświetlić wartość całkowitą w trzech systemach:dec, hex, oct. Wynik działania programu to :123 173 7b.

Ze strumieniem wejściowym cin został stowarzyszony operator >>, odpowiadający za wczytanie informacji ze strumienia wejściowego (którego początek jest na klawiaturze), czyli program

Przykład 9.

void main()
{
int x;
cout << "wprowadz wartość x :";
cin >> x;
cout << "wartość x = " << x;
}

powoduje wczytanie ze strumienia wejściowego (klawiatury) wartości do zmiennej x.

Jak widać operacje we/wy dla standardowych typów realizowane są bardzo prosto. Nie musimy pamiętać o formacie wprowadzania i wyprowadzania wyników, a strumienie cout i cin interpretują je prawidłowo. Trochę trudniej przedstawiają się te operacje dla typów zdefiniowanych przez użytkownika, ale o tym opowiemy dużo później.

Czy tekst był przydatny? Tak Nie
Przeczytaj podobne teksty

Czas czytania: 18 minut