C reťazec na spustiteľný kód. Práca so strunami. Strunová trieda. Konštruktéri tried. Funkcie priradiť (), pripojiť (), vložiť (), nahradiť (), vymazať (), nájsť (), rfind (), porovnať (), c_str (). Príklady

  • 29.07.2019

Habra, ahoj!

Nie je to tak dávno, čo som mal dosť zaujímavú príhodu, do ktorej bol zapletený jeden z učiteľov vysokej školy informatiky.

Konverzácia o linuxovom programovaní sa pomaly posunula do bodu, keď tento muž tvrdil, že zložitosť programovania systémov bola v skutočnosti hrubo prehnaná. Že jazyk C je jednoduchý ako zápalka, v skutočnosti ako jadro Linuxu (jeho slovami).

Mal som so sebou notebook s Linuxom s pánskou sadou vývojových nástrojov C (gcc, vim, make, valgrind, gdb). Už si nepamätám, aký cieľ sme si vtedy dali, ale po pár minútach sa môj súper ocitol za týmto notebookom, úplne pripravený problém vyriešiť.

A doslova hneď na prvých riadkoch urobil vážnu chybu pri prideľovaní pamäte pre ... riadok.

Char * str = (char *) malloc (sizeof (char) * strlen (buffer));
buffer je zásobníková premenná, do ktorej sa zapisovali dáta z klávesnice.

Myslím si, že sa určite nájdu ľudia, ktorí sa budú pýtať: "Môže tu byť niečo zle?"
Verte mi, dá sa.

A čo presne - prečítajte si na strihu.

Trochu teórie – akýsi FaceBez.

Ak viete, prejdite na ďalšiu hlavičku.

Reťazec v C je pole znakov, ktoré by mali priateľsky vždy končiť "\ 0" - znak konca riadku. Reťazce v zásobníku (statické) sú deklarované takto:

Char str [n] = (0);
n je veľkosť poľa znakov, rovnaká ako dĺžka reťazca.

Priradenie (0) - "nulovanie" reťazca (voliteľné, môžete ho deklarovať aj bez neho). Výsledok je rovnaký ako pri funkciách memset (str, 0, sizeof (str)) a bzero (str, sizeof (str)). Používa sa na zabránenie vzniku odpadu v neinicializovaných premenných.

Aj v zásobníku môžete okamžite inicializovať riadok:

Char buf = "predvolený text vyrovnávacej pamäte \ n";
Okrem toho môže byť reťazec deklarovaný ako ukazovateľ a môže mu byť pridelená pamäť na halde:

Char * str = malloc (veľkosť);
size - počet bajtov, ktoré reťazcu pridelíme. Takéto reťazce sa nazývajú dynamické (kvôli tomu, že požadovaná veľkosť sa vypočítava dynamicky + veľkosť pridelenej pamäte je možné kedykoľvek zväčšiť pomocou funkcie realloc ()).

V prípade premennej zásobníka som na určenie veľkosti poľa použil zápis n, v prípade premennej na halde som použil veľkosť zápisu. A to dokonale odráža skutočnú podstatu rozdielu medzi deklaráciou na zásobníku a deklaráciou s alokáciou pamäte na halde, pretože n sa zvyčajne používa, keď hovoríme o počte prvkov. A veľkosť je úplne iný príbeh...

Valgrind nám pomôže

Vo svojom predchádzajúcom článku som to tiež spomenul. Valgrind (, dva je malý návod) je veľmi užitočný program, ktorý pomáha programátorovi sledovať úniky pamäte a kontextové chyby – presne také veci, ktoré sa najčastejšie vyskytujú pri práci s reťazcami.

Pozrime sa na malý zoznam, ktorý implementuje niečo podobné ako program, ktorý som spomenul, a spustite ho cez valgrind:

#include #include #include #define HELLO_STRING "Ahoj Habr! \ n" void main () (char * str = malloc (sizeof (char) * strlen (HELLO_STRING)); strcpy (str, HELLO_STRING); printf ("-> \ t% s" , str); zadarmo (str);)
A vlastne aj výsledok programu:

$ gcc main.c $ ./a.out -> Ahoj Habr!
Zatiaľ nič neobvyklé. Teraz poďme spustiť tento program s valgrindom!

$ valgrind --tool = memcheck ./a.out == 3892 == Memcheck, detektor chýb pamäte == 3892 == Copyright (C) 2002-2015 a GNU GPL "d, Julian Seward a kol. == 3892 == Pomocou Valgrind-3.12.0 a LibVEX; znova spustite s -h pre informácie o autorských právach == 3892 == Príkaz: ./a.out == 3892 == == 3892 == Neplatný zápis veľkosti 2 == 3892 = = na 0x4005B4: main (v /home/indever/prg/C/public/a.out) == 3892 == Adresa 0x520004c je 12 bajtov vo vnútri bloku s veľkosťou 13 alloc "d == 3892 == na 0x4C2DB9D: malloc (vg_replace_malloc.c: 299) == 3892 == podľa 0x400597: main (v /home/indever/prg/C/public/a.out) == 3892 == == 3892 == Neplatné čítanie veľkosti 1 == 3892 == na 0x4C30BC4: strlen (vg_replace_strmem.c: 454) == 3892 == podľa 0x4E89AD0: vfprintf (v /usr/lib64/libc-2.24.so) == 3892 usr == rf (071x4E: print) lib64 / libc-2.24.so) == 3892 == podľa 0x4005CF: main (v /home/indever/prg/C/public/a.out) == 3892 == Adresa 0x520004d je 0 bajtov po bloku veľkosti 13 alloc "d == 3892 == na 0x4C2DB9D: malloc (vg_replace_malloc.c: 299) == 3892 == podľa 0x400597: hlavné (v / doma / indever / prg / C / public / a.out) == 3892 == -> Dobrý deň, Habr! == 3892 == == 3892 == SÚHRN HEAP: == 3892 == používa sa pri ukončení: 0 bajtov v 0 blokoch == 3892 == celkové využitie haldy: 2 alokácie, 2 uvoľnenia, 1 037 pridelených bajtov == 3892 = = == 3892 == Všetky bloky haldy boli uvoľnené – nie sú možné žiadne úniky == 3892 == == 3892 == Pre počty zistených a potlačených chýb spustite znova s: -v == 3892 == SÚHRN CHYB: 3 chyby z 2 kontexty (potlačené: 0 z 0)
== 3892 == Všetky bloky haldy boli uvoľnené - nie sú možné žiadne úniky- nedochádza k žiadnym únikom, a to je dobrá správa. Ale stojí za to sklopiť oči trochu nižšie (aj keď, chcem poznamenať, je to len zhrnutie, základné informácie sú trochu inde):

