PODSTAWY INFORMATYKI Wykład 3
Instrukcja if Ogólna postać if (wyrażenie) instrukcja1 else instrukcja2 Wykonywana jest dokładnie jedna z dwóch instrukcji związanych z if-else. Jeśli wyrazenie jest prawdą (nie 0), to wykonywana jest instrukcja1; jeśli nie, wykonywana jest instrukcja2. Każda instrukcja może być pojedynczą instrukcją lub ciągiem instrukcji ujętą w nawiasy klamrowe.
Warunki - przykłady if (a>b) printf(”tak!\n”); if (a>b && b>c) ... if (2+a == 3*b) ... if ((a>4) == (b<10)) ... Wartościowanie: od lewej do prawej wstrzymywane, gdy znany jest wynik: if (4>9 && a>n++) ... /* czy n będzie zwiększone? */
Operatory inkrementacji i dekrementacji ++ -- mogą być używane jako operatory prefiksowe (przed zmienną, np. ++n) albo postfiksowe (po zmiennej, np. n++) Przykład: jeśli n jest równe 5, to x = n++; ustawia x na 5, ale x = ++n; ustawia x na 6; obydwa przypisania ustawiają n na 6.
Operatory bitowe
Operator bitowy AND Działa na wartościach całkowitych Przykład:
Operatory bitowe - przykłady 6 & 11 = 4 | 1 = 7 ^ 2 = 5 >> 2 = 3 << 2 = ~0 = 2 5 1 12 -1 (por. z !0)
Ujemne liczby całkowite: dlaczego ~0 jest równe -1 Unsigned 0: 0000 1: 0001 2: 0010 ... 15: 1111 Signed -8: 1000 -2: 1110 -1: 1111 0: 0000 1: 0001 2: 0010 7: 0111 ... ...
Strumienie wejściowe i wyjściowe krótki przegląd Standardowe wejście: stdin Standardowe wyjście: stdout Stand. strumień błędów: stderr Szczegóły zostaną omówione później Normalnie strumienie te są związane z konsolą: stdin z klawiaturą stdout i stderr z terminalem tekstowym Strumienie mogą być przekierowywane testapp.exe <dane.txt >wyniki.txt
Wejście znakowe Odczytanie pojedynczego znaku lub EOF: Przykład: int getc(FILE *stream); powyższa deklaracja nazywana jest prototypem Przykład: int c; c = getc(stdin); Czytanie jednego znaku z stdin: int getchar(void); getchar() działa jak getc(stdin)
Wyjście znakowe Zapis pojedynczego znaku do strumienia: Przykład: int putc(int c, FILE *stream); Przykład: putc(’A’,stdout); Zapis pojedynczego znaku do stdout: int putchar(int c); putchar(’A’) działa jak putc(’A’,stdout) Zwracana wartość: wypisany znak
Kopiowanie plików #include <stdio.h> /* kopiowanie wejścia na wyjście: pierwsza wersja */ main() { int c; c = getchar(); while (c != ‘q’) { putchar(c); } return 0;
Kopiowanie plików - druga wersja #include <stdio.h> /* copy input to output; 2nd version */ main() { int c; while ((c = getchar()) != ‘q’) putchar(c); return 0; }
Liczenie znaków #include <stdio.h> /* count characters in input; 1st version */ main() { long nc; nc = 0; while (getchar() != ‘q’) ++nc; printf("%ld\n", nc); return 0; }
Liczenie znaków - druga wersja #include <stdio.h> /* count characters in input; 2nd version */ main() { double nc; for (nc = 0; getchar() != ‘q’; ++nc) ; printf("%.0f\n", nc); return 0; }
Liczenie wierszy #include <stdio.h> /* count lines in input */ main() { int c, nl; nl = 0; /* the number of lines so far */ while ((c = getchar()) != ‘q’) if (c == '\n') /* new line found */ ++nl; printf("%d\n", nl); return 0; }
Zmienne w pamięci Każda zmienna pamiętana jest w pewnym miejscu pamięci ... lub w rejestrze procesora Adres zmiennej jest indeksem tej komórki pamięci, w której znajduje się pierwszy bajt zmiennej Liczba bajtów zajmowanych przez zmienną zależy od jej typu; operator sizeof informuje o tym rozmiarze: sizeof(type)
Wskaźniki Wskaźnik jest adresem, pod którym znajduje się zwykle coś użytecznego Definiowanie zmiennej przechowującej adres: <type> *<variable> Przykład: int *pti; Pobieranie adresu, pod którym przechowywana jest zmienna: operator & int x; pti = &x;
Wskaźniki - dostęp do pamięci int x; pti = &x; x = 5; *pti = *pti + 1; /* teraz x = 6 */ x++; printf(”%d”,x); /* 7 */
Wskaźniki przykład #include<stdio.h> #include<stdlib.h> #include<string.h> int main(void) { char*p[3]={ "tak","nie", "byc moze.Zmien postac pytania„ }; char nap[80]; printf("wprowadz pytanie\n"); gets(nap); printf(p[strlen(nap)%3]); return 0;} /*WYNIK: wprowadz pytanie tak Nnnnn byc moze.Zmien postac pytania*/
int scanf(char *format, ...) ...aby przeczytać z wejścia coś bardziej złożonego niż pojedynczy znak scanf jest odpowiednikiem printf dla strumienia wejściowego udostępnia wiele podobnych konwersji jak printf, ale w przeciwnym kierunku Prototyp: int scanf(char *format, ...) scanf wymaga wskaźników do zmiennych
Przykład scanf /*WYNIK: Podaj liczbę:5 Kwadrat 5 wynosi 25*/ #include <stdio.h> main() { int x; printf(” Podaj liczbę: ”); scanf(”%d”, &x); printf(”Kwadrat %d wynosi %d\n”, x, x*x); return 0; } /*WYNIK: Podaj liczbę:5 Kwadrat 5 wynosi 25*/
<typ> <zmienna>[liczba_elementów] Tablice Definiują więcej “zmiennych” jedną definicją Składnia: <typ> <zmienna>[liczba_elementów] Przykład: int a[10]; Indeksowanie: od 0 - jaki jest ostatni indeks? Dostęp: a[5] = 17;
Tablice przykład 1 #include<stdio.h> #include<stdlib.h> int main(void) { int a1[10],a2[10]; int i; for(i=1;i<11;i++)a1[i-1]=i; for(i=0;i<10;i++)a2[i]=a1[i]; for(i=0;i<10;i++)printf("%d\n",a2[i]); return 0; } WYNIK 1 2 3 4 5 6 7 8 9 10
Łańcuchy znakowe jako tablice Definiowanie zmiennej przechowującej łańcuchy (teksty): jako tablicy znaków: char nazwa[długość] na przykład: char s[20]; Na końcu łańcucha jest znak ’\0’ Uwaga na długość! Funkcje operujące na łańcuchach
Operacje na wskaźnikach Wskaźniki mogą być: porównywane odejmowane powiększane o wartość całkowitą (skalowaną!) pomniejszane o wartość całkowitą (skalowaną!) Dodanie 1 do wskaźnika powiększa adres o rozmiar wskazywanego typu
Dodawanie liczby całkowitej do wskaźnika double *pd; double t[10]; pd = &(t[5]); printf(”%p\n”,pd); /* 0065FB60 */ pd = pd + 3; printf(”%p\n”,pd); /* 0065FB78 */ /* teraz pd wskazuje na t[8] */ 0x18 = 24 = = 3*8
Wskaźniki i tablice Nazwa tablicy jest synonimem adresu jej początkowego elementu Przykład: double x[5]; double *p; p = x; /* tak jak p = &(x[0]); */ *p = 12; p++; /* teraz p wskazuje na x[1] */ *p = 13; /* tutaj x[0]=12, x[1]=13 */
Wyprowadzenie łańcucha bez użycia printf #include <stdio.h> main() { char s[]="Hello again!\n"; char *p; p=s; while (*p) putchar (*p++); return 0; } WYNIKI: Hello again!
Nasza pamięć Człowiek dysponuje (co najmniej) dwoma rodzajami pamięci: dużą: trwałą, ale trudną w zapisie (czas zapisu jest długi) małą: zanikającą w ciągu kilku sekund, ale łatwą do zapisania
Funkcje Człowiek potrafi analizować jednocześnie około 7 obiektów Co stanie się, jeśli program ma więcej niż 7 linii? Program należy podzielić na funkcje: Zapisać kilka linii kodu i nadać im nazwę Uruchamiać je stosując nazwę Oczywiście, nie jest to jedyny powód, dla którego należy stosować funkcje...
Funkcje w języku C Definicja funkcji ma następującą postać: typ_wyniku nazwa_funkcji(opcjonalne deklaracje_parametrów) { deklaracje instrukcje }
Przykład Funkcji podnoszenia do potęgi #include <stdio.h> /* power: podnoszenie base to n-tej potęgi; n >= 0 */ int power(int base, int n) { int i, p; /* zmienne lokalne */ p = 1; for (i = 1; i <= n; ++i) p = p * base; return p; } main(){ int i; for (i = 0; i < 10; ++i) printf("%d %d %d\n", i, power(2,i), power(-3,i)); return 0;}
Wyniki programu 0 1 1 1 2 –3 2 4 9 3 8 –27 4 16 81 5 32 –243 6 64 729 7 128 –2187 8 256 6561 9 512 -19683
Sterowanie - dokończenie Konstrukcje poznane do tej pory: if if-else while for Inne: do-while switch break continue
Pętla do-while Składnia: Przyklad: do instrukcja while (wyrazenie); int n; do { printf(”Podaj liczbę dodatnią: ”); scanf(”%d”,&n); } while (n<=0);
do-while Przykład #include<stdio.h> main() { int n; do { printf("Podaj liczbę dodatnia: "); scanf("%d",&n); } while (n<=0); }
do-while Wyniki programu Podaj liczbę dodatnia: -4 Podaj liczbę dodatnia: -3 Podaj liczbę dodatnia: -2 Podaj liczbę dodatnia: -1 Podaj liczbę dodatnia: 0 Podaj liczbę dodatnia: 1
Instrukcja switch Składnia: switch (wyrażenie) { case wyrazenie-stale: instrukcje default: instrukcje } Przykład: switch (c=getchar()) { case ’+’: printf("Plus"); break; case ’-’: printf("Minus"); default: printf("Inny znak");
Przerywanie pętli Czasem potrzebne okazuje się przerwanie pętli w inny sposób niż przy testowaniu warunku Instrukcja break daje możliwość wyjścia z pętli for, while i do, tak jak ze switch break powoduje, że przerywana jest najbardziej wewnętrzna pętla lub switch
kalkulator Przykład switch, break #include<stdio.h> #include<stdlib.h> int main(void){ int i,j; char dz; printf("wprowadz dzialanie\n"); scanf("%d%c%d",&i,&dz,&j); switch(dz){ case '+':printf("%d",i+j); break; case'-':printf("%d",i-j); case'/':if(j)printf("%d",i/j); case'*':printf("%d",i*j);} return 0;} /*WYNIKI wprowadz dzialanie 3-2 1 4*5 20 4+5 9 4/2 2 */
Pomijanie iteracji: continue Instrukcja continue jest podobna do break, jest stosowana rzadziej Powoduje rozpoczęcie kolejnej iteracji for, while, lub do bez dokańczania bieżącej W pętli while i do oznacza to natychmiastowe przejście do testowania warunku W pętli for, sterowanie przechodzi do części „inkrementującej” Instrukcja continue nie dotyczy switch. Zastosowana w tej instrukcji pomija iterację pętli, w której umieszczono switch
continue: przykład Przetwarzanie tylko nieujemnych elementów tablicy a Ujemne wartości są pomijane for (i = 0; i < n; i++) { if (a[i] < 0) /* skip negative elements */ continue; ... /* do positive elements */ }
goto i etykiety W C dostępna jest instrukcja goto i etykiety do opisywania celu skoku Formalnie, instrukcję goto można zastąpić innymi W praktyce łatwo jest pisać kod bez goto Sytuacja, w której goto jest przydatne: przerywanie zagnieżdżonych pętli w sytuacji wystąpienia błędu: for ( ... ) for ( ... ) { ... if (katastrofa) goto error; } error: /* clean up the mess */
goto: inny przykład Ustalenie, czy dwie tablice a i b mają wspólny element: for (i = 0; i < n; i++) for (j = 0; j < m; j++) if (a[i] == b[j]) goto found; /* nie znaleziono wsp. elem. */ ... found: /* znaleziono: a[i] == b[j] */
Unikanie goto Kod zawierający goto może zawsze zostać zapisany bez tej instrukcji, choć czasem wymaga to wprowadzenia dodatkowych zmiennych i wydłużenia kodu: found = 0; for (i = 0; i < n && !found; i++) for (j = 0; j < m && !found; j++) if (a[i] == b[j]) found = 1; if (found) /* znaleziono: a[i-1] == b[j-1] */ ... else /* nie znaleziono el. wsp. */