Úvod do programovania v jazyku C

Doc. Ing. Pavel Horovčák, CSc., Prof. RNDr. Igor Podlubný, CSc.

Táto on-line príručka vznikla v rámci riešenia výskumného projektu KEGA č. 112/97 "uniWWWerzita"

  1. Úvod
  2. Preprocesor jazyka C
  3. Premenné
  4. Lexikálne prvky jazyka
  5. Riadiace štruktúry
  6. Štandardný vstup a výstup
  7. Reťazce
  8. Súbory
  9. Smerníky
  10. Datové štruktúry
  11. Triedenie
  12. Terminálové funkcie (QNX)
  13. Doporučená literatúra

7 Reťazce

7.1 Predstava o reťazcoch

     Reťazec je jednorozmerné pole znakov, deklarované napr. ako :

  char str[length];

Podobne ako u každého poľa predstavuje názov reťazca str smerník na začiatok tohoto poľa (t.j. str[0]).
Konvencia jazyka C určuje, že všetky reťazce sú štandardne zakončené znakom '\0' (t.j. hodnotou \0), čím sa reťazec líši od iných typov polí. Z toho vyplýva, že efektívna dĺžka reťazca, ktorý možno do daného poľa uložiť, je 0 až length-2 (posledný znak length-1 je potrebné rezervovať pre už spomínanú binárnu nulu). Reťazce teda možno považovať za objekty premennej dĺžky (danou polohou znaku '\0'), ktorá je zhora ohraničená rozsahom deklarovaného poľa (length).
Pri deklarácii prekladač túto koncovú nulu automaticky doplňuje :

  char str[50] = "Iny retazec";
  char *sm = "string";
  char str[] = "STRING";

V prvom prípade sa alokuje pole o 50-ich byte-och a naplní sa reťazcom, samozrejme ukončeným znakom '\0'. V druhom prípade má smerník sm ako počiatočnú hodnotu deklarovanú adresu reťazca "string". V treťom prípade prekladač automaticky alokuje miesto o 6. byte-och :

  0 1 2 3 4 5 6
  S T R I N G \0

  printf("%s", "abc");

Vyššie uvedený príkaz vytlačí všetky znaky reťazca, t.j. znaky abc, zatiaľ čo príkaz :

   printf("%s","abc"+1);

vytlačí znaky reťazca, ktorý začína na adrese o 1 väčšej ako v predchádzajúcom prípade, teda znaky bc.
Dôležité je si stále uvedomovať rozdiel medzi jedným znakom, napr. 'a' a reťazcom dľžky 1, napr. "a", ktorý samozrejme musí byť ukončený binárnou nulou a preto predstavuje pole znakov o dĺžke 2.

V oboch prípadoch kompilátor automaticky doplní binárnu nulu na koniec príslušného reťazca. S reťazcami veľmi často pracujeme pomocou smerníkov, napr. v tvare

7.2 Funkcie pre prácu s reťazcami

Práca s reťazcami je podporovaná celým radom štandardných funkcií, ktoré sa vyskytujú prakticky vo všetkých implementáciách jazyka C. Sú to funkcie pre kopírovanie, spájanie, porovnávanie, vyhľadávanie a určenie dĺžky reťazca. V nižšie uvedených základných funkciách sú parametre s, s1 a s2 typu char * (smerník na char), c je typu int a n typu unsigned int. Všetky funkcie sú definované v hlavičkovom súbore <STRING.H>.

  int strcmp(char *s1, char *s2) (string compare)

- lexikograficky porovnáva reťazce s1 a s2. Vracia číslo >0, ==0, resp. <0 podľa toho, či s1 > , =, resp. < ako s2.

  int strncmp(char *s1, char *s2, unsigned int n) (string max n compare)

- lexikograficky porovnáva maximálne n znakov reťazcov s1 a s2. Vracia číslo >0, ==0, resp. <0 podľa toho, či s1 > , =, resp. < s2.

  unsigned int strlen(char *s) (string length)

- vracia dĺžku reťazca so smerníkom s (bez binárnej koncovej nuly).

  char *strcpy(char *s1, char *s2) (string copy)

- kopíruje jednotlivé znaky reťazca s2 do s1, vrátane koncovej nuly. Vracia smerník na reťazec s1, obsah ktorého sa prepíše. Dĺžka s1 musí byť dostatočná.

  char *strncpy(char *s1, char *s2, unsigned int n) (string max n copy)

- kopíruje jednotlivé znaky reťazca s2 do s1, vrátane koncovej nuly, ak s2 je kratšie ako n. Inak sa kopíruje iba prvých n znakov reťazca s2 a  s1 nie je zakončené binárnou nulou. Vracia smerník na reťazec s1, obsah ktorého sa prepíše. Dĺžka s1 musí byť dostatočná.

  char *strcat(char *s1, char *s2) (string concatenation)

- pripojí jednotlivé znaky reťazca s2 za reťazec s1, vrátane koncovej nuly (pôvodná koncová nula v s1 je prepísaná prvým znakom s2). Vracia smerník na reťazec s1, obsah ktorého sa prepíše. Reťazec s2 ostáva bez zmeny. Dĺžka s1 musí byť dostatočná.

  char *strncat(char *s1, char *s2, unsigned int n) (string max n concatenation)

