Manipulace s řetězcem C - C string handling

C programovací jazyk má sadu funkcí, provádějících operace řetězce (řetězce znaků a bajtů řetězce) ve své standardní knihovny . Jsou podporovány různé operace, jako je kopírování, zřetězení , tokenizace a vyhledávání. Pro znakových řetězců, standardní knihovna používá konvence, že řetězce jsou null zakončený : řetězec n znaků je reprezentován jako pole o n + 1 prvků, z nichž poslední je NULznak (s číselnou hodnotu 0).

Jedinou podporou řetězců ve správném programovacím jazyce je, že kompilátor překládá citované řetězcové konstanty do řetězců zakončených hodnotou null.

Definice

Řetězec je definován jako souvislá posloupnost kódových jednotek ukončená první jednotkou nulového kódu (často se nazývá jednotka kódu NUL ). To znamená, že řetězec nemůže obsahovat jednotku nulového kódu, protože první viděný označuje konec řetězce. Délka z řetězce je počet jednotek kódu před zero kódu jednotce. Paměť obsazená řetězcem je vždy o jednu jednotku kódu větší než délka, protože k uložení nulového terminátoru je potřeba místo.

Obecně termín řetězec znamená řetězec, kde kódová jednotka je typu char, což je přesně 8 bitů na všech moderních počítačích. C90 definuje široké řetězce, které používají kódovou jednotku typu wchar_t, což je 16 nebo 32 bitů na moderních strojích. To bylo určeno pro Unicode, ale stále častěji se místo toho používá UTF-8 v normálních řetězcích pro Unicode.

Řetězce jsou předávány do funkcí předáním ukazatele první jednotce kódu. Protože char*a wchar_t*jsou různé typy, funkce, které zpracovávají široké řetězce, se liší od těch, které zpracovávají normální řetězce, a mají různá jména.

Řetězcové literály ( "text"ve zdrojovém kódu C) jsou během kompilace převedeny na pole. Výsledkem je pole kódových jednotek obsahující všechny znaky plus koncová nulová kódová jednotka. V C90 L"text"produkuje široký řetězec. Řetězcový literál může obsahovat jednotku nulového kódu (jedním ze způsobů je vložit \0do zdroje), ale to způsobí, že řetězec v tomto bodě skončí. Zbytek literálu bude umístěn do paměti (s další nulovou kódovou jednotkou přidanou na konec), ale není možné vědět, že tyto kódové jednotky byly přeloženy z řetězcového literálu, proto takový zdrojový kód není řetězcový literál.

Kódování znaků

Každý řetězec končí při prvním výskytu jednotky nulového kódu příslušného druhu ( charnebo wchar_t). V důsledku toho může bajtový řetězec ( char*) obsahovat znaky jiné než NUL v ASCII nebo v libovolné příponě ASCII , ale nikoli znaky v kódováních, jako je UTF-16 (přestože 16bitová jednotka kódu může být nenulová, její vysoký nebo nízký bajt může být nula). Kódování, která lze uložit do širokých řetězců, jsou definována šířkou wchar_t. Ve většině implementací wchar_tje alespoň 16 bitů, a tak lze uložit všechna 16bitová kódování, jako je UCS-2 . Pokud wchar_tje 32bitové , lze uložit 32bitové kódování, například UTF-32 . (Standard vyžaduje „typ s libovolným širokým znakem“, který v systému Windows již neplatí od posunu UCS-2 na UTF-16.) C ++ 11 a C11 přidávají dva typy s explicitními šířkami char16_ta char32_t.

Kódování s proměnnou šířkou lze použít jak v bajtových řetězcích, tak v širokých řetězcích. Délka řetězce a offsety se měří v bytech nebo wchar_tnikoli ve "znacích", což může být pro začínající programátory matoucí. UTF-8 a Shift JIS se často používají v řetězcích C byte, zatímco UTF-16 se často používá v řetězcích C wide, když wchar_tje to 16 bitů. Zkrácení řetězců pomocí znaků s proměnnou délkou pomocí funkcí, jako jsou funkce, strncpymůže způsobit neplatné sekvence na konci řetězce. To může být nebezpečné, pokud jsou zkrácené části interpretovány kódem, který předpokládá, že je vstup platný.

Podpora literálů Unicode, jako jsou char foo[512] = "φωωβαρ";(UTF-8) nebo wchar_t foo[512] = L"φωωβαρ";(UTF-16 nebo UTF-32, závisí na wchar_t), je definována implementací a může vyžadovat, aby byl zdrojový kód ve stejném kódování, zejména charpokud kompilátory mohou kopírovat cokoli, co je mezi uvozovkami. Některé kompilátory nebo editory budou vyžadovat zadání všech znaků jiných než ASCII jako \xNNsekvencí pro každý bajt UTF-8 a/nebo \uNNNNpro každé slovo UTF-16. Od C11 (a C ++ 11) char foo[512] = u8"φωωβαρ";je k dispozici nová doslovná syntaxe, která zaručuje UTF-8 pro bytestring literál.

