Funcția de comparare a șirurilor în C. Introducerea și ieșirea șirurilor de caractere în C

31.07.2019 Photoshop 3D

Linii. Intrare/ieșire șir. I/O formatat. Procesarea șirurilor folosind funcții standard în limbajul C. Lucrul cu memoria.

1.1. Declararea și inițializarea șirurilor de caractere.

Un șir este o matrice de caractere care se termină cu caracterul gol „\0”. Șirul este declarat ca o matrice obișnuită de caractere, de exemplu,

char s1; // șir lung de nouă caractere

char *s2; // pointer către șir

Diferența dintre pointerii s1 și s2 este că pointerul s1 este o constantă numită, iar pointerul s2 este o variabilă.

Constantele șirurilor sunt cuprinse între ghilimele duble, spre deosebire de caractere, care sunt incluse în ghilimele simple. De exemplu,

„Acesta este un șir.”

Lungime constantă de șir nu poate depăși 509 caractere conform standardului. Cu toate acestea, multe implementări permit lungimi mai mari ale șirurilor.

La inițializarea șirurilor, este mai bine să nu specificați dimensiunea matricei, compilatorul va face acest lucru calculând lungimea șirului și adăugând unul; De exemplu,

char s1 = „Acesta este un șir.”;

În limbajul de programare C, există o modalitate de a lucra cu șiruri. număr mare funcții ale căror prototipuri sunt descrise în fișierele de antet stdlib.h și string.h. Lucrul cu aceste funcții va fi discutat în paragrafele următoare.

1.2. Intrare/ieșire șir.

Pentru a introduce un șir din consolă, utilizați funcția

char* gets(char *str);

care scrie un șir la adresa str și returnează adresa șirului introdus. Funcția oprește introducerea dacă întâlnește un caracter „\n” sau EOF (sfârșitul fișierului). Mergeți la simbol linie nouă nu copiat. Un octet zero este plasat la sfârșitul liniei de citire. Dacă are succes, funcția returnează un pointer la linia citită, iar dacă nu are succes, NULL.

Pentru a scoate un șir în consolă, utilizați funcția standard

int pune (const char *s);

care, dacă are succes, returnează un număr nenegativ, iar dacă nu are succes, returnează EOF.

Prototipurile funcțiilor gets și puts sunt descrise în fișierul antet stdio.h.

#include

printf("Șir de intrare: ");

1.3. I/O formatat.

Pentru introducerea datelor formatate din consolă, utilizați funcția

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

care, dacă are succes, returnează numărul de unități de date citite, iar dacă nu are succes, returnează EOF. Parametrul format trebuie să indice șirul de formatat, care conține specificațiile formatului de intrare. Numărul și tipurile de argumente care urmează șirul de format trebuie să se potrivească cu numărul și tipurile de formate de intrare specificate în șirul de format. Dacă această condiție nu este îndeplinită, atunci rezultatul funcției este imprevizibil.

Spațiul, caracterele „\t” sau „\n” din șirul de format descriu unul sau mai multe caractere goale din fluxul de intrare, care includ următoarele caractere: spațiu, „\t”, „\n”, „\v” , '\f '. Funcția scanf omite caracterele goale din fluxul de intrare.

Caracterele literale dintr-un șir de format, cu excepția caracterului %, necesită ca exact aceleași caractere să apară în fluxul de intrare. Dacă nu există un astfel de caracter, funcția scanf se oprește. Funcția scanf omite caracterele literale.

ÎN caz general Specificația formatului de intrare este:

%[*] [lățime] [modificatoare].

Simbolul „*” denotă omisiune la introducerea unui câmp definit de această specificație;

- „lățimea” definește număr maxim caractere introduse conform acestei specificații;

Tipul poate lua următoarele valori:

c – matrice de caractere,

s – un șir de caractere, liniile sunt separate prin caractere goale,

d – întreg cu semn de 10 s/s,

i – întreg cu semn, sistemul numeric depinde de primele două cifre,

u – întreg fără semn la 10 s/s,

o – întreg fără semn în 8 s/s,

x, X – întreg fără semn la 16 s/s,

e, E, f, g, G – număr flotant,

p – pointer la pointer,

n – indicatorul către un număr întreg,

[…] – o serie de caractere scanate, de exemplu, .

În acest din urmă caz, din fluxul de intrare vor fi introduse numai caracterele cuprinse între paranteze drepte. Dacă primul caracter din paranteze pătrate este „^”, atunci sunt introduse numai acele caractere care nu sunt în matrice. Intervalul de caractere din matrice este specificat folosind simbolul „-”. Când introduceți caractere, sunt introduse și caracterele necompletate de început și octetul nul final al șirului.

Modificatorii pot lua următoarele valori:

h – număr întreg scurt,

l, L – întreg lung sau flotant,

și sunt folosite numai pentru numere întregi sau flotante.

Următorul exemplu arată utilizări ale funcției scanf. Rețineți că specificatorul de format, începând cu introducerea numărului flotant, este precedat de un caracter spațiu.

#include

printf("Introduceți un număr întreg: ");

scanf("%d", &n);

printf("Introduceți un dublu: ");

scanf(" %lf", &d);

printf("Introduceți un caracter: ");

scanf(" %c", &c);

printf("Introduceți un șir: ");

scanf(" %s", &s);

Rețineți că în acest program numărul în virgulă mobilă este inițializat. Acest lucru se face astfel încât compilatorul să includă biblioteca pentru a sprijini lucrul cu numere flotante. Dacă nu se face acest lucru, va apărea o eroare în timpul rulării la introducerea unui număr flotant.

Pentru ieșirea formatată a datelor către consolă, utilizați funcția

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

care, dacă are succes, returnează numărul de unități de date de ieșire, iar dacă nu are succes, returnează EOF. Parametrul format este un șir de format care conține specificații pentru formatele de ieșire. Numărul și tipurile de argumente care urmează șirul de format trebuie să se potrivească cu numărul și tipurile de specificații de format de ieșire specificate în șirul de format. În general, specificația formatului de ieșire arată astfel:

%[steaguri] [lățime] [.precizie] [modificatoare].

- ‘steaguri’ sunt diferite simboluri care specifică formatul de ieșire;

- „lățimea” definește numărul minim de caractere ieșite conform acestei specificații;

- ‘.precision’ definește numărul maxim de caractere afișate;

- ‘modificatorii’ specifică tipul de argumente;

- „tip” specifică tipul argumentului.

Pentru a scoate numere întregi cu semn, utilizați următorul format ieșire:

%[-] [+ | spațiu] [lățime] [l] d

- – aliniere stânga, implicit – dreapta;

+ – este afișat semnul „+”, rețineți că pentru numere negative semnul „-” este întotdeauna afișat;

‘spațiu’ – un spațiu este afișat la poziția caracterului;

d – int tipul de date.

Pentru a scoate numere întregi fără semn, utilizați următorul format de ieșire:

%[-] [#] [lățime] [l]

# – 0 inițial este afișat pentru numerele din 8 c/c sau inițial 0x sau 0X pentru numerele din 16 c/c,

l – modificator de tip de date lung;

u – întreg în 10c/c,

o – întreg în 8 c/c,

x, X – întreg la 16 c/c.

Următorul format de ieșire este utilizat pentru a afișa numere în virgulă mobilă:

%[-] [+ | spațiu] [lățime] [.precizie]

„precizie” - indică numărul de cifre după virgulă pentru formatele f, e și E sau numărul de cifre semnificative pentru formatele g și G. Numerele sunt rotunjite. Precizia implicită este de șase cifre zecimale;

f – numărul punctului fix,

e – un număr în formă exponențială, exponentul este notat cu litera „e”,

E – un număr în formă exponențială, exponentul este notat cu litera „E”,

g – cel mai scurt dintre formatele f sau g,

G – cel mai scurt dintre formatele f sau 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);

// afișează: n = 123 f = 12,340000 e = 1,234000e+001 E = 1,234000E+001 f = 12,34

1.4. Formatarea șirurilor.

Există variante ale funcțiilor scanf și printf care sunt concepute pentru a formata șiruri și sunt numite sscanf și, respectiv, sprintf.

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

citește datele din șirul specificat de str, conform șirului de format specificat de format. Dacă are succes, returnează numărul de date citite, iar dacă nu are succes, returnează EOF. De exemplu,

#include

char str = "a 10 1.2 String Nicio intrare";

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

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

printf("%d\n", n); // imprimări: 10

printf("%f\n", d); // imprimări: 1,200000

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

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

formatează șirul în conformitate cu formatul specificat de parametrul format și scrie rezultatul rezultat în tamponul matricei de caractere. Funcția returnează numărul de caractere scrise în bufferul matricei de caractere, excluzând octetul nul final. De exemplu,

#include

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

char s = "Acesta este un șir.";

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

printf("%s\n", buffer); // afișează: c = c, n = 10, d = 1,200000, s = Acesta este un șir

1.5. Convertiți șirurile de caractere în date numerice.

Prototipurile de funcții pentru conversia șirurilor de caractere în date numerice sunt date în fișierul antet stdlib.h, care trebuie inclus în program.

Pentru a converti un șir într-un număr întreg, utilizați funcția

int atoi (const char *str);

char *str = „-123”;

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

Pentru a converti un șir într-un întreg lung, utilizați funcția

long int atol (const char *str);

care, dacă are succes, returnează întregul în care este convertit șirul str, iar dacă nu are succes, returnează 0. De exemplu,

char *str = „-123”;

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

Pentru a converti un șir într-un număr dublu, utilizați funcția

double atof(const char *str);

care, în caz de succes, returnează un număr flotant de tip double, în care este convertit șirul str, iar în caz de eșec, 0. De exemplu,

char *str = „-123.321”;

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

Următoarele funcții îndeplinesc funcții similare cu atoi, atol, atof, dar oferă funcționalități mai avansate.

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

convertește șirul str într-un număr tip lung int, ceea ce returnează. Parametrii acestei funcții au următoarele scopuri.

Dacă baza este 0, atunci conversia depinde de primele două caractere ale str:

Dacă primul caracter este un număr de la 1 la 9, atunci se presupune că numărul este reprezentat în 10 c/c;

Dacă primul caracter este cifra 0 și al doilea caracter este o cifră de la 1 la 7, atunci se presupune că numărul este reprezentat în 8 c/c;

Dacă primul caracter este 0 și al doilea este „X” sau „x”, atunci se presupune că numărul este reprezentat în 16 c/c.

Dacă baza este un număr între 2 și 36, atunci acea valoare este considerată baza sistemului numeric și orice caracter din afara sistemului numeric încetează conversia. În sistemele numerice de la baza 11 la baza 36, ​​simbolurile „A” la „Z” sau „a” la „z” sunt folosite pentru a reprezenta cifre.

Valoarea argumentului endptr este stabilită de funcția strtol. Această valoare conține un indicator către caracterul care a oprit conversia șirului de caractere. Funcția strtol returnează numărul convertit dacă are succes și 0 dacă nu are succes.

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

funcționează similar cu funcția strtol, dar convertește reprezentarea simbolică a unui număr într-un număr de tip unsigned long int.

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

Transformă reprezentarea simbolică a unui număr într-un dublu.

Toate funcțiile enumerate în acest paragraf nu mai funcționează atunci când întâlnesc primul caracter care nu se potrivește cu formatul numărului în cauză.

În plus, dacă valoarea caracterului unui număr depășește intervalul de valori acceptabile pentru tipul de date corespunzător, atunci funcțiile atof, strtol, strtoul, strtod setează valoarea variabilei errno la ERANGE. Variabila errno și constanta ERANGE sunt definite în fișierul antet math.h. În acest caz, funcțiile atof și strtod returnează valoarea HUGE_VAL, funcția strtol returnează valoarea LONG_MAX sau LONG_MIN, iar funcția strtoul returnează valoarea ULONG_MAX.

Funcțiile non-standard itoa, ltoa, utoa, ecvt, fcvt și gcvt pot fi utilizate pentru a converti datele numerice în șiruri de caractere. Dar este mai bine să folosiți funcția standard sprintf în aceste scopuri.

1.6. Funcții standard pentru lucrul cu șiruri.

Această secțiune discută funcțiile pentru lucrul cu șiruri de caractere, ale căror prototipuri sunt descrise în fișierul antet string.h.

1. Comparație de șiruri. Funcțiile strcmp și strncmp sunt folosite pentru a compara șiruri.

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

compară lexicografic șirurile str1, str2 și returnează –1, 0 sau 1 dacă str1 este, respectiv, mai mic, egal sau mai mare decât str2.

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