== 3892 == SÚHRN CHYB: 3 chyby z 2 kontextov (potlačené: 0 z 0)
3 chyby. V 2 kontextoch. V takom jednoduchom programe. Ako!?

Je to veľmi jednoduché. Celý „trik“ je v tom, že funkcia strlen neberie do úvahy znak konca riadku – „\ 0“. Aj keď to explicitne určíte vo vstupnom riadku (#define HELLO_STRING "Ahoj Habr! \ N \ 0"), bude sa ignorovať.

Mierne nad výsledkom vykonávania programu sú riadky -> Ahoj Habr! o tom, čo a kde sa nepáčilo nášmu vzácnemu valgrindovi, je podrobná správa. Navrhujem, aby ste sa na tieto riadky pozreli sami a vyvodili závery.

V skutočnosti bude správna verzia programu vyzerať takto:

#include #include #include #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); voľný (str);)
Poďme cez valgrind:

$ valgrind --tool = memcheck ./a.out -> Ahoj Habr! == 3435 == == 3435 == SÚHRN HEAP: == 3435 == používa sa pri ukončení: 0 bajtov v 0 blokoch == 3435 == celkové využitie haldy: 2 pridelenia, 2 uvoľnenia, 1 038 pridelených bajtov == 3435 = = == 3435 == Všetky bloky haldy sa uvoľnili – nie sú možné žiadne úniky == 3435 == == 3435 == Pre počty zistených a potlačených chýb spustite znova s: -v == 3435 == SÚHRN CHYB: 0 chýb z 0 kontextov (potlačené: 0 z 0)
Dobre. Žiadne chyby, +1 bajt pridelenej pamäte pomohol vyriešiť problém.

Zaujímavé je, že vo väčšine prípadov bude prvý aj druhý program fungovať rovnako, ale ak pamäť pridelená pre riadok, do ktorého sa nezmestil koncový znak, nebola prázdna, potom funkcia printf () pri výstupe napr. riadku, tiež vypíše všetky odpadky za týmto riadkom - všetko sa zobrazí, kým sa na ceste printf () neobjaví znak konca riadka.

Však viete, (strlen (str) + 1) je také riešenie. Stretávame sa s 2 problémami:

  1. A ak potrebujeme alokovať pamäť pre reťazec vytvorený napríklad pomocou s (n) printf (..)? Nepodporujeme argumenty.
  2. Vzhľad. Riadok deklarácie premennej vyzerá hrozne. Niektorým chlapom sa podarí naskrutkovať (char *) aj malloc, ako keby písali pod plusky. V programe, kde pravidelne potrebujete spracovávať reťazce, má zmysel nájsť elegantnejšie riešenie.
Poďme prísť s riešením, ktoré uspokojí nás aj valgrind.

snprintf ()

int snprintf (char * str, veľkosť_t veľkosť, const char * formát, ...);- funkcia - rozšírenie sprintf, ktoré naformátuje reťazec a zapíše ho do ukazovateľa odovzdaného ako prvý argument. Od sprintf () sa líši tým, že str nebude zapísaných viac bajtov ako je veľkosť.

Funkcia má jednu zaujímavú vlastnosť – aj tak vracia veľkosť vygenerovaného reťazca (okrem znaku konca riadku). Ak je reťazec prázdny, vráti sa 0.

Jeden z problémov, ktoré som opísal pri používaní strlen, súvisí s funkciami sprintf () a snprintf (). Predpokladajme, že musíme niečo napísať do str. Posledný reťazec obsahuje hodnoty ďalších premenných. Náš vstup by mal znieť takto:

Char * str = / * tu alokujte pamäť * /; sprintf (str, "Ahoj,% s \ n", "Habr!");
Vzniká otázka: ako určiť, koľko pamäte by sa malo prideliť pre reťazec str?

Char * str = malloc (sizeof (char) * (strlen (str, "Dobrý deň, % s \ n", "Habr!") + 1)); - nepôjde to. Prototyp funkcie strlen () vyzerá takto:

#include size_t strlen (const char * s);
const char * s neznamená, že reťazec odovzdaný do s môže byť formátovací reťazec s premenlivým počtom argumentov.

Tu nám pomôže užitočná vlastnosť funkcie snprintf (), ktorú som spomenul vyššie. Poďme sa pozrieť na kód pre nasledujúci program:

#include #include #include void main () (/ * Keďže snprintf () neberie do úvahy znak konca riadka, pridajte jeho veľkosť k výsledku * / size_t need_mem = snprintf (NULL, 0, "Dobrý deň, % s! \ n" , "Habr") + sizeof ("\ 0"); char * str = malloc (needed_mem); snprintf (str, need_mem, "Dobrý deň, % s! \ n", "Habr"); printf ("-> \ t% s", str); voľný (str);)
Spustite program vo valgrind:

$ valgrind --tool = memcheck ./a.out -> Ahoj Habr! == 4132 == == 4132 == SÚHRN HEAP: == 4132 == používa sa pri ukončení: 0 bajtov v 0 blokoch == 4132 == celkové využitie haldy: 2 alokácie, 2 uvoľnenia, 1 041 pridelených bajtov == 4132 = = == 4132 == Všetky bloky haldy boli uvoľnené – nie sú možné žiadne úniky == 4132 == == 4132 == Pre počty zistených a potlačených chýb spustite znova s: -v == 4132 == SÚHRN CHYB: 0 chýb z 0 kontextov (potlačený: 0 z 0) $
Dobre. Máme podporu pre argumenty. Vzhľadom na to, že funkcii snprintf () odovzdávame nulu ako druhý argument, zápis pomocou nulového ukazovateľa nikdy nepovedie k Seagfault. Napriek tomu však funkcia stále vráti požadovanú veľkosť reťazca.

Ale na druhej strane sme museli pridať ďalšiu premennú, a to konštrukciu

Size_t need_mem = snprintf (NULL, 0, "Dobrý deň, % s! \ N", "Habr") + sizeof ("\ 0");
vyzerá ešte horšie ako strlen ().

Vo všeobecnosti možno + sizeof ("\ 0") odstrániť explicitným zadaním "\ 0" na konci formátovacieho reťazca (size_t need_mem = snprintf (NULL, 0, "Dobrý deň, % s! \ N \0 "," Habr ");), ale to nie je v žiadnom prípade vždy možné (v závislosti od mechanizmu spracovania riadkov môžeme prideliť bajt navyše).

Je potrebné niečo urobiť. Trochu som sa zamyslel a rozhodol som sa, že teraz nadišla hodina odvolať sa na múdrosť staroveku. Poďme si popísať makro funkciu, ktorá bude volať snprintf () s nulovým ukazovateľom ako prvým argumentom a null ako druhým. A nezabudnime na koniec radu!

#define strsize (args ...) snprintf (NULL, 0, args) + sizeof ("\ 0")
Áno, pre niekoho to môže byť novinka, ale makrá v C podporujú premenlivý počet argumentov a elipsa hovorí preprocesoru, že zadaný argument funkcie makra (v našom prípade je to args) zodpovedá niekoľkým skutočným argumentom.

Pozrime sa na naše riešenie v praxi:

#include #include #include #define strsize (args ...) snprintf (NULL, 0, args) + sizeof ("\ 0") void main () (char * str = malloc (strsize ("Ahoj,% s \ n", "Habr! ")); sprintf (str," Ahoj,% s \ n "," Habr! "); printf (" -> \ t% s ", str); zadarmo (str);)
Beh s valgrundom:

$ valgrind --tool = memcheck ./a.out -> Ahoj Habr! == 6432 == == 6432 == SÚHRN HEAP: == 6432 == používa sa pri ukončení: 0 bajtov v 0 blokoch == 6432 == celkové využitie haldy: 2 alokácie, 2 uvoľnenia, 1 041 pridelených bajtov == 6432 = = == 6432 == Všetky bloky haldy boli uvoľnené – nie sú možné žiadne úniky == 6432 == == 6432 == Pre počty zistených a potlačených chýb spustite znova s: -v == 6432 == SÚHRN CHYB: 0 chýb z 0 kontextov (potlačené: 0 z 0)
Áno, nie sú tam žiadne chyby. Všetko je správne. A valgrind je šťastný a programátor môže ísť konečne spať.

Nakoniec však poviem niečo iné. V prípade, že potrebujeme alokovať pamäť pre akýkoľvek reťazec (aj s argumentmi), už existuje plne funkčné riešenie na kľúč.

Ide o funkciu asprintf:

#define _GNU_SOURCE / * Pozri feature_test_macros (7) * / #include int asprintf (char ** strp, const char * fmt, ...);
Ako prvý argument vezme ukazovateľ na reťazec (** strp) a pridelí pamäť dereferencovanému ukazovateľu.

Náš program asprintf () by vyzeral takto:

#include #include #include void main () (char * str; asprintf (& str, "Dobrý deň, % s! \ n", "Habr"); printf ("-> \ t% s", str); voľné (str);)
A v skutočnosti vo valgrind:

$ valgrind --tool = memcheck ./a.out -> Ahoj Habr! == 6674 == == 6674 == SÚHRN HEAP: == 6674 == používa sa pri ukončení: 0 bajtov v 0 blokoch == 6674 == celkové využitie haldy: 3 pridelenia, 3 uvoľnenia, 1 138 pridelených bajtov == 6674 = = == 6674 == Všetky bloky haldy boli uvoľnené – nie sú možné žiadne úniky == 6674 == == 6674 == Pre počty zistených a potlačených chýb spustite znova s: -v == 6674 == SÚHRN CHYB: 0 chýb z 0 kontextov (potlačené: 0 z 0)
Všetko je v poriadku, ale ako vidíte, bolo pridelených viac pamäte a teraz existujú tri alokácie, nie dve. Na slabých vstavaných systémoch je táto funkcia nežiaduca.
Okrem toho, ak do konzoly napíšeme man asprintf, uvidíme:

VYHOVUJÚCE Tieto funkcie sú rozšíreniami GNU, nie v C alebo POSIX. Sú tiež dostupné pod * BSD. Implementácia FreeBSD nastaví strp na NULL pri chybe.

Z toho je jasné, že táto funkcia je dostupná len v GNU zdroji.

Záver

Na záver chcem povedať, že práca s reťazcami v C je veľmi zložitá téma, ktorá má množstvo nuancií. Napríklad na napísanie "bezpečného" kódu pre dynamickú alokáciu pamäte sa stále odporúča použiť funkciu calloc () namiesto malloc () - calloc upcháva pridelenú pamäť nulami. Alebo po pridelení pamäte použite funkciu memset (). V opačnom prípade môže odpad, ktorý pôvodne ležal v oblasti pridelenej pamäte, spôsobiť otázky počas ladenia a niekedy aj pri práci s reťazcom.

Viac ako polovica mojich kolegov C programátorov (väčšina z nich sú začiatočníci), ktorí na moju žiadosť riešili problém s alokáciou pamäte pre reťazce, to robili tak, že to v konečnom dôsledku viedlo ku kontextovým chybám. V jednom prípade - dokonca k úniku pamäte (no, človek zabudol urobiť zadarmo (str), komu sa to nestane). V skutočnosti ma to podnietilo vytvoriť tento výtvor, ktorý ste práve čítali.

Dúfam, že pre niekoho bude tento článok užitočný. Prečo som to všetko zjednával - žiadny jazyk nie je jednoduchý. Všade má svoje jemnosti. A čím viac jemností jazyka poznáte, tým lepší je váš kód.

Verím, že po prečítaní tohto článku bude váš kód o niečo lepší :)
Veľa šťastia, Habr!

