LINUXSOFT.cz Přeskoč levou lištu

ARCHIV



   

> C/C++ (29) - Standardní knihovna počtvrté

Dnes v rychlosti probereme locale a zpracování jednotlivých znaků, v druhé části článku dojde na standardní i nestandardní ukončování programu.

15.12.2005 06:00 | Jan Němec | Články autora | přečteno 22416×

Národní prostředí

Jazyk C pochází ze 70. let a je to na něm vidět. Před 35 lety jen málo lidí od počítačů napadlo, že by někdo mohl chtít používat jinou znakovou sadu, formát čísel, data a podobně, než používají Američané. Později byla do C jakási podpora národního prostředí doplněna, ale na některých překladačích dosud nefunguje nebo funguje jen částečně (což ale není problém gcc), takže pro použití v přenositelném kódu je jen obtížně použitelná. Standardní knihovna podporuje pouze terminálový vstup a výstup, přičemž řetězce jsou jen pole bytů, která se na výstup posílají v podobě binárně shodné se zápisem ve zdrojovém kódu. Lokalizace tak má jen omezené pole působnosti, neboť neřeší například problematiku fontů nebo překódování do a z unikódu. Troufl bych si proto tvrdit, že lokalizaci programu na úrovni standardní knihovny C je lepší se vyhnout.

#include <stdio.h>
#include <locale.h>

int main(void) {
  printf("%f\n", 1.45);
  setlocale(LC_ALL, "");
  printf("%f\n", 1.45);
  return 0;
}

Na počátku programu je podle normy nastavené přenositelné "C" prostředí. Funkce printf proto vypíše číslovku s desetinnou tečkou. Volání setlocale změní charakter prostředí ve všech (LC_ALL) oblastech (znaková sada, výpis čísel, ...) na výchozí (parametr "") na daném počítači. Na mém česky nainstalovaném Mandriva Linuxu 2006 s gcc 4.0.1 a glibc 2.3.5 uvedený program oddělí při druhém volání printf desetinná místa čárkou. Na českých Windows 98 s MS Visual C++ 6.0 se uvedený program choval stejně, ale níže uvedený převod národních znaků na velká písmena pomocí toupper mi fungoval pouze s gcc na Linuxu.

Lokalizace prostředí na úrovni standardní knihovny je poměrně nebezpečná záležitost. Uživatel možná rád uvidí desetinou čárku místo tečky (toho ovšem lze snadno docílit i bez speciální podpory standardní knihovny), ale pokud jiná část programu například pomocí printf generuje SQL dotazy, kde musí být oddělovačem tečka, je problém na světě. Proto, pokud programátor potřebuje nastavovat locale, je vhodné místo všech oblastí (LC_ALL) použít pouze tu, kde je to nezbytné, například LC_NUMERIC, LC_CTYPE, LC_MESSAGES a podobně. Zájemce, které se mi nepodařilo odradit, odkazuji na manuálovou stránku setlocale.

Práce se znaky

Hlavičkový soubor ctype.h obsahuje řadu funkcí a maker pro detekci typu znaku a konverzi mezi malými a velkými písmeny. Hodit by se mohly zejména tyto rutiny s dostatečně výmluvnými názvy:

int islapha(int c); /* Je znak písmeno? */
int isdigit(int c); /* Je znak číslice? */
int isalnum(int c); /* Je znak písmeno nebo číslice? */
int islower(int c); /* Je znak malé písmeno? */
int isupper(int c); /* Je znak velké písmeno? */
int isprint(int c); /* Je znak tisknutelný? */
int isspace(int c); /* Jde o "bílý" znak? (mezera, tab, ...) */
int tolower(int c); /* Velká písmena na malá, ostatní znaky nechat. */
int toupper(int c); /* Malá písmena na velká, ostatní znaky nechat. */

Co vlastně je nebo není písmeno závisí na znakové sadě a tím pádem i na locale. Ve výchozím nastavení "C" se za písmena považují pouze znaky anglické abecedy. Při nastavení českého locale, budou rutiny fungovat i pro znaky s diakritikou, ale jen s kvalitním překladačem/libc a jen pokud používáte stejnou znakovou sadu, kterou považuje překladač/libc za výchozí pro zvolené locale.

#include <ctype.h>
...

char *pc;
int silne = 0;

pc = heslo;
while (*pc) {
  if (isdigit(*pc)) {
    silne = 1;
    break;
  }
}
if (!silne) {
  puts("Slabé heslo, neobsahuje ani jednu číslici!");
}