compară lexicografic cel mult primele n caractere din șirurile str1 și str2. Funcția returnează -1, 0 sau 1 dacă primele n caractere din str1 sunt, respectiv, mai mici, egale sau mai mari decât primele n caractere din str2.

// exemplu de comparație de șiruri

#include

#include

char str1 = "aa bb";

char str2 = "aa aa";

char str3 = "aa bb cc";

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

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

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

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

2. Copierea liniilor. Funcțiile strcpy și strncpy sunt folosite pentru a copia șiruri.

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

copiează șirul str2 în șirul str1. Întregul șir str2 este copiat, inclusiv octetul nul final. Funcția returnează un pointer către str1. Dacă liniile se suprapun, rezultatul este imprevizibil.

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

copiază n caractere din șirul str2 în șirul str1. Dacă str2 conține mai puțin de n caractere, atunci ultimul octet zero este copiat de câte ori este necesar pentru a extinde str2 la n caractere. Funcția returnează un pointer către șirul str1.

char str2 = "Copiați șirul.";

strcpy(str1, str2);

printf(str1); // imprimă: Copiați șirul.

4. Corzi de legătură. Funcțiile strcat și strncat sunt folosite pentru a concatena șiruri într-un singur șir.

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

adaugă șirul str2 la șirul str1, cu octetul zero final al șirului str1 șters. Funcția returnează un pointer către șirul str1.

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

adaugă n caractere din șirul str2 la șirul str1, cu octetul zero de la urmă al șirului str1 șters. Funcția returnează un pointer către șirul str1. dacă lungimea șirului str2 este mai mică decât n, atunci sunt atașate numai caracterele incluse în șirul str2. După concatenarea șirurilor, un octet nul este întotdeauna adăugat la str1. Funcția returnează un pointer către șirul str1.

#include

#include

char str1 = „Șir”;

char str2 = "catenare";

char str3 = "Da Nu";

strcat(str1, str2);

printf("%s\n", str1); // printează: String catenation

strncat(str1, str3, 3);

printf("%s\n", str1); // imprimă: String catenation Da

5. Căutați un caracter într-un șir. Pentru a căuta un caracter într-un șir, utilizați funcțiile strchr, strrchr, strspn, strcspn și strpbrk.

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

caută prima apariție a caracterului specificat de c în șirul str. Dacă are succes, funcția returnează un pointer la primul caracter găsit, iar dacă nu are succes, NULL.

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

caută ultima apariție a caracterului specificat de c în șirul str. Dacă are succes, funcția returnează un pointer la ultimul caracter găsit, iar dacă nu are succes, NULL.

#include

#include

char str = "Căutare Char";

printf("%s\n", strchr(str, "r")); // imprimă: r căutare

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

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

returnează indexul primului caracter din str1 care nu este în str2.

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

returnează indexul primului caracter din str1 care apare în str2.

char str = "123 abc";