Programátor hovorí:

Ahoj! Prečítal som si tvoj článok. Bol som veľmi smutný a vtipný zároveň. Táto vaša fráza je obzvlášť zabíjajúca: "Keďže premenná typu char sa často používa ako pole, určuje sa počet možných hodnôt." 😆 😆 😆
ja sa ti nesmejem. Vytvorenie webovej stránky je skutočne výkon. Chcem vás len podporiť radou a upozorniť na pár chýb.

1. Hodnota premennej typu char je priradená nasledovne:

Tu:

Char a = * "A";

Ukazovateľ na pole je neadresný a v dôsledku toho sa vráti hodnota prvého prvku poľa, t.j. „A“

2. Nulovanie sa vykonáva takto:

Char a = NULL;
char b = ();

// A takto sa vymaže riadok v tele programu

"" - tento znak sa nazýva nulový terminátor. Umiestňuje sa na koniec riadku. Vy sami ste bez toho, aby ste o tom vedeli, vyplnili pole s1 z vášho článku týmto symbolom. Tento symbol však bolo možné priradiť iba nulovému prvku poľa.

3. Pokojne použite terminológiu.
Znak = je operácia priradenia.
Znak * je operácia popisovania.
Myslím tento fragment článku: "Všetko sa ukázalo byť také jednoduché, pred znak = ste museli dať znak * a museli ste deklarovať číslo prvku (nula zodpovedá prvému)"

