Printf

Versiunea actuală a paginii nu a fost încă examinată de colaboratori experimentați și poate diferi semnificativ de versiunea revizuită la 5 aprilie 2015; verificările necesită 72 de modificări .

printf (din engleză  print formatted , "formatted printing") - un nume generalizat pentru o familie de funcții sau metode de biblioteci comerciale standard sau binecunoscute sau operatori încorporați ai unor limbaje de programare utilizate pentru ieșirea formatată  - ieșire în diverse fluxuri de valori de diferite tipuri formatate în funcție de un șablon dat. Acest șablon este determinat de un șir compus după reguli speciale (șir de format).

Cel mai notabil membru al acestei familii este funcția printf , precum și o serie de alte funcții derivate din printfnume din biblioteca standard C (care face, de asemenea, parte din bibliotecile standard C++ și Objective-C ).

Familia UNIX de sisteme de operare are, de asemenea, un utilitar printf care servește aceleași scopuri de ieșire formatată.

Operatorul FORMAT al lui Fortran poate fi considerat un prototip timpuriu al unei astfel de funcții . Funcția de inferență condusă de șiruri a apărut în precursorii limbajului C ( BCPL și B ). În specificația bibliotecii standard C , a primit cea mai cunoscută formă (cu steaguri, lățime, precizie și dimensiune). Sintaxa șirului șablonului de ieșire (numită uneori șir de format , șir de format sau șir de format ) a fost utilizată ulterior de alte limbaje de programare (cu variații pentru a se potrivi caracteristicilor acestor limbaje). De regulă, funcțiile corespunzătoare acestor limbi sunt numite și printf și/sau derivate ale acesteia.

Unele medii de programare mai recente (cum ar fi .NET ) folosesc, de asemenea, conceptul de ieșire bazată pe șiruri de format, dar cu o sintaxă diferită.

Istorie

Aspect

Fortran Am avut deja operatori care au furnizat rezultate formatate. Sintaxa instrucțiunilor WRITE și PRINT includea o etichetă care se referă la o instrucțiune FORMAT neexecutabilă care conținea o specificație de format. Specificatorii făceau parte din sintaxa operatorului, iar compilatorul putea genera imediat cod care realizează direct formatarea datelor, ceea ce asigura cea mai bună performanță pe computerele acelor vremuri. Cu toate acestea, au existat următoarele dezavantaje:

Primul prototip al viitoarei funcție printf apare în limbajul BCPL în anii 1960 . Funcția WRITEF ia un șir de format care specifică tipul de date separat de datele în sine din variabila șir (tipul a fost specificat fără câmpurile steag, lățime, precizie și dimensiune, dar era deja precedat de un semn de procent %). [1] Scopul principal al șirului de format a fost acela de a trece tipuri de argumente (în limbaje de programare cu tastare statică , determinarea tipului de argument transmis pentru o funcție cu o listă nefixată de parametri formali necesită un mecanism complex și ineficient pentru transmiterea informaţiilor de tip în cazul general). Funcția WRITEF în sine a fost un mijloc de simplificare a ieșirii: în loc de un set de funcții WRCH (ieșire un caracter), WRITES (ieșire un șir), WRITEN , WRITED , WRITEOCT , WRITEHEX (ieșire numere în diverse forme), un singur apel a fost folosit în care era posibil să se intercaleze „doar text” cu valorile de ieșire.

Limbajul Bee care l-a urmat în 1969 folosea deja numele printf cu un șir de format simplu (asemănător cu BCPL ), specificând doar unul dintre cele trei tipuri posibile și două reprezentări numerice: zecimal ( ), octal ( ), șiruri ( ) și caractere ( ), iar singura modalitate de a formata ieșirea în aceste funcții a fost să adăugați caractere înainte și după ieșirea valorii variabilei. [2]%d%o%s%c

C și derivate

De la introducerea primei versiuni a limbajului C ( 1970 ), familia printf a devenit principalul instrument de ieșire în format. Costul analizării șirului de format cu fiecare apel de funcție a fost considerat acceptabil, iar apelurile alternative pentru fiecare tip separat nu au fost introduse în bibliotecă. Specificaţia funcţiei a fost inclusă în ambele standarde lingvistice existente , publicate în 1990 şi 1999 . Specificația din 1999 conține câteva inovații din specificația din 1990.

Limbajul C++ folosește biblioteca standard C (conform standardului din 1990), inclusiv întreaga familie printf .

Ca alternativă, biblioteca standard C++ oferă un set de clase de intrare și ieșire a fluxului. Declarațiile de ieșire ale acestei biblioteci sunt sigure de tip și nu necesită analizarea șirurilor de format de fiecare dată când sunt apelate. Cu toate acestea, mulți programatori continuă să folosească familia printf , deoarece secvența de ieșire cu ei este de obicei mai compactă, iar esența formatului utilizat este mai clară.

Objective-C este un add-on destul de „subțire” pentru C, iar programele de pe acesta pot folosi direct funcțiile familiei printf .

Utilizare în alte limbaje de programare

În plus față de C și derivatele sale (C++, Objective-C), multe alte limbaje de programare folosesc sintaxa șirurilor de format tip printf:

În plus, datorită utilitarului printf inclus cu majoritatea sistemelor de tip UNIX, printf este folosit în multe scripturi shell (pentru sh , bash , csh , zsh , etc.).

Urmatori

Unele limbi și medii de programare mai recente folosesc, de asemenea, conceptul de ieșire bazată pe șiruri de format, dar cu o sintaxă diferită.

De exemplu, .Net Core Class Library (FCL) are o familie de metode System.String.Format , System.Console.Write și System.Console.WriteLine , dintre care unele supraîncărcări își produc datele conform unui șir de format. Deoarece informații complete despre tipurile de obiecte sunt disponibile în runtime .Net, nu este nevoie să treceți aceste informații în șirul de format.

Numirea funcției de familie

Toate funcțiile au stem printf în numele lor . Prefixele dinaintea numelui funcției înseamnă:

Convenții generale

Toate funcțiile iau un șir de format ca unul dintre parametrii ( format ) (descrierea sintaxei șirului de mai jos). Returnează numărul de caractere scrise (tipărite), fără a include caracterul nul de la sfârșitul lui . Numărul de argumente care conțin date pentru ieșirea formatată trebuie să fie cel puțin atât cât este menționat în șirul de format. Argumentele „extra” sunt ignorate.