printf ("n = %d\n", strspn (str, "321"); // afișează: n = 3

printf ("n = %d\n", strcspn (str, "cba"); // afișează: n = 4

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

găsește primul caracter din șirul str1 care este egal cu unul dintre caracterele din șirul str2. Dacă are succes, funcția returnează un pointer la acest caracter, iar dacă nu are succes, NULL.

char str = "123 abc";

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

6. Comparație de șiruri. Funcția strstr este folosită pentru a compara șiruri.

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

găsește prima apariție a lui str2 (fără octetul nul final) în str1. Dacă are succes, funcția returnează un pointer către subșirul găsit, iar dacă nu are succes, NULL. Dacă pointerul str1 indică un șir de lungime zero, atunci funcția returnează pointerul str1.

char str = "123 abc 456;

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

7. Analizarea unui șir în jetoane. Funcția strtok este folosită pentru a analiza un șir în token-uri.

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

returnează un pointer la următorul token (cuvânt) din șirul str1, în care delimitatorii jetonului sunt caractere din șirul str2. Dacă nu mai există jetoane, funcția returnează NULL. La primul apel la funcția strtok, parametrul str1 trebuie să indice un șir care este tokenizat, iar la apelurile ulterioare acest parametru trebuie setat la NULL. După găsirea unui token, funcția strtok scrie un octet nul după acest token în locul delimitatorului.

#include

#include

char str = "12 34 ab cd";

p = strtok(str, " ");

printf("%s\n", p); // tipărește valorile într-o coloană: 12 34 ab cd

p = strtok(NULL, " ");

8. Determinarea lungimii unui șir. Funcția strlen este utilizată pentru a determina lungimea unui șir.

size_t strlen (const char *str);

returnează lungimea șirului, ignorând ultimul octet nul. De exemplu,

char str = "123";

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

1.7. Funcții pentru lucrul cu memoria.

Fișierul antet string.h descrie, de asemenea, funcții pentru lucrul cu blocuri de memorie, care sunt similare cu funcțiile corespunzătoare pentru lucrul cu șiruri.

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

caută prima apariție a caracterului specificat de c în n octeți ai șirului str.

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

compară primii n octeți ai șirurilor str1 și str2.

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

copiează primii n octeți din șirul str1 în șirul str2.

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

copiează primii n octeți de la str1 la str2, asigurându-se că șirurile care se suprapun sunt gestionate corect.

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

copiază caracterul specificat de c în primii n octeți ai str.

Biblioteca de funcții C și C++ include un set bogat de funcții de procesare a șirurilor și a caracterelor. Funcțiile șiruri de caractere operează pe matrice de caractere terminate cu caractere nule. În limbaj C pentru utilizare funcții șir este necesar să includeți un fișier antet la începutul modulului de program , iar pentru cele simbolice - fișierul antet . C++ folosește anteturi pentru a lucra cu funcții șir și caractere Şi respectiv. Acest capitol folosește nume de antet C pentru a facilita prezentarea.

Deoarece limbajele C și C++ nu controlează automat încălcarea limitelor lor atunci când efectuează operațiuni cu matrice, toată responsabilitatea pentru depășirea matricei cade pe umerii programatorului. Neglijarea acestor subtilități poate duce la blocarea programului.

În C și C++, caracterele imprimabile sunt caracterele afișate pe terminal. În mediile ASCII, acestea sunt situate între spațiu (0x20) și tilde (OxFE). Caracterele de control au valori cuprinse între zero și Ox1F; acestea includ și simbolul DEL(Ox7F).

Din punct de vedere istoric, argumentele funcțiilor de caractere au fost valori întregi, dintre care a fost folosit doar octetul mic. Funcțiile de caractere își convertesc automat argumentele în caracter nesemnat. Desigur, sunteți liber să apelați aceste funcții cu argumente de caractere, deoarece caracterele sunt ridicate automat la rangul de numere întregi atunci când funcția este apelată.

În titlu este definit tipul size_t, care este rezultatul aplicării operatorului sizeof și este un tip de întreg fără semn.

C99 a adăugat calificativul de restricție la unii parametri ai mai multor funcții definite inițial în C89. La revizuirea fiecărei astfel de funcții, va fi dat prototipul său utilizat în mediul C89 (precum și în mediul C++), iar parametrii cu atributul restrict vor fi notați în descrierea acestei funcții.

Lista de funcții

Verificați afilierea

isalnum - Verificarea dacă un caracter este alfanumeric
isalpha - Verificarea dacă un simbol aparține literelor
isblank - Verificați dacă există un caracter gol
iscntrl - Verificarea dacă un simbol aparține simbolurilor de control
isdigit - Verificarea dacă un caracter este digital
isgraph - Verifică dacă un caracter este imprimabil, dar nu un spațiu
islower - Verifică dacă un caracter este scris cu minuscule
isprint - Verificarea dacă un caracter este imprimabil
ispunct - Verificarea dacă un simbol aparține semnelor de punctuație
isspace - Verificarea dacă un caracter este un caracter alb
isupper - Verifică dacă un caracter este cu majuscule
isxdigit - Verificați dacă un caracter este hexazecimal

Lucrul cu matrice de caractere

memchr - Parcurge o matrice pentru a găsi prima apariție a unui caracter
memcmp - Compară un număr specificat de caractere din două matrice
memcpy - Copiază caracterele dintr-o matrice în alta
memmove - Copiază caracterele dintr-o matrice în alta, ținând cont de suprapunerea matricei
memset - Completează un număr specificat de caractere dintr-o matrice cu un număr dat

Manipularea șirurilor

strcat - Adaugă o copie a unui singur șir la un șir dat
strchr - Returnează un pointer la prima apariție a octetului inferior al parametrului dat
strcmp - Compară două șiruri în ordine lexicografică
strcoll - Compară un șir cu altul în funcție de parametrul setlocale
strcpy - Copiază conținutul unui șir în altul
strcspn - Returnează un șir care nu conține caracterele specificate
strerror - Returnează un pointer către un șir care conține un mesaj de eroare de sistem
strlen - Returnează lungimea terminată în nul a unui șir

Habra, salut!

Nu cu mult timp în urmă, mi s-a întâmplat un incident destul de interesant, în care a fost implicat unul dintre profesorii unei facultăți de informatică.

Conversația despre programarea Linux a progresat încet la această persoană, susținând că complexitatea programării sistemelor a fost de fapt foarte exagerată. Că limbajul C este la fel de simplu ca o potrivire, de fapt, ca nucleul Linux (în cuvintele lui).

Aveam la mine un laptop cu Linux, care conținea un set de utilități pentru dezvoltare în limbajul C (gcc, vim, make, valgrind, gdb). Nu-mi amintesc ce obiectiv ne-am propus atunci, dar după câteva minute adversarul meu s-a trezit la acest laptop, complet gata să rezolve problema.

Și literalmente la primele rânduri a făcut o greșeală gravă când a alocat memorie la... o linie.

Char *str = (char *)malloc(sizeof(char) * strlen(buffer));
buffer - o variabilă de stivă în care au fost scrise datele de la tastatură.

Cred că vor exista cu siguranță oameni care se vor întreba: „Cum ar putea fi ceva în neregulă cu asta?”
Crede-mă, se poate.

Și ce anume - citiți despre pisică.

Puțină teorie - un fel de LikBez.

Dacă știți, derulați la următorul antet.

Un șir în C este o matrice de caractere, care ar trebui să se termine întotdeauna cu „\0” - caracterul de sfârșit de linie. Șirurile din stivă (statice) sunt declarate astfel:

Char str[n] = (0);
n este dimensiunea matricei de caractere, la fel ca lungimea șirului.

Atribuire ( 0 ) - „reducerea la zero” a șirului (opțional, îl puteți declara fără el). Rezultatul este același cu rularea funcțiilor memset(str, 0, sizeof(str)) și bzero(str, sizeof(str)). Este folosit pentru a preveni lăsarea gunoiului în variabilele neinițializate.

De asemenea, puteți inițializa imediat un șir pe stivă:

Char buf = "text tampon implicit\n";
În plus, un șir poate fi declarat ca un pointer și poate fi alocată memorie pentru el pe heap:

Char *str = malloc(dimensiune);
dimensiune - numărul de octeți pe care îi alocăm pentru șir. Astfel de șiruri sunt numite dinamice (datorită faptului că mărimea potrivită calculat dinamic + dimensiunea memoriei alocată poate fi mărită în orice moment folosind funcția realloc()).

În cazul unei variabile stive, am folosit notația n pentru a determina dimensiunea matricei, în cazul unei variabile heap, am folosit dimensiunea notației. Și aceasta reflectă perfect esența adevărată a diferenței dintre o declarație pe stivă și o declarație cu alocare de memorie pe heap, deoarece n este de obicei folosit când se vorbește despre numărul de elemente. Și dimensiunea este o cu totul altă poveste...

Valgrind ne va ajuta

În articolul meu anterior am menționat și asta. Valgrind (, doi - mic cum să) - foarte program util, care îl ajută pe programator să urmărească scurgerile de memorie și erorile de context - exact lucrurile care apar cel mai adesea când lucrează cu șiruri.

Să ne uităm la o scurtă listă care implementează ceva similar cu programul pe care l-am menționat și să o rulăm prin valgrind:

#include #include #include #define HELLO_STRING "Bună ziua, Habr!\n" void main() ( char *str = malloc(sizeof(char) * strlen(HELLO_STRING)); strcpy(str, HELLO_STRING); printf("->\t%s" , str);
Și, de fapt, rezultatul programului:

$ gcc main.c $ ./a.out -> Bună, Habr!
Nimic neobișnuit încă. Acum să rulăm acest program cu valgrind!

$ valgrind --tool=memcheck ./a.out ==3892== Memcheck, un detector de erori de memorie ==3892== Copyright (C) 2002-2015 și GNU GPL"d, de Julian Seward și colab. == 3892== Folosind Valgrind-3.12.0 și LibVEX din nou cu -h pentru informații despre copyright ==3892== Comandă: ./a.out ==3892== ==3892== Scriere nevalidă de dimensiunea 2 ==3892= = la 0x4005B4: principal (în /home/indever/prg/C/public/a.out) ==3892== Adresa 0x520004c are 12 octeți în interiorul unui bloc de dimensiunea 13 alloc"d ==3892== la 0x4C2DB9D: malloc (vg_replace_malloc.c:299) ==3892== de 0x400597: main (în /home/indever/prg/C/public/a.out) ==3892== ==3892== Citire nevalidă pentru dimensiunea 1 == 3892== la 0x4C30BC4: strlen (vg_replace_strmem.c:454) ==3892== de 0x4E89AD0: vfprintf (în /usr/lib64/libc-2.24.so) ==3892== prin 0x4E89AD0 (în /usr/lib64/libc-2.24.so) ==3892== prin 0x4E89AD0 lib64/libc-2.24.so) ==3892== de 0x4005CF: main (în /home/indever/prg/C/public/a.out) ==3892== Adresa 0x520004d este de 0 octeți după un bloc de dimensiunea 13 alloc"d ==3892== la 0x4C2DB9D: malloc (vg_replace_malloc.c:299) ==3892== prin 0x400597: main (în /home/inever/prg/C/public/a.out) ==3892== -> Bună, Habr! ==3892== ==3892== REZUMAT HEAP: ==3892== în uz la ieșire: 0 octeți în 0 blocuri ==3892== utilizare totală heap: 2 alocări, 2 libere, 1.037 octeți alocați ==3892= = ==3892== Toate blocurile heap au fost eliberate -- nu sunt posibile scurgeri ==3892== ==3892== Pentru numărul erorilor detectate și suprimate, reluați cu: -v ==3892== REZUMAT ERORI: 3 erori din 2 contexte (suprimat: 0 din 0)
==3892== Toate blocurile heap au fost eliberate - nu sunt posibile scurgeri- nu există scurgeri, iar aceasta este o veste bună. Dar merită să-ți cobori ochii puțin mai jos (deși, vreau să remarc, acesta este doar rezumatul, informația principală este un loc puțin diferit):

==3892== REZUMAT ERORI: 3 erori din 2 contexte (suprimat: 0 din 0)
3 greseli. În 2 contexte. Într-un program atât de simplu. Cum!?

Da, foarte simplu. Întregul „lucru amuzant” este că funcția strlen nu ia în considerare caracterul de sfârșit de linie - „\0”. Chiar dacă îl specificați în mod explicit în linia de intrare (#define HELLO_STRING „Hello, Habr!\n\0”), va fi ignorat.

Chiar deasupra rezultatului execuției programului, linia -> Bună, Habr! există un raport detaliat despre ce și unde nu i-a plăcut prețiosului nostru valgrind. Vă sugerez să vă uitați singur la aceste rânduri și să trageți propriile concluzii.

De fapt, versiunea corecta programul va arăta astfel:

#include #include #include #define HELLO_STRING „Bună ziua, Habr!\n” void main() ( char *str = malloc(sizeof(char) * (strlen(HELLO_STRING) + 1)); strcpy(str, HELLO_STRING); printf("->\ t%s", str); liber(str); )
Să o trecem prin valgrind:

$ valgrind --tool=memcheck ./a.out -> Bună, Habr! ==3435== ==3435== REZUMAT HEAP: ==3435== în uz la ieșire: 0 octeți în 0 blocuri ==3435== utilizare totală a heap: 2 alocări, 2 libere, 1.038 octeți alocați ==3435= = ==3435== Toate blocurile heap au fost eliberate -- nu sunt posibile scurgeri ==3435== ==3435== Pentru numărul erorilor detectate și suprimate, reluați cu: -v ==3435== REZUMAT ERORI: 0 erori din 0 contexte (suprimat: 0 din 0)
Mare. Nu există erori, +1 octet de memorie alocată a ajutat la rezolvarea problemei.

Ceea ce este interesant este că, în majoritatea cazurilor, atât primul cât și cel de-al doilea program vor funcționa la fel, dar dacă memoria alocată pentru linia în care caracterul final nu se potrivea nu a fost pusă la zero, atunci funcția printf() la scoaterea unei astfel de linii , va scoate tot gunoiul după această linie - totul va fi tipărit până când un caracter de sfârșit de linie iese în calea printf().

Cu toate acestea, știți, (strlen(str) + 1) este o astfel de soluție. Ne confruntăm cu 2 probleme:

  1. Ce se întâmplă dacă trebuie să alocam memorie pentru un șir generat folosind, de exemplu, s(n)printf(..)? Nu susținem argumentele.
  2. Aspect. Linia de declarație variabilă arată îngrozitor. Unii băieți reușesc să atașeze (char *) la malloc, de parcă ar scrie sub plusuri. Într-un program în care trebuie să procesați în mod regulat șiruri, este logic să găsiți o soluție mai elegantă.
Să venim cu o soluție care să ne mulțumească atât pe noi, cât și pe valgrind.

snprintf()

int snprintf(char *str, size_t size, const char *format, ...);- o funcție - o extensie a sprintf, care formatează un șir și îl scrie în pointerul trecut ca prim argument. Diferă de sprintf() prin faptul că str nu va scrie un octet mai mare decât dimensiunea specificată.

Funcția are unul caracteristică interesantă- în orice caz, returnează dimensiunea șirului generat (fără a lua în considerare caracterul de final de linie). Dacă șirul este gol, atunci este returnat 0.

Una dintre problemele pe care le-am descris cu utilizarea strlen este legată de funcțiile sprintf() și snprintf(). Să presupunem că trebuie să scriem ceva în șirul str. Linia finală conține valorile celorlalte variabile. Intrarea noastră ar trebui să fie cam așa:

Char * str = /* alocă memorie aici */; sprintf(str, „Bună ziua, %s\n”, „Habr!”);
Apare întrebarea: cum să determinați câtă memorie ar trebui alocată pentru șirul str?

Char * str = malloc(sizeof(char) * (strlen(str, "Bună ziua, %s\n", "Habr!") + 1));

#include - nu va merge. Prototipul funcției strlen() arată astfel:
const char *s nu implică faptul că șirul transmis la s poate fi un șir în format variadic.

Acest lucru ne va ajuta aici proprietate utilă funcția snprintf() pe care am menționat-o mai sus. Să ne uităm la codul următorului program:

#include #include #include void main() ( /* Deoarece snprintf() nu ia în considerare caracterul de sfârșit de linie, adăugăm dimensiunea acestuia la rezultat */ size_t needed_mem = snprintf(NULL, 0, "Bună ziua, %s!\n", "Habr") + sizeof("\0"); char *str = malloc(needed_mem); ;
Rulați programul în valgrind:

$ valgrind --tool=memcheck ./a.out -> Bună, Habr! ==4132== ==4132== REZUMAT HEAP: ==4132== în uz la ieșire: 0 octeți în 0 blocuri ==4132== utilizare totală a heap: 2 alocări, 2 libere, 1.041 octeți alocați ==4132= = ==4132== Toate blocurile heap au fost eliberate -- nu sunt posibile scurgeri ==4132== ==4132== Pentru numărul erorilor detectate și suprimate, reluați cu: -v ==4132== REZUMAT ERORI: 0 erori din 0 contexte (suprimat: 0 din 0) $
Mare. Avem susținere a argumentelor. Datorită faptului că trecem null ca al doilea argument la funcția snprintf(), scrierea la un pointer nul nu va provoca niciodată un Seagfault. Cu toate acestea, în ciuda acestui fapt, funcția va returna în continuare dimensiunea necesară pentru șir.

Dar, pe de altă parte, a trebuit să introducem o variabilă suplimentară și designul

Size_t needed_mem = snprintf(NULL, 0, "Bună ziua, %s!\n", "Habr") + sizeof("\0");
arată chiar mai rău decât în ​​cazul lui strlen().

În general, + sizeof("\0") poate fi eliminat dacă specificați în mod explicit "\0" la sfârșitul liniei de format (size_t needed_mem = snprintf(NULL, 0, "Salut, %s!\n) \0 ", "Habr");), dar acest lucru nu este întotdeauna posibil (în funcție de mecanismul de procesare a șirurilor, putem aloca un octet suplimentar).

Trebuie să facem ceva. M-am gândit puțin și am decis că acum este momentul să apelez la înțelepciunea anticilor. Să descriem o funcție macro care va apela snprintf() cu un pointer nul ca prim argument și nul ca al doilea. Și să nu uităm de sfârșitul firului!

#define strsize(args...) snprintf(NULL, 0, args) + sizeof("\0")
Da, poate fi o noutate pentru unii, dar macro-urile C acceptă un număr variabil de argumente, iar punctele de suspensie îi spun preprocesorului că argumentul funcției macro specificat (în cazul nostru, args) corespunde mai multor argumente reale.

Să verificăm soluția noastră în practică:

#include #include #include #define strsize(args...) snprintf(NULL, 0, args) + sizeof("\0") void main() ( char *str = malloc(strsize("Bună ziua, %s\n", "Habr! ")); sprintf(str, "Bună ziua, %s\n", "Habr!"); printf("->\t%s", str); liber(str); )
Să începem cu valgrund:

$ valgrind --tool=memcheck ./a.out -> Bună, Habr! ==6432== ==6432== REZUMAT HEAP: ==6432== în uz la ieșire: 0 octeți în 0 blocuri ==6432== utilizare totală a heap: 2 alocări, 2 libere, 1.041 octeți alocați ==6432= = ==6432== Toate blocurile heap au fost eliberate -- nu sunt posibile scurgeri ==6432== ==6432== Pentru numărul erorilor detectate și suprimate, reluați cu: -v ==6432== REZUMAT ERORI: 0 erori din 0 contexte (suprimat: 0 din 0)
Da, nu există erori. Totul este corect. Și valgrind este fericit, iar programatorul poate să se culce în sfârșit.

Dar, în sfârșit, voi mai spune un lucru. În cazul în care trebuie să alocăm memorie pentru orice șir (chiar și cu argumente), există deja functioneaza pe deplin soluție gata făcută .

Este vorba despre despre funcția asprintf:

#define _GNU_SOURCE /* Vezi feature_test_macros(7) */ #include int asprintf(char **strp, const char *fmt, ...);
Ia un pointer către un șir (**strp) ca prim argument și alocă memorie pointerului dereferențiat.

Programul nostru scris folosind asprintf() va arăta astfel:

#include #include #include void main() ( char *str; asprintf(&str, „Bună ziua, %s!\n”, „Habr”); printf(“->\t%s”, str); liber(str); )
Și, de fapt, în valgrind:

$ valgrind --tool=memcheck ./a.out -> Bună, Habr! ==6674== ==6674== REZUMAT HEAP: ==6674== în uz la ieșire: 0 octeți în 0 blocuri ==6674== utilizare totală a heap: 3 alocări, 3 liberări, 1.138 octeți alocați ==6674= = ==6674== Toate blocurile heap au fost eliberate -- nu sunt posibile scurgeri ==6674== ==6674== Pentru contorizarea erorilor detectate și suprimate, rulați din nou cu: -v ==6674== REZUMAT ERORI: 0 erori din 0 contexte (suprimat: 0 din 0)
Totul este bine, dar, după cum puteți vedea, a fost alocată mai multă memorie, iar acum există trei alocări, nu două.
În plus, dacă scriem man asprintf în consolă, vom vedea:

CONFORME CU Aceste funcții sunt extensii GNU, nu în C sau POSIX. Sunt disponibile și sub *BSD. Implementarea FreeBSD setează strp la NULL în caz de eroare.

Acest lucru arată clar că această funcție este disponibilă numai în sursele GNU.

Concluzie

În concluzie, vreau să spun că lucrul cu șiruri în C este un subiect foarte complex, care are o serie de nuanțe. De exemplu, pentru a scrie codul „sigur” când alocare dinamică memorie, este totuși recomandat să folosiți funcția calloc() în loc de malloc() - calloc umple memoria alocată cu zerouri. Sau, după alocarea memoriei, utilizați funcția memset(). În caz contrar, gunoiul care a fost localizat inițial în zona de memorie alocată poate cauza probleme în timpul depanării și, uneori, atunci când lucrați cu șirul.

Mai mult de jumătate dintre programatorii C pe care îi cunosc (majoritatea dintre ei sunt începători) care au rezolvat problema alocării de memorie pentru șiruri la cererea mea, au făcut-o într-un mod care a dus în cele din urmă la erori de context. Într-un caz - chiar și la o scurgere de memorie (ei bine, o persoană a uitat să facă free(str), nu se întâmplă nimănui). De fapt, asta m-a determinat să creez această creație pe care tocmai ai citit-o.

Sper că acest articol va fi de folos cuiva. De ce fac atâta tam-tam - nici un limbaj nu este simplu. Peste tot are propriile sale subtilități. Și cu cât cunoști mai multe subtilități ale limbii, cu atât mai bine este codul tău.

Cred că după citirea acestui articol codul tău va deveni puțin mai bun :)
Succes, Habr!

Programatorul spune:

Buna ziua! Am citit articolul tau. Eram foarte trist și amuzant în același timp. Această expresie a ta este deosebit de ucigătoare: „Deoarece o variabilă de tip char este adesea folosită ca matrice, numărul de valori posibile este determinat.” 😆 😆 😆
Nu râd de tine. Crearea unui site web este cu adevărat o ispravă. Vreau doar să vă susțin cu sfaturi și să subliniez câteva greșeli.

1. Valoarea unei variabile de tip char este atribuită după cum urmează:

Iată-l:

Car a = *"A";

Pointerul către matrice este de-adresat și, ca rezultat, valoarea primului element al matricei este returnată, adică. 'O'

2. Resetarea are loc după cum urmează:

Car a = NULL;
char b = ();

//Și așa este șters linia din corpul programului

"" - acest simbol se numește terminator zero. Este plasat la capătul liniei. Tu însuți, fără să știi, ai umplut matricea s1 din articolul tău cu acest simbol. Dar a fost posibil să se atribuie acest simbol numai elementului zero al matricei.

3. Simțiți-vă liber să utilizați terminologia.
Semnul = este o operație de atribuire.
Semnul * este o operație de de-adresare.
Mă refer la acest fragment din articol: „Totul s-a dovedit a fi atât de simplu, trebuia să pui semnul * în fața semnului = și trebuia să declari numărul elementului (zero corespunde primului)”

Nu mă înțelege greșit, articolul nu poate exista în forma sa actuală. Nu fi leneș, rescrie-l.
Ai o mare responsabilitate! Sunt serios. Paginile site-ului dvs. sunt incluse în prima pagină a rezultatelor Yandex. Mulți oameni au început deja să-ți repete greșelile.

Noroc! O poți face!

:
Știu asta de mult timp, este doar dificil să recitesc 200 de articole în mod constant pentru a corecta ceva. Și unele tipuri nepoliticoase scriu în așa fel încât chiar și știind ce este mai bine de corectat, nu sunt deloc dispuși să o corecteze.

Voi fi bucuros să corectez alte erori. corectează inexactitățile dacă apar. Apreciez ajutorul tau. mulțumesc, știu asta de mult timp, este doar dificil să recitesc 200 de articole în mod constant pentru a corecta ceva. Și unele tipuri nepoliticoase scriu în așa fel încât chiar și știind ce este mai bine de corectat, nu sunt deloc dispuși să o corecteze.
Cu caracterul tău b = (); Acest lucru nu este deloc zero. Măcar l-aș verifica.
dacă vorbim despre caracterul nul „” ; Știam bine, când am umplut rândul cu ea, că scopul este să arăt o curățenie reală, și nu ceva vizibil pentru ochi, pentru că rândul include gunoi, care uneori îți stau în cale. Ar trebui să fii mai atent cu termenii, „simbol de terminare nul” sau doar „simbol nul”, nu un terminator))) Și simbolul de terminare sună foarte bine.

