Uživatel:Jkl~cswikiversity/Studuji cpp/Opakování základů jazyka C
Vzhled
Na této stránce si dělám stručné výpisky ze seriálu C/C++ na linuxsoft.cz
Varování: toto jsou moje poznámky a já jejich smysl (většinou) chápu. Pokud je nechápete vy, ještě to nemusí znamenat, že je chyba ve vašem přijímači :-).
Klasické céčko
[editovat]Kapitoly 1-4
[editovat]- nejjednodušší výstup stringuje přes puts("whatever");
- kompilace - pokud nechci klasické a.out, tak je lépe použít cc zdrojak.c -o vystup - rovnou bude mít práva 777
- Klasické číselné typy (default je signed):
- (un)signed char + float
- (un)signed short + double
- (un)signed int + long double
- (un)signed long
- globální proměnné definované "venku" těla a funkcí
- lokální proměnné MUSÍ BÝT DEFINOVÁNY před voláním funkcí
- typ konstant lze vnutit - F float, L long double
- lomítka, která jsem neznal:
- \a = bell
- \f = nová stránka
- Konstanty (stringové) se dají hezky skládat:
puts( "Toto je jeden řádek \n" "a tohle druhý" );
Pointery
[editovat]- int *pi;
- &pi - adresa pi
- *pi - hodnota na kterou ukazuje
- všechny funkce se volají hodnotou !! -> chcemeli proměnnou měnit, musíme předat pointer
void nuluj(*pi int){ *pi=0; } // int main(void){ int i=5; nuluj(&i); return(i); }
printf
[editovat]- uložení stringu - const char *s = "Nějaký text";
- printf - parametry
- %i - int (decimal)
- %o, %u, %x, %X unsigned - octal,decimal, hex (0a a 0A)
- %e - double (1.5e12)
- %f double (1500000000000)
- %c chr(i)
- %s const char * - string končící #0
- %p void * - adresa paměti v hex
- printf vrací počet vypsaných znaků
- v případě přetypování to tam nezapomenout napsat !!
Kapitoly 5-8
[editovat]- [1]
- chci-li pomocí printf napsat místo "ff" "0xff" tak použiju %#x místo %x
- printf("Tak takhle se dá taky napsat pí: %#+.4f\n\n",i);
Operátory
[editovat]- i=j=0; // vyhodnocuje se zprava, elegantně nulujeme dvě proměnné na jednou
- výsledkem dělení dvou intů je int, pokud jmenovatele nepřetypujeme (třebas double)
- kromě i+=1 lze psát i řeba i*=15;
- návratovou hodotou i++ je i, ale ++i je i+1 !
- klasické logické operátory not (!), and (&&) a or (||) dělají to co se od nich očekává
- klasické zanořování ifů by mělo fungovat (na rozdíl od některých pohybných iplementací jiných jazyků ;-) )
- podmíněný výraz (i==1:"ano":"ne") nesmí být podle definice vlevo, jedině vpravo (nevrací l-hodotu).
- elegantní prevence dělení nulou: vysledek = i ? 128 / i : 0;
Cykly
[editovat]- while() {} - pascalské while() do begin
- do {} while () - pascalské repeat .. until
- for jako v PHPku (no ono je to veskutečnosti obráceně, žejo ...)
- continue přeskočí zbytek aktuální smyčky
- break ukončí smyčku
- label: .... goto label;
- narozdíl od Pascalu nemusí být label jinak definován (fuj, to je prasárna)
Tak tedy zatím to opakování Céčka jde docela rychle ...
Kapitoly 9-13
[editovat]Pole
[editovat]- pole lze zcela či částečně naplnit již při definici - int b[5] = {1, 2};
- nebo int xor[2][2] = {{0, 1}, {1, 0}};
- je legální přiřazení pole pointru typu pi=policko;*pi=5;
- výpočty s pointery uvažují jejich velikost - a kroky tomu odpovídají - čili máme-li pointer na 2B typ, pak rozdíl v adres "4" neznamená 4 ale 8 bytů !
Stringy
[editovat]- char *s = "krása"; vyhradí konstantu v paměti, tudíž s[3]="v"; hodí segfault :-(
- na to, aby to prošlo, museli bychom string definovat "natvrdo" jako inicializované pole - char s[6] = "krása"; a pak s[3]="v";
- getchar() - čte znak std. vstupu
- ale bacha, až po enteru !!
- putchar(int) vypisuje jeden znak
- puts - viz výše
- char *gets(char *s); - čte string ukončený enterem
- nelze omezit délku (no bezva :-( )
- při chybě vrací null
- elegantní vstup stringu:
char s[256]; puts("Zadej text:"); fgets(s, 256, stdin);
- pokud by byl vstup delší než 256 znaků, zbyl v bufferu a až při dalším volání fgets by se načetl (což teda má taky svoje mouchy, ale dá se "pročistit"
- ekvivalentem printf je scanf - používá v podstatě stejnou syntax, jen se předávají pointery
- scanf("%i%lf", &i, &f); // načte 1 celé a 1 reálné číslo
- scanf vrací počet načtených hodnot
- atoi,atol,atof - převede char * s na int,long či double - ekvivalent paskalského val()
- při chybě vracejí nulu -> je k ničemu. Proč to tam tedy píšou ?!
- další ekvivalenty funkce val
- long strtol(const char *nptr, char **endptr, int base);
- double strtod(const char *nptr, char **endptr);
- obsluha:
char *chyba; l = strtol(s, &chyba, 10); if (chyba == s) { // 0 je OK, jinak ukazuje kam jsme došli puts("Sorry vole, error !"); return -1; }
- modifikace printf vracející stringy
- sprintf(výstupní_string,formátování,proměnné)
- snprintf(výstupní_string,délka_výstupního_stringu,formátování,proměnné)
- prý není v MS visual C++ (koho to ale zajmá, že ;-) ) a jmenuje se tam jinak
- parametry funkce main:
- int main(int argc, char *argv[])
Preprocesor
[editovat]- include bez <> vkládá z aktuálního adresáře (nebo z toho, kam ho nasměrujeme, že ...)
- když náš kód nemá chybu, můžeme jí vygenerovat sami: #error Syntax error somwhere in the code.
- #define #ifdef, #ifndef, #else a #endif + #undef (undefine)
- gcc mujkod.c -DDEFINOVANA_PROMENNA
- příklad:
#include <stdio.h> int main(void){ #ifdef POKUS puts("Toto je pokusný program\n"); #else puts("Toto je odladěný program\n"); #endif // some more code here ... #ifndef POKUS puts("This is COOL software, btw ...\n"); #endif return(0); }
- proměnná může mít samozřejmě i hodnotu - #define N 10
- v define může být cokoliv, takže si můžeme napsat vlastní programovací jazyk ;-)
- makro se může definovat na více řádků stejně jako se to dělá třeba v bashi
- makro může mít parametry
- #define moje_gets(s, size) fgets((s), (size), stdin) //přidá stdin, takže se s tím člověk nemusí zdržovat
- může "volat" víc funkcí, čárka (,) místo středníku "zapomene" hodnotu.
- uvnitře definice makra # obalí proměnnou uvozovkami, ## "přilepí" k předchozímu stringu
Funkce
[editovat]- funkci můžeme i deklarovat jako v pascalských unitách (pokud jí potřebujeme definovat pro použití jinde). Konec hlídání pořadí ... hurá
- pokud se před deklaraci funkce dá static tak je lokální jen pro daný soubor (ekvivalent nezahrnutí do interface unity). Čili implicitně je vše v interface ...
Kapitoly 15-18
[editovat]Speciální proměnné
[editovat]- použití pointeru jako funkce viz můj (nebo koneckonců i jejich) demokód
- void NapisStoTecek(void (* callback)(int)) ... a pak se dá callback normálně zavolat
- NapisStoTecek(nejakafunkce);
- void NapisStoTecek(void (* callback)(int)) ... a pak se dá callback normálně zavolat
- proměnná funkce definovaná jako static je de facto globální (resp. pro všechny instance dané funkce)
- proměnnou jiného souboru můžeme používat, pokud jí nadefinujeme
- ekvivalentem PHPčkového global je konstrukce typu "extern int pocet;"
- proměnnou definovanou jako register se překladač pokusí nacpat do registru
- proměnnou definovanou jako volatile překladač kontroluje i když by si jinak myslel, že se nezměnila (může jí měnit třeba asynchronně běžící vlákno, že)
Hlavičkové soubory
[editovat]- v .h souboru mají být:
- hlavičky všech funkcí
- extern (globální) proměnné
- makra preprocesoru
- uživatelské proměnné
- zabránění vícenásobnému vložení .hčka:
#ifndef _RUTINY_H #define _RUTINY_H extern int globalni_promenna; void rutiny(void); #endif
jednoduché, elegantní, jen by to člověka muselo napadnout ...
- v .c části kódu už globální proměnná nemusí být definována jako extern (fakt ???)
- jako parametry překladači musíme ale dát všechny použité .c soubory
makefile
[editovat]- gcc -c vytvoří jen .o (nemusíme pak znovu pouštět 1. fázi, něco jako .tpu)
- syntax makefile:
cíl: závislost1 .. závislost moc [tabelátor]akce
- implicitní akce pro make je ta první, jinak se musí dát jako parametr
- čili pro klasický Cčkový program
- zakladni ..všechny .o soubory -> gcc ..všechnyO... -o target
- knihovna.o knihovna.h knihovna.c ->gcc knihovna.c -c
- main.o main.c knihovna.h -> gcc main.c -c
- lze definovat proměnné "VAR=hodnota" a pak jí volat ${VAR}
- lze použít #komentář
- cíl .PHONY znamená, že žádný soubor akci (třeba clean) neodpovídá.
- když není splněná závislost a není defnovaný cíl, tak se zavolá ${CC} ${CFLAGS} ${CPPFLAGS} -c -o souborXY.o souborXY.c
- čili je docela záhodno definovat CC, CFLAS (ev. CPPFLAGS)
- ukázkový makefile - [5] a [6]
parametry překladu
[editovat]- O0-O3 je stupeň optimalizace
- -g debug info
Ladící nástroje
[editovat]Kapitoly 19-22
[editovat]- [7]
- switch funkguje stejně jao PHP (opět je to spíš obráceně)
- bitové operátory:
- AND &
- OR |
- XOR ^
- NOT ~
- a*2 odpovídá a <<= 1
- a div 4 odpovídá a>>=2
Dynamoická alokace paměti
[editovat]- void *malloc(size_t size); //vrací pointer na paměť nebo NULL při chybě
- void free(void *ptr); //uvolnění paměti
- void *memcpy(void *dest, const void *src, size_t n); //kopírování, nesmí se překrývat
- void *memmove(void *dest, const void *src, size_t n); //přesun, může se překrývat
- void *memset(void *s, int c, size_t n); // nasteví hodnotu c
- int memcmp(const void *s1, const void *s2, size_t n); //poropvnání
- s1[n]<s2[n] -> záporné číslo, s1[n]>s2[n] -> kladné číslo, OK-> nula
- nezapomenout používat sizeof() !
String.h
[editovat]- size_t strlen(const char *s);//klasický strlen ;-)
- poslední nula se do délky nezapočítává -> potřebujeme tedy strlen+1 bytů
- char *strcpy(char *dest, const char *src); // ekvivalent memcpy pro string
- vrací pointer na kopii
- char *strcat(char *dest, const char *src);
- připiš(kam,"co");
- char *strdup(const char *s); // strcpy s tím, že se automaticky naalokuje paměť. Hezké, hezké ...
- char *strstr(const char *haystack, const char *needle); //poloha jehly v kupce sena
- int strcmp(const char *s1, const char *s2); //funguje stejně jako memcmp
- strncpy a strncat mají jako poslední parametr délku kopírovaných dat
- musíme rúčo připojit nulu
Definice typů
[editovat]#ifdef 64bitPlatforma typedef int signed32; #else typedef long signed32; #endif
- jinak lze použít stejně jako starý, dobrý, pascalský type :-)
record
[editovat]Pascal:
var karlicek=record krestni,prijmeni:string[16]; end;
Céčko:
struct jmeno { char krestni[16]; char prijmeni[16]; } karlicek;
.. ale zase se dá jméno recyklovat o kousek dál: struct jmeno lojzicek;
- pokud nepoužijeme pointer, můžeme klasicky pascalisticky karlicek.prijmeni
- nebo přes typedef struct {} NAZEVTYPU;
- pokud pointer požijeme
JMENO *karlicek; karlicek = (JMENO *) malloc(sizeof(JMENO)); karlicek->krestni ... //ekvivalent (*karlicek).krestni
Union
[editovat]- sdílení paměti
- nějak jsem to nepochopil :-(
Kapitoly 23-25
[editovat]Klasická dynamická pole
[editovat]Pascal
type pseznam=^seznam type SEZNAM=record data:integer; dalsi:pseznam; end;
Cčko:
typedef struct seznam { int data; struct seznam *dalsi; } SEZNAM;
- stromy viz w:AVL_tree
Soubory
[editovat]FILE *f; f= fopen("/etc/passwd", "r"); fclose(f);
- a dále klasické fputs,fputc,fprintf
- fgetc, fgets, fscanf - fgets uloží \n na konec
- stdin,stdout,stderr
- chceme-li soubor otevřít binárně, použijeme "b" - teda třeba rb - čti binárně
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
- ekvivalenty seeK() a fpos() jsou
int fseek(FILE *stream, long offset, int whence); long ftell(FILE *stream);
- feof(f) a ferror(f) jsou ekvivalenty Pascalu (resp. to druhé vrací chyby po seeku)
int remove(const char *pathname); // erase(f);
Funkce s proměnným počtem parametrů v Cčku
[editovat]Musíme dát pozor na typy a taky kolik dat čteme !
#include <stdarg.h> // musíme naincludovat ! int suma(int i, ...) { // první proměnná musí být explicitně uvedena va_list p; // proměnná typu va_list = variable list int j; // if (!i) return 0; // že by to šlo zavolat úplně bez parametru ? To budu muset někdy vyzkoušet ... va_start(p, i); // nastaví p na první proměnnou, v našem případě i do { j = va_arg(p, int); // va_arg vrací hodnotu proměnné i += j; } while (j); // když nebude poslední hodnota nula, tak jsme v p* va_end(p); // va_end ukončí práci se zásobníkem. Může to být jen formalita, ale je to zvykem return i; } int main(void) { int i; i = suma(1, 2, 3, 4, 5, 0); printf("Suma je %i\n", i); return 0; }
Výčtový typ
[editovat]Jde to přes typedef enum:
typedef enum { PRAHA, BRNO, OSTRAVA } MESTA;
a pak pro proměnnou typu MESTA můžeme udělat
switch (mestaVar) { case PRAHA: puts("Čížek"); break; //... }
Kam dál
[editovat]Zrady
[editovat]- soustavy - svinská osmičková definovaná nulou na začátku !!:
012=10(dec)=0x0A 12(dec)=014=0x0C
Pojďte s námi do pohádky, projdeme se pamětí
[editovat]S následujícím kouskem kódu od čertíka Bertíka se i bez Štěpánky můžeme projít pamětí a i když nemusíme potkat staré kamarády, je možné, že to co najdeme nehodíme do smetí :-)
#include <stdio.h> int main (void) { char s[255]; puts("\n\nZadej nějaký text:"); fgets(s, 256, stdin); printf(s); return 0; }
V čem je problém ? V ničem, stačí na výzvu zadat třeba "%c%c%c%c%c" v rozsahu a délce, která nás zajímá (tady max 128x) a printf si údaje prostě "odněkud" vezme -chtělo by to delší pole, co ? ;-)