Funcțiile familiei n ( snprintf , vsnprintf ) returnează numărul de caractere care ar fi tipărit dacă parametrul n (limitarea numărului de caractere de tipărit) ar fi suficient de mare. În cazul codificărilor pe un singur octet , valoarea returnată corespunde lungimii dorite a șirului (fără a include caracterul nul de la sfârșit).

Funcțiile familiei s ( sprintf , snprintf , vsprintf , vsnprintf ) iau ca prim(e) parametru( i ) un pointer către zona de memorie unde va fi scris șirul rezultat. Funcțiile care nu au o limită a numărului de caractere scrise sunt funcții nesigure , deoarece pot duce la o eroare de depășire a tamponului dacă șirul de ieșire este mai mare decât dimensiunea memoriei alocate pentru ieșire.

Funcțiile familiei f scriu un șir în orice flux deschis ( parametrul fluxului ), în special în fluxurile de ieșire standard ( stdout , stderr ). fprintf(stdout, format, …)echivalent cu printf(format, …).

Funcțiile familiei v preiau argumente nu ca un număr variabil de argumente (ca toate celelalte funcții printf), ci ca o listă va list . În acest caz, când funcția este apelată, macro -ul va end nu este executat.

Funcțiile familiei w (primul caracter) sunt o implementare limitată Microsoft a familiei de funcții s : wsprintf , wnsprintf , wvsprintf , wvnsprintf . Aceste funcții sunt implementate în bibliotecile dinamice user32.dll și shlwapi.dll ( n funcții). Nu acceptă ieșirea în virgulă mobilă, iar wnsprintf și wvnsprintf acceptă doar text aliniat la stânga.

Funcțiile familiei w ( wprintf , swprintf ) implementează suport pentru codificări pe mai mulți octeți, toate funcțiile acestei familii funcționează cu pointeri către șiruri de mai mulți octeți ( wchar_t ).

Funcțiile familiei a ( asprintf , vasprintf ) alocă memorie pentru șirul de ieșire folosind funcția malloc , memoria este eliberată în procedura de apelare, în cazul unei erori la executarea funcției, memoria nu este alocată.

Descrierea funcțiilor

Numele parametrilor

Descrierea funcțiilor

Valoare returnată: valoare negativă — semn de eroare; dacă reușesc, funcțiile returnează numărul de octeți scriși/ieșiți (ignorând octetul nul la sfârșit), funcția snprintf tipărește numărul de octeți care ar fi scrisi dacă n ar fi suficient de mare.

La apelarea snprintf , n poate fi zero (caz în care s poate fi un pointer nul ), caz în care nu se face nicio scriere, funcția returnează doar valoarea returnată corectă.

Formatați sintaxa șirului

În C și C++, un șir de format este un șir terminat cu nul. Toate caracterele, cu excepția specificatorilor de format, sunt copiate neschimbate în șirul rezultat. Semnul standard al începutului specificatorului de format este caracterul %( semnul de procente ), pentru a afișa semnul în sine %, se folosește dublarea acestuia %%.

Structura specificatorului de format

Specificatorul de format arată astfel:

% [ steaguri ][ lățime ][ . precizie ][ dimensiune ] tip

Componentele necesare sunt caracterul de început al specificatorului de format ( %) și tipul .

Steaguri
Semn Numele semnului Sens În lipsa acestui semn Notă
- minus valoarea de ieșire este aliniată la stânga în cadrul lățimii minime a câmpului pe dreapta
+ un plus specificați întotdeauna un semn (plus sau minus) pentru valoarea numerică zecimală afișată numai pentru numere negative
  spaţiu puneți un spațiu înaintea rezultatului dacă primul caracter al valorii nu este un semn Ieșirea poate începe cu un număr. Caracterul + are prioritate față de caracterul spațiu. Folosit numai pentru valori zecimale cu semn.
# zăbrele „forma alternativă” de ieșire a valorii La ieșirea numerelor în format hexazecimal sau octal, numărul va fi precedat de o caracteristică de format (0x sau 0, respectiv).
0 zero introduceți câmpul la lățimea specificată în câmpul de lățime a secvenței de evacuare cu simbolul0 tampon cu spații Folosit pentru tipurile d , i , o , u , x , X , a , A , e , E , f , F , g , G . Pentru tipurile d , i , o , u , x , X , dacă este specificată precizia , acest indicator este ignorat. Pentru alte tipuri, comportamentul este nedefinit.

Dacă este specificat un semnalizator minus „-”, acesta este de asemenea ignorat.

Modificator de lățime

Lățimea (caracter zecimal sau asterisc ) specifică lățimea minimă a câmpului (inclusiv semnul pentru numere). Dacă reprezentarea valorii este mai mare decât lățimea câmpului, atunci intrarea este în afara câmpului (de exemplu, %2i pentru o valoare de 100 va oferi o valoare a câmpului de trei caractere), dacă reprezentarea valorii este mai mică decât numărul specificat, apoi va fi captusit (implicit) cu spatii in stanga, comportamentul poate varia in functie de alte steaguri setate. Dacă este specificat un asterisc ca lățime, lățimea câmpului este specificată în lista de argumente înainte de valoarea de ieșire (de exemplu, printf( "%0*x", 8, 15 );va afișa textul 0000000f). Dacă un modificator de lățime negativ este specificat în acest fel, indicatorul - este considerat setat , iar valoarea modificatorului de lățime este setată la absolut.

Modificator de precizie
  • indică numărul minim de caractere care ar trebui să apară la procesarea tipurilor d , i , o , u , x , X ;
  • indică numărul minim de caractere care trebuie să apară după punctul zecimal (punctul) la procesarea tipurilor a , A , e , E , f , F ;
  • numărul maxim de caractere semnificative pentru tipurile g și G ;
  • numărul maxim de caractere de tipărit pentru tipul s ;

Precizia este specificată ca o perioadă urmată de un număr zecimal sau de un asterisc ( * ), dacă nu există niciun număr sau asterisc (este prezent doar un punct), atunci numărul se presupune a fi zero. Un punct este folosit pentru a indica precizia, chiar dacă este afișată o virgulă la ieșirea numerelor în virgulă mobilă.

Dacă după punct este specificat un caracter asterisc, atunci când se procesează șirul de format, valoarea câmpului este citită din lista de argumente. (În același timp, dacă caracterul asterisc este atât în ​​câmpul de lățime, cât și în câmpul de precizie, este indicată mai întâi lățimea, apoi precizia și abia apoi valoarea pentru ieșire). De exemplu, printf( "%0*.*f", 8, 4, 2.5 );va afișa textul 002.5000. Dacă un modificator de precizie negativ este specificat în acest fel, atunci nu există nici un modificator de precizie. [19]