Nechápte ma zle, článok tak, ako je, nemôže existovať. Nebuďte leniví, prepíšte to.
Máte veľkú zodpovednosť! Myslím to vážne. Stránky vášho webu boli zahrnuté na prvej stránke výsledkov vyhľadávania Yandex. Veľa ľudí už po vás začalo opakovať chyby.

Veľa štastia! Vy to zvládnete!

:
Viem to už dávno, len je ťažké stále dokola čítať 200 článkov, aby som niečo opravoval. A niektoré drzé typy píšu tak, že aj keď vedia, čo je lepšie opraviť, už vôbec nie je chuť to opraviť.

Rád opravím aj ostatné chyby. opravte nepresnosti, ak sa objavia. Cením si tvoju pomoc. Dakujem To viem uz davno, len je tazke stale precitat 200 clankov aby sa nieco opravilo. A niektoré drzé typy píšu tak, že aj keď vedia, čo je lepšie opraviť, už vôbec nie je chuť to opraviť.
S vaším znakom b = (); Toto vôbec nie je nulovanie. by skontroloval aspoň b.
ak hovoríme o nulovom znaku ""; Veľmi dobre som vedela, kedy som nimi linku vyplnila a účelom bolo ukázať skutočnú očistu, a nie okom viditeľnú, pretože linka obsahuje odpadky, ktoré občas zavadzia. Vy sami by ste boli opatrnejší s pojmami, „znak s nulovou koncovkou“ alebo jednoducho „postava s nulovou hodnotou“, nie terminátor))) A znak terminátora znie jednoducho cool.

Článok modernizujem, ale nebudem prechádzať do cudzieho štýlu. Ak si myslím, že pre začiatočníka je to prehľadnejšie takto, a nie ako chce, tak to nechám tak. Nechápte ma tiež zle. Slovo „znamenie“ je pre slabého začiatočníka oveľa ľahšie pochopiteľné a zapamätateľné ako definícia a názov každého znaku. V tomto nie je absolútne žiadna chyba, znak je - znak. Menší dôraz na jednu vec dáva väčší dôraz na druhú.

Rád opravím aj ostatné chyby. opravte nepresnosti, ak sa objavia. Cením si tvoju pomoc. Vďaka.

