C standardne funkcije za delo z nizi. Funkcije za obdelavo nizov v C. Operacije nizov

Črte. Vnos/izhod nizov. Formatiran V/I. Obdelava nizov z uporabo standardnih funkcij jezika C Delo s pomnilnikom.

1.1. Deklaracija in inicializacija nizov.

Niz je niz znakov, ki se konča s praznim znakom '\0'. Niz je deklariran kot navaden niz znakov, na primer,

char s1; // niz, dolg devet znakov

char *s2; // kazalec na niz

Razlika med kazalcema s1 in s2 je v tem, da je kazalec s1 imenovana konstanta, kazalec s2 pa spremenljivka.

Konstante nizov so v dvojnih narekovajih, za razliko od znakov, ki so v enojnih narekovajih. na primer

"To je niz."

Dolžina konstante niza po standardu ne sme presegati 509 znakov. Vendar številne izvedbe dovoljujejo daljše dolžine nizov.

Ko inicializirate nize, je bolje, da ne podate velikosti niza; prevajalnik bo to naredil tako, da bo izračunal dolžino niza in mu dodal eno. na primer

char s1 = “To je niz.”;

V programskem jeziku C obstaja veliko število funkcij za delo z nizi, katerih prototipi so opisani v datotekah glave stdlib.h in string.h. O delu s temi funkcijami bomo razpravljali v naslednjih odstavkih.

1.2. Vnos/izhod nizov.

Za vnos niza iz konzole uporabite funkcijo

char* dobi(char *str);

ki zapiše niz na naslov str in vrne naslov vnesenega niza. Funkcija ustavi vnos, če naleti na znak '\n' ali EOF (konec datoteke). Znak za novo vrstico se ne kopira. Na koncu prebrane vrstice se postavi ničelni bajt. Če je uspešna, funkcija vrne kazalec na prebrano vrstico, če je neuspešna, vrne NULL.

Če želite izpisati niz v konzolo, uporabite standardno funkcijo

int postavi (const char *s);

ki, če je uspešen, vrne nenegativno število, in če je neuspešen, vrne EOF.

Prototipa funkcij gets in puts sta opisana v datoteki glave stdio.h.

#vključi

printf("Vhodni niz: ");

1.3. Formatiran V/I.

Za formatiran vnos podatkov iz konzole uporabite funkcijo

int scanf (const char *format, ...);

ki v primeru uspeha vrne število prebranih enot podatkov, v primeru neuspeha pa vrne EOF. Parameter formata mora kazati na niz, ki ga želite formatirati, ki vsebuje specifikacije vhodnega formata. Število in tipi argumentov, ki sledijo formatnemu nizu, se morajo ujemati s številom in vrstami vhodnih formatov, podanih v formatnem nizu. Če ta pogoj ni izpolnjen, potem je rezultat funkcije nepredvidljiv.

Presledek, znak "\t" ali "\n" v formatnem nizu opisuje enega ali več praznih znakov v vhodnem toku, ki vključuje znake: presledek, '\t', '\n', '\v', '\f'. Funkcija scanf preskoči prazne znake v vhodnem toku.

Dobesedni znaki v formatnem nizu, z izjemo znaka %, zahtevajo, da se popolnoma enaki znaki pojavijo v vhodnem toku. Če tega znaka ni, funkcija scanf preneha vnašati. Funkcija scanf preskoči dobesedne znake.

Na splošno je specifikacija vhodne oblike videti takole:

Vrsta %[*] [širina] [modifikatorji].

Simbol '*' označuje izpust pri vnosu polja, določenega s to specifikacijo;

- ‘width’ določa največje število znakov, vnesenih v skladu s to specifikacijo;

Vrsta lahko sprejme naslednje vrednosti:

c – niz znakov,

s – niz znakov, vrstice so ločene s praznimi znaki,

d – predznačeno celo število 10 s/s,

i je predznačeno celo število, številski sistem je odvisen od prvih dveh števk,

u – celo število brez predznaka pri 10 s/s,

o – celo število brez predznaka v 8 s/s,

x, X – celo število brez predznaka pri 16 s/s,

e, E, f, g, G – plavajoče število,

p – kazalec na kazalec,

n – kazalec na celo število,

[…] – niz skeniranih znakov, na primer .

V slednjem primeru bodo iz vhodnega toka vneseni samo znaki v oglatih oklepajih. Če je prvi znak v oglatem oklepaju '^', so vneseni samo tisti znaki, ki niso v matriki. Obseg znakov v matriki je določen s simbolom '-'. Ko vnesete znake, se vnesejo tudi začetni prazni znaki in zadnji ničelni bajt niza.

Modifikatorji lahko sprejmejo naslednje vrednosti:

h – kratko celo število,

l, L – dolgo celo število ali lebdeče,

in se uporabljajo samo za cela ali plavajoča števila.

Naslednji primer prikazuje uporabo funkcije scanf. Upoštevajte, da je pred določiteljem formata, ki se začne z vnosom plavajočega števila, presledek.

#vključi

printf("Vnesite celo število: ");

scanf("%d", &n);

printf("Vnesite dvojno: ");

scanf(" %lf", &d);

printf("Vnesite znak: ");

scanf(" %c", &c);

printf("Vnesite niz: ");

scanf(" %s", &s);

Upoštevajte, da je v tem programu število s plavajočo vejico inicializirano. To se naredi tako, da prevajalnik vključuje knjižnico za podporo dela s plavajočimi številkami. Če tega ne storite, bo med izvajanjem pri vnosu plavajočega števila prišlo do napake.

Za formatiran izpis podatkov v konzolo uporabite funkcijo

int printf (const char *format, ...);

ki, če je uspešen, vrne število izhodnih enot podatkov, in če je neuspešen, vrne EOF. Parameter formata je niz formata, ki vsebuje specifikacije za izhodne formate. Število in tipi argumentov, ki sledijo formatnemu nizu, se morajo ujemati s številom in vrstami specifikacij izhodnega formata, podanih v formatnem nizu. Na splošno je specifikacija izhodne oblike videti takole:

%[flags] [width] [.precision] [modifiers] type

- ‘zastavice’ so različni simboli, ki določajo izhodni format;

- 'width' določa najmanjše število izhodnih znakov v skladu s to specifikacijo;

- ‘.accuracy’ določa največje število prikazanih znakov;

- 'modifikatorji' določajo vrsto argumentov;

- 'type' določa vrsto argumenta.

Za izpis celih števil s predznakom se uporablja naslednja izhodna oblika:

%[-] [+ | prostor] [širina] [l] d

- – poravnava v levo, privzeto – v desno;

+ – prikazan je znak ‘+’, pri negativnih številih je vedno prikazan znak ‘-’;

presledek – presledek je prikazan na mestu znaka;

d – podatkovni tip int.

Za izpis nepredznačenih celih števil se uporablja ta izhodni format:

%[-] [#] [širina] [l]

# – začetni 0 je izhod za številke v 8 c/c ali začetni 0x ali 0X za števila v 16 c/c,

l – modifikator dolgega podatkovnega tipa;

u – celo število v 10c/c,

o – celo število v 8 c/c,

x, X – celo število pri 16 c/c.

Za izpis števil s plavajočo vejico se uporablja ta izhodni format:

%[-] [+ | prostor] [širina] [.natančnost]

"natančnost" - označuje število mest za decimalno vejico za formate f, e in E ali število pomembnih mest za formata g in G. Številke so zaokrožene. Privzeta natančnost je šest decimalnih mest;

f – številka fiksne točke,

e – število v eksponentni obliki, eksponent je označen s črko "e",

E - število v eksponentni obliki, eksponent je označen s črko "E",

g – najkrajši od formatov f ali g,

G – najkrajši od formatov f ali G.

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

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

1.4. Oblikovanje nizov.

Obstajata različici funkcij scanf in printf, ki sta zasnovani za formatiranje nizov in se imenujeta sscanf oziroma sprintf.

int sscanf (const char *str, const char *format, ...);

bere podatke iz niza, podanega s str, v skladu z nizom formata, ki ga podaja format. Če je uspešno, vrne število prebranih podatkov, in če je neuspešno, vrne EOF. na primer

#vključi

char str = "a 10 1.2 Niz brez vnosa";

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

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

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

printf("%f\n", d); // natisne: 1.200000

printf("%s\n", s); // natisne: niz

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

formatira niz v skladu s formatom, podanim s parametrom formata, in zapiše rezultat v matriko znakov medpomnilnika. Funkcija vrne število znakov, zapisanih v medpomnilnik niza znakov, razen končnega ničelnega bajta. na primer

#vključi

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

char s = "To je niz.";

sprintf(medpomnilnik, str, c, n, d, s);

printf("%s\n", medpomnilnik); // natisne: c = c, n = 10, d = 1,200000, s = To je niz

1.5. Pretvori nize v številske podatke.

Prototipi funkcij za pretvorbo nizov v številske podatke so podani v datoteki glave stdlib.h, ki mora biti vključena v program.

Če želite pretvoriti niz v celo število, uporabite funkcijo

int atoi (const char *str);

char *str = “-123”;

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

Če želite pretvoriti niz v dolgo celo število, uporabite funkcijo

dolg int atol (const char *str);

ki, če je uspešen, vrne celo število, v katerega je pretvorjen niz str, in če je neuspešen, vrne 0. Na primer,

char *str = “-123”;

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

Če želite pretvoriti niz v dvojno število, uporabite funkcijo

dvojni atof(const char *str);

ki v primeru uspeha vrne plavajoče število tipa double, v katerega se pretvori niz str, v primeru neuspeha pa 0. Npr.

char *str = “-123.321”;

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

Naslednje funkcije izvajajo podobne funkcije kot atoi, atol, atof, vendar zagotavljajo naprednejšo funkcionalnost.

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

pretvori niz str v dolgo int število, ki ga vrne. Parametri te funkcije imajo naslednje namene.

Če je osnova 0, je pretvorba odvisna od prvih dveh znakov str:

Če je prvi znak številka od 1 do 9, se domneva, da je številka predstavljena v 10 c/c;

Če je prvi znak številka 0 in drugi znak številka od 1 do 7, se predpostavlja, da je število predstavljeno v 8 c/c;

Če je prvi znak 0, drugi pa 'X' ali 'x', se domneva, da je število predstavljeno v 16 c/c.

Če je osnova število med 2 in 36, se ta vrednost šteje za osnovo številskega sistema in vsi znaki zunaj številskega sistema se prenehajo pretvarjati. V številskih sistemih z osnovo 11 do 36 se simboli od 'A' do 'Z' ali 'a' do 'z' uporabljajo za predstavitev števk.

Vrednost argumenta endptr nastavi funkcija strtol. Ta vrednost vsebuje kazalec na znak, ki je preprečil pretvorbo niza str. Funkcija strtol vrne pretvorjeno število, če je uspešno, in 0, če je neuspešno.

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

deluje podobno kot funkcija strtol, vendar pretvori simbolno predstavitev števila v število tipa unsigned long int.

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

Pretvori simbolno predstavitev števila v dvojino.

Vse funkcije, navedene v tem odstavku, prenehajo delovati, ko naletijo na prvi znak, ki ne ustreza obliki zapisa zadevne številke.

Poleg tega, če vrednost znaka števila presega obseg sprejemljivih vrednosti za ustrezen podatkovni tip, potem funkcije atof, strtol, strtoul, strtod nastavijo vrednost spremenljivke errno na ERANGE. Spremenljivka errno in konstanta ERANGE sta definirani v datoteki glave math.h. V tem primeru funkciji atof in strtod vrneta vrednost HUGE_VAL, funkcija strtol vrne vrednost LONG_MAX ali LONG_MIN, funkcija strtoul pa vrne vrednost ULONG_MAX.

Nestandardne funkcije itoa, ltoa, utoa, ecvt, fcvt in gcvt lahko uporabite za pretvorbo številskih podatkov v znakovne nize. Toda za te namene je bolje uporabiti standardno funkcijo sprintf.

1.6. Standardne funkcije za delo z nizi.

Ta razdelek obravnava funkcije za delo z nizi, katerih prototipi so opisani v datoteki glave string.h.

1. Primerjava nizov. Funkciji strcmp in strncmp se uporabljata za primerjavo nizov.

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

leksikografsko primerja nize str1, str2 in vrne –1, 0 ali 1, če je str1 manjši, enak ali večji od str2.

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

leksikografsko primerja največ prvih n znakov iz nizov str1 in str2. Funkcija vrne -1, 0 ali 1, če je prvih n znakov iz str1 manjših, enakih ali večjih od prvih n znakov iz str2.

// primer primerjave nizov

#vključi

#vključi

char str1 = "aa bb";

char str2 = "aa aa";

char str3 = "aa bb cc";

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

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

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

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

2. Kopiranje vrstic. Funkciji strcpy in strncpy se uporabljata za kopiranje nizov.

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

kopira niz str2 v niz str1. Kopira se celoten niz str2, vključno z zaključnim ničelnim bajtom. Funkcija vrne kazalec na str1. Če se črte prekrivajo, je rezultat nepredvidljiv.

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

kopira n znakov iz niza str2 v niz str1. Če str2 vsebuje manj kot n znakov, se zadnji ničelni bajt kopira tolikokrat, kot je potrebno, da se str2 razširi na n znakov. Funkcija vrne kazalec na niz str1.

char str2 = "Kopiraj niz.";

strcpy(str1, str2);

printf(str1); // natisne: Kopiraj niz.

4. Povezovalne vrvice. Funkciji strcat in strncat se uporabljata za združevanje nizov v en niz.

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

pripne niz str2 nizu str1 in zadnji ničelni bajt niza str1 se izbriše. Funkcija vrne kazalec na niz str1.

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

doda n znakov iz niza str2 v niz str1, pri čemer je zadnji ničelni bajt niza str1 izbrisan. Funkcija vrne kazalec na niz str1. če je dolžina niza str2 manjša od n, so dodani samo znaki, vključeni v niz str2. Po združevanju nizov se str1 vedno doda ničelni bajt. Funkcija vrne kazalec na niz str1.

#vključi

#vključi

char str1 = "Niz";

char str2 = "katenacija";

char str3 = "Da Ne";

strcat(str1, str2);

printf("%s\n", str1); // natisne: katenacija niza

strncat(str1, str3, 3);

printf("%s\n", str1); // natisne: katenacija niza Da

5. Poiščite znak v nizu. Za iskanje znaka v nizu uporabite funkcije strchr, strrchr, strspn, strcspn in strpbrk.

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

išče prvo pojavitev znaka, podanega s c v nizu str. Če je uspešna, funkcija vrne kazalec na prvi najdeni znak, če je neuspešna, vrne NULL.

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

išče zadnjo pojavitev znaka, podanega s c v nizu str. Če je uspešna, funkcija vrne kazalec na zadnji najden znak, če je neuspešna, vrne NULL.

#vključi

#vključi

char str = "Iskanje znakov";

printf("%s\n", strchr(str, "r")); // natisne: r iskanje

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

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

vrne indeks prvega znaka iz str1, ki ni v str2.

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

vrne indeks prvega znaka iz str1, ki se pojavi v str2.

char str = "123 abc";

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

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

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

najde prvi znak v nizu str1, ki je enak enemu od znakov v nizu str2. Če je uspešna, funkcija vrne kazalec na ta znak, če je neuspešna, vrne NULL.

char str = "123 abc";

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

6. Primerjava nizov. Funkcija strstr se uporablja za primerjavo nizov.

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

najde prvo pojavitev str2 (brez končnega ničelnega bajta) v str1. Če je uspešna, funkcija vrne kazalec na najdeni podniz, če je neuspešna, vrne NULL. Če kazalec str1 kaže na niz ničelne dolžine, potem funkcija vrne kazalec str1.

char str = "123 abc 456;

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

7. Razčlenjevanje niza v žetone. Funkcija strtok se uporablja za razčlenitev niza v žetone.

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

vrne kazalec na naslednji žeton (besedo) v nizu str1, v katerem so ločila žetonov znaki iz niza str2. Če žetonov ni več, funkcija vrne NULL. Pri prvem klicu funkcije strtok mora parameter str1 kazati na niz, ki je žetoniziran, pri naslednjih klicih pa mora biti ta parameter nastavljen na NULL. Ko najde žeton, funkcija strtok zapiše ničelni bajt za tem žetonom namesto ločila.

#vključi

#vključi

char str = "12 34 ab cd";

p = strtok(str, " ");

printf("%s\n", p); // natisne vrednosti v stolpcu: 12 34 ab cd

p = strtok(NULL, " ");

8. Določanje dolžine niza. Funkcija strlen se uporablja za določanje dolžine niza.

velikost_t strlen (const char *str);

vrne dolžino niza, ne vključuje zadnjega ničelnega bajta. na primer

char str = "123";

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

1.7. Funkcije za delo s pomnilnikom.

Glavna datoteka string.h opisuje tudi funkcije za delo s pomnilniškimi bloki, ki so podobne ustreznim funkcijam za delo z nizi.

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

išče prvo pojavitev znaka, podanega s c, v n bajtih niza str.

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

primerja prvih n bajtov nizov str1 in str2.

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

kopira prvih n bajtov iz niza str1 v niz str2.

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

kopira prvih n bajtov iz str1 v str2, s čimer zagotovi pravilno obravnavo prekrivajočih se nizov.

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

kopira znak, določen s c, v prvih n bajtov str.

Habra, pozdravljen!

Nedolgo nazaj se mi je zgodil precej zanimiv dogodek, v katerega je bil vpleten eden od učiteljev ene fakultete za računalništvo.

Pogovor o programiranju Linuxa je počasi napredoval do tega, da je ta oseba trdila, da je kompleksnost sistemskega programiranja pravzaprav zelo pretirana. Da je jezik C preprost kot vžigalica, pravzaprav kot jedro Linuxa (po njegovih besedah).

S seboj sem imel prenosnik z Linuxom, ki je vseboval gospodski nabor pripomočkov za razvoj v jeziku C (gcc, vim, make, valgrind, gdb). Ne spomnim se, kakšen cilj smo si takrat zadali, a po nekaj minutah se je moj nasprotnik znašel pri tem prenosniku, popolnoma pripravljen rešiti problem.

In dobesedno v prvih vrsticah je naredil resno napako pri dodeljevanju pomnilnika ... vrstici.

Char *str = (char *)malloc(sizeof(char) * strlen(buffer));
buffer - spremenljivka sklada, v katero so bili zapisani podatki s tipkovnice.

Mislim, da se bodo zagotovo našli ljudje, ki se bodo vprašali: "Kako je lahko s tem kaj narobe?"
Verjemite, lahko.

In kaj točno - preberite na mačku.

Malo teorije - neke vrste LikBez.

Če veste, se pomaknite do naslednje glave.

Niz v C je niz znakov, ki se mora vedno končati z "\0" - znakom za konec vrstice. Nizi v skladu (statični) so deklarirani takole:

Char str[n] = (0);
n je velikost niza znakov, enaka dolžini niza.

Dodelitev ( 0 ) - "ničliranje" niza (neobvezno, lahko ga deklarirate brez njega). Rezultat je enak kot pri zagonu funkcij memset(str, 0, sizeof(str)) in bzero(str, sizeof(str)). Uporablja se za preprečevanje puščanja smeti v neinicializiranih spremenljivkah.

Prav tako lahko takoj inicializirate niz v skladu:

Char buf = "privzeto besedilo medpomnilnika\n";
Poleg tega lahko niz deklarirate kot kazalec in mu lahko dodelite pomnilnik na kupu:

Char *str = malloc(velikost);
velikost - število bajtov, ki jih dodelimo nizu. Takšni nizi se imenujejo dinamični (zaradi dejstva, da se zahtevana velikost izračuna dinamično + velikost dodeljenega pomnilnika je mogoče kadar koli povečati s funkcijo realloc()).

V primeru spremenljivke sklada sem uporabil zapis n za določitev velikosti niza; v primeru spremenljivke kopice sem uporabil zapis velikosti. In to odlično odraža pravo bistvo razlike med deklaracijo na skladu in deklaracijo z dodelitvijo pomnilnika na kupu, ker se n običajno uporablja, ko govorimo o številu elementov. Velikost pa je čisto druga zgodba...

Valgrind nam bo pomagal

V prejšnjem članku sem ga tudi omenil. Valgrind ( , dva - majhna navodila) je zelo uporaben program, ki programerju pomaga odslediti puščanje pomnilnika in napake v kontekstu - točno tiste stvari, ki se najpogosteje pojavijo pri delu z nizi.

Oglejmo si kratek seznam, ki izvaja nekaj podobnega programu, ki sem ga omenil, in ga poženimo prek valgrinda:

#vključi #vključi #vključi #define HELLO_STRING "Pozdravljeni, Habr!\n" void main() ( char *str = malloc(sizeof(char) * strlen(HELLO_STRING)); strcpy(str, HELLO_STRING); printf("->\t%s" , str);
In pravzaprav rezultat programa:

$ gcc main.c $ ./a.out -> Pozdravljeni, Habr!
Nič nenavadnega še. Zdaj pa zaženimo ta program z valgrindom!

$ valgrind --tool=memcheck ./a.out ==3892== Memcheck, detektor napak v pomnilniku ==3892== Copyright (C) 2002-2015 in GNU GPL"d, Julian Seward et al. == 3892== Uporaba Valgrind-3.12.0 in LibVEX; ponovno zaženite z -h za informacije o avtorskih pravicah ==3892== Ukaz: ./a.out ==3892== ==3892== Neveljavno pisanje velikosti 2 ==3892= = pri 0x4005B4: main (in /home/indever/prg/C/public/a.out) ==3892== Naslov 0x520004c je 12 bajtov znotraj bloka velikosti 13 alloc"d ==3892== pri 0x4C2DB9D: malloc (vg_replace_malloc.c:299) ==3892== avtor 0x400597: glavno (v /home/indever/prg/C/public/a.out) ==3892== ==3892== Neveljavno branje velikosti 1 == 3892== pri 0x4C30BC4: strlen (vg_replace_strmem.c:454) ==3892== od 0x4E89AD0: vfprintf (v /usr/lib64/libc-2.24.so) ==3892== od 0x4E90718: printf (v /usr/ lib64/libc-2.24.so) ==3892== z 0x4005CF: main (v /home/indever/prg/C/public/a.out) ==3892== Naslov 0x520004d je 0 bajtov za blokom velikosti 13 alloc"d ==3892== pri 0x4C2DB9D: malloc (vg_replace_malloc.c:299) ==3892== od 0x400597: main (v /home/indever/prg/C/public/a.out) ==3892== -> Pozdravljeni, Habr! ==3892== ==3892== POVZETEK KOPICE: ==3892== v uporabi na izhodu: 0 bajtov v 0 blokih ==3892== skupna uporaba kopice: 2 dodelitvi, 2 sprostitvi, 1037 dodeljenih bajtov ==3892= = ==3892== Vsi bloki kopice so bili sproščeni -- puščanja niso možna ==3892== ==3892== Za število odkritih in potlačenih napak znova zaženite z: -v ==3892== POVZETEK NAPAK: 3 napake iz 2 kontekstov (izločeno: 0 od 0)
==3892== Vsi bloki kopice so bili sproščeni - nobeno puščanje ni možno- ni puščanja, kar je dobra novica. Vendar je vredno spustiti oči nekoliko nižje (čeprav želim opozoriti, da je to le povzetek, glavne informacije so nekoliko drugačne):

==3892== POVZETEK NAPAK: 3 napake iz 2 kontekstov (prikrito: 0 od 0)
3 napake. V 2 kontekstih. V tako preprostem programu. Kako!?

Da, zelo preprosto. Celotna "smešna stvar" je, da funkcija strlen ne upošteva znaka za konec vrstice - "\0". Tudi če ga izrecno navedete v dohodni vrstici (#define HELLO_STRING “Hello, Habr!\n\0”), bo prezrt.

Tik nad rezultatom izvajanja programa je vrstica -> Pozdravljeni, Habr! tam je podrobno poročilo o tem, kaj in kje našemu dragocenemu valgrindu ni bilo všeč. Predlagam, da si sami ogledate te vrstice in naredite svoje zaključke.

Pravzaprav bo pravilna različica programa videti takole:

#vključi #vključi #vključi #define HELLO_STRING "Pozdravljeni, Habr!\n" void main() ( char *str = malloc(sizeof(char) * (strlen(HELLO_STRING) + 1)); strcpy(str, HELLO_STRING); printf("->\ t%s", str); brezplačno (str); )
Poženimo ga skozi valgrind:

$ valgrind --tool=memcheck ./a.out -> Pozdravljeni, Habr! ==3435== ==3435== POVZETEK KOPICE: ==3435== v uporabi na izhodu: 0 bajtov v 0 blokih ==3435== skupna uporaba kopice: 2 dodelitvi, 2 sprostitvi, 1038 dodeljenih bajtov ==3435= = ==3435== Vsi bloki kopice so bili sproščeni -- puščanja niso možna ==3435== ==3435== Za število odkritih in potlačenih napak znova zaženite z: -v ==3435== POVZETEK NAPAK: 0 napak iz 0 kontekstov (izločeno: 0 iz 0)
Super. Ni napak, +1 bajt dodeljenega pomnilnika je pomagal rešiti težavo.

Zanimivo je, da bosta v večini primerov tako prvi kot drugi program delovala enako, vendar če pomnilnik, dodeljen za vrstico, v kateri se končni znak ne prilega, ni bil poničen, potem funkcija printf() pri izpisu takšne vrstice , bo izpisal tudi vso smeti za to vrstico - vse bo natisnjeno, dokler znak za konec vrstice ne bo oviral printf().

Vendar veste, (strlen(str) + 1) je taka rešitev. Srečujemo se z dvema težavama:

  1. Kaj pa, če moramo dodeliti pomnilnik za niz, ustvarjen na primer z uporabo s(n)printf(..)? Argumentov ne podpiramo.
  2. Videz. Deklaracijska vrstica spremenljivk izgleda grozno. Nekateri fantje uspejo tudi malloc pripeti (char *), kot da pišejo pod pluse. V programu, kjer morate redno obdelovati nize, je smiselno najti bolj elegantno rešitev.
Poiščimo rešitev, ki bo zadovoljila tako nas kot valgrind.

snprintf()

int snprintf(char *str, size_t size, const char *format, ...);- funkcija - razširitev sprintf, ki oblikuje niz in ga zapiše v kazalec, posredovan kot prvi argument. Od sprintf() se razlikuje po tem, da str ne bo zapisal bajta, večjega od tistega, ki je določen v velikosti.

Funkcija ima eno zanimivo lastnost - v vsakem primeru vrne velikost generiranega niza (brez upoštevanja znaka za konec vrstice). Če je niz prazen, se vrne 0.

Ena od težav, ki sem jih opisal pri uporabi strlen, je povezana s funkcijama sprintf() in snprintf(). Predpostavimo, da moramo nekaj napisati v niz str. Zadnja vrstica vsebuje vrednosti drugih spremenljivk. Naš vnos bi moral biti nekaj takega:

Char * str = /* tukaj dodeli pomnilnik */; sprintf(str, "Pozdravljeni, %s\n", "Habr!");
Postavlja se vprašanje: kako določiti, koliko pomnilnika je treba dodeliti nizu str?

Char * str = malloc(sizeof(char) * (strlen(str, "Pozdravljeni, %s\n", "Habr!") + 1)); - ne bo šlo. Prototip funkcije strlen() izgleda takole:

#vključi velikost_t strlen(const char *s);
const char *s ne pomeni, da je niz, posredovan s, lahko niz v spremenljivem formatu.

Tu nam bo v pomoč uporabna lastnost funkcije snprintf(), ki sem jo omenil zgoraj. Poglejmo si kodo za naslednji program:

#vključi #vključi #vključi void main() ( /* Ker snprintf() ne upošteva znaka za konec vrstice, dodamo njegovo velikost rezultatu */ size_t needed_mem = snprintf(NULL, 0, "Pozdravljeni, %s!\n", "Habr") + sizeof("\0"); char *str (potreben_mem); ;
Zaženite program v valgrindu:

$ valgrind --tool=memcheck ./a.out -> Pozdravljeni, Habr! ==4132== ==4132== POVZETEK KOPICE: ==4132== v uporabi na izhodu: 0 bajtov v 0 blokih ==4132== skupna uporaba kopice: 2 dodelitvi, 2 sprostitvi, 1041 dodeljenih bajtov ==4132= = ==4132== Vsi bloki kopice so bili sproščeni -- puščanja niso možna ==4132== ==4132== Za število odkritih in potlačenih napak znova zaženite z: -v ==4132== POVZETEK NAPAK: 0 napak iz 0 kontekstov (izločeno: 0 iz 0) $
Super. Imamo argumentirano podporo. Ker kot drugi argument funkciji snprintf() posredujemo ničelno vrednost, pisanje v ničelni kazalec ne bo nikoli povzročilo napake Seagfault. Kljub temu pa bo funkcija še vedno vrnila zahtevano velikost za niz.

Toda po drugi strani smo morali uvesti dodatno spremenljivko in dizajn

Size_t needed_mem = snprintf(NULL, 0, "Pozdravljeni, %s!\n", "Habr") + sizeof("\0");
izgleda še slabše kot v primeru strlen().

Na splošno lahko + sizeof("\0") odstranite, če izrecno podate "\0" na koncu vrstice formata (size_t needed_mem = snprintf(NULL, 0, "Pozdravljeni, %s!\n \0 ", "Habr");), vendar to nikakor ni vedno mogoče (odvisno od mehanizma obdelave nizov lahko dodelimo dodaten bajt).

Nekaj ​​moramo narediti. Malo sem razmišljal in se odločil, da je zdaj čas, da se obrnem na modrost starodavnih. Opišimo makro funkcijo, ki bo poklicala snprintf() z ničelnim kazalcem kot prvim argumentom in ničelnim kazalcem kot drugim. In ne pozabimo na konec vrstice!

#define strsize(args...) snprintf(NULL, 0, args) + sizeof("\0")
Da, morda je za nekatere novica, toda makri C podpirajo spremenljivo število argumentov in elipsa pove predprocesorju, da navedeni argument funkcije makra (v našem primeru args) ustreza več dejanskim argumentom.

Preverimo našo rešitev v praksi:

#vključi #vključi #vključi #define strsize(args...) snprintf(NULL, 0, args) + sizeof("\0") void main() ( char *str = malloc(strsize("Pozdravljeni, %s\n", "Habr! ")); sprintf(str, "Pozdravljeni, %s\n", "Habr!"); printf("->\t%s", str); brezplačno (str); )
Začnimo z valgrundom:

$ valgrind --tool=memcheck ./a.out -> Pozdravljeni, Habr! ==6432== ==6432== POVZETEK KOPICE: ==6432== v uporabi na izhodu: 0 bajtov v 0 blokih ==6432== skupna uporaba kopice: 2 dodelitvi, 2 sprostitvi, 1041 dodeljenih bajtov ==6432= = ==6432== Vsi bloki kopice so bili sproščeni -- puščanja niso možna ==6432== ==6432== Za število odkritih in potlačenih napak znova zaženite z: -v ==6432== POVZETEK NAPAK: 0 napak iz 0 kontekstov (izločeno: 0 iz 0)
Da, ni nobenih napak. Vse je pravilno. In valgrind je vesel, programer pa lahko končno zaspi.

Toda na koncu bom povedal še nekaj. V primeru, da moramo dodeliti pomnilnik za kateri koli niz (tudi z argumenti), ga že imamo popolnoma delujoča pripravljena rešitev.

Govorimo o funkciji asprintf:

#define _GNU_SOURCE /* Glej feature_test_macros(7) */ #include int asprintf(char **strp, const char *fmt, ...);
Kot prvi argument vzame kazalec na niz (**strp) in dereferenciranemu kazalcu dodeli pomnilnik.

Naš program, napisan z asprintf(), bo videti takole:

#vključi #vključi #vključi void main() ( char *str; asprintf(&str, "Pozdravljeni, %s!\n", "Habr"); printf("->\t%s", str); free(str); )
In pravzaprav v valgrindu:

$ valgrind --tool=memcheck ./a.out -> Pozdravljeni, Habr! ==6674== ==6674== POVZETEK KOPICE: ==6674== v uporabi na izhodu: 0 bajtov v 0 blokih ==6674== skupna uporaba kopice: 3 dodelitve, 3 osvoboditve, 1138 dodeljenih bajtov ==6674= = ==6674== Vsi bloki kopice so bili sproščeni -- puščanja niso možna ==6674== ==6674== Za število odkritih in potlačenih napak znova zaženite z: -v ==6674== POVZETEK NAPAK: 0 napak iz 0 kontekstov (izločeno: 0 iz 0)
Vse je v redu, vendar, kot lahko vidite, je bilo dodeljeno več pomnilnika in zdaj obstajajo trije aloc-ji, ne dva. V šibkih vgrajenih sistemih je uporaba te funkcije nezaželena.
Poleg tega, če v konzolo napišemo man asprintf, bomo videli:

SKLADNOST S Te funkcije so razširitve GNU, ne v C ali POSIX. Na voljo so tudi pod *BSD. Izvedba FreeBSD ob napaki nastavi strp na NULL.

To pojasnjuje, da je ta funkcija na voljo samo v virih GNU.

Zaključek

Na koncu želim povedati, da je delo z nizi v C zelo zapletena tema, ki ima številne nianse. Na primer, za pisanje "varne" kode pri dinamičnem dodeljevanju pomnilnika je priporočljivo uporabiti funkcijo calloc() namesto malloc() - calloc zapolni dodeljeni pomnilnik z ničlami. Ali pa po dodelitvi pomnilnika uporabite funkcijo memset(). V nasprotnem primeru lahko smeti, ki so bile prvotno nameščene v dodeljenem pomnilniškem območju, povzročijo težave med odpravljanjem napak in včasih pri delu z nizom.

Več kot polovica programerjev C, ki jih poznam (večinoma so začetniki), ki so na mojo zahtevo rešili problem dodeljevanja pomnilnika za nize, je to naredilo na način, ki je na koncu povzročil napake v kontekstu. V enem primeru - celo do uhajanja pomnilnika (no, oseba je pozabila narediti free(str), to se nikoli nikomur ne zgodi). Pravzaprav me je to spodbudilo, da sem ustvaril to stvaritev, ki ste jo pravkar prebrali.

Upam, da bo ta članek komu koristen. Zakaj zganjam ves ta hrup - noben jezik ni preprost. Povsod ima svoje posebnosti. In več subtilnosti jezika poznate, boljša je vaša koda.

Verjamem, da bo po branju tega članka vaša koda postala malo boljša :)
Srečno, Habr!

Oznake: C vrstice. Niz znakov.

Nizi v C. Uvod.

To je uvodni članek o nizih C. Podrobnejši opis in primeri bodo prišli, ko se bomo naučili delati s spominom in kazalci. V računalniku so vse vrednosti shranjene kot številke. In tudi črte, tam ni simbolov ali črk. Izraz je niz števil. Vsaka številka ustreza določenemu znaku, ki je vzet iz tabele kodiranja. Ko je simbol prikazan na zaslonu, je prikazan na določen način.
Matrike tipa char se uporabljajo za shranjevanje nizov. Še enkrat ponavljam - tip char je numerični, shrani en bajt podatkov. Toda glede na tabelo kodiranja je vsaka od teh številk povezana z znakom. In v nasprotni smeri - vsak znak je določen s svojo serijsko številko v kodirni tabeli.
Na primer

#vključi #vključi void main() ( char c = "A"; int i = 65; printf("prikaži kot char %c\n", c); printf("prikaži kot int %d\n", c); printf(" prikaz kot char %c\n", i); printf("prikaz kot char %d\n", i); getch(); )

Ustvarili smo dve spremenljivki, eno vrste char, drugo int. Črka "A" ima številčno vrednost 65. Je črka, ne niz, zato je obdana z enojnimi narekovaji. Lahko ga natisnemo kot pismo

Printf("prikaži kot char %c\n", c);

Potem bo izhod
A
Če ga izpišete kot številko, bo
65
Enako lahko storite s številko 65, ki je shranjena v spremenljivki like int.
Posebni znaki imajo tudi svojo številko

#vključi #vključi void main() (printf("%c", "\a"); printf("%d", "\a"); printf("%c", 7); getch(); )

Tukaj bo najprej "oddan" zvočni signal, nato njegova številčna vrednost, nato spet zvočni signal.
Niz v C je niz vrste char, katerega zadnji element shrani končni znak "\0". Številska vrednost tega znaka je 0, zato lahko rečemo, da se niz konča z ničlo.
Na primer

#vključi #vključi void main() ( char beseda; beseda = "A"; beseda = "B"; beseda = "C"; beseda = "\0"; //beseda = 0; enakovredno printf("%s", beseda) ; getch();

Za izpis je bil uporabljen ključ %s. V tem primeru se vrstica natisne do prvega končnega znaka, ker funkcija printf ne pozna velikosti niza besed.
Če v tem primeru ne postavite

Beseda = "\0";

potem bo izpisan niz znakov poljubne dolžine, dokler ne naletimo na prvi bajt, napolnjen z ničlami.

#vključi #vključi void main() ( char word = "ABC"; char text = ("H", "E", "L", "L", "O"); printf("%s\n", beseda); printf ("%s", besedilo);

V tem primeru je vse pravilno. Niz "ABC" se konča z ničlo in z njim inicializiramo niz besed. Besedilni niz se inicializira črko za črko, vsi preostali znaki, kot izhaja iz poglavja o nizih, so zapolnjeni z ničlami.

Branje vrstic

Če želite od uporabnika zahtevati niz, morate ustvariti medpomnilnik. Velikost medpomnilnika je treba izbrati vnaprej, tako da se vnesena beseda prilega vanj. Pri branju vrstic obstaja nevarnost, da bo uporabnik vnesel več podatkov, kot jih dovoljuje medpomnilnik. Ti podatki bodo prebrani in shranjeni v pomnilniku ter bodo prepisali vrednosti drugih ljudi. Na ta način lahko izvedete napad tako, da zabeležite potrebne bajte, v katerih je na primer vredno iti na del kode z zlonamernim programom, ali beležite podatke.

#vključi #vključi void main() ( char buffer; scanf("%19s", buffer); printf("%s", buffer); getch(); )

V tem primeru je število vnesenih znakov omejeno na 19, velikost medpomnilnika pa je za 1 večja, saj je treba shraniti terminalski znak. Napišimo preprost program, ki uporabnika vpraša po nizu in vrne njegovo dolžino.

#vključi #vključi void main() ( char buffer; unsigned len = 0; scanf("%127s", buffer); while (buffer != "\0") ( len++; ) printf("length(%s) == %d" , medpomnilnik, len);

Ker je številska vrednost znaka "\0" enaka nič, lahko pišemo

Medtem ko (buffer != 0) ( len++; )

Oziroma še krajše

Medtem ko (medpomnilnik) ( len++; )

Sedaj pa napišimo program, ki uporabnika vpraša po dveh besedah ​​in ju primerja

#vključi #vključi /* Rezultat primerjave bo število 0, če so besede enake 1, če je prva beseda večja od druge v leksikografskem vrstnem redu -1, če je druga beseda večja */ void main() ( char firstWord; / /Prva beseda char secondWord; //Druga beseda unsigned i; /Counter int cmpResult = 0; //Rezultat primerjave scanf("%127s", secondWord);< 128; i++) { if (firstWord[i] >secondWord[i]) ( //Bolj, če se je druga beseda že končala, ker //takrat se konča z nič cmpResult = 1; break; ) else if (firstWord[i]< secondWord[i]) { cmpResult = -1; break; } } printf("%d", cmpResult); getch(); }

Ker ima vsaka črka številčno vrednost, jih lahko primerjamo med seboj kot številke. Poleg tega so običajno (vendar ne vedno!) črke v kodirnih tabelah razvrščene po abecedi. Zato bo razvrščanje po številski vrednosti tudi razvrščanje po abecedi.

Delo z nizi. Razred nizov. Konstruktorji razreda. Funkcije assign() , append() , insert() , replace() , erase() , find() , rfind() , compare() , c_str() . Primeri

1. Kakšen je namen razreda nizov v programih C++?

Razred nizov je zasnovan za delo z nizi char*, ki so nizi z ničelnimi zaključki. Razred nizov je bil predstavljen kot alternativa delu z nizi char*. Vrstice, ki se končajo z znakom ‘\0’ imenovane tudi C-strune. Ker je niz razred, lahko deklarirate objekte tega razreda.

2. Katere module (knjižnice) je treba povezati za uporabo zmožnosti razreda nizov v MS Visual Studio C++?

Če želite uporabiti zmožnosti razreda nizov v MS Visual Studio (C++), morate vključiti knjižnico in imenski prostor std.

#vključi uporaba imenskega prostora std;
3. Kako se deklarira spremenljivka tipa niz? Primeri

Deklaracija spremenljivke tipa string poteka na enak način kot navadne spremenljivke. Možna varianta deklaracije s sočasno inicializacijo.

// tip string string s1; // spremenljivka z imenom s1 tipa niz string s2 = "To je spremenljivka niza" ; // deklaracija z inicializacijo // z uporabo spremenljivke tipa niz z operatorjem dodelitve s1 = s2; // s1 = "To je spremenljivka niza" s2 = "Novo besedilo" ;
4. Kakšne so prednosti in slabosti uporabe razreda nizov v primerjavi s tipom char*?

Ustvarjanje novega tipa niz je posledica pomanjkljivosti pri delu z nizi znakov, ki jih je tip char* pokazal. V primerjavi s tipom char* ima tip string naslednje glavne prednosti:

  • sposobnost obdelave nizov z uporabo standardnih operatorjev C++ ( = , + , = = , <> in tako naprej.). Kot veste, so bile pri uporabi tipa char* celo najbolj preproste operacije z nizi videti zapletene in so zahtevale pisanje pretirane programske kode;
  • zagotavljanje večje zanesljivosti (varnosti) programske kode. Na primer, pri kopiranju nizov tip niza zagotavlja ustrezna dejanja, ki se lahko zgodijo, če je izvorni niz večji od ciljnega niza;
  • zagotavljanje niza kot neodvisnega podatkovnega tipa. Deklaracija vrste niza kot niza je enaka za vse spremenljivke v programu, kar zagotavlja konsistentnost podatkov.

Glavna pomanjkljivost tipa string v primerjavi s tipom char* je počasnejša hitrost obdelave podatkov. To je posledica dejstva, da je tip niza dejansko vsebniški razred. In delo z razredom zahteva dodatno implementacijo programske kode, kar pa zahteva dodaten čas.

5. Katere operatorje lahko uporabljamo s predmeti razreda nizov?

Razred nizov je priročen, ker vam omogoča priročno manipuliranje z nizi z uporabo standardnih (preobremenjenih) operatorjev.

Naslednje operatorje je mogoče uporabiti s predmeti razreda nizov:

  • = – dodelitev
  • + – veriženje (združevanje nizov)
  • += – pripis z veriženjem
  • == – enakost
  • != – neenakost
  • < - manj
  • <= - manj ali enako
  • > - več
  • >= - več ali enako
  • – indeksiranje

primer, ki prikazuje uporabo zgornjih izjav

// tip niza, operacije na nizih niz s1 = "s-1" ; niz s2 = "s-2" ; niz s3; bool b; // operacija "=" (dodelitev niza) s3 = s1; // s3 = "s-1" // operacija "+" - združevanje nizov s3 = s3 + s2; // s3 = "s-1s-2" // operacija "+=" - dodelitev z veriženjem s3 = "s-3"; s3 += "abc" ; // s3 = "s-3abc" // operacija "==" - primerjava nizov b = s2==s1; // b = false b = s2=="s-2" ; // b = res // operacija "!=" - primerjava nizov (ni enako) s1 = "s1"; s2 = "s2"; b = s1 != s2; // b = res // operacije "<" и ">" - primerjava nizov s1 = "abcd" ; s2 = "de"; b = s1 > s2; // b = napačno b = s1< s2; // b = true // operacije "<=" и ">=" - primerjava nizov (manj ali enako, večje ali enako) s1 = "abcd" ; s2 = "ab"; b = s1 >= s2; // b = res b = s1<= s2; // b = false b = s2 >= "ab" ; // b = res // operacija - indeksiranje znak c; s1 = "abcd" ; c = s1; // c = "c" c = s1; // c = "a"
6. Ali razred nizov vsebuje konstruktorje?

Kot vsak razred ima tudi razred nizov številne konstruktorje. Glavne so naslednje:

Vrvica(); niz (const char * str); niz (const niz & str);

7. Primeri inicializacije s konstruktorji

Spodaj so primeri inicializacije spremenljivk tipa niz

Niz s1("Pozdravljeni!"); niz s2 = "Pozdravljeni!" ; // inicializacija - niz konstruktorja (const char * str) char * ps = "Pozdravljeni" ; niz s3(ps); // inicializacijski niz s4(s3); // inicializacija - niz konstruktorja (const string & str) niz s5; // inicializacija - string() konstruktor

8. Dodeljevanje nizov. dodeli(). Primeri

Če želite en niz dodeliti drugemu, lahko uporabite eno od dveh metod:

  • uporabite operator dodelitve ‘=’ ;
  • uporabite funkcijo assign() iz razreda nizov.

Funkcija assign() ima več preobremenjenih izvedb.

Prva možnost je klic funkcije brez parametrov

String &assign(void);

V tem primeru obstaja preprosta dodelitev enega niza drugemu.

Druga možnost vam omogoča kopiranje določenega števila znakov iz niza:

String &assign(const string & s, size_type st, size_type num);

  • s – objekt, iz katerega je vzet izvorni niz;
  • st – indeks (položaj) v vrstici, od katere se začne kopiranje števila znakov;
  • num – število znakov, ki jih je treba kopirati s položaja st;
  • size_type – redni podatkovni tip.

Tretja različica funkcije assign() kopira prve znake num niza s klicatelju:

String & assign(const char * s, size_type num);

  • s – niz, ki se konča z znakom ‘\0’ ;
  • num je število znakov, ki so kopirani v klicni objekt. Prvih num znakov iz niza s se kopira.

Spodaj je primer z različnimi izvedbami funkcije assign().

Primer.

// dodelitev nizov, funkcija assign(). niz s1 = "mesto"; niz s2; niz s3; char * ps = "mesto"; s3 = s1; // s3 = "mesto" s2.assign(s1); // s2 = "mesto" s2.assign(s1, 0, 4); // s2 = "najboljši" s2.assign(ps, 8); // s2 = "bestprog"
9. Kombiniranje nizov. Funkcija append(). Primer

Funkcija append() se uporablja za združevanje nizov. Operacijo lahko uporabite tudi za dodajanje vrstic ‘+’ , Na primer:

Niz s1; niz s2; s1 = "abc" ; s2 = "def" ; s1 = s1 + s2; // s1 = "abcdef"

Vendar je funkcija append() dobra, če morate dodati del niza.

Funkcija ima naslednje izvedbene možnosti:

String &append(const string & s, size_type start); string &append(const char * s, size_type num);

V prvi izvedbi funkcija prejme sklic na objekt niza s, ki je dodan klicnemu objektu. V drugi izvedbi funkcija prejme kazalec na niz tipa const char *, ki se konča z znakom '\0'.

Primer. Predstavitev funkcije append().

Niz s1 = "abcdef" ; s2 = "1234567890" ; pripni (s2, 3, 4); // s1 = "abcdef4567" char * ps = "1234567890" ; s1 = "abcdef" ; s1.priloži(ps, 3); // s1 = "abcdef123"

10. Vstavljanje znakov v niz. funkcija vstavi(). Primer

Če želite vstaviti eno vrstico na dano mesto druge vrstice, morate uporabiti funkcijo insert(), ki ima več možnosti izvedbe.

Prva različica funkcije omogoča vstavljanje celotnega niza s na podani začetni položaj klicne vrstice (klicajočega objekta):

Niz & vstavi (size_type start, const string &s);

Druga različica funkcije vam omogoča, da vstavite del (parametri insStart, num) niza s na podani začetni položaj klicnega niza:

String & insert(size_type start, const string &s, size_type insStart, size_type num);

V zgornjih funkcijah:

  • s – niz, ki se vstavi v klicno vrstico;
  • začetek – položaj v klicni vrstici, iz katerega je vstavljen niz s;
  • insStart – položaj v nizu s, iz katerega pride do vstavljanja;
  • num – število znakov v nizu s, ki so vstavljeni s položaja insStart.
niz s1 = "abcdef" ; niz s2 = "1234567890" ; s1.vstavi(3, s2); // s1 = "abc"+"1234567890"+"def"="abc1234567890def" s2.vstavi(2, s1, 1, 3); // s2 = "12bcd34567890"
11. Zamenjava znakov v nizu. zamenjaj(). Primer

Funkcija replace() zamenja znake v klicnem nizu. Funkcija ima naslednje izvedbene možnosti:

String &replace(size_type start, size_type num, const string &s); niz &replace(size_type start, size_type num, const string &s, size_type replStart, size_type replNum);

V prvi izvedbi je klicni niz zamenjan z nizom s. V klicni vrstici je mogoče določiti položaj (začetek) in število znakov (num), ki jih je treba zamenjati z nizom s.

Druga različica funkcije replace() se od prve razlikuje po tem, da omogoča zamenjavo le dela niza s s klicnim nizom. V tem primeru sta podana dva dodatna parametra: položaj replStart in število znakov v nizu s, ki tvorita podniz, ki nadomešča klicni niz.

Primer. Predstavitev funkcije replace().

Niz s1 = "abcdef" ; niz s2 = "1234567890" ; s2.zamenjaj(2, 4, s1); // s2 = "12abcdef7890" s2 = "1234567890" ; s2.replace(3, 2, s1); // s2 = "123abcdef67890" s2 = "1234567890" ; s2.replace(5, 1, s1); // s2 = "12345abcdef7890" // zamenjava znakov, funkcija replace(). niz s1 = "abcdef" ; niz s2 = "1234567890" ; s2.zamenjaj(2, 4, s1); // s2 = "12abcdef7890" s2 = "1234567890" ; s2.replace(3, 2, s1); // s2 = "123abcdef67890" s2 = "1234567890" ; s2.replace(5, 1, s1); // s2 = "12345abcdef7890" s2 = "1234567890" ; s2.replace(5, 1, s1, 2, 3); // s2 = "12345cde7890" s2 = "1234567890" ; s2.zamenjaj(4, 2, s1, 0, 4); // s2 = "1234abcd7890"

12. Odstranjevanje določenega števila znakov iz niza. funkcijo erase(). Primer

Če želite odstraniti znake iz klicnega niza, uporabite funkcijo erase():

Niz & brisanje (size_type index=0, size_type num = npos);

  • indeks – indeks (položaj), od katerega želite odstraniti znake v klicni vrstici;
  • num – število odstranjenih znakov.

Primer.

Niz s = "01234567890" ; s.izbriši(3, 5); // s = "012890" s = "01234567890" ; s.izbriši(); // s = ""

13. Iskanje znaka v nizu. Funkciji Find() in rfind(). Primeri

V razredu nizov je iskanje niza v podnizu možno na dva načina, ki se razlikujeta v smeri iskanja:

  • s skeniranjem niza od začetka do konca s funkcijo find();
  • s skeniranjem niza od konca do začetka s funkcijo rfind().

Prototip funkcije find() izgleda takole:

Size_type find(const string &s, size_type start = 0) const;

  • s je podniz, ki se išče v nizu, ki kliče to funkcijo. Funkcija išče prvo pojavitev niza s. Če je podniz s najden v nizu, ki je poklical to funkcijo, se vrne položaj prvega pojavitve. V nasprotnem primeru se vrne -1;

Prototip funkcije rfind() izgleda takole:

Size_type rfind(const string &s, size_type start = npos) const;

  • s je podniz, ki ga iščemo v klicnem nizu. Iskanje podniza v nizu poteka od konca proti začetku. Če je podniz s najden v klicnem nizu, potem funkcija vrne položaj prvega pojavitve. V nasprotnem primeru funkcija vrne -1;
  • npos – položaj zadnjega znaka klicne vrstice;
  • začetek – položaj, s katerega poteka iskanje.

Primer 1. Delček kode, ki prikazuje rezultat funkcije find().

// vnesite niz, funkcija find() niz s1 = "01234567890" ; niz s2 = "345" ; niz s3 = "abcd" ; int pos; pos = s1.find(s2); // pos = 3 pos = s1.find(s2, 1); // pos = 3 pos = s1.find("jklmn" , 0); // pos = -1 pos = s1.find(s3); // pos = -1 pos = s2.find(s1); // položaj = -1

Primer 2. Predstavitev funkcije rfind().

// tip niza, funkcije find() in rfind(). niz s1 = "01234567890" ; niz s2 = "345"; niz s3 = "abcd" ; niz s4 = "abcd---abcd" ; int pos; pos = s1.rfind(s2); // pos = 3 pos = s1.rfind(s2, 12); // pos = 3 pos = s1.rfind(s2, 3); // pos = 3 pos = s1.rfind(s2, 2); // pos = -1 pos = s2.rfind(s1); // pos = -1 pos = s1.rfind(s3, 0); // položaj = -1 // razlika med funkcijama find() in rfind(). pos = s4.rfind(s3); // pos = 7 pos = s4.find(s3); // položaj = 0
14. Primerjava delov nizov. primerjaj(). Primer

Ker je vrsta niza razred, lahko uporabite operacijo za primerjavo dveh nizov med seboj ‘= =’ . Če sta niza enaka, bo rezultat primerjave res. V nasprotnem primeru bo rezultat primerjave napačen.

Če pa morate del enega niza primerjati z drugim, je za to na voljo funkcija compare().

prototip funkcije compare():

int primerjava(size_type start, size_type num, const string &s) const;
  • s – niz, ki se primerja s klicnim nizom;
  • začetek – položaj (indeks) v nizu s, od katerega se začne ogled znakov niza za primerjavo;
  • num je število znakov v nizu s, ki se primerja s klicajočim nizom.

Funkcija deluje na naslednji način. Če je klicni niz manjši od niza s, potem funkcija vrne -1 (negativno vrednost). Če je klicni niz večji od niza s, funkcija vrne 1 (pozitivna vrednost). Če sta dva niza enaka, funkcija vrne 0.

Primer. Predstavitev funkcije compare():

// vrsta niza, funkcija primerjaj(). niz s1 = "012345" ; niz s2 = "0123456789" ; int res; res = s1.primerjaj(s2); // res = -1 res = s1.compare("33333"); // res = -1 res = s1.compare("012345"); // res = 0 res = s1.compare("345"); // res = -1 res = s1.compare(0, 5, s2); // res = -1 res = s2.compare(0, 5, s1); // res = -1 res = s1.compare(0, 5, "012345"); // res = -1 res = s2.compare(s1); // res = 1 res = s2.compare("456"); // res = -1 res = s2.compare("000000"); // res = 1
15. Prejemanje niza z znakom za konec vrstice '\0' (char *). Funkcija c_str(). Primer

Če želite dobiti niz, ki se konča z znakom ‘\0’ Uporabljena je funkcija c_str().

Prototip funkcije:

const char * c_str() const;

Funkcija je deklarirana z modifikatorjem const. To pomeni, da funkcija ne more spremeniti klicajočega objekta (niza).

Primer 1. Pretvarjanje vrste niza v const char *.

niz s = "abcdef" ; const char * ps; ps = s.c_str(); // ps = "abcdef"

Primer 2.

Naslednje prikazuje pretvorbo niza iz niza v vrsto System::String za prikaz v kontrolniku oznake za aplikacije Windows Forms.

// vnesite niz, funkcija c_str() niz s = "abcdef" ; Niz ss; ss = gcnew String(s.c_str()); // Pretvorba label1->Besedilo = ss; // prikaz na obrazcu

V tej lekciji bomo obravnavali nize v slogu C; morda ste te nize že videli na naši spletni strani ali v katerem koli drugem učbeniku. Dejansko so C-nizi samo nizi znakov, vendar s svojimi posebnostmi vedno vemo, kje je konec vrstice. V tem članku si bomo ogledali več funkcij za delo z nizi, na primer, vi - kopirajte, združite, pridobite dolžino niza.

Kaj so strune?

Upoštevajte, da poleg nizov v slogu C, ki so v bistvu preprosti nizi, obstajajo tudi literali nizov, kot je ta "literal". V resnici so tako nizi kot literali preprosto nizi znakov, ki se nahajajo drug ob drugem v pomnilniku računalnika. Še vedno pa obstaja razlika med nizi in literali: literalov ni mogoče spremeniti, nizov pa lahko.

Vsaka funkcija, ki sprejme niz v slogu C, lahko sprejme tudi literal kot parameter. Obstaja tudi nekaj entitet v C, ki so lahko videti kot nizi, čeprav v resnici niso. Zdaj govorim o znakih, ki so v enojnih narekovajih, tukaj je primer - "a", kot lahko vidite, to ni niz. Znak se lahko na določeni lokaciji dodeli nizu, vendar znakov ni mogoče obdelati kot niz. Če se spomnite, matrike delujejo kot kazalci, tako da, če v niz podate en sam znak, bo to obravnavano kot napaka.

Iz zgoraj navedenega bi morali ugotoviti, da so nizi nizi znakov, nizovni literali pa besede, obdane z dvojnimi narekovaji. Tu je še en primer dobesednega izraza:

"To je statični niz"

Ste pozabili na specifičnost strun, ki je bila omenjena malo višje? Torej se morajo C-nizi vedno končati z ničelnim znakom, dobesedno »\0«. Če želite torej deklarirati niz, sestavljen iz 49 črk, morate rezervirati dodatno celico za ničelni znak:

Char myString;

Kot lahko vidite iz primera, je dolžina matrike 50 znakov, od katerih bo 49 vrstica in en, zadnji, bo ničelni znak. Pomembno si je zapomniti, da mora biti na koncu C-vrstic vedno ničelni znak, tako kot je na koncu vsakega stavka pika. Čeprav ničelni znak ni prikazan, ko je niz izpisan, še vedno zavzame prostor v pomnilniku. Tehnično lahko torej v nizu petdesetih elementov shranite samo 49 črk, ker je zadnji znak potreben za zaključek niza. Poleg tega se lahko kazalci uporabljajo tudi kot niz. Če ste prebrali članek o , lahko naredite nekaj takega:

Char *myString; // kazalec tipa char myString = malloc(sizeof(*myString) * 64); // dodelitev pomnilnika

V tem primeru smo matriki myString dodelili 64 pomnilniških mest. Za sprostitev pomnilnika uporabite funkcijo free().

Brezplačno (myString);

Uporaba nizov

Nizi so uporabni, ko morate izvesti različne operacije z besedilnimi informacijami. Na primer, če želite, da uporabnik vnese ime v program, bi uporabili niz. Uporaba scanf() za vnos niza deluje, vendar lahko povzroči prepolnitev medpomnilnika. Navsezadnje je lahko vhodni niz večji od velikosti vmesnega niza. Obstaja več načinov za rešitev tega problema, vendar je najenostavnejši način uporaba , ki je deklarirana v datoteki glave .

Pri branju vnosa uporabnika bo prebral vse znake razen zadnjega. Po tem bo na koncu prebrane vrstice postavljen ničelni zaključek. Funkcija fgets() bo brala znakov, dokler uporabnik ne pritisne Enter. Oglejmo si primer uporabe funkcije fgets():

#vključi int main() ( char myString; // dolg niz printf("Vnesite dolg niz: "); fgets(myString, 100, stdin); // preberite niz iz vhodnega toka printf("Vnesli ste naslednji niz: %s", myString);

Prvi parameter funkcije fgets() je niz, drugi parameter je velikost niza, tretji parameter pa je kazalec na vhodni tok podatkov.

Rezultat programa:

<ВВОД>...

Kot lahko vidite, je iz izhoda programa v vhodno vrstico vstopil znak za novo vrstico - "\n". To se je zgodilo, ker je fgets() preštel pritisk gumba Enter v niz myString in dokončal svoje delo. To pomeni, da boste morda morali ročno odstraniti znak za novo vrstico. Eden od načinov za to je naštevanje po znakih. Spremenimo program in odstranimo znak za novo vrstico:

#vključi int main() ( char myString; // dolg niz printf("Vnesite dolg niz: "); fgets(myString, 100, stdin); // branje niza iz vhodnega toka int i; for (i = 0; jaz< 100; i++) { if (myString[i] == "\n") { myString[i] = "\0"; break; } } printf("Вы ввели следующую строку: %s", myString); getchar(); }

Upoštevajte, da če vnosni niz vsebuje manj kot 100 znakov, bo v niz vključen tudi znak za novo vrstico. Zato lahko ta znak odstranimo s preprosto surovo silo. Programu smo dodali zanko, v kateri se potikamo po znakih niza, vrstice 12-19. In ko naletimo na znak nove vrstice, ga zamenjamo z ničelnim znakom, vrstica 16. Rezultat programa:

Vnesite dolgo vrstico: Usoda pušča svoj pečat Vnesli ste naslednjo vrstico: Usoda pušča svoj pečat Če želite zapreti to okno, kliknite<ВВОД>...

To je vse za zdaj. V naslednjem članku vam bom povedal o posebnih funkcijah za delo z nizi.

P.S.: Vsi radi gledamo različne video posnetke, vendar se včasih zgodi, da nekaterih formatov video datotek ni vedno mogoče predvajati. Torej, to težavo lahko rešite s programom - xilisoft converter ultimate. Videoposnetke lahko preprosto hitro pretvorite iz ene oblike v drugo. Poleg tega lahko ta program pretvori tudi zvočne datoteke in animirane slike.

 
Članki Avtor: tema:
Kako namestiti
To gradivo je namenjeno namestitvi operacijskih sistemov Windows z bliskovnega pogona USB. Mnogi skeptiki še vedno trdijo, da je to nemogoče, in če je mogoče, je zelo težko, a sam obstoj tega materiala dokazuje nasprotno. Win namestitev
Kako spremeniti temo v Firefoxu Tema Firefox
Teme za Mozillo. Kje prenesti in kako namestiti. Videz brskalnika lahko spremenite na različne načine. Povedal vam bom o dveh od njih. Brezplačne teme za Mozilla Firefox Tema bo popolnoma spremenila zasnovo vašega brskalnika. To se naredi zelo preprosto. Dovolj
Pogojniki v PHP
Kot drugi programski jeziki ima PHP izbirne stavke. Skupaj obstajajo tri vrste: pogojni operator if...else ; stikalo stikalo ; pogojno delovanje (? ); Omeniti velja, da PHP nima brezpogojnega skoka na oznako goto, vendar ne more
Ruski postcrossing: napaka narave Kaj je »preklicana znamka« na razglednici
Vsak kulturan človek želi izvedeti nekaj novega o svetu, o ljudeh, ki živijo v drugih državah, o kulturnih tradicijah različnih delov našega čudovitega planeta, o običajih, o hrani, o naravi in ​​še marsičem. Danes, da potešimo vašo radovednost in...