Jazyky C a C++

Petr Peringer
peringer AT fit.vutbr.cz
Vysoké učení technické v Brně
FIT, Božetěchova 2,
61266 BRNO

6. listopadu 2000

Úvod

Tyto 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 \fbox{rámečky} pro zvýraznění.


Zdroje informací

Literatura

1
Kerninghan, B.; Ritchie, D.: The C Programming Language, 2nd edition, Addison-Wesley, 1989

2
Stroustrup, B.: The C++ programming language, third edition, Addison-Wesley, 1997

3
Booch, G.: Object-Oriented Design with applications, 2nd edition, The Benjamin/Cummings Publishing Company 1994

4
ISO: Programming languages - C, WG14/N869 Committee Draft, January 18, 1999

5
ISO: Rationale for International Standard Programming Languages - C, N897 Revision 2, October 20, 1999

6
ISO: Working Paper for Draft Proposed International Standard for Information Systems - Programming Language C++, December 1996

7
Herout, P.: Učebnice jazyka C, 3. vydání, Kopp, 1998

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 C
Pů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

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

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 $\Longleftrightarrow$ float

char $\Longleftrightarrow$ 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]);


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

Podmínka cyklu:
celočíselný výraz, 0 je FALSE, všechny ostatní hodnoty znamenají TRUE
Operace porovnání:
< > <= >= == != 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 $\rightarrow$ 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ů


Poznámky

/* text C poznámky */
// text C99 poznámky do konce řádku
ISO 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


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 T
Objekt takto označený nelze modifikovat přiřazením.
volatile T
Tato 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

Přípona 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


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)>"


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


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


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 int
ale 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

Adresový prostor - obrázek Ukazatele a argumenty funkcí


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:


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:

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:

Vícerozměrná pole

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:

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:

Unie (1.p union)


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ů

Celočíselná rozšíření (integral promotions)
zachovávají hodnotu včetně znaménka
char, short int, bitová pole $\rightarrow$ int nebo unsigned


Poznámka:    argumenty funkcí, K&R float $\rightarrow$ double


Obvyklé aritmetické konverze u binárních aritmetických operací

  1. Je-li jeden operand typu long double, je druhý převeden na long double a výsledek je také long double
  2. jinak, je-li double ...
  3. jinak, je-li float ...


    Jinak se provedou celočíselná rozšíření pro oba operandy a:

  4. jestliže oba operandy mají stejný typ neprovádí se další konverze,
  5. jinak, jsou-li oba opernady signed nebo oba unsigned převede se vše na typ s větším rozsahem.
  6. Jinak, jestliže unsigned typ má větší nebo stejný rozsah, je druhý operand převeden na unsigned.

  7. Jinak, jestliže signed operand může reprezentovat všechny hodnoty unsigned operandu, je druhý operand převeden na signed.

  8. Jinak jsou oba převedeny na odpovídající unsigned typ.


Problém signed/unsigned char

- vzniká při převodu znaků na int:

signed char $\rightarrow$ int rozsah $-128$ .. $127$
unsigned char $\rightarrow$ int rozsah $0$ .. $255$


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
( ) [ ] -> . $\rightarrow$
! ~ + - ++ -- & * (typecast) sizeof $\leftarrow$
* / % $\rightarrow$
+ - $\rightarrow$
<< >> $\rightarrow$
< <= > >= $\rightarrow$
== != $\rightarrow$
& $\rightarrow$
^ $\rightarrow$
| $\rightarrow$
&& $\rightarrow$
|| $\rightarrow$
?: $\leftarrow$
= *= /= %= += -= &= ^= |= <<= >>= $\leftarrow$
, $\rightarrow$


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í


Funkce s proměnným počtem argumentů

typ jmeno(typ1 parametr1, ... );


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


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


\begin{boxedminipage}[t]{7.5cm}
\begin{verbatim}/* modul1.c */
int x; /* de...
...);
int main() {
f2(5);
s--;
return 0;
}\end{verbatim}\end{boxedminipage}

\begin{boxedminipage}[t]{7.5cm}
\begin{verbatim}/* modul2.c */
extern int x...
... {
s++;
}
void f2(double d) {
x = 10*d;
}\end{verbatim}\end{boxedminipage}

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í.


\begin{boxedminipage}[t]{7cm}
\begin{verbatim}/* rozhrani.h */
extern int x;
void f2(double d);\end{verbatim}\end{boxedminipage}


\begin{boxedminipage}[t]{7.5cm}
\begin{verbatim}/* modul1.c */
...

\begin{boxedminipage}[t]{7.5cm}
\begin{verbatim}/* modul2.c */
...

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) .....


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

Návratové hodnoty:


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ů

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_SET
proměnné:
  stdin, stdout, stderr
funkce:
  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:

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:


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:


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.


About this document ...

Jazyky C a C++

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


Petr Peringer 2000-11-06