Ahoj zas!
Chcem si to ujasniť. Termín „nultý terminátor“ (terminátor) používal môj učiteľ na univerzite. Toto je zrejme stará škola!
Čo sa týka nulovacích riadkov.
char b = (); Toto je naozaj nulovanie. Celé pole je vyplnené nulami. Neverte tomu - skontrolujte to!
Ak vezmeme do úvahy reťazec v jeho prirodzenom, každodennom zmysle, potom „prázdny“ bude reťazec, v ktorom nie je jediný znak. Preto v 99,9 % prípadov stačí predpísať nulový znak. Spracovanie reťazca zvyčajne pokračuje až po prvý znak null a už nie je dôležité, ktoré znaky nasledujú. Chápem, že ste chceli vynulovať reťazec. Práve som sa rozhodol ponúknuť časom overenú klasickú verziu.

:
Keď „Spracovanie reťazca zvyčajne siaha až po prvý znak nuly a to, aké znaky nasledujú, už nie je dôležité“ – áno, reťazec sa vynuluje
Ak vezmeme do úvahy "skutočné nulovanie všetkých buniek v reťazci (o ktorom som písal)" - nie, nie nulovanie a dokonca ani prvý znak nie je nula. Zaškrtol som túto možnosť. MinGW (CodeBlock) - celé pole dáva znak "a"
Nemyslím si, že je to dôvod na polemiku.

V programe môžu byť reťazce definované takto:

  • ako reťazcové konštanty;
  • ako polia znakov;
  • cez ukazovateľ na typ znaku;
  • ako polia reťazcov.

Okrem toho musí existovať alokácia pamäte na uloženie reťazca.

Akákoľvek postupnosť znakov uzavretá v úvodzovkách "" sa považuje za reťazcová konštanta.

Pre správny výstup musí každý riadok končiť nulovým znakom "\ 0", ktorého celočíselná hodnota je 0. Pri deklarovaní reťazcovej konštanty sa k nej automaticky pridá nulový znak. Takže postupnosť znakov, ktorá je reťazcovou konštantou, bude umiestnená v pamäti RAM počítača vrátane nulového bajtu.

Na ukladanie reťazca sú vyčlenené sekvenčné pamäťové bunky. Reťazec je teda pole znakov. Na uloženie kódu každého znaku v reťazci je pridelený 1 bajt.

Na umiestnenie niektorých servisných znakov do reťazcovej konštanty sa používajú symbolické kombinácie. Ak teda chcete do reťazca vložiť znak dvojitej úvodzovky, musí mu predchádzať znak spätnej lomky: ‚\‘‘.

Konštanty reťazca sú alokované v statickej pamäti. Počiatočná adresa postupnosti znakov v úvodzovkách sa považuje za adresu reťazca. Konštanty reťazca sa často používajú na komunikáciu s používateľom vo funkciách, ako je printf ().

Pri určovaní pole znakov je potrebné oznámiť kompilátoru požadovanú veľkosť pamäte.

čaro;

Kompilátor môže tiež nezávisle určiť veľkosť poľa znakov, ak je inicializácia poľa špecifikovaná pri jeho deklarovaní ako reťazcová konštanta:

char m2 =;
char m3 = ( "T", "and", "x", "and", "e", "", "d", "o", "l", "and", "n", "s", "", "p", "o", "l", "n", "s", "", "s", "v", "e", "g", "e", "y", "", "m", "g", "l", "o", "y", "\ 0"};

V tomto prípade sú názvy m2 a m3 ukazovatele na prvé prvky polí:

  • m2 sa rovná & m2
  • m2 sa rovná 'G'
  • m2 sa rovná 'o'
  • m3 sa rovná & m3
  • m3 je ekvivalentné 'x'

Pri deklarovaní poľa znakov a jeho inicializácii pomocou reťazcovej konštanty môžete explicitne určiť veľkosť poľa, ale špecifikovaná veľkosť poľa musí byť väčšia ako veľkosť inicializačnej reťazcovej konštanty:

char m2 = "Horské štíty spia v tme noci.";

Ak chcete zadať reťazec, môžete použiť ukazovateľ na typ postavy.

char * m4;

V tomto prípade môže byť deklarácii poľa k premennej m4 priradená adresa poľa:

m4 = m3;
* m4 je ekvivalentné m3 = "T"
* (m4 + 1) je ekvivalentné m3 = "a"

Tu je m3 konštanta ukazovateľa. Nemôžete zmeniť m3, pretože by to znamenalo zmenu pozície (adresy) poľa v pamäti, na rozdiel od m4.

Pre ukazovateľ môžete použiť operáciu zvýšenia (presun na ďalší znak):

Pole reťazcov znakov

Niekedy je v programoch potrebný popis pole reťazcov znakov... V tomto prípade môžete použiť index riadkov na prístup k viacerým rôznym riadkom.

char * básnik = ( "Básnik je mŕtvy!", "- otrok cti -",
"Kamarát," "ohováraný fámou ..."};

V tomto prípade je poet pole štyroch ukazovateľov na reťazce znakov. Každý reťazec znakov je pole znakov, takže existujú štyri ukazovatele na polia. Ukazovateľ básnika odkazuje na prvý riadok:
*básnik ekvivalentné k "NS",
* básnik [l] ekvivalentné k "-" .

Inicializácia sa vykonáva podľa pravidiel definovaných pre polia.
Citované texty sú ekvivalentné inicializácii každého reťazca v poli. Čiarka oddeľuje susedné
sekvencie.
Okrem toho môžete explicitne nastaviť veľkosť reťazcov znakov pomocou popisu, ako je tento:

char básnik;

Rozdiel je v tom, že tento tvar definuje „obdĺžnikové“ pole, v ktorom majú všetky čiary rovnakú dĺžku.

Voľné pole

Popis

сhar * básnik;


definuje voľné pole, kde dĺžka každého reťazca je určená ukazovateľom, ktorý tento reťazec inicializuje. Voľné pole neplytvá pamäťou.