Přehled funkcí

Většina funkcí, které fungují na řetězcích C, je deklarována v string.hzáhlaví ( cstringv C ++), zatímco funkce, které fungují na C širokých řetězcích, jsou deklarovány v wchar.hzáhlaví ( cwcharv C ++). Tato záhlaví také obsahují deklarace funkcí používaných pro zpracování vyrovnávacích pamětí paměti; název je tedy něco jako nesprávné pojmenování.

Funkce deklarované v string.hjsou extrémně populární, protože jako součást standardní knihovny C zaručeně fungují na jakékoli platformě, která podporuje C. S těmito funkcemi však existují určité problémy se zabezpečením, například potenciální přetečení vyrovnávací paměti, pokud není používáno opatrně a správně , což přimělo programátory upřednostňovat bezpečnější a možná méně přenosné varianty, z nichž některé populární jsou uvedeny níže. Některé z těchto funkcí také porušují správnost konstitu tím, že přijmou constukazatel řetězce a vrátí v constukazateli řetězec bez ukazatele. Chcete -li to opravit, některé byly rozděleny do dvou přetížených funkcí ve verzi C ++ standardní knihovny.

V historické dokumentaci byl místo „bajtu“ pro řetězce C často používán výraz „znak“, což mnohé vede k domněnce, že tyto funkce pro UTF-8 nějak nefungují . Ve skutečnosti jsou všechny délky definovány jako v bajtech a to platí pro všechny implementace a tyto funkce fungují také s UTF-8 i s jednobajtovým kódováním. Dokumentace BSD byla opravena, aby to bylo jasné, ale dokumentace POSIX, Linux a Windows stále používá „znak“ na mnoha místech, kde je „byte“ nebo „wchar_t“ správný termín.

Funkce pro zpracování vyrovnávacích pamětí mohou zpracovávat sekvence bajtů, které jako součást dat obsahují null-byte. Názvy těchto funkcí obvykle začínají memopačně než strpředpona.

Konstanty a typy

název Poznámky
NULL Makro expandující na konstantu nulového ukazatele ; tj. konstanta představující hodnotu ukazatele, která zaručeně není platnou adresou objektu v paměti.
wchar_t Typ používaný pro kódovou jednotku v širokých řetězcích, obvykle 16bitové nebo 32bitové hodnoty bez znaménka. Pro tyto kódové jednotky není specifikována žádná konkrétní interpretace; standard C vyžaduje pouze to, aby wchar_t byl dostatečně široký, aby pojal nejširší znakovou sadu mezi podporovanými místními systémy . Teoreticky může mít wchar_t stejnou velikost jako char , a proto není schopen pojmout kódové jednotky UTF-32 nebo UTF-16 .
wint_t Celočíselný typ, který může obsahovat libovolnou hodnotu wchar_t i hodnotu makra WEOF. Tento typ se nezměnil integrovanými akcemi. Obvykle 32bitová podepsaná hodnota.
mbstate_t Obsahuje všechny informace o stavu převodu požadované od jednoho volání funkce k druhému.

Funkce

Bajtový
řetězec
Široký
provázek
Popis
řetězec
manipulace
strcpy wcscpy Zkopíruje jeden řetězec do druhého
strncpy wcsncpy Zapisuje přesně n bytů, kopíruje ze zdroje nebo přidává hodnoty null
strcat wcscat Připojí jeden řetězec k druhému
strncat wcsncat Připojí ne více než n bajtů z jednoho řetězce do druhého
strxfrm wcsxfrm Transformuje řetězec podle aktuálního národního prostředí
Smyčcové
vyšetření
strlen wcslen Vrátí délku řetězce
strcmp wcscmp Porovná dva řetězce ( třícestné srovnání )
strncmp wcsncmp Porovná konkrétní počet bajtů ve dvou řetězcích
strcoll wcscoll Porovná dva řetězce podle aktuálního národního prostředí
strchr wcschr Vyhledá první výskyt bajtu v řetězci
strrchr wcsrchr Vyhledá poslední výskyt bajtu v řetězci
strspn wcsspn Vrátí počet počátečních bajtů v řetězci, které jsou ve druhém řetězci
strcspn wcscspn Vrátí počet počátečních bajtů v řetězci, které nejsou ve druhém řetězci
strpbrk wcspbrk Vyhledá v řetězci první výskyt bajtu v sadě
strstr wcsstr Vyhledá první výskyt podřetězce v řetězci
strtok wcstok Rozdělí řetězec na tokeny
Smíšený strerror N/A Vrátí řetězec obsahující zprávu odvozenou z kódu chyby

