• Příklady řetězcových funkcí v C. Vstup a výstup znakových řetězců v C

    Deklarace řetězců

    Řetězec v jazyce C je jednorozměrné pole znaků, jehož posledním prvkem je znak konce řádku - nula (řetězec zakončený nulou, tedy řetězec ukončený NULL).

    Oznámení typ proměnnéŘetězec v C je možný třemi způsoby, z nichž dva inicializují řetězec v době deklarace.

    První způsob:

    Deklarace pole znaků (nezapomeňte přidat mezeru pro ukončující null):

    Značky;

    Druhý způsob:

    Přiřaďte řetězcové proměnné počáteční hodnotu (v tomto případě může kompilátor vypočítat délku řetězce sám):

    Char s = "Příklad inicializace řetězce";

    Napravo od přiřazovacího znaku je řetězcová konstanta. Na konec řetězce se automaticky přidá nula ('\0'). Konstanty znakového řetězce jsou umístěny ve statické třídě úložiště.

    Třetí způsob:

    Implicitní indikace, že se používá pole. Na levé straně znaku přiřazení je uveden ukazatel na symbol:

    Char *s="Druhá možnost inicializace";

    Proměnná s bude ukazatelem na toto místo paměť s náhodným přístupem, kde se nachází řetězcová konstanta. Tato forma zápisu má potenciální chybu v tom, že ukazatel na znak je často označován jako řetězec. Níže uvedený záznam je pouze ukazatel na znak, protože pro řetězec není místo:

    Char*s;

    Zadání řetězce ze standardního vstupního zařízení (klávesnice)

    Existuje sada funkcí pro práci s řetězci. Pro vstup ze standardního vstupního zařízení (klávesnice) se nejčastěji používají knihovní funkce ze standardního vstupního / výstupního modulu: scanf A dostane.

    Chcete-li zadat řetězec pomocí funkce scanf, používá formát « %s» a všimněte si, že znak adresy není použit před identifikátorem řádku « & » , protože jednorozměrné pole je již reprezentováno ukazatelem na jeho začátek:

    scanf("%s", s);

    Funkce dostane()čte znaky, dokud nedosáhne skokového znaku nový řádek. Funkce přijímá všechny znaky až do znaku nového řádku, ale bez něj. Na konec řetězce se přidá koncová nula ('\0'). Funkce dostane() vloží posloupnost znaků načtených z klávesnice do parametru řetězce a vrátí ukazatel na tento řetězec (pokud byla operace úspěšná), nebo NULL (v případě chyby). V níže uvedeném příkladu se po úspěšném dokončení operace na obrazovce zobrazí dva stejné řádky:

    #zahrnout int main() ( char s; char *p; p=gets(s); printf(" \n Zadán řetězec %s. ",s); if (p) printf(" \n Zadán řetězec %s. ", p); vrátit 0;)

    Mimochodem si všimneme, že funkce get se často používá k zadávání jakýchkoli dat z klávesnice jako řetězce za účelem dalšího převodu funkcí sscanf na požadovaný formát nebo pro předběžnou analýzu vstupních dat, například:

    #zahrnout #zahrnout #zahrnout int main() ( char s; int x, err; do ( printf(" \n Zadejte celé číslo -> "); get(s); err=sscanf(s, "%d",&x); if (err !=1) printf(" \n Chyba vstupu. "); ) while (chyba!=1); printf("\n Zadáno celé číslo -> %d", x); návrat 0; )

    Tisk řetězců na standardní výstup (obrazovka monitoru)

    Pro výstup řádků do standardní zařízení výstup (obrazovka monitoru) lze použít dvě funkce printf A klade. Ve funkci printf se jako formát předává "%s". Pohodlí použití této funkce spočívá v tom, že kromě řetězce můžete okamžitě zobrazit data jiných typů. funkce klade je, že po výstupu řádku dojde automaticky k přechodu na další řádek.

    Řetězcové funkce

    Pro převod řetězců v jazyce C je k dispozici knihovna řetězců. Každá z funkcí má svůj vlastní formát záznamu (prototyp).

    Nejpoužívanější funkce jsou popsány v tomto článku. - číst

    Ukázka programů (výpis) pracujících s řetězci

    Není náhodou, že jsem téma o řetězcích umístil do sekce "Pole". Protože řetězec je ve skutečnosti pole znaků. Zde je příklad:

    char str = "Toto je jen řetězec";

    Stejný řádek pro lepší pochopení lze napsat takto:

    char str = ("E","t","o"," ","p","p","o","s","t","o","","s", "t", "p", "o", "k", "a");

    Tito. všechny stejné pole, pouze sestávající ze znaků. Lze s ním tedy pracovat, stejně jako s celočíselnými poli.

    Teď to zkusíme práce se strunami v c. V úvodních lekcích jsme probrali, že znaky jsou celočíselné typy, tzn. každý znak má svou vlastní číselnou hodnotu. Zde je příklad a jeho řešení:

    1. je nutné převést zadané slovo na velká písmena:
    2. #zahrnout
      #zahrnout

      int main()
      {
      char str = "sergey";

      str[i] -= 32;
      }
      for (int i=0; str[i] != "\0";i++)(
      printf("%c", str[i]);
      }
      getch();

      návrat 0;
      }

      pro získání číselného kódu stačí použít specifikátor %d ve funkci printf. Ano a ještě jeden důležitý bod: ukončení libovolné linky je nulový terminátor, který je označen zvláštní charakter - "\0".

    Dalším způsobem, jak zadat řetězec, je deklarovat jej pomocí char*. Zde je příklad:

    char*str = "drát";

    Tito. vznikne ukazatel na řetězec, který se nachází někde v paměti.

    A takto můžete zadat řetězce pomocí operátoru scanf, který je pro nás již nativní:

    charstr; scanf("%s", str);

    Jsou zde dvě jemnosti:

    1. znak převzetí adresy zde není potřeba, protože název pole, jak již víme, je adresa
    2. Délka vstupního řetězce nesmí přesáhnout 15 znaků, protože poslední musí být zakončení null. Navíc kompilátor sám doplní tento znak za váš poslední zadaný znak.

    Protože jazyk C je strukturovaný jazyk, existují již vestavěné funkce pro práce se strunami a se symboly. Pro zpracování řetězců budete muset zahrnout soubor: ctype.h. Soubor obsahuje funkce pro určení velikosti písmen, formátu znaků. V podstatě vše, co potřebujete vědět o symbolu, lze provést pomocí funkcí souboru ctype.h.

    Někdy může být nutné převést řetězec na jiný datový typ. Pro převod řetězců na jiné typy existuje knihovna stdlib. Zde jsou jeho funkce:

    1. int atoi(char*str)
    2. dlouhý atol (char *str)
    3. dvojitý atof (char*str)

    Někdy jsou tyto funkce velmi užitečné, například když potřebujete z řetězce extrahovat rok nebo číselnou hodnotu. Práce s řetězci v c (si) je velmi důležité téma, proto se snažte tuto lekci pochopit.

    Struny. Linkový vstup/výstup. Formátovaný I/O. Zpracování řetězců pomocí standardních funkcí jazyka C. Práce s pamětí.

    1.1. Deklarace a inicializace řetězců.

    Řetězec je pole znaků, které končí prázdným znakem '\0'. Řetězec je deklarován jako normální pole znaků, např.

    char s1; // řetězec dlouhý devět znaků

    char*s2; // ukazatel na řetězec

    Rozdíl mezi ukazateli s1 a s2 je v tom, že ukazatel s1 je pojmenovaná konstanta, zatímco ukazatel s2 je proměnná.

    Řetězcové konstanty jsou uzavřeny v uvozovkách, na rozdíl od znaků, které jsou uzavřeny v jednoduchých uvozovkách. Například,

    "Toto je řetězec."

    Délka řetězcová konstanta standardně nesmí přesáhnout 509 znaků. Mnoho implementací však umožňuje delší řetězce.

    Při inicializaci řetězců je lepší neuvádět rozměr pole, to udělá kompilátor tak, že spočítá délku řetězce a přidá k němu jedničku. Například,

    char s1 = “Toto je řetězec.”;

    V programovacím jazyce C pro práci s řetězci existuje velký počet funkce, jejichž prototypy jsou popsány v hlavičkových souborech stdlib.h a string.h. Práce s těmito funkcemi bude popsána v následujících odstavcích.

    1.2. Linkový vstup/výstup.

    Chcete-li zadat řetězec z konzoly, použijte funkci

    char* get(char*str);

    který zapíše řetězec na adresu str a vrátí adresu vstupního řetězce. Funkce zastaví vstup, pokud narazí na znak '\n' nebo EOF (konec souboru). Znak nového řádku se nezkopíruje. Na konec čteného řádku je umístěn nulový bajt. V případě úspěchu funkce vrátí ukazatel na řádek čtení a v případě selhání vrátí hodnotu NULL.

    Pro výstup řetězce do konzole použijte standardní funkci

    int vloží (const char *s);

    který vrací nezáporné číslo při úspěchu a EOF při neúspěchu.

    Prototypy funkcí get a puts jsou popsány v hlavičkovém souboru stdio.h.

    #zahrnout

    printf("Vstupní řetězec: ");

    1.3. Formátovaný I/O.

    Pro formátovaný vstup z konzoly použijte funkci

    int scanf (const char *formát, ...);

    který vrací počet přečtených datových jednotek při úspěchu a EOF při selhání. Parametr format musí ukazovat na formátovací řetězec, který obsahuje specifikace vstupního formátu. Počet a typy argumentů, které následují za formátovacím řetězcem, musí odpovídat počtu a typům vstupních formátů zadaným ve formátovacím řetězci. Pokud tato podmínka není splněna, pak je výsledek funkce nepředvídatelný.

    Mezera, "\t" nebo "\n" znaky ve formátovacím řetězci popisují jeden nebo více prázdných znaků ve vstupním toku, které zahrnují následující znaky: mezera, '\t', '\n', '\v', '\f'. Funkce scanf přeskočí prázdné znaky ve vstupním proudu.

    Doslovné znaky ve formátovacím řetězci, s výjimkou znaku %, vyžadují, aby se ve vstupním proudu objevily přesně stejné znaky. Pokud takový znak neexistuje, scanf zastaví vstup. Funkce scanf přeskakuje doslovné znaky.

    Obecně platí, že specifikace vstupního formátu je:

    %[*] [šířka] [modifikátory] typ

    Znak '*' označuje mezeru při zadávání pole definovaného touto specifikací;

    - 'width' definuje maximální počet znaků zadaných touto specifikací;

    Typ může nabývat následujících hodnot:

    c je pole znaků,

    s – řetězec znaků, řetězce jsou odděleny prázdnými znaky,

    d je celé číslo se znaménkem při 10 s/s,

    i je celé číslo se znaménkem, číselný systém je založen na prvních dvou číslicích,

    u je celé číslo bez znaménka za 10 s/s,

    o je celé číslo bez znaménka v 8 s/s,

    x, X je celé číslo bez znaménka za 16 s/s,

    e, E, f, g, G - plovoucí číslo,

    p je ukazatel na ukazatel,

    n je ukazatel na celé číslo,

    […] je pole naskenovaných znaků, například .

    V druhém případě budou ze vstupního proudu zadány pouze znaky uzavřené v hranatých závorkách. Pokud je první znak uvnitř hranaté závorky se rovná '^', pak se zadají pouze znaky, které nejsou zahrnuty v poli. Rozsah znaků v poli je určen znakem '-'. Při zadávání znaků se zadávají také úvodní prázdné znaky a ukončovací prázdný bajt řetězce.

    Modifikátory mohou nabývat následujících hodnot:

    h je krátké celé číslo,

    l, L - dlouhé celé číslo nebo float,

    a používají se pouze pro celá čísla nebo plovoucí čísla.

    Následující příklad ukazuje případy použití funkce scanf. Všimněte si, že specifikátoru formátu předchází mezera před zadáním plovoucího čísla.

    #zahrnout

    printf("Zadejte celé číslo: ");

    scanf("%d", &n);

    printf("Zadejte double: ");

    scanf(" %lf", &d);

    printf("Zadejte znak: ");

    scanf(" %c", &c);

    printf("Zadejte řetězec: ");

    scanf("%s", &s);

    Všimněte si, že v tomto programu je inicializováno číslo s plovoucí desetinnou čárkou. To se provádí proto, aby kompilátor obsahoval knihovnu pro podporu práce s plovoucími čísly. Pokud tak neučiníte, při zadávání plovoucího čísla dojde za běhu k chybě.

    Pro formátovaný výstup do konzole použijte funkci

    int printf (const char *formát, ...);

    který vrací počet výstupních jednotek při úspěchu a EOF při neúspěchu. Parametr format je formátovací řetězec, který obsahuje specifikace výstupního formátu. Počet a typy argumentů, které následují za formátovacím řetězcem, musí odpovídat počtu a typům specifikací výstupního formátu uvedeným ve formátovacím řetězci. Obecně platí, že specifikace výstupního formátu je:

    %[flags] [šířka] [.precision] [modifikátory] typ

    - „vlajky“ jsou různé symboly, které určují výstupní formát;

    - 'width' definuje minimální počet výstupních znaků podle této specifikace;

    - '.precision' definuje maximální počet znaků, které mají být vytištěny;

    - ‚modifikátory‘ určují typ argumentů;

    - 'type' určuje typ argumentu.

    K tisku celých čísel se znaménkem se používá následující výstupní formát:

    %[-] [+ | prostor] [šířka] [l] d

    - – zarovnání doleva, standardně – doprava;

    + - zobrazí se znaménko '+', všimněte si, že pro záporná čísla znak '-' se zobrazuje vždy;

    „mezera“ – na pozici znaku se zobrazí mezera;

    d je datový typ int.

    Pro výstup celých čísel bez znaménka se používá následující výstupní formát:

    %[-] [#] [šířka] [l]

    # - zobrazí počáteční 0 pro čísla v 8 c/c nebo počáteční 0x nebo 0X pro čísla v 16 c/c,

    l – modifikátor typu long;

    u je celé číslo v 10c/c,

    o je celé číslo v 8 c/c,

    x, X je celé číslo v 16 c/c.

    Pro zobrazení čísel s plovoucí desetinnou čárkou se používá následující výstupní formát:

    %[-] [+ | mezera] [šířka] [.přesnost]

    "přesnost" označuje počet číslic za desetinnou čárkou pro formáty f, e a E nebo počet platných číslic pro formáty g a G. Čísla jsou zaokrouhlena. Výchozí nastavení je s přesností na šest desetinných míst;

    f je číslo pevného bodu,

    e je číslo v exponenciálním tvaru, exponent je označen písmenem "e",

    E je číslo v exponenciálním tvaru, exponent je označen písmenem "E",

    g je nejkratší z formátů f nebo g,

    G je nejkratší z formátů f nebo G.

    printf ("n = %d\n f = %f\n e = %e\n E = %E\nf = %.2f", -123, 12,34, 12,34, 12,34, 12,34);

    // tiskne: n = 123 f = 12,340000 e = 1,234000e+001 E = 1,234000E+001 f = 12,34

    1.4. Formátování řetězce.

    Existují varianty funkcí scanf a printf, které jsou navrženy pro formátování řetězců a nazývají se sscanf a sprintf.

    int sscanf (const char *str, const char *formát, ...);

    čte data z řetězce určeného parametrem str podle formátovacího řetězce určeného parametrem format. Vrátí množství přečtených dat při úspěchu, EOF při neúspěchu. Například,

    #zahrnout

    char str = "a 10 1.2 String Žádný vstup";

    sscanf(str, "%c %d %lf %s", &c, &n, &d, s);

    printf("%c\n", c); // vytiskne: a

    printf("%d\n", n); // tiskne: 10

    printf("%f\n", d); // výtisky: 1,200000

    printf("%s\n", s); // vytiskne: String

    int sprintf (char *buffer, const char *format, ...);

    naformátuje řetězec podle formátu určeného parametrem format a zapíše výsledek do pole znaků vyrovnávací paměti. Funkce vrací počet znaků zapsaných do pole znaků vyrovnávací paměti, s výjimkou ukončovacího nulového bajtu. Například,

    #zahrnout

    char str = "c = %c, n = %d, d = %f, s = %s";

    char s = "Toto je řetězec.";

    sprintf(vyrovnávací paměť, str, c, n, d, s);

    printf("%s\n", buffer); // vytiskne: c = c, n = 10, d = 1,200000, s = Toto je řetězec

    1.5. Převeďte řetězce na číselná data.

    Prototypy funkcí pro převod řetězců na číselná data jsou uvedeny v hlavičkovém souboru stdlib.h, který musí být součástí programu.

    Chcete-li převést řetězec na celé číslo, použijte funkci

    int atoi (const char *str);

    char *str = "-123";

    n = atoi(str); // n = -123

    Chcete-li převést řetězec na dlouhé celé číslo, použijte funkci

    long int atol (const char *str);

    která, pokud je úspěšná, vrátí celé číslo, na které byl řetězec str převeden, a v případě neúspěšnosti 0. Například

    char *str = "-123";

    n = atol(str); // n = -123

    Chcete-li převést řetězec na dvojitý, použijte funkci

    double atof (const char *str);

    která v případě úspěchu vrátí plovoucí číslo typu double, na které se převede řetězec str, a v případě neúspěchu 0. Např.

    char *str = "-123,321";

    n = atof(str); // n = -123,321

    Následující funkce provádějí podobné akce jako atoi, atol, atof, ale poskytují více funkcí.

    long int strtol (const char *str, char **endptr, int základ);

    převede řetězec str na dlouhý int, který vrátí. Parametry této funkce mají následující účel.

    Pokud je základní argument 0, pak převod závisí na prvních dvou znacích str:

    Pokud je prvním znakem číslice od 1 do 9, pak se předpokládá, že číslo je reprezentováno v 10 c/c;

    Pokud je prvním znakem číslo 0 a druhým znakem je číslo od 1 do 7, pak se předpokládá, že číslo je reprezentováno v 8 c/c;

    Pokud je první znak 0 a druhý znak je 'X' nebo 'x', předpokládá se, že číslo je reprezentováno 16 c/c.

    Pokud je základním argumentem číslo od 2 do 36, pak se tato hodnota bere jako základ číselné soustavy a jakýkoli znak, který přesahuje tento systém, zastaví převod. Číselné soustavy se základem 11 až 36 používají k reprezentaci číslic symboly 'A' až 'Z' nebo 'a' až 'z'.

    Hodnotu argumentu endptr nastavuje funkce strtol. Tato hodnota obsahuje ukazatel na znak, který zastavil převod str. Funkce strtol vrátí převedené číslo při úspěchu a 0 při selhání. Například

    n = strtol("12a", &p, 0);

    printf(" n = %ld, %stop = %c, n, *p); // n = 12, stop = a

    n = strtol("012b", &p, 0);

    printf(" n = %ld, %stop = %c, n, *p); // n = 10, stop = b

    n = strtol("0x12z", &p, 0);

    printf(" n = %ld, %stop = %c, n, *p); // n = 18, stop = z

    n = strtol("01117", &p, 0);

    printf(" n = %ld, %stop = %c, n, *p); // n = 7, stop = 7

    unsigned long int strtol (const char *str, char **endptr, int base);

    funguje podobně jako funkce strtol, ale převádí znakové vyjádření čísla na číslo typ nepodepsaný dlouhá int.

    double strtod (const char *str, char **endptr);

    převede symbolickou reprezentaci čísla na dvojnásobek.

    Všechny funkce uvedené v tomto odstavci přestanou fungovat, když narazí na první znak, který neodpovídá formátu příslušného čísla.

    Pokud navíc znaková hodnota čísla přesahuje rozsah povolených hodnot pro odpovídající datový typ, pak funkce atof, strtol, strtoul, strtod nastaví hodnotu proměnné errno na ERANGE. Proměnná errno a konstanta ERANGE jsou definovány v hlavičkovém souboru math.h. Funkce atof a strtod vrátí HUGE_VAL, funkce strtol vrátí LONG_MAX nebo LONG_MIN a funkce strtoul vrátí ULONG_MAX.

    K převodu číselných dat na znakové řetězce lze použít nestandardní funkce itoa, ltoa, utoa, ecvt, fcvt a gcvt. Pro tyto účely je ale lepší použít standardní funkci sprintf.

    1.6. Standardní funkce pro práci s řetězci.

    V této části jsou uvažovány funkce pro práci s řetězci, jejichž prototypy jsou popsány v hlavičkovém souboru string.h.

    1. Porovnání řetězců. Funkce strcmp a strncmp se používají k porovnání řetězců.

    int strcmp (const char *str1, const char *str2);

    lexikograficky porovná řetězce str1, str2 a vrátí -1, 0 nebo 1, pokud je str1 menší, rovno nebo větší než str2.

    int strncmp (const char *str1, const char *str2, velikost_t n);

    lexikograficky porovnává nejvýše prvních n znaků z řetězců str1 a str2. Funkce vrátí -1, 0 nebo 1, pokud je prvních n znaků str1 menší, rovno nebo větší než prvních n znaků str2.

    // příklad porovnání řetězců

    #zahrnout

    #zahrnout

    char str1 = "aa bb";

    char str2 = "aa aa";

    char str3 = "aa bb cc";

    printf("%d\n", strcmp(str1, str3)); // tiskne: -1

    printf("%d\n", strcmp(str1, str1)); // tiskne: -0

    printf("%d\n", strcmp(str1, str2)); // tiskne: 1

    printf("%d\n", strncmp(str1, str3, 5)); // tiskne: 0

    2. Kopírování řádků. Ke kopírování řetězců se používají funkce strcpy a strncpy.

    char *strcpy (char *str1, const char *str2);

    zkopíruje řetězec str2 do řetězce str1. Řetězec str2 se zkopíruje celý, včetně ukončovacího nulového bajtu. Funkce vrací ukazatel na str1. Pokud se čáry překrývají, pak je výsledek nepředvídatelný.

    char *strncpy (char *str1, const char *str2, velikost_t n);

    zkopíruje n znaků z řetězce str2 do řetězce str1. Pokud řetězec str2 obsahuje méně než n znaků, pak se poslední prázdný bajt zkopíruje tolikrát, kolikrát je potřeba k rozšíření řetězce str2 na n znaků. Funkce vrací ukazatel na řetězec str1.

    char str2 = "Kopírovat řetězec.";

    strcpy(str1, str2);

    printf(str1); // prints: Kopírovat řetězec.

    4. Spojovací struny. Funkce strcat a strncat se používají ke zřetězení řetězců do jednoho řetězce.

    char* strcat (char *str1, const char *str2);

    připojí řetězec str2 k řetězci str1, přičemž ukončovací prázdný bajt řetězce str1 vymaže. Funkce vrací ukazatel na řetězec str1.

    char* strncat (char *str1, const char *str2, velikost_t n);

    zřetězí n znaků z řetězce str2 do řetězce str1 a vymaže ukončující prázdný bajt str1. Funkce vrací ukazatel na řetězec str1. pokud je délka řetězce str2 menší než n, pak se připojí pouze znaky obsažené v řetězci str2. Po zřetězení řetězce se k str1 vždy přidá nulový bajt. Funkce vrací ukazatel na řetězec str1.

    #zahrnout

    #zahrnout

    char str1 = "řetězec";

    char str2 = "katenace";

    char str3 = "Ano Ne";

    strcat(str1, str2);

    printf("%s\n", str1); // tiskne: Řetězec řetězce

    strncat(str1, str3, 3);

    printf("%s\n", str1); // tiskne: Řetězec řetězce Ano

    5. Vyhledejte znak v řetězci. Funkce strchr, strrchr, strspn, strcspn a strpbrk se používají k hledání znaku v řetězci.

    char* strchr (const char *str, int c);

    hledá první výskyt znaku určeného parametrem c v řetězci str. V případě úspěchu funkce vrátí ukazatel na první nalezený znak a v případě selhání vrátí hodnotu NULL.

    char* strrchr (const char *str, int c);

    hledá poslední výskyt znaku určeného parametrem c v řetězci str. V případě úspěchu funkce vrátí ukazatel na poslední nalezený znak a v případě selhání vrátí hodnotu NULL.

    #zahrnout

    #zahrnout

    char str = "hledání znaků";

    printf("%s\n", strchr(str, "r")); // vytiskne: r hledat

    printf("%s\n", strrchr(str, "r")); // tiskne: rch

    size_t strspn (const char *str1, const char *str2);

    vrátí index prvního znaku v str1, který není v str2.

    size_t strcspn (const char *str1, const char *str2);

    vrátí index prvního znaku v str1, který je v str2.

    char str = "123 abc";

    printf("n = %d\n", strspn(str, "321"); // vytiskne: n = 3

    printf ("n = %d\n", strcspn (str, "cba"); // vytiskne: n = 4

    char* strpbrk (const char *str1, const char *str2);

    najde první znak v str1, který se rovná jednomu ze znaků v str2. V případě úspěchu funkce vrátí ukazatel na tento znak a v případě selhání vrátí hodnotu NULL.

    char str = "123 abc";

    printf("%s\n", strpbrk(str, "bca")); // vytiskne: abc

    6. Porovnání strun. Funkce strstr se používá k porovnání řetězců.

    char* strstr (const char *str1, const char *str2);

    najde první výskyt řetězce str2 (bez koncového null bajtu) v řetězci str1. V případě úspěchu funkce vrátí ukazatel na nalezený podřetězec a v případě selhání vrátí hodnotu NULL. Pokud ukazatel str1 ukazuje na řetězec nulové délky, funkce vrátí ukazatel str1.

    char str = "123 abc 456;

    printf("%s\n", strstr(str, "abc"); // tisk: abc 456

    7. Analýza řetězce na tokeny. Funkce strtok se používá k analýze řetězce na tokeny.

    char* strtok (char *str1, const char *str2);

    vrací ukazatel na další token (slovo) v řetězci str1, kde oddělovače tokenů jsou znaky z řetězce str2. Pokud jsou tokeny u konce, funkce vrátí hodnotu NULL. Při prvním volání funkce strtok musí parametr str1 ukazovat na řetězec, který je analyzován do tokenů, a při dalších voláních musí být tento parametr nastaven na hodnotu NULL. Po nalezení tokenu zapíše funkce strtok za tento token místo oddělovače prázdný bajt.

    #zahrnout

    #zahrnout

    char str = "12 34 ab cd";

    p = strtok(str, " ");

    printf("%s\n",p); // vypíše hodnoty ve sloupci: 12 34 ab cd

    p = strtok(NULL, " ");

    8. Určení délky provázku. Funkce strlen se používá k určení délky řetězce.

    size_t strlen(const char *str);

    vrátí délku řetězce, ignoruje poslední prázdný bajt. Například,

    char str = "123";

    printf("délka = %d\n",strlen(str)); // tiskne: len = 3

    1.7. Funkce pro práci s pamětí.

    Hlavičkový soubor string.h také popisuje funkce pro práci s paměťovými bloky, které jsou obdobou odpovídajících funkcí pro práci s řetězci.

    void* memchr(const void *str, int c, velikost_t n);

    hledá první výskyt znaku určeného c v n bajtech str.

    int memcmp(const void *str1, const void *str2, velikost_t n);

    porovnává prvních n bajtů str1 a str2.

    void* memcpy(const void *str1, const void *str2, velikost_t n);

    zkopíruje prvních n bajtů z řetězce str1 do řetězce str2.

    void* memmove(const void *str1, const void *str2, velikost_t n);

    zkopíruje prvních n bajtů z str1 do str2, čímž zajistí správné zpracování překrývajících se řetězců.

    void* memset(const void *str, int c, velikost_t n);

    kopíruje postavu nastavit podle parametru c, na prvních n bajtů str.

    Habra, ahoj!

    Není to tak dávno, co jsem měl docela zajímavou příhodu, do které byl zapojen jeden z učitelů jedné vysoké školy informatiky.

    Konverzace o programování pod Linuxem se pomalu stočila k tomu, že tato osoba začala namítat, že složitost programování systémů je ve skutečnosti značně přehnaná. Že jazyk C je ve skutečnosti jednoduchý jako zápalka Linuxové jádro(z jeho slov).

    Měl jsem s sebou notebook s Linuxem, který měl pánskou sadu vývojových nástrojů C (gcc, vim, make, valgrind, gdb). Už si nepamatuji, jaký cíl jsme si tehdy dali, ale po pár minutách stál můj soupeř za tímto notebookem, zcela připraven problém vyřešit.

    A doslova hned na prvních řádcích udělal vážnou chybu při přidělování paměti pro ... řádek.

    Char *str = (char *)malloc(sizeof(char) * strlen(buffer));
    buffer je proměnná zásobníku, která byla naplněna daty z klávesnice.

    Myslím, že se určitě najdou lidé, kteří se budou ptát: "Je tady něco špatně?".
    Věřte, že může.

    A co přesně - přečtěte si na kočce.

    Trochu teorie – jakýsi LikBez.

    Pokud víte, přejděte na další záhlaví.

    Řetězec v C je pole znaků, které by v dobrém slova smyslu mělo vždy končit "\0" - znak konce řádku. Řetězce na zásobníku (statické) jsou deklarovány takto:

    Char str[n] = ( 0);
    n je velikost pole znaků, stejná jako délka řetězce.

    Assignment ( 0 ) - "nulování" řetězce (volitelné, můžete deklarovat i bez něj). Výsledek je stejný jako u funkcí memset(str, 0, sizeof(str)) a bzero(str, sizeof(str)). Používá se k zabránění vyhazování odpadků do neinicializovaných proměnných.

    Můžete také okamžitě inicializovat řetězec na zásobníku:

    Char buf = "výchozí text vyrovnávací paměti\n";
    Kromě toho lze řetězec deklarovat jako ukazatel a přidělit mu paměť na hromadě:

    Char *str = malloc(velikost);
    size - počet bajtů, které řetězci přidělíme. Takové řádky se nazývají dynamické (vzhledem k tomu, že správná velikost počítáno dynamicky + velikost přidělené paměti lze kdykoli zvětšit pomocí funkce realloc().

    V případě proměnné zásobníku jsem pro určení velikosti pole použil zápis n, v případě proměnné na haldě jsem použil velikost zápisu. A to dokonale vystihuje pravou podstatu rozdílu mezi deklarací na zásobníku a deklarací s alokací paměti na haldě, protože n se obvykle používá, když se mluví o počtu prvků. A velikost je úplně jiný příběh...

    Valgrind nám pomůže

    Zmiňoval jsem to i ve svém předchozím článku. Valgrind ( , dva - malý návod) - velmi užitečný program, která pomáhá programátorovi vystopovat úniky paměti a kontextové chyby, tedy právě ty věci, které se nejčastěji objevují při práci s řetězci.

    Podívejme se na malý výpis, který implementuje něco podobného jako program, který jsem zmínil, a spusťte jej přes valgrind:

    #zahrnout #zahrnout #zahrnout #define HELLO_STRING "Ahoj Habr!\n" void main() ( char *str = malloc(sizeof(char) * strlen(HELLO_STRING)); strcpy(str, HELLO_STRING); printf("->\t%s" , str); free(str); )
    A vlastně i výsledek programu:

    $ gcc main.c $ ./a.out -> Ahoj Habr!
    Zatím nic neobvyklého. Nyní spusťte tento program pomocí valgrind!

    $ valgrind --tool=memcheck ./a.out ==3892== Memcheck, detektor chyb paměti ==3892== Copyright (C) 2002-2015 a GNU GPL"d, Julian Seward et al. == 3892== Použití Valgrind-3.12.0 a LibVEX; spusťte znovu s -h pro informace o autorských právech ==3892== Příkaz: ./a.out ==3892== ==3892== Neplatný zápis velikosti 2 ==3892= = na 0x4005B4: hlavní (v /home/indever/prg/C/public/a.out) ==3892== Adresa 0x520004c je 12 bajtů uvnitř bloku o velikosti 13 alloc"d ==3892== na 0x4C2DB9D: malloc (vg_replace_malloc.c:299) ==3892== podle 0x400597: main (v /home/indever/prg/C/public/a.out) ==3892== ==3892== Neplatné čtení velikosti 1 == 3892== na 0x4C30BC4: strlen (vg_replace_strmem.c:454) ==3892== podle 0x4E89AD0: vfprintf (v /usr/lib64/libc-2.24.so) ==3892== podle 0x84E907 lib64/libc-2.24.so) ==3892== podle 0x4005CF: main (v /home/indever/prg/C/public/a.out) ==3892== Adresa 0x520004d je 0 bajtů po bloku o velikosti 13 alloc"d ==3892== v 0x4C2DB9D: malloc (vg_replace_malloc.c:299) ==3892== podle 0x400597: hlavní (v /home/indever/prg/C/public/a.out) ==3892== -> Ahoj Habr! ==3892== ==3892== SHRNUTÍ HADY: ==3892== používá se při ukončení: 0 bajtů v 0 blocích ==3892== celkové využití haldy: 2 přidělení, 2 uvolnění, 1 037 přidělených bajtů ==3892= ===3892== Všechny bloky haldy byly uvolněny -- nejsou možné žádné úniky ==3892== ==3892== Pro počty zjištěných a potlačených chyb spusťte znovu s: -v ==3892== SOUHRN CHYB: 3 chyby ze 2 kontextů (potlačeno: 0 z 0)
    ==3892== Všechny bloky haldy byly uvolněny - nejsou možné žádné úniky- nedochází k únikům, a to potěší. Ale stojí za to sklopit oči o něco níže (i když, chci poznamenat, je to jen shrnutí, hlavní informace jsou na trochu jiném místě):

    ==3892== SOUHRN CHYB: 3 chyby ze 2 kontextů (potlačeno: 0 z 0)
    3 chyby. ve 2 kontextech. V tak jednoduchém programu. Jak!?

    Ano, velmi jednoduché. Celý „trik“ spočívá v tom, že funkce strlen nebere v úvahu znak konce řádku – „\0“. I když je ve vstupním řetězci výslovně uveden (#define HELLO_STRING "Ahoj Habr!\n\0"), bude ignorován.

    Těsně nad výsledkem provádění programu jsou řádky -> Ahoj Habr! existuje podrobná zpráva o tom, co a kde se našemu drahému valgrindovi nelíbilo. Navrhuji, abyste se na tyto řádky sami podívali a udělali si vlastní závěry.

    Vlastně, správná verze program bude vypadat takto:

    #zahrnout #zahrnout #zahrnout #define HELLO_STRING "Ahoj Habr!\n" void main() ( char *str = malloc(sizeof(char) * (strlen(HELLO_STRING) + 1)); strcpy(str, HELLO_STRING); printf("->\ t%s", str); free(str); )
    Procházíme valgrindem:

    $ valgrind --tool=memcheck ./a.out -> Ahoj Habr! ==3435== ==3435== SOUHRN HADY: ==3435== používá se při ukončení: 0 bajtů v 0 blocích ==3435== celkové využití haldy: 2 přidělení, 2 uvolnění, 1 038 přidělených bajtů ==3435= ===3435== Všechny bloky haldy byly uvolněny -- nejsou možné žádné úniky ==3435== ==3435== Pro počty zjištěných a potlačených chyb spusťte znovu s: -v ==3435== SOUHRN CHYB: 0 chyb z 0 kontextů (potlačeno: 0 z 0)
    Skvělý. Nejsou žádné chyby, problém pomohl vyřešit +1 bajt přidělené paměti.

    Zajímavé je, že ve většině případů bude první i druhý program fungovat stejně, ale pokud paměť přidělená pro řádek, který se nevešel na koncový znak, nebyla nastavena na nulu, pak funkce printf () při zobrazení takového řádek, zobrazí také všechny odpadky za tímto řádkem - vše se bude tisknout, dokud znak konce řádku nebude překážet printf ().

    Nicméně víte, (strlen(str) + 1) je takové řešení. Čelíme 2 problémům:

    1. Co když ale potřebujeme alokovat paměť pro řetězec vytvořený například pomocí s(n)printf(..)? Argumenty nepodporujeme.
    2. Vzhled. Řádek s deklarací proměnné vypadá prostě hrozně. Některým klukům se také podaří přidat (char *) do malloc, jako by psal pod plusy. V programu, kde pravidelně potřebujete zpracovávat řetězce, má smysl najít elegantnější řešení.
    Pojďme přijít s řešením, které uspokojí nás i valgrind.

    snprintf()

    int snprintf(char *str, velikost_t velikost, const char *formát, ...);- function - rozšíření sprintf, které naformátuje řetězec a zapíše jej do ukazatele předaného jako první argument. Liší se od sprintf() v tom, že do str nebude zapsáno více bajtů, než je zadaná velikost.

    Funkce má jednu zajímavá vlastnost- v každém případě vrátí velikost vygenerovaného řetězce (kromě znaku konce řádku). Pokud je řetězec prázdný, vrátí se 0.

    Jeden z problémů se strlen, který jsem popsal, souvisí s funkcemi sprintf() a snprintf(). Předpokládejme, že potřebujeme něco napsat do řetězce str. Poslední řetězec obsahuje hodnoty dalších proměnných. Náš záznam by měl vypadat takto:

    Char * str = /* zde alokovat paměť */; sprintf(str, "Dobrý den, %s\n", "Habr!");
    Vyvstává otázka: jak určit, kolik paměti by mělo být přiděleno pro řetězec str?

    Char * str = malloc(sizeof(char) * (strlen(str, "Dobrý den, %s\n", "Habr!") + 1)); - to nebude fungovat. Prototyp funkce strlen() vypadá takto:

    #zahrnout size_t strlen(const char *s);
    const char *s neznamená, že řetězec předaný do s může být řetězec v variadic formátu.

    Tady nám to pomůže užitečná vlastnost funkce snprintf(), kterou jsem zmínil výše. Podívejme se na kód následujícího programu:

    #zahrnout #zahrnout #zahrnout void main() ( /* Protože snprintf() nerespektuje znak konce řádku, přidejte jeho velikost k výsledku */ size_t need_mem = snprintf(NULL, 0, "Ahoj, %s!\n", "Habr") + sizeof("\0"); char *str = malloc(needed_mem); snprintf(str, need_mem, "Ahoj, %s!\n", "Habr"); printf("->\t %s", str); free(str); )
    Spusťte program ve valgrind:

    $ valgrind --tool=memcheck ./a.out -> Ahoj Habr! ==4132== ==4132== SOUHRN HADY: ==4132== při ukončení se používá: 0 bajtů v 0 blocích ==4132== celkové využití haldy: 2 přidělení, 2 uvolnění, přidělených 1 041 bajtů ==4132= ===4132== Všechny bloky haldy byly uvolněny -- nejsou možné žádné úniky ==4132== ==4132== Pro počty zjištěných a potlačených chyb spusťte znovu s: -v ==4132== SOUHRN CHYB: 0 chyb z 0 kontextů (potlačeno: 0 z 0) $
    Skvělý. Máme podporu pro argumenty. Vzhledem k tomu, že funkci snprintf() předáme jako druhý argument nulu, zápis nulovým ukazatelem nikdy nepovede k Seagfault. Navzdory tomu však funkce stále vrátí velikost požadovanou pro řetězec.

    Ale na druhou stranu jsme museli zavést další proměnnou, a to konstrukci

    Size_t need_mem = snprintf(NULL, 0, "Dobrý den, %s!\n", "Habr") + sizeof("\0");
    vypadá ještě hůř než v případě strlen().

    Obecně platí, že + sizeof("\0") lze odstranit explicitním zadáním "\0" na konci formátovacího řetězce (size_t need_mem = snprintf(NULL, 0, "Dobrý den, %s!\n \0 ”, “Habr”);), ale to není v žádném případě vždy možné (v závislosti na mechanismu zpracování řetězce můžeme alokovat bajt navíc).

    Něco se musí udělat. Chvíli jsem přemýšlel a rozhodl jsem se, že nyní je čas apelovat na moudrost starověku. Pojďme si popsat makro funkci, která bude volat snprintf() s nulovým ukazatelem jako prvním argumentem a nulovým ukazatelem jako druhým. A nezapomeňme na konec řady!

    #define strsize(args...) snprintf(NULL, 0, args) + sizeof("\0")
    Ano, pro někoho to může být novinka, ale makra v C podporují proměnný počet argumentů a elipsa říká preprocesoru, že zadaný argument funkce makra (v našem případě args) odpovídá několika skutečným argumentům.

    Pojďme si naše řešení ověřit v praxi:

    #zahrnout #zahrnout #zahrnout #define strsize(args...) snprintf(NULL, 0, args) + sizeof("\0") void main() ( char *str = malloc(strsize("Dobrý den, %s\n", "Habr! ")); sprintf(str, "Dobrý den, %s\n", "Habr!"); printf("->\t%s", str); free(str); )
    Běh s valgrundem:

    $ valgrind --tool=memcheck ./a.out -> Ahoj Habr! ==6432== ==6432== SOUHRN HADY: ==6432== při ukončení se používá: 0 bajtů v 0 blocích ==6432== celkové využití haldy: 2 přidělení, 2 uvolnění, přidělených 1 041 bajtů ==6432= ===6432== Všechny bloky haldy byly uvolněny -- nejsou možné žádné úniky ==6432== ==6432== Pro počty zjištěných a potlačených chyb spusťte znovu s: -v ==6432== SOUHRN CHYB: 0 chyb z 0 kontextů (potlačeno: 0 z 0)
    Ano, nejsou tam žádné chyby. Všechno je správně. A valgrind je šťastný a programátor může jít konečně spát.

    Na závěr ale řeknu ještě jednu věc. V případě, že potřebujeme alokovat paměť pro libovolný řetězec (i s argumenty), již existuje plně funkční řešení na klíč.

    Mluvíme o funkci asprintf:

    #define _GNU_SOURCE /* Viz feature_test_macros(7) */ #include int asprintf(char **strp, const char *fmt, ...);
    Jako svůj první argument bere ukazatel na řetězec (**strp) a alokuje paměť na dereferencovaném ukazateli.

    Náš program napsaný pomocí asprintf() bude vypadat takto:

    #zahrnout #zahrnout #zahrnout void main() ( char *str; asprintf(&str, "Dobrý den, %s!\n", "Habr"); printf("->\t%s", str); free(str); )
    A ve skutečnosti ve valgrindu:

    $ valgrind --tool=memcheck ./a.out -> Ahoj Habr! ==6674== ==6674== SOUHRN HADY: ==6674== při ukončení se používá: 0 bajtů v 0 blocích ==6674== celkové využití haldy: 3 přidělení, 3 uvolnění, přidělených 1 138 bajtů ==6674= ===6674== Všechny bloky haldy byly uvolněny -- nejsou možné žádné úniky ==6674== ==6674== Pro počty zjištěných a potlačených chyb spusťte znovu s: -v ==6674== SOUHRN CHYB: 0 chyb z 0 kontextů (potlačeno: 0 z 0)
    Vše je v pořádku, ale jak vidíte, bylo přiděleno více paměti a nyní jsou alokace tři, nikoli dvě.Na slabých vestavěných systémech je tato funkce nežádoucí.
    Pokud navíc do konzole napíšeme man asprintf, uvidíme:

    VYHOVUJÍCÍ Tyto funkce jsou GNU rozšíření, nikoli v C nebo POSIX. Jsou také k dispozici pod *BSD. Implementace FreeBSD nastaví strp na NULL při chybě.

    To objasňuje, že tato funkce je dostupná pouze ve zdrojích GNU.

    Závěr

    Na závěr chci říci, že práce s řetězci v C je velmi komplexní téma, které má řadu nuancí. Například pro zápis "bezpečného" kódu s dynamickou alokací paměti se stále doporučuje používat místo malloc() funkci calloc() - calloc zaplní přidělenou paměť nulami. No, nebo po přidělení paměti použijte funkci memset (). V opačném případě může odpad, který původně ležel v oblasti přidělené paměti, způsobit problémy během ladění a někdy dokonce i při práci s řetězcem.

    Více než polovina programátorů v jazyce C, které znám (většina začátečníků), kteří na mé přání vyřešili problém s alokací paměti pro řetězce, to udělala tak, že to nakonec vedlo ke kontextovým chybám. V jednom případě - dokonce i k úniku paměti (no, člověk zapomněl udělat zdarma (str), komu se to nestane). Vlastně to je to, co mě inspirovalo k vytvoření tohoto výtvoru, který jste právě četli.

    Doufám, že tento článek bude pro někoho užitečný. Proč jsem to všechno ohradil - žádný jazyk není jednoduchý. Všude má své jemnosti. A čím více složitostí jazyka znáte, tím lepší je váš kód.

    Věřím, že po přečtení tohoto článku se váš kód trochu zlepší :)
    Hodně štěstí Habr!