funktsiooni diskrimineerimine sprintf, vsprintf, sprintf_s, vsprintf_s, _snprintf, _vsnprintf, snprintf, vsnprintf

Sprintf Vsprintf Sprintf_s



Kas pärast pealkirjas olevate funktsioonide nimede lugemist on teil uimane Selliste detailide takerdumise vältimiseks tulevikus tuleb täna analüüsida nende funktsioonide sarnasusi ja erinevusi.

labori keskkond:



Kasutage VS2017 Windowsi all
Kasutage Linuxi all gcc4.9.4



Funktsiooni ohutuse kontrollimiseks kujundasime järgmise struktuuri



pilt

const int len = 4 #pragma pack(push) #pragma pack(1) struct Data { char buf[len] char guard Data() { for (int i = 0 i 

pilt

Andmete kirjutamisel väljale Data.buf muudetakse välise andmemälu olukorra korral väli Data.gurad mälu. Kasutame seda funktsiooni ohutuse järeldamiseks.



1. sprintf (Linux / Windows)

Funktsiooni prototüüp Linuxis: int sprintf (char * str, const char * formaat, ...)
Test kood:

pilt

int main() { Data data data.Display() int ret = sprintf(data.buf, '%d', 12) std::cout << 'ret = ' << ret << std::endl data.Display() std::cin.get() return 0 }

pilt

VS2017 keskkonnas on see funktsioon märgitud ohtlikuks. Kui seda kasutatakse, annab koostaja hoiatuse. Kui peate seda kasutama, peate koostamisel lisama makro definitsiooni: _CRT_SECURE_NO_WARNINGS, et käskida kompilaatoril turvahoiatust eirata. Seda funktsiooni saab tavapäraselt kasutada Linuxis. Ja see funktsioon käitub Windowsi ja Linuxi puhul samamoodi. üksikasjad järgmiselt:

1. Kui lähteandmete pikkus on [alla] len, kirjutab sprintf andmed täielikult sihtmällu ja tagab, et saba lõpeb 0-ga, ja tagastab kirjutatud baitide arvu. Sel ajal on funktsiooni käitumine ohutu.
Näiteks:

sprintf (data.buf, '% d', 12)

Väljund:

pilt

sizeof(Data) = 5 buf = ****hot guard = 15 --------------- ret = 2 sizeof(Data) = 5 buf = 12 guard = 15 ---------------

pilt

2. Kui lähteandmete pikkus on [võrdne] len, kirjutab sprintf andmed täielikult sihtmällu ja kirjutab sihtmälu lõppu täiendava 0 ning tagastab kirjutatud baitide arvu. Praegu on funktsioon kopeeritud väljaspool piire. Seega, kui kasutaja arvab, et eraldatud mälu rahuldab just kopeerimisvajadust, on tegelikult tekkinud potentsiaalne risk.

Näiteks:

sprintf (data.buf, '% d', 1234)

Väljund:

pilt

sizeof(Data) = 5 buf = ****hot guard = 15 --------------- ret = 4 sizeof(Data) = 5 buf = 1234 guard = 0 memory has been broken. ---------------

pilt

3. Kui lähteandmete pikkus on [suurem kui] len, kirjutab sprintf andmed täielikult sihtmällu ja tagastab kirjutatud baitide arvu, hoolimata mäluvälisest olukorrast, isegi veakood pole tagasi.

Näiteks:

sprintf (data.buf, '% d', 123456)

Väljund:

pilt

sizeof(Data) = 5 buf = ****hot guard = 15 --------------- ret = 6 sizeof(Data) = 5 buf = 123456 guard = 53 memory has been broken. ---------------

pilt

Kokkuvõte: ülaltoodud kolme katsetulemuste komplekti saab kontrollida nii Windowsi kui ka Linuxi puhul. On näha, et sprintfi funktsiooni ohutegur on peaaegu 0, seega ei soovitata seda kõigile kasutada.
vsprintf käitub samamoodi nagu sprintf.

Kaks, sprintf_s (ainult Windows)

Funktsiooni sprintf puuduste korvamiseks on kõrgema versiooni MSVC keskkonnas kasutusele võetud funktsioon sprintf_s, mis toetab helistades kasutaja poolt edastatud sihtmälu pikkust ja funktsiooni prototüüpi saab väljendada järgmiselt: :

int sprintf_s (char * buf, size_t buf_size, const char * formaat, ...)

1. Kui lähteandmete pikkus on [alla] len, kirjutab sprintf andmed täielikult sihtmällu ja tagab, et saba lõpeb 0-ga, ja tagastab kirjutatud baitide arvu. Sel ajal on funktsiooni käitumine ohutu.
Näiteks:

sprintf_s (data.buf, len, '% d', 12)

Väljund:

pilt

sizeof(Data) = 5 buf = ****hot guard = 15 --------------- ret = 2 sizeof(Data) = 5 buf = 12 guard = 15 ---------------

pilt

2. Kui lähteandmete pikkus on [võrdne] või [suurem kui] len, käivitab selle funktsiooni kutsumine väite. Silumisrežiimis ilmub käitamisvea viipekast, mis ütleb kasutajale vabastamisrežiimis 'Puhver on liiga väike', programm jookseb otse kokku.

Näiteks:

sprintf_s (data.buf, len, '% d', 1234)

Käivitage silumisrežiimis, see käivitab väite, nagu allpool näidatud:

Kokkuvõte: funktsiooni sprintf_s saab kasutada ainult Windowsis. Kuigi mälu ei kahjustata, käivitab see väite ja põhjustab programmi katkemise. Peate seda kasutama ettevaatlikult.
vsprintf_s käitub samamoodi nagu sprintf_s.

Kolm, _snprintf (ainult Windows)

Võib-olla sellepärast, et sprintf_s pole piisavalt turvaline. Funktsioon nimega _snprintf on kasutusele võetud ka MSVC keskkonnas. Selle funktsiooni prototüüp sarnaneb sprintf_-dega ja seda saab väljendada järgmiselt:

int _snprintf (char * buf, size_t buf_size, const char * formaat, ...)

Selle jõudlus on järgmine:
Näide 1: Kui lähteandmete pikkus on [alla] len, saab need kirjutada täielikult ja lõpeb 0-ga ning tagastatakse tegelik kirjutatud baitide arv:

_snprintf (data.buf, len, '% d', 12)

Väljund:

pilt

sizeof(Data) = 5 buf = ****hot guard = 15 --------------- ret = 2 sizeof(Data) = 5 buf = 12 guard = 15 ---------------

pilt

Näide 2: Kui lähteandmete pikkus on [võrdne] len, saab need kirjutada täielikult, töötlemata lõpus ja tagastatakse tegelikult kirjutatud baitide arv:

_snprintf (data.buf, len, '% d', 1234)

Väljund:

pilt

sizeof(Data) = 5 buf = ****hot guard = 15 --------------- ret = 4 sizeof(Data) = 5 buf = 1234 guard = 15 ---------------

pilt

Näide 3: Kui lähteandmete pikkus on [suurem kui] len, saab kirjutada maksimaalselt [len] tähemärki ja lõpp pole halb. Töötlemine tagastatakse. [-1]:

_snprintf (data.buf, len, '% d', 123456)

Väljund:

pilt

sizeof(Data) = 5 buf = ****hot guard = 15 --------------- ret = -1 sizeof(Data) = 5 buf = 1234 guard = 15 ---------------

pilt

Kokkuvõte: funktsiooni _snprintf saab kasutada ainult Windowsis. See võib kirjutada maksimaalselt [suurus] tähemärki, mitte kunagi mälu hävitada ega katkestust käivitada, kuid see ei saa garanteerida, et sihtmälu lõpeb nulliga. Tagasiväärtuse kaudu saate teada, kas funktsioonikõne on edukas. Kui tagastusväärtus> = 0, tähendab see, et kõne õnnestus ja tegelike kirjutatud märkide arv tagastatakse, kui tagasiväärtuse väärtus on -1, see tähendab, et sihtmälu on liiga väike ja kõne ebaõnnestus, kuid olen proovinud kõige parem teha täidis.

_vsnprintf käitub samamoodi nagu _snprintf.

Neli, snprintf (Linux / Windows)

Funktsiooni prototüüp Linuxis on:

int snprintf (char * str, size_t size, const char * formaat, ...)

Seda funktsiooni saab kasutada nii Windowsi kui ka Linuxi puhul ja käitumine on järjepidev. See tähendab: kirjutage sihtmällu maksimaalselt [suurus-1] tähemärki ja veenduge, et see lõpeks nulliga. Tagastusväärtus on [kirjutatavate baitide arv], mitte [tegelikult kirjutatud baitide arv].
Näide 1: Kui lähteandmete pikkus on [alla] len, saab need kirjutada täielikult ja lõpeb 0-ga ning tagastatakse tegelik kirjutatud baitide arv:

snprintf (data.buf, len, '% d', 12)

Väljund:

pilt

sizeof(Data) = 5 buf = ****hot guard = 15 --------------- ret = 2 sizeof(Data) = 5 buf = 12 guard = 15 ---------------

pilt