Modificator de dimensiune

Câmpul dimensiune vă permite să specificați dimensiunea datelor transmise funcției. Necesitatea acestui câmp se explică prin particularitățile transmiterii unui număr arbitrar de parametri unei funcții în limbajul C: funcția nu poate determina „independent” tipul și dimensiunea datelor transferate, deci informații despre tipul parametrilor și ale acestora dimensiunea exactă trebuie transmisă în mod explicit.

Având în vedere influența specificațiilor de dimensiune asupra formatării datelor întregi, trebuie remarcat faptul că în limbajele C și C++ există un lanț de perechi de tipuri de numere întregi semnate și nesemnate, care, în ordinea nedescrescătoare a dimensiunilor, sunt dispuse astfel:

tip semnat Tip nesemnat
semnat char nesemnat char
semnat scurt ( scurt ) unsigned short int ( unsigned short )
semnat int ( int ) unsigned int ( nesemnat )
semnat long int ( lung ) unsigned long int ( unsigned long )
semnat long long int ( long long ) unsigned long long int ( unsigned long long )

Dimensiunile exacte ale tipurilor sunt necunoscute, cu excepția tipurilor cu semn și caracter nesemnat .

Tipurile asociate semnate și nesemnate au aceeași dimensiune, iar valorile reprezentabile în ambele tipuri au aceeași reprezentare în ele.

Tipul char are aceeași dimensiune ca și tipurile char semnat și nesemnat și partajează un set de valori reprezentabile cu unul dintre acele tipuri. În plus, se presupune că char  este un alt nume pentru unul dintre aceste tipuri; o astfel de presupunere este acceptabilă pentru prezenta considerație.

În plus, C are tipul _Bool , în timp ce C++ are tipul bool .

Când se transmit argumente unei funcții care nu corespund parametrilor formali din prototipul funcției (care sunt toate argumente care conțin valori de ieșire), aceste argumente sunt supuse promoțiilor standard , și anume:

  • argumentele float sunt turnate la dublu ;
  • argumentele de tipuri unsigned char , unsigned short , signed char și short sunt turnate la unul dintre următoarele tipuri:
    • int dacă acest tip este capabil să reprezinte toate valorile tipului original sau
    • nesemnat altfel;
  • argumentele de tip _Bool sau bool sunt turnate la tipul int .

Astfel, funcțiile printf nu pot lua argumente de tip float , _Bool sau bool , sau tipuri întregi mai mici decât int sau unsigned .

Setul de specificatori de dimensiune utilizat depinde de specificatorul de tip (vezi mai jos).

specificatorul %d, %i, %o, %u, %x,%X %n Notă
dispărut int sau unsigned int pointer către int
l long int sau unsigned long int pointer la long int
hh Argumentul este de tip int sau unsigned int , dar este forțat să tastați signed char sau unsigned char , respectiv pointer către caracterul semnat există oficial în C începând cu standardul din 1999 și în C++ începând cu standardul din 2011.
h Argumentul este de tip int sau unsigned int , dar este forțat să tastați short int sau unsigned short int , respectiv pointer la scurt int
ll long long int sau unsigned long long int pointer la long long int
j intmax_t sau uintmax_t pointer către intmax_t
z size_t (sau tip semnat echivalent cu dimensiunea) pointer către un tip cu semn echivalent ca dimensiune cu size_t
t ptrdiff_t (sau un tip echivalent nesemnat) pointer către ptrdiff_t
L __int64 sau nesemnat __int64 pointer către __int64 Pentru Borland Builder 6 (specificatorul llse așteaptă la un număr de 32 de biți)

Specificațiile hși hhsunt utilizate pentru a compensa promoțiile de tip standard împreună cu tranzițiile de la tipurile semnate la cele nesemnate sau invers.

De exemplu, luați în considerare o implementare C în care tipul char este semnat și are o dimensiune de 8 biți, tipul int are o dimensiune de 32 de biți și este utilizată o modalitate suplimentară de codificare a numerelor întregi negative.

char c = 255 ; printf ( "%X" , c );

Un astfel de apel va produce ieșire FFFFFFFF, care poate să nu fie ceea ce se aștepta programatorul. Într-adevăr, valoarea lui c este (char)(-1) , iar după promovarea tipului este -1 . Aplicarea formatului %Xface ca valoarea dată să fie interpretată ca nesemnată, adică 0xFFFFFFFF .

char c = 255 ; printf ( "%X" , ( caracter nesemnat ) c ); char c = 255 ; printf ( "%hhX" , c );

Aceste două apeluri au același efect și produc rezultatul FF. Prima opțiune vă permite să evitați înmulțirea semnului atunci când promovați tipul, a doua o compensează deja „în interiorul” funcției printf .

specificatorul %a, %A, %e, %E, %f, %F, %g,%G
dispărut dubla
L dublu lung
specificatorul %c %s
dispărut Argumentul este de tip int sau unsigned int , dar este forțat să tastați char char*
l Argumentul este de tip wint_t , dar este forțat să tastați wchar_t wchar_t*
Specificator de tip

Tipul indică nu numai tipul valorii (din punctul de vedere al limbajului de programare C), ci și reprezentarea specifică a valorii de ieșire (de exemplu, numerele pot fi afișate în formă zecimală sau hexazecimală). Scris ca un singur personaj. Spre deosebire de alte câmpuri, este obligatoriu. Dimensiunea maximă de ieșire acceptată dintr-o singură secvență de evadare este, conform standardelor, de cel puțin 4095 de caractere; în practică, majoritatea compilatorilor acceptă cantități substanțial mai mari de date.

Valori de tip:

  • d , i  — număr zecimal cu semn, tipul implicit este int . Implicit, este scris cu aliniere la dreapta, semnul este scris doar pentru numere negative. Spre deosebire de funcțiile din familia scanf , pentru funcțiile din familia printf , specificațiile %d și %i sunt complet sinonime;
  • o  — număr octal nesemnat, tipul implicit este unsigned int ;
  • u  este un număr zecimal fără semn, tipul implicit este unsigned int ;
  • x și X  sunt numere hexazecimale fără semn, x folosește litere mici (abcdef), X folosește litere mari (ABCDEF), tipul implicit este unsigned int ;
  • f și F  sunt numere în virgulă mobilă, tipul implicit este dublu . În mod implicit, ele sunt scoase cu o precizie de 6, dacă numărul modulo este mai mic de unu, înainte de virgulă se scrie un 0. Valorile ±∞ sunt prezentate sub forma [-]inf sau [-]infinit (în funcție de platformă); valoarea lui Nan este reprezentată ca [-]nan sau [-]nan (orice text de mai jos) . Folosind F se tipăresc valorile specificate cu majuscule ( [-]INF , [-]INFINITY , NAN ).
  • e și E  sunt numere în virgulă mobilă în notație exponențială (de forma 1.1e+44), tipul implicit este double . e scoate caracterul „e” cu litere mici, E  - cu litere mari (3.14E+0);
  • g și G  este un număr în virgulă mobilă, tipul implicit este dublu . Forma de reprezentare depinde de valoarea mărimii ( f sau e ). Formatul diferă ușor de virgulă mobilă prin faptul că nu sunt scoase zerouri din dreapta punctului zecimal. De asemenea, punctul punct și virgulă nu este afișat dacă numărul este un întreg;
  • a și A (începând de la standardele limbajului C din 1999 și C++ din 2011) — un număr în virgulă mobilă în formă hexazecimală, tipul implicit este dublu ;
  • c  — ieșirea simbolului cu codul corespunzător argumentului transmis, tipul implicit este int ;
  • s  - ieșirea unui șir cu un octet de terminare nul; dacă modificatorul de lungime este l , este scos șirul wchar_t* . Pe Windows, valorile tipului s depind de tipul de funcții utilizate. Dacă se folosește o familie de printffuncții, atunci s denotă șirul char* . Dacă se utilizează o familie de wprintffuncții, atunci s denotă șirul wchar_t* .
  • S  este la fel ca s cu modificatorul de lungime l ; Pe Windows, valoarea tipului S depinde de tipul de funcții utilizate. Dacă se utilizează o familie de printffuncții, atunci S reprezintă șirul wchar_t* . Dacă se folosește o familie de wprintffuncții, atunci S indică șirul char* .
  • p -  ieșire pointer , aspectul poate varia semnificativ în funcție de reprezentarea internă în compilator și platformă (de exemplu, platforma MS-DOS pe 16 biți utilizează notația formei FFEC:1003, platforma pe 32 de biți cu adresare plată utilizează adresa a formei 00FA0030);
  • n  - înregistrează cu pointer, transmis ca argument, numărul de caractere scrise în momentul apariţiei secvenţei de comandă conţinând n ;
  • %  - caracter pentru afișarea semnului procentual (%), folosit pentru a activa ieșirea caracterelor procentuale în șirul printf, întotdeauna folosit în forma %%.
Ieșirea numerelor în virgulă mobilă

În funcție de localitatea curentă , atât o virgulă, cât și un punct (și posibil un alt simbol) pot fi utilizate atunci când se afișează numere în virgulă mobilă. Comportamentul printf în raport cu caracterul care separă partea fracționară și întreagă a numărului este determinat de localitatea utilizată (mai precis, variabila LC NUMERIC ). [douăzeci]

Macrocomenzi speciale pentru un set extins de aliasuri de tip de date întregi

Al doilea standard C (1999) oferă un set extins de alias-uri pentru tipurile de date întregi int N _t , uint N _t , int_least N _t , uint_least N _t , int_fast N _t , uint_fast N _t (unde N este adâncimea de biți necesară), intptr_t , uintptr_t , intmax_t , uintmax_t .

Fiecare dintre aceste tipuri se potrivește sau nu cu oricare dintre tipurile de numere întregi standard încorporate. Din punct de vedere formal, atunci când scrie cod portabil, programatorul nu știe dinainte ce specificație standard sau extinsă de dimensiune ar trebui să aplice.

int64_t x = 100000000000 ; int lățime = 20 ; printf ( "%0*lli" , lățime , x ); Greșit, deoarece int64_t poate să nu fie același cu long long int .

Pentru a putea deduce valorile obiectelor sau expresiilor de aceste tipuri într-un mod portabil și convenabil, implementarea definește pentru fiecare dintre aceste tipuri un set de macro-uri ale căror valori sunt șiruri care combină specificații de dimensiune și tip.

Numele macrocomenzilor sunt după cum urmează:

O pereche de tipuri semnate și nesemnate Nume macro
int N_t și uint N_t _ _ PRITN
int_least N _t și uint_least N _t PRITLEASTN
int_fastN_t și uint_fastN_t _ _ _ _ PRITFASTN
intmax_t și uintmax_t PRITMAX
intptr_t și uintptr_t PRITPTR

Iată T , una dintre următoarele specificații de tip: d, i, u, o, x, X.

int64_t x = 100000000000 ; int lățime = 20 ; printf ( "%0*" PRIi64 , lățime , x ); Modul corect de a scoate o valoare de tip int64_t în limbajul C.

Este posibil să observați că tipurile intmax_t și uintmax_t au un specificator de dimensiune standard j, astfel încât macro-ul este cel mai probabil întotdeauna definit ca . PRITMAX"jT"

Extensii XSI în standardul Unix unic

Conform standardului Single UNIX (practic echivalent cu standardul POSIX ), următoarele completări la printf sunt definite în raport cu ISO C, sub extensia XSI (X/Open System Interface):

  • Se adaugă capacitatea de a scoate un parametru arbitrar după număr (indicat ca n$imediat după caracterul începutului secvenței de control, de exemplu, printf("%1$d:%2$.*3$d:%4$.*3$d\n", hour, min, precision, sec);).
  • S-a adăugat steag „'” (ghilimele simple), care pentru tipurile d , i , o , u prescrie separarea claselor cu caracterul corespunzător.
  • tip C echivalent cu lc ISO C (ieșire de caractere de tip wint_t ).
  • tip S echivalent cu ls ISO C (ieșire șir ca wchar_t* )
  • S-au adăugat coduri de eroare EILSEQ, EINVAL, ENOMEM, EOVERFLOW.

Extensii non-standard

Biblioteca GNU C

Biblioteca GNU C ( libc ) adaugă următoarele extensii:

  • tipul m tipărește valoarea variabilei globale errno (codul de eroare al ultimei funcții).
  • tipul C este echivalent cu lc .
  • steagul ' (ghilimele simple) este folosit pentru a separa clasele la tipărirea numerelor. Formatul de separare depinde de LC_NUMERIC
  • dimensiunea lui q indică tipul long long int (pe sistemele în care long long int nu este acceptat , acesta este același cu long int
  • dimensiunea Z este un alias pentru z , a fost introdus în libc înainte de apariția standardului C99 și nu este recomandat pentru utilizare în codul nou.
Înregistrarea propriilor tipuri

GNU libc acceptă înregistrarea tipului personalizat, permițând programatorului să definească formatul de ieșire pentru propriile structuri de date. Pentru a înregistra un nou tip , utilizați funcția
int register_printf_function (int type, printf_function handler-function, printf_arginfo_function arginfo-function), unde:

  • type  — litera pentru tip (dacă tip = 'Y', atunci apelul va arăta ca '%Y');
  • handler-function  - un pointer către o funcție care este apelată de funcțiile printf dacă tipul specificat în tip este întâlnit în șirul de format ;
  • arginfo-function  este un pointer către o funcție care va fi apelată de funcția parse_printf_format .

Pe lângă definirea de noi tipuri, înregistrarea permite redefinirea tipurilor existente (cum ar fi s , i ).

Microsoft Visual C

Microsoft Visual Studio pentru limbajele de programare C/C++ în formatul specificației printf (și alte funcții de familie) oferă următoarele extensii:

  • dimensiune cutie:
valoarea câmpului tip de
I32 semnat __int32 , nesemnat __int32
I64 semnat __int64 , nesemnat __int64
eu ptrdiff_t , dimensiunea_t
w echivalent cu l pentru șiruri și caractere
arțar

Mediul de matematică Maple are și o funcție printf care are următoarele caracteristici:

Formatare
    • %a, %A: obiectul Maple va fi returnat în notație text, aceasta funcționează pentru toate obiectele (de exemplu, matrice, funcții, module etc.). Litera minusculă indică să înconjurați caractere (nume) cu backtick-uri care ar trebui să fie înconjurate de backticks în intrarea în printf.
    • %q, %Q: la fel ca %a/%A, dar nu va fi procesat un singur argument, ci totul începând cu cel care se potrivește cu steag-ul de formatare. Astfel, indicatorul %Q/%q poate apărea doar ultimul în șirul de format.
    • %m: Formatați obiectul conform reprezentării sale interne Maple. Practic folosit pentru a scrie variabile într-un fișier.

Exemplu:

> printf("%a =%A", `+`, `+`); `+` = + > printf("%a =%m", `+`, `+`); `+` = I"+f*6"F$6#%(buildingGF$"$Q"F$F$F$F"%*protectedG Concluzie

Funcția fprintf a lui Maple ia fie un descriptor de fișier (returnat de fopen), fie un nume de fișier ca prim argument. În acest din urmă caz, numele trebuie să fie de tip „simbol”, dacă numele fișierului conține puncte, atunci trebuie să fie închis în backticks sau convertit cu funcția convert (nume_fișier, simbol).

Vulnerabilități

Funcțiile familiei printf iau o listă de argumente și dimensiunea lor ca parametru separat (în șirul de format). O nepotrivire între șirul de format și argumentele transmise poate duce la un comportament imprevizibil, coruperea stivei, executarea codului arbitrar și distrugerea zonelor de memorie dinamică. Multe funcții ale familiei sunt numite „unsafe” ( în engleză  unsafe ), deoarece nici măcar nu au capacitatea teoretică de a se proteja împotriva datelor incorecte.

De asemenea, funcțiile din familia s (fără n , precum sprintf , vsprintf ) nu au limite în ceea ce privește dimensiunea maximă a șirului scris și pot duce la o eroare de depășire a memoriei tampon (când datele sunt scrise în afara zonei de memorie alocată).

Comportament atunci când șirul de format și argumentele transmise nu se potrivesc

Ca parte a convenției de apelare cdecl , curățarea stivei este realizată de funcția de apelare. Când printf este apelat , argumentele (sau pointerii către ele) sunt plasate în ordinea în care sunt scrise (de la stânga la dreapta). Pe măsură ce șirul de format este procesat, funcția printf citește argumente din stivă. Sunt posibile următoarele situații:

  • numărul și tipul de argumente se potrivesc cu cele specificate în șirul de format (funcționare normală a funcției)
  • mai multe argumente transmise funcției decât cele specificate în șirul de format (argumente suplimentare)
  • Mai puține argumente transmise funcției decât este cerut de șirul de format (argumente insuficiente)
  • Argumentele de dimensiune greșite au fost transmise funcției
  • Argumentele de dimensiunea corectă, dar tipul greșit au fost transmise funcției

Specificațiile limbajului C descriu doar două situații (funcționare normală și argumente suplimentare). Toate celelalte situații sunt eronate și duc la un comportament nedefinit al programului (în realitate, ducând la rezultate arbitrare, până la execuția unor secțiuni de cod neplanificate).

Prea multe argumente

Când trece un număr excesiv de argumente, funcția printf citește argumentele necesare pentru a procesa corect șirul de format și revine la funcția de apelare. Funcția de apelare, în conformitate cu specificația, șterge stiva de parametrii trecuți funcției apelate. În acest caz, parametrii suplimentari pur și simplu nu sunt utilizați, iar programul continuă fără modificări.

Nu sunt suficiente argumente

Dacă există mai puține argumente pe stivă la apelarea printf decât este necesar pentru a procesa șirul de format, atunci argumentele lipsă sunt citite din stivă, în ciuda faptului că există date arbitrare pe stivă (nu sunt relevante pentru munca printf ) . Dacă prelucrarea datelor a fost „reușită” (adică nu a terminat programul, nu a blocat sau nu a scris în stivă), după revenirea la funcția de apelare, valoarea indicatorului de stivă este returnată la valoarea inițială și programul continuă.

La procesarea valorilor stivei „extra”, sunt posibile următoarele situații:

  • Citirea cu succes a unui parametru „extra” pentru ieșire (număr, indicator, simbol etc.) - valoarea „aproape aleatorie” citită din stivă este plasată în rezultatele de ieșire. Acest lucru nu reprezintă un pericol pentru funcționarea programului, dar poate duce la compromiterea unor date (ieșirea valorilor stivei pe care un atacator le poate folosi pentru a analiza funcționarea programului și pentru a obține acces la informațiile interne/private ale programului).
  • O eroare la citirea unei valori din stivă (de exemplu, ca urmare a epuizării valorilor stivei disponibile sau a accesării paginilor de memorie „inexistente”) - o astfel de eroare este cel mai probabil să provoace prăbușirea programului.
  • Citirea unui pointer către un parametru. Șirurile sunt transmise folosind un pointer, când se citesc informații „arbitrare” din stivă, valoarea citită (aproape aleatorie) este folosită ca indicator către o zonă de memorie aleatoare. Comportamentul programului în acest caz este nedefinit și depinde de conținutul acestei zone de memorie.
  • Scrierea unui parametru prin pointer ( %n) - în acest caz, comportamentul este similar cu situația cu citirea, dar este complicat de posibilele efecte secundare ale scrierii într-o celulă de memorie arbitrară.
Nepotrivirea tipului de argument

Formal, orice discrepanță între tipul de argument și așteptare cauzează un comportament nedefinit al programului. În practică, există mai multe cazuri care sunt deosebit de interesante din punctul de vedere al practicii de programare:

  • Argumentul este de același tip ca cel de așteptat, dar are o dimensiune diferită.
  • Argumentul are aceeași dimensiune ca cea de așteptat, dar un tip diferit.

Alte cazuri, de regulă, duc la un comportament evident incorect și sunt ușor de detectat.

Nu se potrivește dimensiunea argumentului întreg sau în virgulă mobilă

Pentru un argument întreg (cu o specificație de format întreg), sunt posibile următoarele situații:

  • Transmiterea parametrilor care sunt mai mari decât se aștepta (citirea celui mai mic din cel mai mare). În acest caz, în funcție de ordinea de octeți acceptată și de direcția de creștere a stivei, valoarea afișată poate fie să coincidă cu valoarea argumentului, fie să se dovedească a nu avea nicio legătură cu aceasta.
  • Transmiterea parametrilor care sunt mai mici decât se aștepta (se citesc mai mare de la mai mic). În acest caz, este posibilă o situație când sunt citite zone de stivă care depășesc limitele argumentelor trecute. Comportamentul funcției în acest caz este similar cu comportamentul într-o situație cu lipsă de parametri. În general, valoarea de ieșire nu se potrivește cu valoarea așteptată.

Pentru un argument real (cu o specificație de format reală), pentru orice nepotrivire de dimensiune, valoarea de ieșire, de regulă, nu se potrivește cu valoarea transmisă.

De regulă, dacă dimensiunea oricărui argument este greșită, procesarea corectă a tuturor argumentelor ulterioare devine imposibilă, deoarece este introdusă o eroare în indicatorul la argumente. Cu toate acestea, acest efect poate fi compensat prin alinierea valorilor pe stivă.

Alinierea valorilor pe stivă

Multe platforme au reguli de aliniere a valorilor întregi și/sau reale care necesită (sau recomandă) ca acestea să fie plasate la adrese care sunt multipli de dimensiunea lor. Aceste reguli se aplică și pentru transmiterea argumentelor funcției pe stivă. În acest caz, o serie de nepotriviri între tipurile de parametri așteptați și reali pot trece neobservate, creând iluzia unui program corect.

uint32_t a = 1 ; uint64_t b = 2 , c = 3 ; printf ( "%" PRId64 "%" PRId64 "%" PRId64 , b , a , c ); În acest exemplu, parametrul atip real are uint32_to specificație de format nevalidă asociată %"PRId64"cu tipul uint64_t. Cu toate acestea, pe unele platforme cu un tip de 32 de biți int, în funcție de ordinea de octeți acceptată și de direcția de creștere a stivei, eroarea poate trece neobservată. Parametrii actuali bși cvor fi aliniați la o adresă care este un multiplu al mărimii lor (de două ori dimensiunea a). Și „între” valori a, bva rămâne un spațiu gol (de obicei zero) de 32 de biți în dimensiune; când BOM este procesată, valoarea de %"PRId64"32 de biți a, împreună cu acest spațiu alb, vor fi interpretate ca o singură valoare de 64 de biți.

O astfel de eroare poate apărea în mod neașteptat la portarea codului programului pe o altă platformă, schimbarea compilatorului sau modul de compilare.

Discrepanță potențială de dimensiune

Definițiile limbajelor C și C++ descriu doar cerințele cele mai generale pentru dimensiunea și reprezentarea tipurilor de date. Prin urmare, pe multe platforme, reprezentarea unor tipuri de date formal diferite se dovedește a fi aceeași. Acest lucru face ca unele nepotriviri de tip să rămână nedetectate pentru o lungă perioadă de timp.

De exemplu, pe platforma Win32, se acceptă în general că dimensiunile tipurilor intși long intsunt aceleași (32 de biți). Astfel, apelul printf("%ld", 1)sau printf("%d", 1L)va fi executat „corect”.

O astfel de eroare poate apărea în mod neașteptat la portarea codului programului pe o altă platformă, schimbarea compilatorului sau modul de compilare.

Când scrieți programe în limbajul C++, trebuie să aveți grijă să obțineți valorile variabilelor declarate folosind aliasuri de tip întreg, în special size_tși ptrdiff_t; definiția formală a bibliotecii standard C++ se referă la primul standard C (1990). Al doilea standard C (1999) definește specificatorii de dimensiune pentru tipuri size_tși și pentru un număr de alte tipuri pentru utilizare cu obiecte similare. ptrdiff_tMulte implementări C++ le suportă și ele.

dimensiunea_t s = 1 ; printf ( "%u" , s ); Acest exemplu conține o eroare care poate apărea pe platformele sizeof (unsigned int)în care sizeof (size_t). dimensiunea_t s = 1 ; printf ( "%zu" , s ); Modul corect de a deduce valoarea unui obiect tip este size_tîn limbajul C. Tip nepotrivire atunci când dimensiunea corespunde

Dacă argumentele transmise au aceeași dimensiune, dar au un tip diferit, atunci programul va rula adesea „aproape corect” (nu va cauza erori de acces la memorie), deși valoarea de ieșire este probabil să fie lipsită de sens. Trebuie remarcat faptul că amestecarea tipurilor întregi pereche (semnate și nesemnate) este permisă, nu provoacă un comportament nedefinit și uneori este folosită în mod deliberat în practică.

Când se utilizează o specificație de format %s, o valoare de argument a unui tip întreg, real sau de tip pointer, altul decât char*, va fi interpretată ca adresa unui șir. Această adresă, în general, poate indica în mod arbitrar o zonă de memorie inexistentă sau inaccesibilă, ceea ce va duce la o eroare de acces la memorie, sau către o zonă de memorie care nu conține o linie, ceea ce va duce la ieșire aiurea, eventual foarte mare. .

Vulnerabilitatea șirului de format

Deoarece printf (și alte funcții ale familiei) poate scoate textul șirului de format fără modificări, dacă nu conține secvențe de escape, atunci este posibilă ieșirea textului de către comandă
printf(text_to_print);
Dacă text_to_print este obținut din surse externe (citește dintr-un fișier , primit de la utilizator sau de la sistemul de operare), atunci prezența unui semn de procente în șirul rezultat poate duce la consecințe extrem de nedorite (până la înghețarea programului).

Exemplu de cod incorect:
printf(" Current status: 99% stored.");
acest exemplu conține o secvență de evadare „% s” care conține caracterul secvenței de evadare (%), un steag (spațiu) și un tip de date șir ( s ). Funcția, după ce a primit secvența de control, va încerca să citească indicatorul către șirul din stivă. Deoarece nu au fost transferați parametri suplimentari funcției, valoarea care trebuie citită din stivă este nedefinită. Valoarea rezultată va fi interpretată ca un pointer către un șir terminat cu nul. Ieșirea unui astfel de „șir” poate duce la o descărcare arbitrară a memoriei, o eroare de acces la memorie și o corupție a stivei. Acest tip de vulnerabilitate se numește atac de șir de format .  [21]

Buffer overflow

Funcția printf , atunci când scoate un rezultat, nu este limitată de numărul maxim de caractere de ieșire. Dacă, ca urmare a unei erori sau o neglijență, sunt afișate mai multe caractere decât se aștepta, cel mai rău lucru care se poate întâmpla este „distrugerea” imaginii de pe ecran. Creată prin analogie cu printf , funcția sprintf nu a fost, de asemenea, limitată în dimensiunea maximă a șirului rezultat. Totuși, spre deosebire de terminalul „infinit”, memoria pe care o alocă aplicația pentru șirul rezultat este întotdeauna limitată. Iar în cazul depășirii limitelor așteptate, înregistrarea se face în zone de memorie aparținând altor structuri de date (sau, în general, în zone de memorie inaccesibile, ceea ce înseamnă că programul se blochează pe aproape toate platformele). Scrierea în zone arbitrare ale memoriei duce la efecte imprevizibile (care pot apărea mult mai târziu și nu sub forma unei erori de program, ci sub forma corupției datelor utilizatorului). Lipsa unei limite privind dimensiunea maximă a șirului este o eroare fundamentală de planificare atunci când se dezvoltă o funcție. Din această cauză funcțiile sprintf și vsprintf au starea nesigură . În schimb, a dezvoltat funcțiile snprintf , vsnprintf , care iau un argument suplimentar care limitează șirul maxim rezultat. Funcția swprintf , care a apărut mult mai târziu (pentru lucrul cu codificări pe mai mulți octeți), ia în considerare acest neajuns și ia un argument pentru a limita șirul rezultat. (De aceea nu există nicio funcție snwprintf ).

Un exemplu de apel periculos la sprintf :

charbuffer[65536]; char* nume = get_user_name_from_keyboard(); sprintf(buffer, „Nume utilizator:%s”, nume);

Codul de mai sus presupune implicit că utilizatorul nu va tasta 65 de mii de caractere pe tastatură, iar tamponul „ar trebui să fie suficient”. Dar utilizatorul poate redirecționa intrarea dintr-un alt program sau poate introduce mai mult de 65.000 de caractere. În acest caz, zonele de memorie vor fi corupte și comportamentul programului va deveni imprevizibil.

Dificultăți de utilizare

Lipsa verificării tipului

Funcțiile familiei printf folosesc tipuri de date C. Dimensiunile acestor tipuri și raporturile lor pot varia de la platformă la platformă. De exemplu, pe platformele pe 64 de biți, în funcție de modelul ales ( LP64 , LLP64 sau ILP64 ), dimensiunile tipurilor int și long pot diferi. Dacă programatorul setează șirul de format la „aproape corect”, codul va funcționa pe o platformă și va da rezultat greșit pe alta (în unele cazuri, poate duce la coruperea datelor).

De exemplu, codul printf( "text address: 0x%X", "text line" );funcționează corect pe o platformă de 32 de biți ( dimensiunea ptrdiff_t și dimensiunea int 32 de biți) și pe un model IPL64 de 64 de biți (unde dimensiunile ptrdiff_t și int sunt de 64 de biți), dar va da un rezultat incorect pe un model de 64 de biți. -platforma de biți a unui model LP64 sau LLP64, unde dimensiunea lui ptrdiff_t este de 64 de biți și dimensiunea lui int este de 32 de biți. [22]

În Oracle Java , tipurile înfășurate cu identificare dinamicăprintf sunt utilizate în analogul unei funcții , [6] în Embarcadero Delphi  - un strat intermediar , [23] în diverse implementări în C++ [24]  - supraîncărcare a operațiunilor , în C + + 20  - șabloane variabile. În plus, formatele ( , etc.) nu specifică tipul argumentului, ci doar formatul de ieșire, astfel încât schimbarea tipului argumentului poate provoca o urgență sau poate întrerupe logica de nivel înalt (de exemplu, „întrerupe” dispunerea tabelului) - dar nu strica memoria. array of const%d%f

Lipsa standardizării

Problema este agravată de standardizarea insuficientă a șirurilor de format în diferite compilatoare: de exemplu, versiunile timpurii ale bibliotecilor Microsoft nu au acceptat "%lld"(a trebuit să specificați "%I64d"). Există încă o divizare între Microsoft și GNU după tip size_t: %Iuprimul și %zucel din urmă. GNU C nu necesită o swprintflungime maximă de șir într-o funcție (trebuie să scrieți snwprintf).

Incapacitatea de a rearanja argumentele

Funcțiile familiei printfsunt convenabile pentru localizarea software-ului : de exemplu, este mai ușor de tradus decât fragmentele de «You hit %s instead of %s.»șir și . Dar și aici există o problemă: este imposibil să rearanjați șirurile înlocuite în locuri pentru a obține: . «You hit »« instead of »«.»«Вы попали не в <2>, а в <1>.»

Extensiile printffolosite în Oracle Java și Embarcadero Delphi vă permit încă să rearanjați argumentele.

utilitar printf

În standardul POSIX , este descris utilitarul printf , care formatează argumentele conform modelului corespunzător, similar cu funcția printf .

Utilitarul are următorul format de apel: , unde printf format [argument …]

  • format  este un șir de format, similar ca sintaxă cu funcția printf format șir .
  • argument  este o listă de argumente (0 sau mai multe) scrise sub formă de șir.

Exemple de implementare

Exemplul 1 C (limbaj de programare)

#include <stdio.h> #include <locale.h> #define PI 3.141593 int main () { setlocale ( LC_ALL , "RUS" ); număr int = 7 ; plăcinte float = 12,75 ; cost int = 7800 ; printf ( "%d concurenți au mâncat %f plăcinte cu cireșe. \n " , număr , plăcinte ); printf ( "Valoarea lui pi este %f \n " , PI ); printf ( "La revedere! Arta ta costă prea mult (%c%d) \n " , '$' , 2 * cost ); returnează 0 ; }

Exemplul 2 C (limbaj de programare)

#include <stdio.h> #define PAGINI 959 int main () { printf ( "*%d* \n " , PAGINI ); printf ( "*%2d* \n " , PAGINI ); printf ( "*%10d* \n " , PAGINI ); printf ( "*%-10d* \n " , PAGINI ); returnează 0 ; } /* Rezultat: *959* *959* * 959* *959 * */

Exemplul 3 C (limbaj de programare)

#include <stdio.h> #define BLURB „Imitație autentică!” int main () { const dublu CHIRIA = 3852,99 ; printf ( "*%8f* \n " , ÎNCHIRIAT ); printf ( "*%e* \n " , ÎNCHIRIAT ); printf ( "*%4.2f* \n " , ÎNCHIRIAT ); printf ( "*%3.1f* \n " , ÎNCHIRIAT ); printf ( "*%10.3f* \n " , ÎNCHIRIAT ); printf ( "*%10.3E* \n " , ÎNCHIRIAT ); printf ( "*%+4.2f* \n " , ÎNCHIRIAT ); printf ( "%x %X %#x \n " , 31 , 31 , 31 ); printf ( "**%d**% d% d ** \n " , 42 , 42 , -42 ); printf ( "**%5d**%5.3d**%05d**%05.3d** \n " , 6 , 6 , 6 , 6 ); printf ( " \n " ); printf ( "[%2s] \n " , BLURB ); printf ( "[%24s] \n " , BLURB ); printf ( "[%24.5s] \n " , BLURB ); printf ( "[%-24.5s] \n " , BLURB ); returnează 0 ; } /* rezultat *3852.990000* *3.852990e+03* *3852.99* *3853.0* * 3852.990* * 3.853E+03* *+3852.99* 1f 1F 0x1f ** 0x1f ** 42** ** 042** **-42 ** **00006** 006** [Imitație autentică!] [Imitație autentică!] [Authe] [Authe ] */

Link -uri

  1. Scurtă descriere a limbajului BCPL . Consultat la 16 decembrie 2006. Arhivat din original pe 9 decembrie 2006.
  2. B Language Guide Arhivat 6 iulie 2006.
  3. Descrierea funcției sprintf în documentația Perl . Consultat la 12 ianuarie 2007. Arhivat din original la 14 ianuarie 2007.
  4. O descriere a operatorului de formatare pentru tipurile de șiruri în Python Arhivat 9 noiembrie 2006.
  5. Descrierea funcției PHP printf . Consultat la 23 octombrie 2006. Arhivat din original pe 6 noiembrie 2006.
  6. 1 2 Descrierea funcției java.io.PrintStream.printf() în Java 1.5 . Consultat la 12 ianuarie 2007. Arhivat din original la 13 ianuarie 2007.
  7. Descrierea funcției printf în documentația Ruby . Consultat la 3 decembrie 2006. Arhivat din original pe 5 decembrie 2006.
  8. Descrierea funcției string.format în documentația Lua . Data accesului: 14 ianuarie 2010. Arhivat din original pe 15 noiembrie 2013.
  9. Descrierea funcției de format în documentația TCL . Consultat la 14 aprilie 2008. Arhivat din original pe 4 iulie 2007.
  10. Descrierea modelului de șir pentru printf în documentația GNU Octave . Consultat la 3 decembrie 2006. Arhivat din original pe 27 octombrie 2006.
  11. Descrierea printf în documentația Maple{{subst:AI}}
  12. R. Fourer, D. M. Gay și B. W. Kernighan. AMPL: A Modeling Language for Mathematical Programming, Ed. a doua Pacific Grove, CA: Brooks/Cole--Thomson Learning, 2003.
  13. GNU Emacs Lisp Reference Manual, Formating Strings Arhivat 27 septembrie 2007 la Wayback Machine
  14. Descrierea modulului Printf în documentația OCaml . Consultat la 12 ianuarie 2007. Arhivat din original la 13 ianuarie 2007.
  15. Descrierea modulului Printf din documentația Haskell . Consultat la 23 iunie 2015. Arhivat din original pe 23 iunie 2015.
  16. std::println! - Rugina . doc.rust-lang.org. Preluat la 24 iulie 2016. Arhivat din original la 18 august 2016.
  17. format . www.freepascal.org. Preluat la 7 decembrie 2016. Arhivat din original la 24 noiembrie 2016.
  18. fmt - Limbajul de programare Go . golang.org. Preluat la 25 martie 2020. Arhivat din original la 4 aprilie 2020.
  19. §7.19.6.1 ISO/IEC 9899:TC2
  20. § 7.11.1.1 ISO/IEC 9899:TC2, LC_NUMERIC definește, în special, forma de reprezentare a separatorului zecimal.
  21. Printf Vulnerability Description, Robert C. Seacord: Secure Coding in C and C++. Addison Wesley, septembrie 2005. ISBN 0-321-33572-4
  22. Descrierea problemelor de portare a aplicațiilor de la arhitectura pe 32 la 64 de biți . Consultat la 14 decembrie 2006. Arhivat din original pe 8 martie 2007.
  23. System.SysUtils.Format Arhivat pe 11 ianuarie 2013 la Wayback Machine 
  24. De exemplu, boost::formatdocumentația arhivată la 26 martie 2013 la Wayback Machine 

Surse

  • printf , fprintf , snprintf , vfprintf , vprintf , vsnprintf , vsprintf în ISO/IEC 9899:TC2 (ISO C) [3]
  • printf , fprintf , sprintf , snprintf în standardul Single Unix [4]
  • vprintf , vfprintf , vsprintf , vsnprintf în standardul POSIX [5]
  • wprintf , swprintf , wprintf în standardul POSIX [6]
  • vfwprintf , vswprintf , vwprintf în standardul POSIX [7]
  • wsprintf pe MSDN [8]
  • wvnsprintf pe MSDN [9]
  • wnsprintf pe MSDN [10]
  • wvsprintf pe MSDN [11]
  • wnsprintf pe MSDN [12]
  • asprintf , vasprintf în paginile de manual pe Linux [13] , în documentația libc [14]
  • Consultați manualul libc [15] pentru o descriere a sintaxei șirurilor de format .
  • Descrierea șirului de format din documentația pentru Microsoft Visual Studio 2005 [16]
  • Descrierea funcției register_printf_function [17] , [18]
  • Limbajul de programare C. Prelegeri si exercitii. Autor: Stephen Prata. ISBN 978-5-8459-1950-2 , 978-0-321-92842-9; 2015

Vezi și