Pripojí maximálne n znakov reťazca s2 za reťazec s1 (pôvodná koncová nula v s1 je prepísaná prvým znakom s2, za posledný znak s1 doplní novú koncovú nulu). Vracia smerník na reťazec s1, obsah ktorého sa prepíše. Reťazec s2 ostáva bez zmeny. Dĺžka s1 musí byť dostatočná.

  char *strchr(char *s, int c) (string search character)

- vracia smerník na prvý výskyt znaku c v reťazci s. Ak sa znak c v reťazci s nevyskytuje, vracia hodnotu NULL.

  char *strrchr(char *s, int c) (string right search character)

Vracia smerník na posledný výskyt znaku c v reťazci s, t.j. na prvý výskyt znaku c sprava. Ak sa znak c v reťazci s nevyskytuje, vracia hodnotu NULL.

  char *strstr(char *s1, char *s2) (string search string)

- vracia smerník na prvý výskyt reťazca s2 v reťazci s1. Ak sa reťazec s2 v reťazci s1 nevyskytuje, vracia NULL.

Všetky funkcie je možné jednoducho zapísať v jazyku C. Funkciu pre určenie dĺžky reťazca pozri príklad v kap. 4.2. Funkciu pre pripojenie reťazca s2 za s1 možno zapísať takto :

  char *strcat(char *s1, char *s2)
  {
    char *p;

    p=s1;
    while (*p++);          /* nastavi smernik na koniec s1 */
    --p;                   /* nastavi smernik na koncovu nulu */
    while (*p++ = *s2++);  /* prikopíruje s2 za s1 */
    return (s1);
  }

Ďalší príklad ilustruje vyhľadanie podreťazca s2 v reťazci s1 :


/* priklad pr7_1.c vyhľadanie podreťazca */
#include <stdio.h>
#include <string.h>

void main(void)
{
  char *s1 = "Text v ktorom hladame vzor";
  char *s2 = "vzor", *pom;

  pom = strstr(s1, s2);
  if (pom != NULL)
    printf("Podretazec %s najdeny v %s na pozicii %d\n", pom,s1,pom - s1);
  else
    printf("Podretazec %s sa v %s nenachdza\n",pom,s1);
}


Reťazec je možné spracovávať i ako pole po jednotlivých znakoch, vrátane koncovej nuly, napr. :

   s[0]='A'; s[1]='B'; s[2]='C'; s[3]='\0';
   printf("%s %c %s",s,*(s+1),s+1);

Príkaz printf vytlačí ABC B BC (smerníková aritmetika). Najčastejší spôsob inicializácie reťazca využíva funkciu strcpy

     V ďalšom príklade program číta reťazec, vytlačí ho od konca a určí počet znakov 'a' v tomto reťazci :


/* priklad pr7_2.c   čítanie a tlač reťazca, počet znakov */
#include <stdio.h>

int charcount(char *s, char c);

void main(void)
{
  char a='a',b,c[80];
  int i=-1;

  printf("Zadaj retazec = ");
  while((c[++i]=getchar()) != '\n');
  c[i]='\0';
  printf("Retazec %s v opacnom poradi :\n",c);
  while(i) putchar(c[i--]);
  printf("\nobsahuje %d vyskytov znaku %c\n", charcount(c,a);
}

int charcount(char *s, char c)
{
  int count=0;

  while(*s!='\0') if(*s++ == c) count++;
  return(count);
}


Načítanie reťazca, vymazanie všetkých znakov 'a' v tomto reťazci a vytlačenie upraveného reťazca realizuje nasledujúci príklad :


/* priklad pr7_3.c   čítanie a tlač reťazca, vyhodit znaky */
#include <stdio.h>

void remchar(char *s, char c);
void main(void)
{
  char a='a',c[80];

  printf("Zadaj retazec = ");
  scan("%s",c);
  printf("Retazec : %s\n",c);
  remchar(c,a);
  printf("Retazec bez znakov %c :%s\n",c,a);
}

void remchar(char *s, char c)
{
  char *pom=s;

  while(*s!='\0')
  {
    if(*s++ != c) *pom++ = *s;
    s++;
  }
  *pom = '\0';
}


Posledný príklad v tejto časti uvádza porovnanie mechanizmu vytvorenia reťazca príkazom sprintf v prvej časti príkladu a štandardným spôsobom s využitím funkcií pre prácu s reťazcami v druhej časti :


/* priklad pr7_4.c   vstup - vystup retazca formatovy retazec */
#include <stdio.h>
#include <string.h>

void main(void)
{
  char s2[30],s3[30];
  int i;

  /*  vytvorenie retazca prikazom sprintf    */
  printf("Vstup retazca:");
  gets(s2);
  i=strlen(s2);
  switch(i)
  {
  case 1:
    {
      sprintf(s3,"Dlzka <%s> je %d znak",i);
      break;
    }
  case 2:
  case 3:
  case 4:
    {
      sprintf(s3,"Dlzka <%s> je %d znaky",i);
      break;
    }
  case 0:
  default:
    {
      sprintf(s3,"Dlzka <%s> je %d znakov",i);
      break;
    }
  }
  printf("%s\n",s3);

  /*  klasicky pristup - standardne funkcie */
  strcpy(s3,"Dlzka <");
  strcat(s3,s2);
  strcat(s3,"> je ");
  sprintf(s2,"%d",i);
  strcat(s3,s2);
  strcat(s3," znak");
  switch(i)
  {
  case 2:
  case 3:
  case 4:
    {
      strcat(s3,"y");
      break;
    }
  case 0:
  default:
    {
      strcat(s3,"ov");
      break;
    }
  }
  printf("%s\n",s3);
}