Näide 2: Kui lähteandmete pikkus on [võrdne] len, kirjutatakse tegelikult ainult [len-1] tähemärki ja viimane märk täidetakse 0-ga, kuid tagastusväärtus on [len]:

snprintf (data.buf, len, '% d', 1234)

Väljund:

pilt

sizeof(Data) = 5 buf = ****hot guard = 15 --------------- ret = 4 sizeof(Data) = 5 buf = 123 guard = 15 ---------------

pilt

Näide 3: Kui lähteandmete pikkus on [suurem kui] len, saab kirjutada ainult [len-1] tähemärki ja viimane märk täidetakse 0-ga, kuid tagastusväärtus on [baitide arv, mis tuleks kirjutada]:

snprintf (data.buf, len, '% d', 123456)

Väljund:

pilt

sizeof(Data) = 5 buf = ****hot guard = 15 --------------- ret = 6 sizeof(Data) = 5 buf = 123 guard = 15 ---------------

pilt

Kokkuvõte: funktsiooni snprintf saab kasutada mõlema Linuxi / Windowsi platvormi all. See võib kirjutada kuni [suurus-1] tähemärki, mitte kunagi mälu hävitada ega katkestust käivitada ja alati tagada, et sihtmälu võib lõppeda nulliga. Ainus probleem on see, et tagastusväärtus pole usaldusväärne ja võimatu on järeldada, kas kõne ebaõnnestus.

Vsnprintf käitumine on sama mis snprintf.

Siinkohal on sprintfi seeria seotud funktsioonid kõik valmis ja tundub, et täiuslikku funktsiooni pole olemas. Kuid nüüd, kui teate nende konkreetset käitumist, saate vastavalt rakenduse stsenaariumile valida sobiva funktsiooni.

Täiendus: Nüüd, kui see on siia kirjutatud, lubage mul kasutada võimalust strcpy funktsiooniklastri uurimiseks.

Test kood:

pilt

int main() { Data data data.Display() const char * ret = strncpy(data.buf, '12345678', len) std::cout << 'ret = ' << ret << std::endl data.Display() std::cin.get() return 0 }

pilt

Üks, strcpy (Linux / Windows)
Funktsiooni prototüüp on: char * strcpy (char * dest, const char * src)
Vanimal stringi kopeerimisfunktsioonil on lihtne põhimõte. See kopeerib tähemärgid allikast stringist järjest sihtmärgi aadressile, kuni see kohtub 0. Mälu kattumise korral on vaja eritöötlust. See tagastab alati tegelikult kirjutatud tähemärkide arvu ja ei tegele mäluga väljaspool piire ning see on ka ebakindel, nii et ma ei korda seda siin.


Kaks, strcpy_s (ainult Windows)
See on Windowsi ainulaadne funktsioon, prototüüpi võib kirjeldada järgmiselt:
int strcpy_s (char * dest, size_t size, const char * src)
Pange tähele, et tagastusväärtus pole enam sihtringi esimene aadress, vaid int.
Kui allikastringi pikkus on [väiksem] või [võrdne] sihtmäluga, saab seda funktsiooni turvaliselt käivitada ja tagastusväärtus on [0]. Kui lähtealuse stringi pikkus on [suurem kui] sihtmälu, käivitab see funktsioon väite. Põhjustage programmi katkestamine. See funktsioon ei põhjusta mälu rikkumist.

Kolm, strncpy_s (ainult Windows)
See on Windowsi ainulaadne funktsioon, prototüüpi võib kirjeldada järgmiselt:
int strncpy_s (char * dest, size_t dest_size, const char * src, size_t count)
Tagasiväärtus on ka int.
Lisaks sihtmälu suuruse täpsustamisele saab selle funktsiooniga määrata ka kopeeritavate märkide arvu, mis on samaväärne topeltkaitsega.
Kuid pange tähele, et [loe<= dest_size-1] must be satisfied, this function can be called correctly, otherwise it will trigger the assert interrupt.

Neli, strncpy (Linux / Windows)
Funktsiooni prototüüp: char * strncpy (char * dest, const char * src, size_t size)
Käitumine sarnaneb strcpy-ga. See kopeerib tähemärgid allikast stringist kordamööda sihtaadressile, kuni ilmub 0 või sihtmälu on täis, ja kopeerib kuni [size] tähemärki. See funktsioon ei kahjusta mälu ega põhjusta programmi katkestamist, kuid see ei saa garanteerida, et sihtring lõpeb 0-ga.
Näiteks:

strncpy(data.buf, '12345', len)

Väljund:

pilt

sizeof(Data) = 5 buf = ****hot guard = 15 --------------- ret = 1234 sizeof(Data) = 5 buf = 1234 guard = 15 ---------------

pilt