Voi moderniza articolul, dar nu voi trece la stilul altcuiva. Dacă cred că este mai ușor pentru un începător să înțeleagă în acest fel decât așa cum își dorește, atunci o voi lăsa așa. Nu mă înțelege greșit nici pe mine. Cuvântul „semn” este mult mai ușor de înțeles și de amintit pentru un începător slab decât definiția și numele fiecărui semn. Nu este deloc greșeală în asta, este un semn - un semn. Mai puțin accent pe unul dă mai mult accent pe celălalt.

Voi fi bucuros să corectez alte erori. corectați eventualele inexactități dacă apar. Apreciez ajutorul tau. Multumesc.

Salut din nou!
vreau sa clarific. Termenul „zero-terminator” (terminator din engleză limitator) a fost folosit de profesorul meu de la universitate. Se pare că asta e școala veche!
Cât despre resetarea rândurilor.
char b = (); Aceasta este cu adevărat o resetare. Întreaga matrice este umplută cu zerouri. Dacă nu mă crezi, verifică-l!
Dacă luăm în considerare o linie în sensul său natural, de zi cu zi, atunci o linie „goală” va fi o linie în care nu există un singur caracter. Prin urmare, în 99,9% din cazuri este suficient să puneți un caracter nul la început. În mod obișnuit, un șir este procesat până la primul caracter nul, iar caracterele care vin după el nu mai sunt importante. Am înțeles că ai vrut să resetați linia. Tocmai am decis să ofer o opțiune clasică testată în timp.