Manipulace s pamětí
memset wmemset Vyplní vyrovnávací paměť opakovaným bajtem
memcpy wmemcpy Zkopíruje jednu vyrovnávací paměť do druhé
memmove wmemmove Zkopíruje jednu vyrovnávací paměť do druhé, případně překrývající se vyrovnávací paměti
memcmp wmemcmp Porovná dvě vyrovnávací paměti (třícestné srovnání)
memchr wmemchr Vyhledá první výskyt bajtu ve vyrovnávací paměti
  1. ^ Pro funkce širokého řetězce nahraďtewchar_tv popisu „bajt“

Vícebajtové funkce

název Popis
mblen Vrátí počet bajtů v dalším vícebajtovém znaku
mbtowc Převede další vícebajtový znak na široký znak
wctomb Převede široký znak na vícebajtovou reprezentaci
mbstowcs Převede vícebajtový řetězec na široký řetězec
wcstombs Převede široký řetězec na vícebajtový řetězec
btowc Pokud je to možné, převeďte jednobajtový znak na široký
wctob Pokud je to možné, převeďte široký znak na jednobajtový znak
mbsinit Zkontroluje, zda objekt stavu představuje počáteční stav
mbrlen Vrátí počet bajtů v dalším vícebajtovém znaku v daném stavu
mbrtowc Převede další vícebajtový znak na široký znak v daném stavu
wcrtomb Převede široký znak na vícebajtovou reprezentaci v daném stavu
mbsrtowcs Převede vícebajtový řetězec na široký řetězec v daném stavu
wcsrtombs Převede široký řetězec na vícebajtový řetězec v daném stavu

Všechny tyto funkce mají ukazatel na a mbstate_tobjekt, který musí volající udržovat. Toto bylo původně určeno ke sledování stavů posunu vmbkódování, ale moderní, jako je UTF-8, to nepotřebují. Tyto funkce však byly navrženy za předpokladu, žetoaletakódování není kódování s proměnnou šířkou, a proto jsou navrženy tak, aby se zabývaly přesně jednímwchar_tnajednou předávání podle hodnoty, nikoli pomocí řetězcového ukazatele. Protože UTF-16 je kódování s proměnnou šířkou,mbstate_t byl znovu použit ke sledování náhradních párů v širokém kódování, ačkoli volající stále musí detekovat a volat mbtowc dvakrát pro jednu postavu.

Číselné převody

Bajtový
řetězec
Široký
provázek
Popis
atof N/A převede řetězec na hodnotu s plovoucí desetinnou čárkou ('atof' znamená 'ASCII to float')
atoi
atol
atoll
N/A převede řetězec na celé číslo ( C99 ) ('atoi' znamená 'ASCII na celé číslo')
strtof( C99 )
strtod
strtold( C99 )
wcstof( C99 )
wcstod
wcstold( C99 )
převede řetězec na hodnotu s plovoucí desetinnou čárkou
strtol
strtoll
wcstol
wcstoll
převede řetězec na celé číslo se znaménkem
strtoul
strtoull
wcstoul
wcstoull
převede řetězec na celé číslo bez znaménka
  1. ^ Zde řetězec odkazuje buď na bajtový řetězec, nebo široký řetězec

Standardní knihovna C obsahuje několik funkcí pro číselné převody. Funkce, které se zabývají řetězci bajtů, jsou definovány v stdlib.hzáhlaví ( cstdlibhlavička v C ++). Funkce, které se zabývají širokými řetězci, jsou definovány v wchar.hzáhlaví ( cwcharhlavička v C ++).

Tyto strtoxxxfunkce nejsou const korektní , protože přijmout constukazatel řetězce a vrátí bez možnosti constukazatel v rámci řetězce.

Také od normativního dodatku 1 (C95) atoxxjsou funkce považovány za zahrnuté do strtoxxxfunkcí, a proto ani C95, ani žádný pozdější standard neposkytuje rozsáhlé verze těchto funkcí. Argumentem proti atoxxje, že nerozlišují mezi chybou a 0.

Oblíbená rozšíření