Reťazcové operácie

Väčšina operácií s reťazcami v jazyku C funguje na ukazovateľoch. Ak chcete umiestniť reťazec znakov do pamäte RAM, musíte:

  • alokovať blok pamäte RAM pre pole;
  • inicializujte reťazec.

Na pridelenie pamäte na uloženie reťazca možno použiť funkcie dynamického prideľovania pamäte. V tomto prípade je potrebné vziať do úvahy požadovanú veľkosť riadku:

char * meno;
meno = (char *) malloc (10);
scanf ("% 9s", názov);

Na zadanie reťazca bola použitá funkcia scanf () a vstupný reťazec nemôže presiahnuť 9 znakov. Posledný znak bude obsahovať "\ 0".

Funkcie vstupu reťazca

Na zadanie reťazca možno použiť funkciu scanf (). Funkcia scanf () je však určená na získanie slova a nie reťazca. Ak na zadanie použijete formát „% s“, reťazec sa zadá pred nasledujúcim prázdnym znakom (nie však vrátane), ktorým môže byť medzera, tabulátor alebo posun riadkov.

Ak chcete zadať reťazec vrátane medzier, použite funkciu

char * dostane (char *);


alebo jej ekvivalent

char * get_s (char *);

Ukazovateľ na vstupný reťazec sa odovzdá funkcii ako argument. Funkcia žiada používateľa, aby zadal reťazec, ktorý vloží do poľa, kým používateľ neklikne Zadajte.

Funkcie výstupu reťazca

Na zobrazenie riadkov môžete použiť vyššie uvedenú funkciu

printf ("% s", str); // str je ukazovateľ na reťazec

alebo v skrátenom formáte

printf (str);

Funkciu je možné použiť aj na zobrazenie čiar

int vloží (char * s);

ktorý vypíše reťazec s a presunie kurzor na nový riadok (na rozdiel od printf ()). Funkciu puts () je možné použiť aj na výstup reťazcových konštánt v úvodzovkách.

Funkcia zadávania znakov

Na zadávanie znakov môžete použiť funkciu

char getchar ();


ktorý vráti hodnotu znaku zadaného z klávesnice. Špecifikovaná funkcia bola použitá v príkladoch diskutovaných vyššie na oneskorenie okna konzoly po spustení programu, kým sa nestlačí kláves.

Funkcia výstupu znakov

Na zobrazenie znakov možno použiť funkciu

char putchar (char);


ktorý vráti hodnotu znaku na zobrazenie a vypíše znak odovzdaný ako argument na obrazovku.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

#include
#include
#include
int main () (
char s, sym;
int počet, i;
systém ("chcp 1251");
systém ("cls");
printf ( "Zadajte reťazec: ");
dostane_s (s);
printf ( "Zadajte znak:");
sym = getchar ();
počet = 0;
pre (i = 0; s [i]! = "\ 0"; i ++)
{
if (s [i] == sym)
počítať ++;
}
printf ("On line \ n");
kladie (s); // Linkový výstup
printf ("znak");
putchar (sym); // Zobrazenie symbolu
printf ( "vyskytuje sa % d-krát", počítať);
getchar (); getchar ();
návrat 0;
}

Výsledok popravy

Základné funkcie štandardnej knižnice string.h

Hlavné funkcie štandardnej knižnice string.h sú uvedené v tabuľke.

Funkcia Popis

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

spojí s2 s s1, vráti s1

char * strncat (char * s1, char * s2, int n)

pripojí najviac n znakov s2 k s1, reťazec ukončí "\ 0", vráti s1

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

skopíruje reťazec s2 do reťazca s1 vrátane "\ 0", vráti s1
);
strncpy (m3, m1, 6); // nepridáva "\ 0" na koniec riadku
kladie ( "Výsledok strncpy (m3, m1, 6)");
vloží (m3);
strcpy (m3, m1);
kladie ( "Výsledok strcpy (m3, m1)");
vloží (m3);
kladie ( "Výsledok strcmp (m3, m1) je");
printf ("% d", strcmp (m3, m1));
strncat (m3, m2, 5);
kladie ( "Strncat výsledku (m3, m2, 5)");
vloží (m3);
strcat (m3, m2);
kladie ( "Strcat výsledku (m3, m2)");
vloží (m3);
kladie ( "Počet znakov v reťazci m1 sa rovná strlen (m1):");
printf ("% d \ n", strlen (m1));
_strnset (m3, "f", 7);
kladie ( "Strnset výsledku (m3," f ", 7)");
vloží (m3);
_strset (m3, "k");
kladie ( "Strnset výsledku (m3," k ")");
vloží (m3);
getchar ();
návrat 0;
}

Výsledok popravy

Knižnica funkcií C a C++ obsahuje bohatú sadu funkcií na spracovanie reťazcov a znakov. Funkcie reťazca fungujú na poliach znakov ukončených nulou. V jazyku C, ak chcete použiť reťazcové funkcie, musíte na začiatok programového modulu zahrnúť hlavičkový súbor a pre symbolický - hlavičkový súbor ... C ++ používa hlavičky na prácu s funkciami reťazcov a znakov. a resp. V tejto kapitole sa kvôli jednoduchosti používajú názvy hlavičiek C.

Keďže v jazykoch C a C++ pri vykonávaní operácií s poliami nedochádza k automatickej kontrole porušenia ich hraníc, všetka zodpovednosť za pretečenie polí padá na plecia programátora. Zanedbanie týchto jemností môže viesť k zlyhaniu programu.