:
Când „De obicei, procesarea șirurilor ajunge până la primul caracter nul și ce caractere vin după acesta nu mai este important” - da, șirul este anulat
Dacă luăm în considerare „reducerea la zero reală a tuturor celulelor unui rând (despre care am scris)” - nu, nu zero, și chiar și primul caracter nu este zero. Am bifat varianta asta. MinGW(CodeBlock) - întreaga matrice oferă caracterul „a”
Nu cred că acesta este un motiv de dezbatere.

Vă rugăm să suspendați AdBlock pe acest site.

Deci, șiruri în limbaj C. Nu sunt prevăzute pentru ei tip separat date, așa cum se face în multe alte limbaje de programare. În C, un șir este o matrice de caractere. Pentru a marca sfârșitul unei linii, se folosește caracterul „\0”, despre care am discutat în ultima parte a acestei lecții. Nu este afișat în niciun fel pe ecran, așa că nu îl veți putea privi.

Crearea și inițializarea unui șir

Deoarece un șir este o matrice de caractere, declararea și inițializarea unui șir sunt similare cu operațiuni similare cu matrice unidimensionale.

Următorul cod ilustrează diferitele moduri de inițializare a șirurilor.

Listarea 1.

Char str;

char str1 = ("Y","o","n","g","C","o","d","e","r","\0");

