Petr Peringer
peringer AT fit.vutbr.cz
Vysoké učení technické v Brně
FIT, Božetěchova 2,
61266 BRNO
6. listopadu 2000
ÚvodTyto slajdy jsou určeny pro studenty kursu CPP na ÚIVT. Obsahují základní popis jazyků C a C++ vhodný pro studenty, kteří již zvládli některý z vyšších programovacích jazyků (Pascal,...). Obsah slajdů je velmi stručný, podrobnější informace jsou součástí výkladu.
Pro speciální případy jsou použity poznámky na okraji slajdů. Programy jsou sázeny následujícím způsobem:
---------|---------|---------|---------|---------| text programu a poznámky (max 50 znaků)Někdy budou použity
pro zvýraznění.
Zdroje informací
http://www.fit.vutbr.cz/study/courses/CPP/public/
Programovací jazyk C C
Historie
ALGOL 60 (1960) CPL (1963) Cambridge BCPL (1967) B (1970) Bell Laboratories C (1972) K&R ANSI C (1989) norma pro USA ISO C90 (1990) mezinárodní norma ISO C90 AMD1 (1994) doplňky a opravy ISO C99 (1999) nová norma jazyka CPůvodní definicí je kniha Kerninghan, Ritchie: The C Programming Language (Addison-Wesley 1978).
Normy: ISO/IEC 9899:1990, ISO/IEC 9899:1999
Existuje celá řada překladačů a vývojových prostředí pro jazyk C. Aktuální přehled najdete na WWW stránce.
Doporučené prostředí GNU C existuje pro všechny běžné platformy a je volně šiřitelné.
Charakteristika jazyka C
Nevýhody
lint):
printf
Doporučený styl psaní programů
if ( a > b ) { /* varianta A */
text odsazený o 2-8 znaků
}
if ( a > b ) /* varianta B */
{
text odsazený o 2-8 znaků
}
Zarovnávání textu programu je důležité pro jeho dobrou čitelnost. Překladače zarovnání nevyžadují ani nekontrolují.
Poznámka:
Existují programy pro automatické formátování zdrojových textů (například
GNU indent)
Poznámka:
Existují programy pro automatické generování dokumentace ze zdrojových textů
programů (například doxygen).
První program
/* soubor ahoj.c */
#include <stdio.h> /* vloží rozhraní */
int main(void) {
printf("Ahoj!\n");
return 0;
}
Způsob zpracování
bcc ahoj.c - překlad, sestavení (Borland C) ahoj - spuštění (v MSDOS) cc ahoj.c - Linux, UNIX, GNU C, DJGPP ./a.out
Struktura programu v jazyku C
main() reprezentující hlavní program
Základní datové typy
void prázdná množina hodnot char znak short krátké celé číslo int celé číslo (podle registru procesoru) long dlouhé celé číslo float reálné číslo double reálné číslo s dvojnásobnou přesností
Typové konverze
int
float
char
int
Řetězce
"text, který může obsahovat speciální znaky" \a alert \t tabelátor \n znak nový řádek \\ znak \ \" znak "
Poznámka: Pozor na jména souborů v MS-DOSu
Funkce printf
printf("formátovací řetězec" [, parametry]);
%
má speciální význam - označuje začátek formátu
Základní formáty
%f číslo v plovoucí čárce %d desítkové celé číslo %o oktalové celé číslo %x šestnáctkové celé číslo %c znak %s řetězec znaků %% znak %
Příklad: formát reálného čísla
float x, y;
printf(" %5.0f %5.1f \n", x, y );
| |
+--------------+ typ musí souhlasit
%5.1f nejméně 5 znaků, 1 číslice za d. tečkou
%5f nejméně 5 znaků
%.2f 2 místa za desetinnou tečkou
%f implicitní počet míst (.6)
Proměnné a základní operace
Příklad
#include <stdio.h> /* printf() */
int main(void) {
int i,j,k; /* lokální proměnné */
float x;
i = 1; /* přiřazení */
while(i<=10) {
x = 1.0/i; /* problém int/int */
printf("%f\n", x);
i++; /* i := i + 1 */
}
return 0; /* konec programu */
}
Cyklus while
0 je FALSE, všechny ostatní hodnoty znamenají TRUE
< > <= >= == != mají hodnotu 0 nebo 1
Příkaz for
for(<poč. nastavení>; <test>; <nastav další krok>)
for(;;) { .... } /* nekonečný cyklus */
Příklad
int main(void) {
int i;
for(i=1; i<=10; i++)
printf(" %f \n", 1.0/i);
return 0;
}
Symbolické konstanty
#define JMENO hodnota
#define ZACATEK 1
#define KONEC 10
int main(void) {
int i;
for( i=ZACATEK; i<=KONEC; i++)
printf(" %f \n", 1.0/i);
return 0;
}
Standardní vstup
Příklad: počítání znaků
int main(void) {
long nc = 0; /* zaručený rozsah do 2e9 */
while(getchar()!=EOF) /* čtení znaku */
nc++;
printf("%ld znaků \n", nc);
} /* ^------------------------ long */
Druhá verze
int main(void)
{
long nc;
for( nc = 0; getchar()!=EOF; nc++)
; /* prázdný příkaz - vše je v záhlaví */
printf("%ld \n", nc);
return 0;
}
Příklad: kopírování souboru
Standardní vstup
standardní výstup
#include <stdio.h> /* dovoz rozhraní */
int main(void) {
int c = getchar(); /* čtení jednoho znaku */
while(c!=EOF) { /* znak konce souboru */
putchar(c); /* zápis jednoho znaku */
c = getchar();
}
return 0;
}
Lepší varianta
#include <stdio.h> /* dovoz rozhraní */
int main(void) {
int c; /* přiřazení je součást výrazu */
while ( (c = getchar()) != EOF )
putchar(c); /* != má vyšší prioritu než = */
return 0;
}
Příklad: počítání slov
#define YES 1
#define NO 0
int main(void)
{
int c;
long int nl=0; /* počet řádků */
long int nw=0; /* počet slov */
long int nc=0; /* počet znaků */
int inword = NO; /* stav automatu */
while( (c = getchar()) != EOF ) {
nc++;
if( c == '\n' ) /* nový řádek */
nl++;
if( c == ' ' || c == '\n' || c == '\t' )
inword = NO; /* oddělovač */
else if( inword == NO ) {
inword = YES; /* začíná slovo */
nw++;
}
}
printf("%ld %ld %ld\n", nl, nw, nc);
return 0;
}
Pole
Příklad: histogram počtu číslic
#define VARIANTA 1
int main(void)
{
int c; /* načtený znak */
int i;
int ndigit[10]; /* index vždy od nuly! */
for(i=0; i<10; i++)
ndigit[i] = 0; /* nulování pole */
while( (c=getchar()) != EOF )
#if VARIANTA
if(c>='0' && c<='9') /* je číslice? */
#else
if(isdigit(c)) /* je číslice? */
#endif
ndigit[c-'0']++; /* znak --> číslo */
printf("digit: number\n");
for(i=0; i<10; i++)
printf("'%c': %d\n", i+'0', ndigit[i]);
printf("\n");
return 0;
}
Příklad: definice funkce
Podle ISO C
int power(int x, int n) { /* umocňování */
int p;
for(p=1; n>0; n-- )
p = p * x;
return p; /* návrat s hodnotou p */
}
Podle K&R (zastaralé, nepoužívat)
power(x, n) /* K&R: implicitní typ int */
int x, n;
{ int i, p;
for(i=p=1; i<=n; i++ )
p = p * x;
return p;
}
Volání funkce
printf("%d %d %d\n", i, power(2,i), power(-3,i));
Poznámka: Předávání parametrů hodnotou (kromě pole)
Poznámka: Prototyp funkce
Příklad: nalezení nejdelšího řádku v textu
#define MAXLINE 1000 /* implementační limit */
int getline(char s[], int lim) {
int c, i;
for(i=0; i<lim-2 && (c=getchar())!=EOF
&& c!='\n'; i++)
s[i] = c;
if(c=='\n') s[i++] = c;
s[i] = '\0'; /* konvence: končí znakem '\0' */
return i;
}
void copy(char s1[], char s2[]) { /* s1 do s2 */
int i = 0;
while( (s2[i]=s1[i]) != '\0' ) i++;
}
int main(void) {
char line[MAXLINE]; /* načtený řádek */
char save[MAXLINE]; /* nejdelší řádek */
int len, max = 0;
while( (len=getline(line,MAXLINE)) > 0 )
if( len > max ) { /* je delší? */
max = len;
copy(line, save); /* pole předá odkazem */
}
if( max > 0 ) printf("%s", save);
return 0;
}
Jazyk C - systematická definice
Základní množina znaků
a-zA-Z
0-9
! & * . < [ ^ |
" ' + / = \ _ }
# ( , : > ] { ~
% ) - ; ?
Poznámky
/* text C poznámky */ // text C99 poznámky do konce řádkuISO C nedovoluje vnořené poznámky. Poznámka je přepsána na jednu mezeru až po rozvoji maker
Poznámka: Některé zastaralé (ne ISO C) implementace nevkládají mezeru
Operátory a separátory
! &= + -- /= << == >>= ^ |=
% ( ++ -= : <<= > ? ^= ||
%= ) += -> ; <= >= [ { }
& * , . < = >> ] | ~
&& *= - /
Identifikátory
#define YES 1
int i, j, k;
int muj_dlouhy_identifikator;
_ (__TIME__)
Klíčová slova
auto do goto short typedef break double if signed union case else int sizeof unsigned char enum long static void const extern register struct volatile continue float return switch while default for _Bool _Complex _Imaginary inline restrict
Jednoduché typy a velikost údajů
void |
nezabírá paměť |
char |
znak (vždy 1 bajt, celočíselný typ) |
short int |
krátké celé číslo |
int |
standardní celé číslo |
long int |
dlouhé celé číslo |
float |
reálné číslo (malá přesnost) |
double |
reálné číslo dvojnásobná přesnost |
long double |
reálné číslo extra přesnost (10B) |
long long int |
C99 velmi dlouhé celé číslo (64b) |
float complex |
C99 komplexní číslo |
bool |
C99 booleovské hodnoty true/false |
|char| <= |short| <= |int| <= |long| <= |long long|
Příklady pro různé architektury
| Alpha/64bit | PC/32bit | PC/16bit | |
char |
1 | 1 | 1 |
short |
2 | 2 | 2 |
int |
4 nebo 8 | 4 | 2 |
long |
8 | 4 | 4 |
long long |
8 | 8 | (8) |
float |
4 | 4 | 4 |
double |
8 | 8 | 8 |
Modifikátory signed a unsigned
unsigned char signed char unsigned unsigned int unsigned long
Specifikace const a volatile
const TObjekt takto označený nelze modifikovat přiřazením.
volatile TTato specifikace zabrání překladači provádět optimalizace při přístupu k objektu. Významné zvláště při komunikaci mezi procesy.
Poznámka: const-correctness
Literály
Celočíselné literály
číslice1-9 číslice0-9
0 číslice0-7
0x číslice0-9,a-f,A-F
l nebo L určuje typ long,
ll nebo LL určuje typ long long,
u nebo U určuje specifikaci unsigned.
Příklady
0L 2999888777UL 1234567890123456789LL 077 0x3F 0xFFFFU
Literály v plovoucí řádové čárce
Jsou typu double (float jen s příponou F nebo f
a long double jen s L nebo l).
Příklady:
0. .0 1.0e-3L 3.14159F 3e1 -2e+9
Znakové literály
'<znak>' znakový literál, je typu int
Speciální znaky (escape-sequence)
\' |
apostrof |
\" |
uvozovky |
\a |
alert - pípnutí |
\b |
backspace - posun zpět (BS) |
\f |
nová stránka (FF) |
\n |
nový řádek (LF, CRLF) |
\r |
návrat vozíku (CR) |
\t |
horizontální tabelátor (HT) |
\v |
vertikální tabelátor (VT) |
\\ |
znak \ |
\x<číslo> |
znak s ordinálním číslem hexadecimálně |
\<číslo> |
oktalově |
Příklady
'a' ' ' '\\' '\n' '\x80' '\0'
Víceznakové a 'široké' literály
'xxx' je typu int
L'xxx' je typu wchar_t
Poznámka: UNICODE ('\u03AF'), mezinárodní abecedy, UTF-8,
'\U12345678'
Řetězcové literály
"<znaky řetězce (i speciální a multi-byte)>" L"<znaky řetězce (i speciální a multi-byte)>"
'\0'
char[])
L"xxx" je typu wchar_t[]
Příklady
"" // prázdný řetězec (char[1])
"text" // pole char[5]
"\n\n" // char[3] '\n' '\n' '\0'
"text se znakem \" v řetězci"
" text \
pokračování textu na následujícím řádku"
"řetězec1" <mezery, nové řádky>
"řetězec2" // "řetězec1řetězec2"
Příklady použití literálů
void f(char inchar) {
if (inchar=='\0') // ...
if (inchar=='\004') // ...
if (inchar=='\006') // ...
}
Makra
#define NUL '\0'
#define EOT '\004'
#define ACK '\006'
void f(char inchar) {
if (inchar==NUL) // ...
if (inchar==EOT) // ...
if (inchar==ACK) // ...
}
Konstanty
const char NUL = '\0';
const char EOT = '\004';
const char ACK = '\006';
void f(char inchar) { /* ... */ }
Deklarace
datový_typ deklarátor [=ini_hodnota], ... ;
Příklady
char line[100]; /* definice pole 100 znaků */ void f(void); /* deklarace funkce - prototyp */ const char backslash = '\\'; int i = 0; double eps = 1.0e-5;
Okamžik inicializace
0
Příklad
int x; /* inicializováno při překladu na 0 */
void f(void) {
int i; /* neinicializováno */
int j = 1; /* při každém volání funkce */
}
Rozsah deklarace
#define do konce textu modulu
nebo do příkazu #undef
Viditelnost
int x=10;
int main() {
double x=1.1; /* ve funkci platí double x */
printf("%f",x);
return 0;
}
void printx(void) { /* zde platí int x */
printf("%d",x);
}
typedef
Vytváření nových jmen (synonym) datových typů.
Příklad
typedef int LENGTH; LENGTH len, maxlen;Nejde o vytvoření nového typu, pouze jména. Je podobné jako
#define LENGTH intale má více možností. Například
typedef int (*PFI)(); /* ukazatel na funkci */ PFI numcmp, swap;nebo
typedef char * STRING; STRING s, lineptr[10];
Poznámka: pozor na ukazatele:
char *s, *lineptr[10];
Ukazatel, pole
Ukazatele a adresy
int *px = NULL;
int x,y;
px = &x;
& je operátor získání adresy objektu
y = *px;
* je unární operátor zpřístupnění objektu
px = &x;
y = *px; /* ekvivalentní y = x */
*px = 0;
*px += 1;
(*px)++;
Příklad: výměna hodnot
void swap(int *px, int *py) {
int temp = *px;
*px = *py;
*py = temp;
}
Volání: int x,y; /*...*/ swap(&x,&y);
^^^^^ pozor!
Ukazatele a pole
int a[10], *pa; pa = &a[0]; /* ukazatel na první prvek pole */ x = *pa; /* je ekvivalentní x = a[0] */
Ukazuje-li pa na i-tý prvek pole, potom (pa+1) ukazuje na prvek
i+1. V našem příkladu *(pa+1) je stejné jako a[1]
Obecně platí:
a[i] == *(pa+i)je možné namísto
pa = &a[0] psát pa = a
a[i] == *(a+i)
Poznámka: Není možné zapsat a++ a podobně.
Příklad: délka řetězce
int strlen(const char *s) {
int n;
for(n=0; *s!='\0'; s++) n++;
return n;
}
Poznámky:
f(&a[2]) resp. f(a+2)
int p[] = { 1, 2, 3, };
Adresová aritmetika
S ukazateli lze provádět aditivní operace. Jednotkou výpočtu je velikost objektu na který ukazatel ukazuje.
Příklad:
int *ip = pole; ip++; /* posun o sizeof(int) bajtů */
Poznámka:
operace mají smysl jen v rámci jednoho pole:
p++ (vpřed), p-- (vzad)
p+=5 (vpřed o 5 prvků)
p2 - p1 (počet prvků mezi ukazateli)
Dva ukazatele nelze sčítat, násobit, dělit, posouvat ani kombinovat s float a double.
Příklad: délka řetězce
int strlen(const char *s) {
const char *p = s;
while( *p ) p++;
return p-s; /* rozdíl ukazatelů */
}
Přehled povolených operací:
ptr+int, ptr-int, ptr++, ptr-- |
posun |
ptr1 - ptr2 |
rozdíl |
ptr[i] |
indexování |
*ptr |
zpřístupnění |
ptr1 = ptr2 |
přiřazení |
Upozornění:
Operace s neinicializovaným ukazatelem jsou nebezpečné a nelze je kontrolovat překladačem!
Typická chyba:
char *s; *s = 'A'; /* zápis znaku na chybné místo */
Řetězce
Řetězec je pole znaků, lze jej tedy přiřadit ukazateli:
char *message = ""; /* inicializace */ message = "Now is the time."; /* nejde o kopii! */
Operace s řetězci:
<string.h>
char *strcpy(char *s1, const char *s2) {
char *s = s1;
while( *s++ = *s2++ );
return s1;
}
Upozornění:
strcpy(kam,odkud)
s1 a s2
char *strcat(char *s1, const char*s2);
int strcmp(const char*s1, const char *s2);
strncpy, memcpy, strncat, strncmp,
strchr, strstr, strlen)
Vícerozměrná pole
static int day_tab[2][13] = {
{ 0,31,28,31,30,31,30,31,31,30,31,30,31 },
{ 0,31,29,31,30,31,30,31,31,30,31,30,31 },
}; /* prvky jsou uloženy po řádcích */
int day_of_year(int year, int month, int day) {
int i, leap;
leap = year % 4 == 0 && year % 100 != 0
|| year % 400 == 0;
for( i=1; i<month; i++)
day += day_tab[leap][i];
return day;
}
a[x][y] jako a[x,y] (není hlášena chyba!)
int f(int day_tab[][13]); )
T[]) je ekvivalentní parametru typu
ukazatel (T*), který se používá častěji
Inicializace pole ukazatelů
Příklad:
funkce vracející jméno měsíce
char *month_name(int n)
{
/* pole ukazatelů na řetězce */
static char *name[] = {
"illegal month",
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
return (n<1 || n>12)? name[0] : name[n];
}
Poznámka:
char a[20][10]; /* pole znakových polí */ char *b[10]; /* pole ukazatelů na řetězce */
Argumenty povelového řádku
Programu TEST lze zadat argumenty při spuštění:
TEST argument1 argument2 .... poslední_argument argv[0] argv[1] argv[2] argv[argc-1]
Příklad: výpis argumentů
int main(int argc, char *argv[]) {
int i;
for(i=1; i<argc; i++)
printf("%s ", argv[i]);
printf("\n");
return 0;
}
druhá varianta - výpis argumentů
int main(int argc, char *argv[]) {
while(--argc>0)
printf("%s ", *++argv);
printf("\n");
return 0;
}
Poznámka: argv[0] je jméno programu (nebo ""),
argv[argc] má hodnotu NULL
Struktury
Deklarace:
struct Date {
int day;
int month;
int year;
};
struct Date d = {21,10,1993};
struct Date dat1, dat2;
Odkaz na člen struktury:
jméno_proměnné . jméno_členu
Příklad: x = d.year
Je možné vnoření struktur
Omezení kladená na struktury:
. |
zpřístupnění položky |
= |
přiřazení nebo inicializace |
& |
získání ukazatele na proměnnou |
Struktury lze předávat jako parametry funkcím a vracet jako funkční hodnotu
Neúplná deklarace struktury:
struct ABC;Lze použít pro definici ukazatelů:
struct ABC *p;Nelze použít pro definici proměnné:
struct ABC x; /* chyba */
Ukazatel na strukturu:
struct Date *uk = &d;
Operátor -> pro přístup k položce přes ukazatel
Například x = uk->year; je ekvivalentní příkazu
x = (*uk).year;
Operátory . a -> mají vysokou prioritu (viz tabulka operátorů)
Příklad: ++uk->day je ekvivalentní ++(uk->day)
Příklad:
struct Date *p = ptr1; int day = p->day;
Velikost struktury v bajtech
sizeof(struct XYZ) sizeof dat1
Poznámka: Obecně neplatí, že velikost struktury je součtem
velikostí jednotlivých položek. Důvodem je zarovnání
položek a velikosti struktury (alignment).
Poznámka: Struktura může obsahovat ukazatel na sebe sama:
struct treenode {
char *word;
int count;
struct treenode *left; /* levý následník */
struct treenode *right; /* pravý následník */
};
Příklad: počítání všech slov v textu
#define MAXWORD 20
int main() { /* neúplná implementace */
struct treenode *root = NULL;
char word[MAXWORD];
int t;
while((t=getword(word,MAXWORD)) != EOF)
if(t==LETTER)
root = tree(root,word);
treeprint(root);
}
/* vložení do stromu */
struct treenode *tree(struct treenode *p, char *w) {
int cond;
if( p == NULL ) { /* nové slovo */
p = talloc();
p->word = strsave(w);
p->count = 1;
p->left = p->right = NULL;
} else if( (cond=strcmp(w,p->word)) == 0 )
p->count++; /* slovo se opakuje */
else if( cond<0 ) /* < do levého */
p->left = tree(p->left,w);
else /* > do pravého */
p->right = tree(p->right,w);
return p;
}
Bitová pole (bitfields)
Příklad:
struct {
unsigned is_kw : 1; /* na jednom bitu */
unsigned is_ext : 1;
unsigned : 2; /* výplň */
unsigned is_stat : 1;
unsigned : 0; /* zarovnání */
unsigned num_bit : 6; /* na 6 bitech */
} flags;
Nevýhody:
& (získání adresy)
Unie (1.p union)
{ .name = value }
Příklad:
union u_tag { if(typ==INT)
int ival; printf("%d\n", u.ival);
float fval; else if(typ==FLOAT)
char *pval; printf("%f\n", u.fval);
} u; else if(typ==STRING)
printf("%s\n", u.pval);
else
error("chyba");
union u_tag x = { 1 };
//C99: union u_tag x = { .fval = 0.1 };
Ukazatele na funkce
Ukazatel na funkci je adresa kódu funkce, která může být použita pouze k vyvolání funkce (ukazatelová aritmetika není použitelná). Ukazatel
T (*fp)(void);
reprezentuje "ukazatel na funkci bez parametrů vracející typ T".
Máme-li funkci:
T funkce(void) {
/* kód funkce */
}
potom přiřazení do ukazatele má v ANSI C tvar:
fp = &funkce;
Použití ukazatele k volání funkce:
(*fp)(); /* volání funkce */
lze zkrátit:
fp();
Příklad: řazení pole ukazatelů
void sort( void *v[], unsigned n,
int (*cmp)(const void *p1, const void *p2))
{
int gap, i, j;
/* řadicí algoritmus -- netestováno, doladit! */
for( gap=n/2; gap>0; gap/=2 )
for( i=gap; i<n; i++ )
for( j=i-gap; j>=0; j-=gap ) {
void *tmp;
if( cmp(v[j],v[j+gap]) <= 0 ) /* porovnání */
break;
tmp = v[j]; /* výměna */
v[j] = v[j+gap];
v[j+gap] = tmp;
}
}
Použití:
char *lineptr[100]; /* pole řetězců */ int nlines; sort(lineptr,nlines,&strcmp);kde
int strcmp(const char *s1, const char *s2);
je standardní funkce z knihovny (<string.h>).
Výčtový typ Je obecnější než v Pascalu - lze explicitně specifikovat hodnoty položek (konstant)
Příklad:
enum dny { PO, UT, ST, CT, PA, SO, NE };
enum bity { Prvni=1, LSB=Prvni, Druhy,
Treti=4, Ctvrty=8 };
Konstanty ve výčtu jsou typu int a mají hodnoty rostoucí postupně od nuly
Proměnné výčtového typu jsou kompatibilní s typem int
Příklad:
enum dny d=PO;
int i = d; /* hodnota 0 */
d++; /* přičte 1 */
printf("%d", d); /* vytiskne 1 */
d = 2; /* v C přípustné, ale nepoužívat */
enum { RAZ, DVA, TRI } e; /* proměnná */
typedef enum Boolean { FALSE, TRUE } Boolean;
Poznámka: struct A {}; enum A {}; union A {};
CHYBA! -- nelze pojmenovat stejně
Aritmetické operátory
+ - |
(binární i unární) |
* / |
|
% |
modulo |
Pro komutativní operátory není definováno pořadí vyhodnocení operandů
Relační operátory
== != < > <= >=
Logické operátory
|| |
logické nebo (OR) |
&& |
logické a (AND) |
! |
logické ne (NOT) |
Zkrácené vyhodnocování:
A || B |
je-li A!=0, pak se B nevyhodnocuje |
A && B |
je-li A==0, pak se B nevyhodnocuje |
Příklad:
(year%4 == 0 && year%100 != 0 || year%400 == 0)
Implicitní konverze typů
char, short int, bitová pole
int nebo unsigned
Poznámka: argumenty funkcí,
K&R float
double
long double, je druhý převeden na
long double a výsledek je také long double
double ...
float ...
Jinak se provedou celočíselná rozšíření pro oba operandy a:
signed nebo oba unsigned
převede se vše na typ s větším rozsahem.
unsigned typ má větší nebo stejný rozsah, je
druhý operand převeden na unsigned.
signed operand může reprezentovat všechny hodnoty
unsigned operandu, je druhý operand převeden na signed.
unsigned typ.
Problém signed/unsigned char
- vzniká při převodu znaků na int:
signed char int |
rozsah .. |
unsigned char int |
rozsah |
Příklad:
int getchar(void); /* vrací EOF (tj. -1) nebo znak 0 .. 255 */ int c; /* musí být typu int !!! */ c = getchar(); if(c==EOF) .... /* pro char c; bude chybné */
Explicitní konverze
(typ) výraz
Příklad:
int i1,i2; ... (double)i1/i2
Operátory ++ a -
++ |
inkrementace (zvýšení o 1) |
-- |
dekrementace |
| prefixový zápis | ++i |
před použitím hodnoty |
| postfixový zápis | i++ |
po použití hodnoty |
Příklad:
int x, n=5; x = n++; /* x = 5, n = 6 */ x = ++n; /* x = 7, n = 7 */
Poznámka: Tyto unární operátory lze použít pouze pro proměnné!
(přesněji: L-hodnoty)
nelze (55+5)++
Příklad: odstranění znaku z řetězce
int squeeze( char s[], int c ) {
int i,j;
for( i = j = 0; s[i] != '\0'; i++ )
if( s[i] != c )
s[j++] = s[i];
s[j] = '\0';
}
Logické operátory po bitech
& |
bitové AND |
| |
bitové OR |
^ |
bitové XOR |
~ |
bitová negace (NOT) |
Posuvy
<< |
posun vlevo |
>> |
posun vpravo |
Poznámka: unsigned - logický posun,
signed - nedefinováno/aritmetický posun
Příklad: čtení bitového pole z proměnné
int getbits(unsigned x, unsigned p, unsigned n) {
return (x>>(p+1-n)) & ~( ~0 << n) ;
} | | | |
\----------/ \---------/
posun na pravý maska 000000111111
okraj slova n jedniček
Jak se vyhnout závislosti na počtu bitů slova:
~0 = samé jedničky ve slově libovolné délky (nezávislé)
x & ~077 |
nezávislé na délce slova |
x & 017700 |
závislé - uvažuje 16 bitů |
Přiřazovací operátory a výrazy
+= -= *= /= %= <<= >>= &= ^= |=
Zkrácení zápisu a vyhodnocení, opakuje-li se levá a pravá strana přiřazení.
c1 op= c2 je ekvivalentní c1 = c1 op (c2)
Příklad: (pozor na závorky)
x *= y + 1 x = x * (y + 1)
Příklad: součet jednotkových bitů
int bitcount(unsigned n) {
int b;
for( b=0; n != 0; n >>= 1 )
if(n & 1) /* hodnota nejnižšího bitu */
b++;
return b;
}
Není nutné optimalizovat společné podvýrazy:
yyval[yypv[p3+p4]+yypv[p1+p2]] += 2;Přiřazení má hodnotu a typ levé strany.
Poznámka: vedlejší efekty
i = i++; /* nedefinováno! */
Podmíněné výrazy
e1 ? e2 : e3
Je-li e1!=0, pak výraz má hodnotu e2, jinak e3
Vyhodnocuje se pouze jeden z výrazů e2, e3
Příklad: funkce max
int max(int a, int b) {
if(a>b)
return a;
else
return b;
}
2. verze:
int max(int a, int b) {
return (a>b ? a : b);
}
Příklad: formátování tisku
for(i=0; i < N; i++)
printf("%6d%c", a[i], (i%10==9 || i==N-1)?'\n':' ');
Priorita a asociativita operátorů
| operátory | asociativita |
( ) [ ] -> . |
|
! ~ + - ++ -- & * (typecast) sizeof |
|
* / % |
|
+ - |
|
<< >> |
|
< <= > >= |
|
== != |
|
& |
|
^ |
|
| |
|
&& |
|
|| |
|
?: |
|
= *= /= %= += -= &= ^= |= <<= >>= |
|
, |
Poznámka: je možné přeskupení komutativních operátorů
* + & ^ |
Výraz-příkaz
Výraz se stane příkazem, zapíšeme-li za něj středník.
Hodnota takového výrazu se zanedbá.
Příklad:
x = 0;
i++;
printf("xxx");
Složený příkaz - blok
{ deklarace příkaz1 příkaz2 .... příkazN }
Poznámka: za } není středník!
C99: deklarace proměnných mohou být mezi příkazy
Podmíněný příkaz
if( výraz ) příkaz1 [ else příkaz2 ]
Poznámka: nejednoznačnost se řeší jako v Pascalu
Příklad: binární vyhledávání
/* hledáme x v poli v[] o rozměru n */
int binary( int x, int v[], int n) {
int low = 0;
int mid;
int high = n - 1;
while( low<=high ) {
mid = (low + high) / 2;
if( x<v[mid] )
high = mid - 1;
else if ( x>v[mid] )
low = mid + 1;
else
return mid; /* nalezeno mid */
}
return -1; /* nenalezeno */
}
Příkaz switch
switch( výraz ) {
case konstantní_výraz : příkaz1
....
[ default : příkazNPLUS1; ]
}
Poznámka: funguje jinak než v Pascalu!
Příklad: počítání číslic, prázdných znaků a ostatních
int main() {
int c, i, nwhite, nother, ndigit[10];
nwhite = nother = 0;
for(i=1; i<10; i++)
ndigit[i] = 0;
while( (c=getchar()) != EOF )
switch(c) {
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8':
case '9': ndigit[c-'0']++;
break; /* nepokračovat */
case ' ':
case '\n':
case '\t': nwhite++;
break; /* ukončí switch */
default : nother++;
break;
}
printf("digits = ");
for(i=0; i<10; i++)
printf(" %d ", ndigit[i]);
printf("\n ws=%d, other=%d\n", nwhite, nother);
}
Cykly while a for
while ( <výraz> )
<příkaz>
for( <výraz1>; <výraz2>; <výraz3> )
<příkaz>
for(;;) příkaz /* nekonečný cyklus */
Výrazy 1 2 3 jsou libovolné a nekladou se na ně žádná omezení.
Příklad: řazení vzestupně (shell-sort)
/* funkce řadí pole v[] o rozměru n vzestupně */
void shell(int v[], int n) {
int gap, i, j, temp;
for( gap=n/2; gap>0; gap/=2)
for( i=gap; i<n; i++)
for( j=i-gap; j>=0 && v[j]>v[j+gap]; j-=gap)
{
temp = v[j];
v[j] = v[j+gap];
v[j+gap] = temp;
}
}
Operátor čárka - postupné vyhodnocení výrazů
Příklad: obrácení řetězce na místě
void reverse(char s[]) {
int c, i, j;
for( i=0, j=strlen(s)-1; i<j; i++, j--)
{
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
Poznámka: čárky oddělující argumenty funkcí nejsou operátory!
Cyklus do-while
do příkaz while( výraz ); /* cykl dokud podmínka platí */
Poznámka: provede se alespoň jednou
Příklad: konverze čísla na znaky v opačném pořadí
do {
s[i++] = n % 10 + '0';
}while( (n/=10) > 0 );
Příkaz break
Ukončuje nejblíže nadřazený příkaz
switch, while, for, nebo do-while
Příklad: vynechání koncových mezer a tabulátorů
int main() {
int n;
char line[MAXLINE];
while( (n=getline(line,MAXLINE)) > 0 ) {
while( --n >= 0 )
if( !isspace(line[n]) )
break;
line[n+1] = '\0';
printf("%s\n", line);
}
return 0;
}
Příkaz continue
Přeskočí zbytek těla cyklu a pokračuje podmínkou cyklu
for( i=0; i<N; i++ ) {
if( a[i] < 0 )
continue;
.... /* pouze pro >= 0 */
}
Příkaz goto a návěští
goto identifikátor; ... identifikátor : příkaz;
Poznámka: neskákat do strukturovaných příkazů
Příklad:
T funkce()
{
for(...)
for(...)
{
if(chyba) goto error;
}
error: printf("chyba!\n");
/* ošetření chyby */
}
Poznámka: používat opatrně, minimalizovat použití
Funkce
typ jméno ( deklarace_argumentů )
{
deklarace lokálních proměnných
příkazy // C99: i deklarace
}
Funkce může vracet struktury, unie ale ne pole
Příkaz return
return; /* u funkce void f() */ return e; /* e=výraz kompatibilní s typem fce */
Rekurze
Příklad: výpis celého čísla desítkově
void printd(int n) {
int i;
if(n<0) {
putchar('-');
n = -n;
}
if((i=n/10) != 0)
printd(i);
putchar(n%10 + '0');
}
Argumenty funkcí
inline, __FUNCTION__
Funkce s proměnným počtem argumentů
typ jmeno(typ1 parametr1, ... );
va_list, va_start(), va_arg(), va_end()
Příklad:
int printf(const char *fmt, ... ); int sprintf(char *s, const char *fmt, ... );
Externí proměnné
Program je množina externích objektů (globálních proměnných a funkcí)
Globální proměnné
Deklarace funkce je vždy externí:
extern int plus(int,int); int plus(int,int);
U proměnných je rozdíl:
extern int a; int a;
Poznámka: extern je u funkcí zbytečné
Použití: deklarace před použitím, modularita
Poznámka: použití nedeklarované funkce - překladač
předpokládá extern int f();
Rozsah platnosti
Úsek programu, ve kterém je jméno definované
| automatické proměnné | blok |
| návěští | funkce |
| globální proměnné | od deklarace do konce souboru |
| jména parametrů | prototyp, funkce |
Statické proměnné
existují trvale bez ohledu na aktivaci funkcí
static proměnná nebo funkce je platná pouze v rámci souboru
Příklad:
int count; /* statická externí */
static char buffer[100]; /* statická */
static int bufp = 0;
static int plus(int a, int b)
{
return a+b;
}
Proměnné register
Příklad:
int swap(int *x, int *y) {
register int tmp = *x;
*x = *y;
*y = tmp;
}
Bloková struktura programu
Inicializace proměnných
static int nula; static int dva = 2;
void binary(int x, int v[], int n) {
int high = n-1; /* nemusí být konst. výraz */
static int z = 5; /* zde musí být konstanta */
//....
}
Inicializace polí
int ndigit[10] = { 1, [5]=0, 1, }; // C99
int n2digit[] = { 1, 1, 1, 0, 0 };
char pattern[] = "the"; /* použití řetězce */
char pattern2[] = { 't', 'h', 'e', '\0' };
Inicializace struktur
struct complex {
double Re;
double Im;
} c1 = { .Re=1.2, .Im=3.8 }; // C99
Překladové jednotky (moduly)
Modul je samostatná překladová jednotka - soubor *.c
Příklad: - pozor, nevhodný styl
Překlad a sestavení více modulů:
cc modul1.c modul2.c(případně projekt v integrovaném prostředí)
Poznámka: Problém konzistence
Příklad: - správné řešení
Moduly zveřejňují rozhraní v souboru *.h (header file).
V rozhraní jsou pouze externí deklarace proměnných a funkcí a definice typů a maker.
Rozhraní se vkládá (#include) do modulů, aby byla
při překladu zajištěna konzistence deklarací.
Preprocesor jazyka C
Direktivy začínají znakem # v prvním sloupci
Vložení souboru
#include <stdio.h> #include "modul2.h"
Definice makra
#define JMENO text \
text na dalším řádku
/* každý výskyt JMENO se rozvine na text */
#undef JMENO
Příklad:
#define EOF -1
#define NULL 0
if(p != NULL) { ... }
while((c=getchar()) != EOF) { ... }
Makra s parametry
#define MAX(a,b) ((a)>(b)?(a):(b))
Poznámka: pozor na závorky
x = MAX(p>q,r>s);
x = ((p>q)>(r>s)?(p>q):(r>s));
Podmíněný překlad
Vynechání úseků programu
#ifdef JMENO #ifndef JMENO ..... ..... #endif /* JMENO */ #endif /* !JMENO */ #if konstantní_výraz ..... #elif konstantní_výraz ..... #else ..... #endif
Konstantní výrazy:
defined(linux) && defined(i386)
__BORLANDC__ >= 0x0300
Parametry překladu
Nastavení parametrů překladače:
#pragma options
Příklad: Borland C / DOS
#pragma option -K #pragma warn amb
Spojování parametrů a vytváření řetězců
# |
vytvoření řetězce |
## |
spojení identifikátorů |
Příklad:
#define SPOJ(a,b) a##b #define PRINT(x) printf(#x " = %d\n", x)
Použití:
SPOJ(file,id) fileid
PRINT(pocet) printf("pocet" " = %d\n", pocet)
Standardní ISO-C knihovny ISO-C-lib
| soubor | co obsahuje |
assert.h |
Makro assert pro ladění. |
ctype.h |
Makra pro klasifikaci znaků. |
errno.h |
Definuje pojmenování chybových kódů. |
float.h |
Parametry pro floating-point funkce. |
limits.h |
Rozsahy celých čísel. |
locale.h |
Funkce pro národní/jazykovou podporu. |
math.h |
Matematické funkce. |
setjmp.h |
Typy pro funkce longjmp a setjmp. |
signal.h |
Konstanty a deklarace pro funkce signal a raise |
stdarg.h |
Makra pro práci s proměnným počtem argumentů. |
stddef.h |
Některá makra a datové typy. |
stdio.h |
Typy,makra a funkce pro standardní vstup/výstup |
stdlib.h |
Obecně použitelné funkce (konverze, řazení) |
string.h |
Funkce pro práci s řetězci a pamětí. |
time.h |
Typy a funkce pro práci s časem. |
Dovezení rozhraní:
#include <soubor.h>
assert.h - makro pro ladění
#define assert(podminka) if(!podminka) .....
| text | "Assertion failed: " |
| text podmínky | x < 0 |
| jméno souboru | __FILE__ |
| číslo řádku | __LINE__ |
| C99: jméno funkce | __func__ |
NDEBUG
#define NDEBUG #include <assert.h>
Příklad:
double logarithm(const double x) {
assert( x > 0.0 );
/* zde platí podmínka */
....
}
ctype.h - makra pro klasifikaci znaků
int isalnum(int c) |
písmeno nebo číslice [A-Za-z0-9] |
int isalpha(int c) |
písmeno [A-Za-z] |
int iscntrl(int c) |
řídicí znak |
int isdigit(int c) |
číslice [0-9] |
int isgraph(int c) |
tisknutelný znak bez mezery |
int islower(int c) |
malé písmeno [a-z] |
int isprint(int c) |
tisknutelný znak včetně mezery |
int ispunct(int c) |
tisknutelný znak bez alnum a mezery |
int isspace(int c) |
oddělovač |
int isupper(int c) |
velké písmeno [A-Z] |
int isxdigit(int c) |
šestnáctková číslice [0-9A-Fa-f] |
int toupper(int c) |
převod znaku na velké písmeno |
int tolower(int c) |
převod znaku na malé písmeno |
Poznámka: rozsah parametru:
všechny hodnoty unsigned char a hodnota EOF
Poznámka: makra vrací FALSE == 0 a TRUE != 0
errno.h - pojmenování konstant-chybových kódů
int errno je globální (pseudo)proměnná nastavovaná std. funkcemi, na začátku
programu je nulová
Norma definuje pouze základní hodnoty:
EDOM |
doménová chyba |
EILSEQ |
C99: chyba konverze na wchar_t |
ERANGE |
přetečení nebo podtečení |
float.h - charakteristiky floating-point typů
limits.h - rozsahy celočíselných typů
CHAR_BIT
SCHAR_MIN, SCHAR_MAX, UCHAR_MAX, CHAR_MIN, CHAR_MAX
SHRT_MIN, SHRT_MAX, USHRT_MAX
INT_MIN, INT_MAX, UINT_MAX
LONG_MIN, LONG_MAX, ULONG_MAX
C99: LLONG_MIN, LLONG_MAX, ULLONG_MAX
locale.h - funkce pro národní/jazykovou podporu
Konvence pro formáty: datum, čas, měna, znaky abecedy, řazení řetězců, ...
Ovlivňuje chování standardních funkcí (isalpha(), ...)
Definuje:
typ struct lconv, makra
LC_ALL,
LC_COLLATE,
LC_CTYPE,
LC_MONETARY,
LC_NUMERIC,
LC_TIME a funkci
#include <locale.h> char *setlocale(int category, const char *locale); struct lconv *localeconv(void);Po startu programu platí:
setlocale(LC_ALL, "C");
math.h - matematické funkce
všechny běžné matematické funkce:
sin, cos, tan, log, ...
fabs, floor, ceil, fmod
setjmp.h - nelokální skoky
int setjmp(jmp_buf jmpb); void longjmp(jmp_buf jmpb, int retval);
setjmp - příprava pro nelokální skok
longjmp - provede nelokální skok
Poznámka: setjmp musí být voláno před longjmp
longjmp obnoví stav programu tak, jakoby setjmp
skončil s návratovou hodnotou retval.
Poznámka: longjmp nemůže předat hodnotu 0 v retval, v
takovém případě je změněna na 1.
longjmp může být voláno pouze z funkce, která byla vyvolána
z funkce, která zavolala příslušný setjmp.
setjmp je použitelné pro ošetření chyb a výjimek
Návratové hodnoty:
setjmp vrací 0 když je volán. Když dojde k návratu z setjmp
po volání longjmp, setjmp vrací nenulovou hodnotu.
longjmp se nikdy nevrací
Příklad: použití setjmp(), longjmp()
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
void subroutine(jmp_buf);
int main() {
int value;
jmp_buf jmp;
value = setjmp(jmp);
if (value != 0) {
printf("Byl volán longjmp(jmp,%d)\n", value);
exit(value);
}
printf("Volání podprogramu ... \n");
subroutine(jmp);
return 0;
}
void subroutine(jmp_buf jmp) {
longjmp(jmp,1);
}
signal.h - zpracování signálů
Signály jsou vysílány pomocí raise() nebo při výskytu mimořádné
události. Je možné instalovat funkce, které se provedou při příjmu
odpovídajícího signálu.
void (*signal(int sig, void (*func)(int s)))(int); int raise(int sig);
raise |
vyšle signál číslo sig procesu |
signal |
určuje jak bude přijatý signál zpracován |
Definované konstanty:
SIG_DFL |
ukončí proces |
SIG_ERR |
indikuje chybu při návratu z funkce signal |
SIG_IGN |
ignoruje signál |
Uživatelem specifikované obslužné funkce mohou končit return
nebo voláním abort, _exit, exit, nebo longjmp.
Typy signálů:
SIGABRT |
abnormální ukončení |
SIGFPE |
chyba operace v plovoucí čárce (dělení nulou) |
SIGILL |
nelegální operace |
SIGINT |
ctrl-C |
SIGSEGV |
chyba přístupu k paměti |
SIGTERM |
požadavek ukončení programu |
stdarg.h - pro funkce s proměnným počtem argumentů
va_list |
typ ukazatele na argumenty |
void va_start(va_list ap, lastfix); type va_arg(va_list ap, type); void va_end(va_list ap);Makra
va_arg, va_end, a va_start poskytují prostředky pro
přenositelný přístup k proměnnému počtu argumentů
va_start nastaví ap tak, aby ukazoval na první z volitelných
argumentů funkce. Například:
void f(int i, char *lastfix, ...);
va_arg expanduje na výraz, který má hodnotu typu type (druhý
parametr makra) a přečte jeden z volitelných argumentů
Poznámka: Nelze použít typy char, unsigned char, nebo
float jako druhý parametr va_arg.
va_end ukončuje zpracování proměnného počtu parametrů
Makro va_start musí být voláno před va_arg nebo
va_end. Makro va_end má být voláno až po přečtení všech
parametrů pomocí va_arg.
První použití va_arg vrací první volitelný parametr. Každé další
volání va_arg vrací další parametr v seznamu parametrů.
Příklad: funkce s proměnným počtem argumentů
#include <stdio.h>
#include <stdarg.h>
/* součet seznamu čísel ukončeného nulou */
int sum(int i1, ...) {
int total = i1;
va_list ap;
int arg;
va_start(ap, i1);
while ((arg = va_arg(ap,int)) != 0)
total += arg;
va_end(ap);
return total;
}
int main(void) {
printf("Součet = %d\n", sum(1, 2, 3, 4, 0));
return 0;
}
stddef.h - některá makra a datové typy
typy:
ptrdiff_t |
rozdíl ukazatelů |
size_t |
velikost objektů |
wchar_t |
'široké' znaky (UNICODE) |
makra:
NULL |
|
offsetof( typ, člen ) |
Příklady:
struct xx {
int a, b;
};
static size_t off = offsetof( struct xx, b );
static size_t sz = sizeof( struct xx );
static void *ptr = NULL;
static int p[10];
static ptrdiff_t dif = (char*)&p[10] - (char*)&p[0];
static wchar_t c = L'H';
stdio.h - standardní vstup/výstup
typy:
size_t |
velikost objektů v paměti |
FILE |
soubor |
fpos_t |
pozice v souboru |
makra:
NULL, EOF FOPEN_MAX, FILENAME_MAX SEEK_CUR, SEEK_END, SEEK_SETproměnné:
stdin, stdout, stderrfunkce:
remove, rename, tmpfile fopen, freopen, fread, fwrite, fclose fprintf, fscanf, printf, scanf, sprintf, sscanf vfprintf, vprintf, vsprintf, fgetc, fgets, fputc, fputs, getc, getchar, putc, putchar, gets, puts ungetc, fgetpos, fseek, fsetpos, ftell, rewind clearerr, feof, ferror, perror
funkce getchar
int getchar(void);
čte jeden znak stdin. Narazí-li na konec souboru, vrací hodnotu
EOF. Ekvivalent getc(stdin).
funkce putchar
int putchar(char c);
zapisuje jeden znak do stdout. Nelze-li zapsat, vrací EOF,
jinak vrací c. Ekvivalent putc(stdin).
Příklad: konverze na malá písmena (filtr)
#include <stdio.h>
#include <ctype.h>
int main() {
int c;
while((c=getchar())!=EOF)
putchar(isupper(c)?tolower(c):c);
}
funkce gets
char *gets(char *s);čte řádek ze
stdin. Vrací s při úspěšném čtení, NULL
při EOF. Znak '\n' přečte, ale do s ho neuloží (na
rozdíl od fgets).
Poznámka: nepoužívat - nekontroluje délku vstupu
funkce fgets
char *fgets(char *s, int size, FILE *stream);čte řádek ze souboru
stream. Vrací s při úspěšném čtení, NULL
při EOF. Znak '\n' přečte a uloží do s (na
rozdíl od gets).
funkce puts
int puts(const char *s);zapíše řetězec do
stdout a přejde na nový řádek (na rozdíl od fputs).
Vrací EOF při chybě, jinak vrací nezápornou hodnotu.
funkce printf - formátovaný výstup do stdout
int printf(const char *fmt, ...);
fmt: řetězec obsahující formát tisku
% - prefix formátu d - desítkově celé číslo o - oktalově celé číslo x - šestnáctkově celé číslo u - desítkově bez znaménka c - znak s - řetězec e - pohyblivá čárka s exponentem f - pohyblivá čárka bez exponentu g - kratší z %e nebo %f
Příklad:
%5.2lf |
5 míst, 2 desetinná, long float |
%10s |
min. délka 10 |
%-10s |
zarovnání doleva |
%10.5s |
délka 10, tiskne pouze 5 znaků, zprava mezery |
%.10s |
tisk řetězce, je-li delší max. 10 znaků |
% d |
znaménko '-' nebo ' ' na začátku čísla |
%#g |
vždy desetinná tečka, ponechá koncové nuly |
%06x |
ponechá úvodní nuly: -00001 |
funkce scanf - formátovaný vstup ze stdin
int scanf(const char *fmt, ...);
vrací počet úspěšně načtených formátů
fmt = formátová specifikace:
' ', '\t', '\n' se ignorují
% - prefix formátu
* - potlačí přiřazení
| formát | vstup | argument |
d, ld |
desítkové číslo | int*, long* |
o, lo |
osmičkové číslo | int*, long* |
x, lx |
šestnáctkové číslo | int*, long* |
h |
short | short* |
c |
jediný znak | char* |
s |
řetězec | char[], char* |
f, lf |
reálné číslo | float*, double* |
Příklad:
int i;
float x;
char name[50];
scanf("%2d %f %*d %2s", &i, &x, name); /* & */
vstup: 56789 0123 45a72
výsledek: i = 56; x = 789.0; name = "45"
fprintf, fscanf - tisk a čtení ze souboru
int fprintf(FILE *f, const char *fmt, ...); int fscanf(FILE *f, const char *fmt, ...);
sprintf, sscanf - formátové konverze v paměti
int sprintf(char *s, const char *fmt, ...); int sscanf(const char *s, const char *fmt, ...);
Poznámky:
scanf vyžaduje ukazatele jako parametry
scanf, sprintf -- nebezpečí přepsání paměti
Příklad:
int i;
char c;
scanf(" %d ", i ); /* pozor - chyba! */
scanf(" %d ", &c ); /* pozor - chyba! */
Práce se soubory
funkce fopen - otevření souboru
FILE *fopen(const char *name, const char *mode);
name je jméno souboru, mode je režim otevření:
"r" "rb" |
čtení, čtení binárního souboru |
"w" "wb" |
zápis |
"a" "ab" |
přidávání na konec |
Poznámka: kombinace "r+", "w+b", "rb+" atd...
Při chybě vrací NULL
Příklad:
const char *name = "test.txt";
FILE *fp;
fp = fopen( name, "r" );
if( fp == NULL )
error("soubor %s nelze otevřít pro čtení", name);
funkce fclose - uzavření souboru
int fclose(FILE *f);Vrací
EOF v případě chyby, jinak nulu
Příklad: zřetězení souborů na stdout (cat)
#include <stdio.h>
void filecopy(FILE *fp);
int main(int argc, char *argv[]) {
FILE *fp;
if( argc==1 )
filecopy(stdin);
else
while( --argc > 0 )
if( (fp=fopen(*++argv,"r")) == NULL ) {
printf("cat: can't open %s \n", *argv);
continue; /* další soubor */
}
else {
filecopy(fp);
fclose(fp);
}
return 0;
}
void filecopy(FILE *fp) { /* neefektivní */
int c;
while( (c=getc(fp)) != EOF )
putc( c, stdout );
}
stdlib.h - obecně použitelné funkce
div_t, ldiv_t |
typy pro div, ldiv |
EXIT_FAILURE, EXIT_SUCCESS |
parametry exit() |
RAND_MAX |
maximum rand() |
MB_CUR_MAX |
max. počet bajtů v 'xxx' |
funkce:
abs, div, labs, ldiv bsearch, qsort, rand, srand atof, atoi, atol, strtod, strtol, strtoul mblen, mbstowcs, mbtowc, wcstombs, wctomb calloc, free, malloc, realloc abort, atexit, exit, getenv, system
převod řetězce na číslo
double atof( const char * s); int atoi( const char * s ); long atol( const char * s ); double strtod( const char *s, char **endptr); long strtol( const char *s, char **endptr, int base); unsigned long strtoul(const char *s, char **e, int b);
endptr - ukazatel do řetězce po konverzi (není-li NULL)
base - základ číselné soustavy
funkce atexit - volání funkcí na konci programu
int atexit( void (*func)(void) );
funkce exit - zpracování chyb
void exit(int e);ukončí program s návratovým kódem
e
Příklad:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
FILE *fp;
if( (fp=fopen(argv[1],"r")) == NULL ) {
fprintf(stderr, "can't open %s \n", argv[1]);
exit(1); /* ukončení s chybou */
} else {
DoSomething();
}
exit(0); /* ukončení bez chyby */
}
Dynamické přidělování paměti
void *malloc(size_t size); void *realloc(void *ptr, size_t size); void *calloc(size_t memb, size_t size); void free(void *ptr);
| malloc | přidělí paměť o zadané velikosti |
| free | uvolní přidělenou paměť |
Příklad:
#include <stdlib.h>
typedef struct prvek {
int data;
struct prvek *dalsi;
} prvek;
prvek *novy_prvek(void) {
prvek *p = malloc(sizeof(prvek));
if( p == NULL ) /* pozor! musí se testovat! */
error("chyba: málo paměti");
return p;
}
void zrus_prvek(prvek *ptr) {
free(ptr);
}
string.h - funkce pro práci s řetězci a pamětí
strlen |
délka řetězce |
strcpy |
kopie řetězce |
strncpy |
kopie řetězce (max. n znaků) |
strcat |
připojení řetězce |
strncat |
připojení řetězce (max. n znaků) |
strcmp |
porovnání dvou řetězců |
strncmp |
porovnání dvou řetězců (max. n znaků) |
strcoll |
porovnání podle národní abecedy (LC_COLLATE) |
strchr |
vyhledání znaku |
strrchr |
vyhledání znaku od konce |
strstr |
vyhledání podřetězce |
size_t, NULL
Práce s pamětí
počet bajtů je dalším parametrem funkcí
memcpy, memmove, memcmp, memchr, memset, ...
Příklad:
int pole[SIZE]; int pole2[SIZE]; memset(pole, 0, SIZE * sizeof(int) ); memcpy(pole2, pole, SIZE * sizeof(int) );
time.h - typy a funkce pro práci s časem
CLOCKS_PER_SEC |
počet tiků za sekundu |
clock_t |
systémový čas [tiky] |
time_t |
čas [s] |
struct tm |
tm_sec, tm_mday, ... |
funkce:
clock |
čas od začátku programu [tiky] |
difftime |
rozdíl časů v sec |
mktime |
převod na interní reprezentaci času |
time |
současná hodnota času [s] |
asctime, ctime, gmtime, localtime, strftime
Příklad:
struct tm t;
t.tm_year = 1995 - 1900; /* rok od 1900 */
t.tm_mon = 10 - 1; /* měsíc 0..11 */
t.tm_mday = 6; /* den 1..31 */
t.tm_hour = 12;
t.tm_min = 0;
t.tm_sec = 1;
t.tm_isdst = -1; /* letní čas? */
if( mktime(&t) != -1 ) /* OK */
if(t.tm_wday == 5)
puts("pátek");
ISO-C99 ISO-C99
Nové vlastnosti jazyka C podle normy C99:
long long a nové funkce
inline funkce
(typ) { inicializace }
T f(int n) {
int pole[n]; // variable-length array
}
T f(int n, int m, int a[n][m+1]) { /* ... */ }
// [*][*]
struct S { int n; T array[]; };
_Bool a makra v <stdbool.h>
(konstanty true, false a typ bool).
<complex.h>
jsou definovány typy complex, imaginary a
celá řada funkcí. Například:
double complex cacos(double complex z); float complex cacosf(float complex z); long double complex cacosl(long double complex z);jsou definice funkce
acos.
<tgmath.h>
<math.h> |
<complex.h> |
generické |
| funkce | funkce | makro |
| acos | cacos | acos |
| asin | casin | asin |
| atan | catan | atan |
| cos | ccos | cos |
| sin | csin | sin |
| tan | ctan | tan |
| exp | cexp | exp |
| log | clog | log |
| pow | cpow | pow |
| sqrt | csqrt | sqrt |
| fabs | cabs | fabs |
| ... | ... | ... |
restrict ukazatel je jedinou přístupovou cestou k objektu.
Je vhodné pro lepší optimalizaci;
kvalifikátor restrict lze kdykoli vynechat bez změny významu.
void f (char * restrict s,
const char * restrict s2) {
int n = 32;
while(n--)
*s++ = *s2++;
}
snprintf, vscanf
__func__
\uXXXX \UXXXXXXXX
#define debug(...) fprintf(stderr, __VA_ARGS__) #define showlist(...) puts(#__VA_ARGS__)
#pragma definice
#pragma STDC co jak
co := {FP_CONTRACT|FENV_ACCESS|CX_LIMITED_RANGE}
jak := {ON|OFF|DEFAULT}
#define M _Pragma(string-literal) // v makrech
Standardní knihovny C99
| soubor | co obsahuje |
complex.h |
komplexní čísla |
fenv.h |
nastavení parametrů prostředí pro výpočty v plovoucí čárce (režim zaokrouhlování, zpracování výjimek) |
inttypes.h |
rozšíření definic celočíselných typů v stdint.h
o makra pro formáty pro printf a deklarace funkcí strtoimax,
strtoumax, wcstoimax a wcstoumax |
stdbool.h |
podpora typu bool |
stdint.h |
celočíselné typy - například
intmax_t, uintmax_t,
int8_t, uint8_t,
int_least32_t, int_fast32_t,
atd. |
tgmath.h |
generická makra |
iso646.h |
AMD1: podpora omezených znakových sad (and == && atd.) |
wchar.h |
AMD1: podpora wchar_t, definuje například funkce:
fwprintf,
swprintf,
wcstol,
wcscpy,
wcsmemcpy,
atd. |
wctype.h |
AMD1: ctype.h pro typ wchar_t - definuje
typy a funkce iswdigit atd. |
This document was generated using the LaTeX2HTML translator Version 99.2beta6 (1.42)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -split +0 -no_footnode CPP.tex
The translation was initiated by Petr Peringer on 2000-11-06