V C a C++ sú tlačiteľné znaky znaky zobrazené na termináli. V prostrediach ASCII sa nachádzajú medzi medzerou (0x20) a vlnovkou (OxFE). Kontrolné znaky majú hodnoty medzi nulou a Ox1F; tieto zahŕňajú aj symbol DEL (Ox7F).

Historicky sú argumenty symbolických funkcií celočíselné hodnoty, z ktorých sa používa iba najmenej významný bajt. Symbolické funkcie automaticky konvertujú svoje argumenty na znak bez znamienka. Samozrejme, môžete tieto funkcie volať so symbolickými argumentmi, pretože symboly sa pri volaní funkcie automaticky zvýšia na úroveň celých čísel.

V názve Je definovaný typ size_t, ktorý je výsledkom operátora sizeof a je akýmsi celým číslom bez znamienka.

C99 pridal kvalifikátor obmedzenia k niektorým parametrom niekoľkých funkcií pôvodne definovaných v C89. Každá takáto funkcia bude diskutovaná s jej prototypom používaným v prostredí C89 (ako aj v prostredí C++) a parametre s atribútom obmedzenia budú uvedené v popise tejto funkcie.

Zoznam funkcií

Kontrola príslušnosti

isalnum – skontrolujte, či je znak alfanumerický
isalpha - Kontrola, či znak patrí k písmenám
isblank – Skontrolujte, či nie je prázdny znak
iscntrl – Skontrolujte, či symbol patrí do ovládacieho prvku
isdigit – skontrolujte, či je znak digitálny
isgraph – Skontrolujte, či je znak vytlačiteľný, ale nie medzera
islower – skontrolujte, či je znak malý
isprint – Skontrolujte, či je znak vytlačiteľný
ispunct – Skontrolujte, či znak patrí medzi interpunkčné znamienka
isspace – Skontrolujte, či je znak prázdny
isupper – Skontrolujte, či je znak veľký
isxdigit – Skontrolujte, či je znak hexadecimálny

Práca s poliami znakov

memchr - Slučka cez pole, aby ste našli prvý výskyt znaku
memcmp - Porovnáva zadaný počet znakov v dvoch poliach
memcpy - Skopíruje znaky z jedného poľa do druhého
memmove – Skopíruje znaky z jedného poľa do druhého, pričom berie do úvahy prekrývajúce sa polia
memset - Vyplní zadaný počet znakov v poli daným

Manipulácia s reťazcom

strcat - Pripojiť kópiu jedného reťazca k danému
strchr - Vráti ukazovateľ na prvý výskyt najmenej významného bajtu daného parametra
strcmp - lexikograficky porovnáva dva reťazce
strcoll - Porovnáva jeden reťazec s druhým podľa setlocale
strcpy - Skopíruje obsah jedného reťazca do druhého
strcspn – Vráti reťazec bez zadaných znakov
strerror - Vráti ukazovateľ na reťazec obsahujúci systémové chybové hlásenie
strlen - Vráti dĺžku reťazca ukončeného nulou

Reťazce v C++

Reťazec je postupnosť (pole) znakov. Ak sa vo výraze vyskytuje jeden znak, musí byť uzavretý jednoduché úvodzovky... Pri použití vo výrazoch je reťazec uzavretý v dvojité úvodzovky. Na konci riadku je nulový znak \0 ... V C ++ môžu byť reťazce opísané pomocou symbolov (pole prvkov typu char), ktorý by mal poskytnúť miesto na uloženie ukončovača vedenia.

Napríklad 25-miestny popis reťazca by mal vyzerať takto:

Môžete tiež opísať pole reťazcov:

Je definované pole 3 riadkov po 25 bajtoch.

Na prácu s ukazovateľmi môžete použiť ( znak *). Adresa prvého znaku bude počiatočnou hodnotou ukazovateľa.

Pozrime sa na príklad deklarovania a zobrazenia reťazcov.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

#include "stdafx.h"
#include
pomocou menného priestoru std;
int main ()
{
setlocale (LC_ALL, "Rus");
// opíšeme 3 riadky, s3 je ukazovateľ
char s2 [20], * s3, s4 [30];
cout<< «s2=» ; cin >> s2; // vstupný reťazec s2
cout<< «s2=» << s2<< endl;
// zapíšeme do s3 adresu reťazca, kde je uložený s4. Teraz v premenných
// (ukazovatele) s3 a s4 ukladajú hodnotu rovnakej adresy
s3 = s4;
cout<< «s3=» ; cin >> s3; // vstupný reťazec s3
// zobrazenie riadkov s3 a s4, hoci v dôsledku priradenia s3 = s4;
// teraz sú s3 a s4 rovnaké
cout<< «s3=» << s3<< endl;
cout<< «s4=» << s4<< endl;
systém ("pauza");
návrat 0;
}

Výsledok programu:

Treba však poznamenať, že ak používateľ zadá slová oddelené medzerou do jednej premennej, program bude fungovať inak:

Ide o to, že funkcia cin vstúpi do riadkov pred nájdeným priestorom. Všestrannejšia funkcia je getline.

cin.getline (char * s, int n);

Navrhnuté pre vstupné reťazce klávesnice s s medzerami by riadok nemal obsahovať viac ako n postavy. Preto pre správne zadanie riadkov obsahujúcich medzeru je potrebné v našom programe nahradiť cin >> s na cin.getline (s, 80).

Reťazcové operácie

S reťazcom možno zaobchádzať ako s poľom znakov pomocou algoritmov na spracovanie poľa alebo špeciálnych funkcií na spracovanie reťazcov, z ktorých niektoré sú uvedené nižšie. Ak chcete pracovať s týmito riadkami, musíte pripojiť knižnicu cstring.