char str2 = „Bună ziua!”;

char str3 = "Bună ziua!"; Fig.1 Declarația și inițializarea șirurilor de caractere Pe prima linie pur și simplu declarăm o matrice de zece caractere. Nu este chiar o sfoară, pentru că... nu există niciun caracter nul \0, deocamdată este doar un set de caractere.

A doua linie.

Cel mai simplu mod

initializare in frunte. Declaram fiecare simbol separat. Principalul lucru aici este să nu uitați să adăugați caracterul nul \0 .

A treia linie este analogă cu a doua linie. Fii atent la imagine. Deoarece Există mai puține caractere în linia din dreapta decât există elemente în matrice, elementele rămase vor fi umplute cu \0 .

A patra linie. După cum puteți vedea, nu există nicio dimensiune specificată aici. Programul îl va calcula automat și va crea o matrice de caractere de lungimea necesară. În acest caz, caracterul nul \0 va fi inserat ultimul.

Cum se scoate un șir Să extindem codul de mai sus într-un program cu drepturi depline care va afișa liniile create pe ecran.< 10; i = i + 1) printf("%c\t",str[i]); printf("\n"); puts(str1); printf("%s\n",str2); puts(str3); return 0; }


Lista 2. #include int main(void) ( char str; char str1 = ("Y","o","n","g","C","o","d","e","r","r"," \0"); char str2 = "Bună!"; char str3 = "Bună!"; for(int i = 0; i