název Plošina Popis
bzero POSIX , BSD Vyplní vyrovnávací paměť nulovými bajty, zastaralá o memset
memccpy SVID , POSIX zkopíruje až na zadaný počet bajtů mezi dvě paměťové oblasti, které se nesmí překrývat, a zastaví se, když je nalezen daný bajt.
mempcpy GNU varianta memcpyvrácení ukazatele na bajt následující za posledním zapsaným bajtem
strcasecmp POSIX, BSD verze bez rozlišení velkých a malých písmen strcmp
strcat_s Okna varianta, strcatkterá před kopírováním zkontroluje velikost cílové vyrovnávací paměti
strcpy_s Okna varianta, strcpykterá před kopírováním zkontroluje velikost cílové vyrovnávací paměti
strdup POSIX přiděluje a duplikuje řetězec
strerror_r POSIX 1, GNU jeho varianta strerrorje bezpečná pro vlákna. Verze GNU není kompatibilní s verzí POSIX.
stricmp Okna verze bez rozlišení velkých a malých písmen strcmp
strlcpy BSD, Solaris varianta, strcpykterá zkrátí výsledek, aby se vešel do cílového bufferu
strlcat BSD, Solaris varianta, strcatkterá zkrátí výsledek, aby se vešel do cílového bufferu
strsignal POSIX: 2008 vrací řetězcovou reprezentaci signálního kódu . Není bezpečné vlákno.
strtok_r POSIX jeho varianta strtokje bezpečná pro vlákna

Náhrady

Přes dobře zavedené potřebě nahradit strcata strcpyfunkcemi, které neumožňují přetečení zásobníku, žádný uznávaný standard vznikl. Toto je částečně kvůli mylné víře, u mnoha C programátorů, že strncata strncpymají požadované chování; k tomu však nebyla navržena ani jedna funkce (byly určeny k manipulaci s vyrovnávací pamětí řetězců pevné velikosti s nulovým polstrováním, datovým formátem, který se v moderním softwaru běžně nepoužívá), a chování a argumenty jsou neintuitivní a často i nesprávně napsané odborníkem programátoři.

Nejoblíbenější náhradou jsou funkce strlcata strlcpy, které se objevily v OpenBSD 2.4 v prosinci 1998. Tyto funkce vždy zapíší jednu NUL do cílového bufferu, v případě potřeby zkrátí výsledek a vrátí velikost bufferu, který by byl potřeba, což umožňuje detekci zkrácení a poskytuje velikost pro vytvoření nové vyrovnávací paměti, která nebude zkrácena. Byly kritizovány na základě údajné neefektivity, podpory používání řetězců C (namísto nějaké lepší alternativní formy řetězce) a skrývání dalších potenciálních chyb. V důsledku toho nebyly zahrnuty do knihovny GNU C (používané softwarem v Linuxu), přestože jsou implementovány v knihovnách C pro OpenBSD, FreeBSD , NetBSD , Solaris , OS X a QNX , stejně jako v alternativních C knihovnách pro Linux, například musl představený v roce 2011. Nedostatek podpory knihovny GNU C nezastavil různé autory softwaru v jeho používání a sdružování náhrady, mimo jiné SDL , GLib , ffmpeg , rsync , a dokonce i interně v linuxovém jádře . K dispozici jsou implementace open source pro tyto funkce.

Někdy memcpynebo memmovese používají, protože mohou být efektivnější, než strcpykdyž opakovaně nekontrolují NUL (u moderních procesorů to platí méně). Protože jako parametr potřebují délku vyrovnávací paměti, správné nastavení tohoto parametru může zabránit přetečení vyrovnávací paměti.

V rámci svého životního cyklu rozvoje zabezpečení z roku 2004 společnost Microsoft představila řadu „zabezpečených“ funkcí včetně strcpy_sa strcat_s(spolu s mnoha dalšími). Tyto funkce byly standardizovány s několika drobnými změnami jako součást volitelné C11 (příloha K) navržené ISO/IEC WDTR 24731. Tyto funkce provádějí různé kontroly, včetně toho, zda řetězec není příliš dlouhý, aby se vešel do vyrovnávací paměti. Pokud kontroly selžou, je volána uživatelem specifikovaná funkce „handler-constraint handler“, která program obvykle přeruší. Některé funkce provádějí destruktivní operace před voláním obslužné rutiny omezení běhu; například strcat_snastaví cíl na prázdný řetězec, což může ztížit zotavení z chybových stavů nebo jejich ladění. Tyto funkce přitahovaly značnou kritiku, protože zpočátku byly implementovány pouze ve Windows a současně Microsoft Visual C ++ začal vytvářet varovné zprávy, které navrhovaly programátorům používat tyto funkce místo standardních. Někteří to spekulovali jako o pokusu Microsoftu zamknout vývojáře na jeho platformě. Ačkoli jsou k dispozici open-source implementace těchto funkcí, tyto funkce nejsou v běžných unixových knihovnách C k dispozici. Zkušenosti s těmito funkcemi ukázaly značné problémy s jejich přijetím a chyby v používání, proto je pro příští revizi normy C navrženo odstranění přílohy K. Využití memset_sBylo také navrženo jako způsob, aby se zabránilo nežádoucím optimalizace kompilátoru.

Viz také

Poznámky

Reference

externí odkazy

  • Rychlé memcpy v C , více příkladů kódování C pro cílení na různé typy architektur instrukcí CPU