Ak chcete previesť číslo na reťazec, môžete použiť funkciu sprintf z knižnice stdio.h.

Niektoré funkcie pre prácu s reťazcami:

Funkčný prototyp Popis funkcie
size_t strlen (const char * s) vypočíta dĺžku reťazca s v bajtoch.
char * strcat (char * dest, const char * scr) pripojí reťazec src na koniec reťazca dest, výsledný výraz sa vráti ako výsledok
char * strcpy (char * dest, const char * scr) skopíruje reťazec sc do miesta v pamäti, na ktoré ukazuje cieľ
char strncat (char * dest, const char * dest, size_t maxlen) pripojí reťazec znakov maxlen z src na koniec dest
char * strncpy (char * dest, const char * scr, size_t maxlen) skopíruje maxlen znaky src do miesta v pamäti, na ktoré ukazuje cieľ
int ctrcmp (const char * s1, const char * s2) porovná dva reťazce v lexikografickom poradí, pričom zohľadní rozdiel medzi veľkými a malými písmenami, funkcia vráti 0, ak sú reťazce rovnaké, vráti - 1, ak je s1 v abecednom poradí pred s2, a inak 1.
int strncmp (const char * s1, const char * s2, size_t maxlen) porovná maximálne znaky dvoch reťazcov v lexikografickom poradí, funkcia vráti 0, ak sa reťazce zhodujú, vráti - 1, ak je s1 v abecednom poradí pred s2, a inak 1.
dvojitý atof (konšt. znak * s) prevedie reťazec na reálne číslo, v prípade neúspešného prevodu sa vráti číslo 0
dlhý atol (konšt. znak * s) skonvertuje reťazec na dlhé celé číslo, ak konverzia zlyhá, vráti sa 0
char * strchr (const char * s, int c); vráti ukazovateľ na prvý výskyt znaku c na reťazec, na ktorý ukazuje s... Ak je symbol c nenájdené, vráti hodnotu NULL
char * strupr (char * s) skonvertuje znaky reťazca, na ktorý ukazuje s, na veľké písmená a potom ho vráti

Typ údajov reťazca

Okrem práce s reťazcami ako s poľom znakov existuje v C ++ špeciálny dátový typ reťazec... Na zadanie premenných tohto typu môžete použiť cin alebo špeciálna funkcia getline.

getline (cin, s);

Tu s- názov premennej typu vstupu reťazec.

Pri popise premennej tohto typu môžete okamžite priradiť hodnotu tejto premennej.

var (s);

Tu var- názov premennej, s- strunová konštanta. Výsledkom tohto operátora je vytvorenie premennej var typu reťazec a zapíše sa do nej hodnota reťazcovej konštanty s... Napríklad,

reťazec v ("Ahoj");

Vytvorí sa reťazec v, do ktorého sa zapisuje hodnota Ahoj.

Prístup k i-tý riadkový prvok s typu reťazec vykonávané štandardným spôsobom s [i]... Nad riadkami ako reťazec sú definované nasledujúce operácie:

  • priradenia, napríklad s1 = s2;
  • zreťazenie reťazca (s1 + = s2 alebo s1 = s1 + s2) - pridá reťazec s2 k reťazcu s1, výsledok sa uloží do reťazca s1, príklad zreťazenia reťazca:
  • porovnania reťazcov na základe lexikografického poradia: s1 = s2, s1! = s2, s1 s2, s1<=s2, s1>= s2 - výsledkom bude boolovská hodnota;

Pri spracovaní reťazcov ako reťazec môžete použiť nasledujúce funkcie:

  • s.substr (pos, dĺžka)- vráti podreťazec z reťazca s počnúc od čísla poz dlhý dĺžka postavy;
  • s.prázdny ()- vráti true, ak reťazec s prázdny, inak falošný;
  • s.insert (pos, s1)- vloží riadok s1 v rade s počnúc od pozície poz;
  • s.remove (poz., dĺžka)- odstraňuje zo šnúrky s podreťazec dĺžka dlhý poz postavy;
  • s.find (s1, poz.)- vráti číslo prvého výskytu reťazca s1 v rade s, vyhľadávanie začína číslom poz, parameter poz môže chýbať, v tomto prípade vyhľadávanie začína od začiatku riadku;
  • s.findfirst (s1, poz.)- vráti číslo prvého výskytu ľubovoľného znaku z reťazca s1 v rade s, vyhľadávanie začína číslom poz ktorý môže chýbať.

Ruština pre struny

Myslím, že ste si už všimli, že pri zobrazovaní ruských písmen sa v konzole objavujú znaky "vľavo". Aby ste predišli tomuto nedorozumeniu, musíte použiť funkciu tretej strany CharToOemA... Pripojíme knižnicu okná.h, je potrebný, aby naša funkcia mohla konvertovať reťazce do iného kódovania. Tiež potrebujeme ďalšie pole znakov. Zdrojový kód programu bude vyzerať takto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

#include "stdafx.h"
#include
#include
pomocou menného priestoru std;
int main ()
(setlocale (LC_ALL, "Rus");
char s [255] = ( "Musím sa premeniť"} ;
char * pre = nový znak [255];
CharToOemA (s, pre); // transformovať
cout<< s;
vymazať pred;
systém ("pauza >> void");
návrat 0;
}

Práve opísaná metóda nie je dostatočne pohodlná. Na „ruský“ problém však existuje jednoduchšie riešenie. Ako vidíte, program používa funkciu setlocale (), namiesto toho je pohodlnejšie napísať do hlavnej funkcie nasledujúcu konštrukciu.