| ← knihovna errno.h | C/C++ | knihovna stdio.h → |
S knihovnou <string.h> jsme se již setkali, když jsme si povídali o funkcích pracujících s řetězci. V této kapitole se budeme zabývat funkcemi, které jsou také v knihovně <string.h>, ale jejich funkce není vždy určena jen pro práci s řetězci, ale jde spíše o funkce pracující s pamětí. Najdete zde například funkce, které dokáží vynulovat zadanou oblast paměti (zadanou pomocí délky a ukazatele), nebo zkopírovat (rychle a efektivně) část paměti z jednoho místa na druhé, atp.
Pamatujte si, že funkce, které zde jsou, pracují s daty po bytech. Pokud tedy budete mít pole typu int a použijete funkci, která vyplní pole třeba jedničkou, pak to neznamená, že v každém prvku tohoto pole bude jednička, ale v každém bajtu. Pro 16bitové překladače to znamená, že typ int má velikost dvou bajtů, a číslo 1 se v něm tedy objeví dvakrát! (0000000100000001bitově = 257decimálně).
Není těch funkcí mnoho, že. Pokud jste ti šťastnější a používáte k programování Linux, zkuste nápovědu man bstring. Najdete tam další podobné funkce. Jsou však již implementačně závislé, proto je třeba dobře zvážit, zda jejich použití stojí za snížení přenositelnosti kódu.
V příkladu vidíte použití funkcí pro práci s pamětí. Všimněte si práce s ukazateli. Při práci s těmito funkcemi si dávejte pozor, aby nemohlo nikdy dojít k pokusu o zápis mimo alokovanou paměť. Pamatujte si, že používání těchto funkcí je optimální co do rychlosti. Pokud chcete zkopírovat strukturu T2 do T1, je jistě nejrychlejší použít přiřazení T1 = T2;, ale jak je v příkladu ukázáno, pokud kopírujete jen několik částí ze struktury ležících za sebou, je výhodnější použít memcpy() než přiřazovat jednu položku struktury za druhou. Obdobně se může memcpy() hodit při kopírování pole struktur.
1: /*------------------------------------------------*/ 2: /* pamet1.c */ 4: #include <stdio.h> 5: #include <string.h> 7: void porovnej(const char *s1, const char *s2, const int n) 8: { 9: int x; 10: x = memcmp(s1, s2, n); 11: printf("%2i: %s %s %s\n", n, s1, x < 0 ? "<" : x > 0 ? ">" : "=", s2); 12: } 14: typedef struct { 15: short int x; 16: char pole[10]; 17: float f; 18: } Trida; 20: void tiskni(const Trida * T) 21: { 22: unsigned int i; 23: printf("T = {%3i,{", T->x); 24: for (i = 0; i < sizeof(T->pole) / sizeof(T->pole[0]); i++) 25: printf("%i,", T->pole[i]); 26: printf("\b},{%.40f}}\n", T->f); 27: } 29: int main(void) 30: { 31: Trida T1, T2; 32: char text1[] = "1234567890"; 33: char text2[] = "abcdefghij"; 35: porovnej("aaazzzzz", "aabaa", 4); 36: porovnej("aaaaa", "aabaa", 2); 38: tiskni((Trida *) memset(&T1, 0, sizeof(T1))); 39: tiskni((Trida *) memset(&T2, 1, sizeof(T2))); 41: /* Vzpomente si na aritmetiku ukazatelu. Vyraz &T1 + n neukazuje n 42: * bajtu za adresu &T1, ale n*sizeof(T1) bajtu za adresu T1. Tedy o n 43: * struktur dale. Proto pouzijeme k pretypovani ukazatele typ char, 44: * ktery jiz ma velikost 1 bajtu 45: * a tak muzeme pomoci sizeof posouvat ukazatele po bajtech */ 47: memcpy((char *) &T1 + sizeof(T1.x), (char *) &T2 + sizeof(T1.x), 48: sizeof(T1) - sizeof(T1.x)); 49: tiskni(&T1); 51: printf("text1+3: %s\n", (char *) memcpy(text1 + 3, text2 + 6, 3)); 52: printf("text1 : %s\n", text1); 54: /* chybne pouziti - pameti se prekryvaji */ 55: (void) memcpy(text2 + 1, text2, 6); 56: printf("text2+1: %s (memcpy)\n", text2); 58: /* spravne pouziti */ 59: memcpy(text2, "abcdefghij", 10); 60: (void) memmove(text2 + 1, text2, 6); 61: printf("text2+1: %s (memmove)\n", text2); 63: return 0; 64: } 66: /*------------------------------------------------*/
Výstup z programu:
4: aaazzzzz < aabaa
2: aaaaa = aabaa
T = { 0,{0,0,0,0,0,0,0,0,0,0},{0.0000000000000000000000000000000000000000}}
T = {257,{1,1,1,1,1,1,1,1,1,1},{0.0000000000000000000000000000000000000237}}
T = { 0,{1,1,1,1,1,1,1,1,1,1},{0.0000000000000000000000000000000000000237}}
text1+3: ghi7890
text1 : 123ghi7890
text2+1: aabbdefhij (memcpy)
text2+1: aabcdefhij (memmove)
K tomuto programu je třeba ještě něco dodat. Porovnávání struktur pomocí memcmp() je poněkud sporné co do přenositelnosti kódu. Některé platformy (ať už softwarové nebo hardwarové) mohou mezi jednotlivé části struktury vkládat bity (z jakéhosi důvodu to opravdu dělají, zvláště překladače C++). Tyto bity pak mohou mít různé hodnoty i pro struktury, jejichž obsah je stejný. Z toho důvodu může funkce memcmp() vyhodnotit stejné struktury jako různé. Jediným řešením tohoto problému je porovnávání všech složek struktur hezky jedné po druhé.
Funkce sprintf() a snprintf() nejsou funkcemi ze souboru <string.h>, ale ze souboru <stdio.h>. Jelikož však pracují také s pamětí, jako výše probírané funkce, a s řetězci, dovolil jsem si je sem na závěr „přifařit“.
Jejich deklarace jsou:
int sprintf(char *str, const char *format, ...); int snprintf(char *str, size_t size, const char *format,...);
Funkce sprintf() zapíše formátovaný výstup stejně jako funkce printf() s tím rozdílem, že výstup jde do paměti, kam ukazuje str. Funkce snprintf() navíc zapíše pouze nanejvýš size znaků (lol, to je věta xD). Funkce snprintf() je tedy bezpečnější co do rizika překročení limitu alokované paměti. Funkci sprintf() by jste tedy měli používat jen v případě, že si můžete být jisti, že formátovaný řetězec nepřekročí délku pole str.
Funkce sprintf() je rychlejší než snprintf(), protože nekontroluje délku vytvářeného řetězce. Ovšem za tu cenu, že může způsobit neoprávněný přístup do paměti.
Funkce snprintf() vrátí hodnotu -1 v případě, že se výstup musel kvůli limitu délky size zkrátit. V případě úspěchu vracejí obě funkce počet úspěšně zapsaných znaků do paměti.
Pokud si vzpomínáte na funkce, které převádějí řetězce na čísla (např. atof()), pak opačný převod se provádí právě pomocí funkce snprintf().
1: /*------------------------------------------------*/ 2: /* snpr1.c */ 4: #include <stdio.h> 5: #include <string.h> 7: int main(void) 8: { 9: char str[50]; 10: snprintf(str, 5, "%f", 3.8); 11: printf("Retezec %s je dlouhy %i znaku.\n", str, strlen(str)); 12: return 0; 13: } 15: /*------------------------------------------------*/
Výstup z programu:
Retezec 3.80 je dlouhy 4 znaku.
Všimněte si, že řetězec je dlouhý o jeden znak méně, než byl zadaný limit pro funkci snprintf(). Ten poslední znak je totiž vyhrazen pro označení konce řetězce '\0'.
Funkce sscanf() je též ze souboru stdio.h a dělá totéž co scanf, jenom místo standardního vstupu čte data z paměti, kam ukazuje str. Deklarace funkce je následující:
int sscanf(const char *str, const char *format, ...);
Myslím, že její použití při porovnání s funkcí scanf() je natolik zřejmé, že není nutné uvádět příklad (nebo jsem jen líný jej uvést? :-).
| ← knihovna errno.h | C/C++ | knihovna stdio.h → |