Fig.2

  • Diverse moduri
  • afișarea unui șir pe ecran
  • După cum puteți vedea, există mai multe modalități de bază de a afișa un șir pe ecran.

utilizați funcția printf cu specificatorul %s

utilizați funcția puts

utilizați funcția fputs, specificând fluxul standard pentru ieșire ca stdout ca al doilea parametru.

Singura nuanță este cu funcțiile put și fputs. Rețineți că funcția puts împachetează ieșirea la următoarea linie, dar funcția fputs nu.

După cum puteți vedea, concluzia este destul de simplă.

Cum se scoate un șir Introducerea șirurilor

Funcția gets întrerupe programul, citește un șir de caractere introdus de la tastatură și îl plasează într-o matrice de caractere, al cărui nume este transmis funcției ca parametru.
Funcția gets se iese cu caracterul corespunzător tastei enter și scris în șir ca un caracter nul.
Ai observat pericolul? Dacă nu, compilatorul vă va avertiza cu drag. Problema este că funcția gets iese numai atunci când utilizatorul apasă enter. Acest lucru este plin de faptul că putem merge dincolo de matrice, în cazul nostru - dacă sunt introduse mai mult de 20 de caractere.
Apropo, erorile de depășire a tamponului erau considerate anterior cel mai comun tip de vulnerabilitate. Se găsesc și astăzi, dar folosirea lor pentru a pirata programe a devenit mult mai dificilă.

Deci ce avem? Avem o sarcină: scriem un șir într-o matrice de dimensiune limitată. Adică trebuie să controlăm cumva numărul de caractere introduse de utilizator. Și aici funcția fgets ne vine în ajutor:

Lista 4.

Cum se scoate un șir int main(void) (char str; fgets(str, 10, stdin); puts(str); return 0; )

Funcția fgets ia trei argumente ca intrare: o variabilă în care să scrieți șirul, dimensiunea șirului de scris și numele fluxului din care să obțineți datele pentru a scrie în șir. în acest caz,- stdin. După cum știți deja din lecția 3, stdin este fluxul de intrare standard asociat de obicei cu tastatura. Nu este deloc necesar ca datele să provină din fluxul stdin în viitor vom folosi și această funcție pentru a citi datele din fișiere.

Dacă în timpul execuției acestui program introducem un șir mai mare de 10 caractere, doar 9 caractere de la început și o întrerupere de linie va fi în continuare scrisă în tablou, fgets va „taia” șirul la lungimea necesară.

Vă rugăm să rețineți că funcția fgets nu citește 10 caractere, ci 9! După cum ne amintim, în șiruri de caractere ultimul caracter este rezervat caracterului nul.

Să verificăm. Să rulăm programul din ultima listă. Și introduceți linia 1234567890. Linia 123456789 va fi afișată pe ecran.


Fig.3 Exemplu de funcție fgets

Apare o întrebare. Unde s-a dus al zecelea personaj? Și voi răspunde. Nu a mers nicăieri, rămâne în fluxul de intrare. Rulați următorul program.

Lista 5.

Cum se scoate un șir int main(void) (car str; fgets(str, 10, stdin); puts(str); int h = 99; printf("do %d\n", h); scanf("%d",&h) ; printf("după %d\n", h);

Iată rezultatul muncii ei.


Fig.4 Buffer stdin non-gol

Lasă-mă să explic ce s-a întâmplat. Am numit funcția fgets. Ea a deschis fluxul de intrare și a așteptat să introducem datele. Am introdus 1234567890\n de la tastatură (\n Adică apăsând tasta Enter). Acesta a mers la fluxul de intrare stdin. Funcția fgets, așa cum era de așteptat, a luat primele 9 caractere 123456789 din fluxul de intrare, le-a adăugat caracterul nul \0 și l-a scris în șirul str . Au mai rămas 0\n în fluxul de intrare.

Apoi declarăm variabila h. Îi afișăm valoarea pe ecran. Apoi numim funcția scanf. Aici este de așteptat să putem introduce ceva, dar... există 0\n suspendat în fluxul de intrare, apoi funcția scanf percepe aceasta ca intrarea noastră și scrie 0 la variabila h. Apoi îl afișăm pe ecran.

Acesta, desigur, nu este tocmai comportamentul pe care îl așteptăm. Pentru a rezolva această problemă, trebuie să ștergem tamponul de intrare după ce am citit intrarea utilizatorului din acesta. În acest scop este folosit functie speciala fflush. Are un singur parametru - fluxul care trebuie șters.

O vom repara ultimul exemplu astfel încât activitatea sa să fie previzibilă.

Lista 6.

Cum se scoate un șir int main(void) ( char str; fgets(str, 10, stdin); fflush(stdin); // șterge fluxul de intrare puts(str); int h = 99; printf("do %d\n", h ) ; scanf("%d",&h);

Acum programul va funcționa așa cum ar trebui.


Fig.4 Spălarea tamponului stdin cu funcția fflush

Pentru a rezuma, se pot remarca două fapte. Primul. Pe în acest moment folosirea funcției gets este nesigură, așa că este recomandat să folosiți funcția fgets peste tot.

Doilea. Nu uitați să ștergeți tamponul de intrare dacă utilizați funcția fgets.

Aceasta se încheie conversația despre introducerea șirurilor. Să mergem mai departe.