Ukončení programu

Zatím jsme vždy ukončovali chod programu pomocí return ve funkci main. V některých případech může být tento postup nešikovný, běžným případem jsou rekurzivní nebo jiná vnořená volání funkcí, kdy program zjistí, že by se měl ukončit v nejvnitřnějším volání a je příliš komplikované zajistit návrat ze všech instancí všech funkcí. Dalším případem je ukončení programu programátorem knihovny, který nemůže ovlivnit volající kód. Standardní knihovna proto nabízí funkce abort, exit a atexit.

#include <stdlib.h>

void abort(void);
void exit(int status);
int atexit(void (*function)(void));

Funkce abort zajistí okamžité chybové ukončení programu, na linuxu signálem SIGABRT. Buffery všech otevřených souborů by měly být zapsány a soubory uzavřeny, ale programátor by na to pochopitelně neměl spoléhat. Funkce je určena především pro nestandardní ukončení programu při ladění, případně po nějaké velmi vážné chybě programu, kdy už nemá smysl snažit se cokoli zachránit a kdy je záměrem uživatele informovat následující hláškou:

[honza@localhost ~]$ ./abort
Neúspěšně ukončen (SIGABRT)
[honza@localhost ~]$

Ukončení programu mimo main nemusí vždy znamenat chybu, případně chyba není tak vážná, aby bylo nutné program ukončovat nestandardním způsobem. V tom případě použijeme funkci exit. Její volání (včetně parametru) odpovídá return ve funkci main. Jedná-li se o řádné ukončení programu, voláme exit(0), v opačném případě s nějakou nenulovou hodnotou nebo lépe exit(EXIT_SUCCESS) a exit(EXIT_FAILURE).

Občas se hodí provést na konci programu nějakou akci bez ohledu na to, zda byl programu ukončen returnem v main nebo funkcí exit. Není jednoduché toho dosáhnout, neboť program může být ukončen i z knihovny, kterou nemá programátor pod kontrolou. Naštěstí existuje funkce atexit, která zaregistruje uživatelskou funkci, jež bude runtimem zavolaná v době ukončení programu. Je-li funkcí více, volají se v opačném pořadí, než byly zaregistrovány.

#include <stdio.h>
#include <stdlib.h>

void po_main(void) {
  puts("po_main");
}

void nakonec(void) {
  puts("nakonec");
}

int main(void) {
  puts("main - začátek");
  
  puts("registruji nakonec");
  atexit(nakonec);

  puts("registruji po_main");
  atexit(po_main);

  puts("main - konec pomocí exit");

  exit(EXIT_SUCCESS); /* Jen test funkce exit, return by to zvládnul stejně. */


  puts("Sem bych se neměl dostat...");
  return 0;
}

Ladění s pomocí assert

Při vývoji větších projektů je třeba počítat s chybami. Nejobtížněji se hledají chyby typu přepsání paměti za polem nebo přístupu na neinicializovaný ukazatel, neboť se projevují jen někdy a často až dodatečně a daleko od místa chyby. Do programu se proto běžně přidává kód, který se snaží chyby odhalit již v místě jejich vzniku a zpravidla jen vypíše chybovou hlášku a ukončí program. Podobný kód však může zbytečně zdržovat již odladěný program a navíc v distribuční verzi (zejména komerčního software) může být někdy přiznání chyby a násilné ukončení programu větší zlo než chyba sama o sobě. Již víme, že s pomocí makra preprocesoru a podmíněného překladu můžeme vyvolávat ladící testy jen ve vývojové verzi programu. Standardní knihovna se nám to snaží zjednodušit a nabízí rutinu assert.

#include <assert.h>

int main(void) {

  int pole[10];
  int i;
  
  for (i = 0; i <= 10; i++) {
    assert(i < sizeof(pole) / sizeof(int));
    pole[i] = 0;
  }
  return 0;
}

V příkladu se snažím vynulovat pole, ale dopustil jsem se známé chyby "+1", kdy přistupuji o jeden prvek za konec pole. Chování programu v tomto případě není definované, záleží na tom, co se nachází za polem. Naštěstí jsem vložil do cyklu ladící test assert, který v případě nesplnění podmínky ukončí program pomocí funkce abort a vypíše chybovou hlášku:

assert: assert.c:10: main: Assertion `i < sizeof(pole) / sizeof(int)' failed.

Ladící testy můžeme snadno vypnout, pokud definujeme makro NDEBUG. Rozhodující je přitom místo (posledního) inkludu hlavičkového souboru assert.h, #define NDEBUG tedy musí předcházet #include <assert.h>. Nejlepší je ovšem definice pomocí parametru překladače:

gcc assert.c -o assert -DNDEBUG

Uvedený příklad je trochu vykonstruovaný. Programátor, který správně testuje meze polí přímo v cyklu pravděpodobně neudělá chybu v ukončovací podmínce toho samého cyklu. V praxi se spíše setkáme s testováním parametrů funkcí, užitečné je to zejména pokud jeden programátor napíše knihovnu poskytující sadu funkcí (jejichž parametrem například nesmí být NULL) a jiný programátor tuto knihovnu používá.

Pokračování příště

Standardní knihovnu jsme již probrali. V příštím dílu se zamyslíme nad tím, co ve standardní knihovně nejvíce chybí. Vydalo by to na několik dalších samostatných seriálů, proto vždy uvedu jen stručný popis a především odkaz na web, který se problematice věnuje. Ze základní syntaxe jazyka C nám chybí výčtový typ, i na něj dojde v příštím dílu.

Verze pro tisk

pridej.cz

 

DISKUZE

Nejsou žádné diskuzní příspěvky u dané položky.



Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

28.11.2018 23:56 /František Kučera
Prosincový sraz spolku OpenAlt se koná ve středu 5.12.2018 od 16:00 na adrese Zikova 1903/4, Praha 6. Tentokrát navštívíme organizaci CESNET. Na programu jsou dvě přednášky: Distribuované úložiště Ceph (Michal Strnad) a Plně šifrovaný disk na moderním systému (Ondřej Caletka). Následně se přesuneme do některé z nedalekých restaurací, kde budeme pokračovat v diskusi.
Komentářů: 1

12.11.2018 21:28 /Redakce Linuxsoft.cz
22. listopadu 2018 se koná v Praze na Karlově náměstí již pátý ročník konference s tématem Datová centra pro business, která nabídne odpovědi na aktuální a často řešené otázky: Jaké jsou aktuální trendy v oblasti datových center a jak je optimálně využít pro vlastní prospěch? Jak si zajistit odpovídající služby datových center? Podle jakých kritérií vybírat dodavatele služeb? Jak volit vhodné součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně datové centrum spravovat? Jak co nejlépe eliminovat možná rizika? apod. Příznivci LinuxSoftu mohou při registraci uplatnit kód LIN350, který jim přinese zvýhodněné vstupné s 50% slevou.
Přidat komentář

6.11.2018 2:04 /František Kučera
Říjnový pražský sraz spolku OpenAlt se koná v listopadu – již tento čtvrtek – 8. 11. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma umění a technologie, IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář

4.10.2018 21:30 /Ondřej Čečák
LinuxDays 2018 již tento víkend, registrace je otevřená.
Přidat komentář

18.9.2018 23:30 /František Kučera
Zářijový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 20. 9. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář

9.9.2018 14:15 /Redakce Linuxsoft.cz
20.9.2018 proběhne v pražském Kongresovém centru Vavruška konference Mobilní řešení pro business. Návštěvníci si vyslechnou mimo jiné přednášky na témata: Nejdůležitější aktuální trendy v oblasti mobilních technologií, správa a zabezpečení mobilních zařízení ve firmách, jak mobilně přistupovat k informačnímu systému firmy, kdy se vyplatí používat odolná mobilní zařízení nebo jak zabezpečit mobilní komunikaci.
Přidat komentář

12.8.2018 16:58 /František Kučera
Srpnový pražský sraz spolku OpenAlt se koná ve čtvrtek – 16. 8. 2018 od 19:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát jsou tématem srazu databáze prezentaci svého projektu si pro nás připravil Standa Dzik. Dále bude prostor, abychom probrali nápady na využití IoT a sítě The Things Network, případně další témata.
Přidat komentář

16.7.2018 1:05 /František Kučera
Červencový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 19. 7. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát bude přednáška na téma: automatizační nástroj Ansible, kterou si připravil Martin Vicián.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

31.7.2023 14:13 / Linda Graham
iPhone Services

30.11.2022 9:32 / Kyle McDermott
Hosting download unavailable

13.12.2018 10:57 / Jan Mareš
Re: zavináč

2.12.2018 23:56 / František Kučera
Sraz

5.10.2018 17:12 / Jakub Kuljovsky
Re: Jaký kurz a software by jste doporučili pro začínajcího kodéra?

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2024) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze