Praktické postřehy
přednášejícího na MFF UK:
Funkce, které vrací hodnoty změnou globálních
parametrů: "To je jako když vám
někdo má umýt okna, a on u toho ještě zhasne a namydlí
schody."
"Pascal, to je taková maminka.
Všude jsou zaoblený rohy, nízkonapěťový zásuvky, a když
upadnete, tak do měkkýho. To se musíte hodně snažit, abyste si
ublížili".
"V céčku se všude válej otevřený břitvy. Takže můžete všechno, ale musíte vědět, že
když uděláte takle (posunek naznačující podříznutí krku), tak
končíte."
Všeobecná pravidla pro
psaní kódu v "C"
Upozornění: Tento
dokument vystavený na Internetu je přesnou kopií vnitřního
dokumentu z 27.2.2019. Proto v něm je tolik polámaných odkazů.
Účel
- dosáhnout co nejlepších výsledků při programování procesoru.
Tyto výsledky jsou:
- Funkce koncového produktu
- Udržovatelnost programového kódu ve smyslu:
- dobře navržená modularita (většinou se kryje i s hlediskem
přenositelnosti a vícenásobné použitelnosti)
- dobře navržená vícestupňová dokumentace
- transparence ladění
- přenositelnost na zvolené platformy: Linux, Windows, 32bit,
64bit, MSVC 6.0, VS 2017, Kdevelop, ...
- Rozšiřitelnost a možnost zlepšování funkcí produktu
Vlastnosti jazyka - proč právě 'C'
Požadavky
Programovací jazyk musí umožňovat:
- Vytváření velkých programů s vysokou
rychlostí chodu.
- Snadné ladění, tj. struktura zápisu má
být podobná strojovému kódu.
- Přenositelnost na jiné procesory a do
jiných systémů.
- Širokou podporu od výrobců operačních
systémů.
Další žádoucí vlastnosti:
- Úplná kontrola generovaného kódu, tj.
kombinovatelnost s assemblerem,
- Kombinovatelnost s jinými jazyky, tj.
volání z VB, Javy.
Těmto požadavkům vyhovuje jen 'C'. Jeho další
velkou výhodou je robustnost, konzistence daná H-soubory a dobrý výhled do budoucnosti:
- Vždy budou existovat kvalitní
kompilátory.
- Budou vyvíjeny kvalitní a rozmanité
knihovny.
- Lze v něm vytvářet velké transparentní
programy.
Podstata jazyka
Program v 'C' je pouhá hromada funkcí, globálních proměnných a
prototypů, ovládaná příkazy preprocesoru C. Je to jednoduché a
účinné.
Kompilátor C s následným linkerem vlastně pouze obsazují daty blok
paměti. Program a data jsou v něm umístěny podle rozmyslu
kompilátoru a linkeru, správný start programu se uskutečňuje skokem
na místo bloku, kde je umístěna 1. instrukce programu. Další chod
programu je určen vlastnostmi procesoru. Ten pracuje tak dlouho,
dokud narazí:
- Buď na koncovou instrukci, čili skok na adresu, kde jsou
nachystané instrukce operačního systému očekávající návrat
programu.
- Nebo na některý ze zakázaných instrukčních kódů, což napovídá
o náhodném bloudění programu v paměti, na což procesor odpoví
výjimečnou situací, která je ovšem podchycována operačním
systémem, který nahlásí chybu programu.
- Nebo na platný kód instrukce, která však chce pracovat s daty,
která jsou pro program nedostupná - opět nastane výjimečná
situace procesoru podchycená operačním systémem a nahlášení
chyby programu.
- Nebo na platný kód instrukce, jejíž data jsou pro program
dostupná. To je velmi nebezpečná situace, protože v takovém
případě:
- Program nahodile bloudí, přitom může přepisovat neurčenými
daty jiná neurčená data a také může spustit jakoukoli akci
počítače, tedy například i diskovou operaci.
- Dá se čekat, že po nějaké době přece jen narazí, ale potom
může být velmi těžké najít místo a důvod, kde a proč začal
bloudit.
- Naštěstí takové případy jsou řídké, sama jejich možnost
ukazuje na potřebu mít se na pozoru a dbát na dostatečně
častou archivaci projektu, čili všech dat, která jsou potřebná
pro jeho obnovení.
Počítač naprogramovaný v 'C' lze přirovnat k lidskému tělu:
- Počítač:
- zdroj je zažívání,
- dráty a spoje jsou cévy,
- processor a paměť jsou mozek,
- snímače jsou smysly,
- periferie jsou ruce, nohy a
mluvidla
.
- 'C' program:
- instrukce jsou svalová vlákna -
dělají elementární úkony,
- funkce jsou svalové skupiny -
řeší úlohy,
- funkční prototypy (.H soubory)
jsou kosti - tvoří strukturu, bez které by se vše
zhroutilo v chaotickou kupu,
- volání funkcí jsou nervy -
vyvolávají akce v určitém místě,
- globální proměnné jsou hormony -
řídí akce globálně,
- příkaz goto je obdobou
zkratkovitého jednání, kdy člověk zahazuje nepovedenou
věc,
- prostor jmen je shodný se
schopností přirozeného jazyka jednoznačně pojmenovávat:
- funkce jsou slovesa,
- parametry
jsou podstatná jména,
- upřesňující
parametry jsou přídavná jména a
příslovce, případně i ostatní slovní druhy.
Toto srovnání ukazuje, že v programovacím jazyku mají své místo
globální proměnné i příkaz goto - jen je třeba umět je správně
používat.
C
a ostatní jazyky
Ostatní jazyky sice mají různé nástroje:
- systémové - např. try - catch (Java),
- bezpečnostní - např. stringové objekty nedovolující přepsat
paměť mimo string (C++),
- chytré příkazy - např. strtrim (Basic), záporné indexy ve
stringu (Python)
- pracovité příkazy - např. dictionary a tuple (Python)
ale toto vše se nechá udělat i v 'C' a přitom zachovat
transparentnost kódu.
Porozumění paměťovým modelům

Nalezeno na https://www.viva64.com/en/a/0050/
Od samého počátku vývoje počítačů bylo jasné, že čím více paměti a
čím rychlejší procesor, tím lepší bude počítač. Velikost paměti
prvních počítačů byla omezována jejich cenou, takže se vyskytovaly
velikosti 4KB, 8KB, 16KB, 32KB i 64KB. První počítače se 64KB stály
několik milionů korun, zabíraly plochu malého sálu, pracovaly
nepřetržitě a obsluhovalo je asi deset lidí.
Přelom přišel s vynálezy IBM: diskové paměti, polovodičové
paměti a stavebnice zvaná IBM PC, která používala 1MB paměti. Z té
doby pochází výrok jednoho počítačového odborníka, že 1MB paměti
je maximum, které bude kdy moci běžný uživatel využít.
V té době se také začalo mluvit o paměťových modelech. Ty
vycházejí z toho, že paměťové adresy i délky paměťových objektů
jsou čísla v rozsahu:
- 16 bitů čili 2 B - lze adresovar 216 = 65 536 bytů
= 64KB,
- 32 bitů čili 4 B - lze adresovat 232 = 4 294 967
296 bytů = 4 GB,
- 64 bitů čili 8 B - lze adresovat 264 = 18 446 744
073 709 551 616 bytů = 18TB.
Příslušné paměťové modely jsou:
- 16ti bitový - IP16 - s tím se dnes již těžko setkáme.
- 32 bitový - ILP32 - paměťový model omezený na 32 bit,
tedy na 4 GB paměti.
- 32 bitový s 64 bitovými délkami objektů - ILP32LL64
- to je dnes nejrozšířenější paměťový model, pro něj se píše
většina programů. Je jednoduchý tím, že běžné věci jsou 32 bit a
omezeně lze pracovat v 64bitové oblasti.
- 64 bitový s 32 bitovými délkami objektů - IL32LLP64.
Takový model používá Microsoft Win64. Je výhodný tím, 64 bitové
a 32 bitové verze programů se málo liší, mimo jiné není
třeba měnit zažité parametry ve Windows SDK.
- 64 bitový - I32LP64. Takový model používá Linux. Je
výhodný tím, že při programování odpadají všechna omezení, ale
64 bitové a 32 bitové verze programů se dosti liší. short
a int jsou v tomto modelu kratší než 64 bitů, aby byly
se co nejméně měnily parametry knihovních rutin
Použitý
model
Vytvořeno na základě porovnání stavu
Windows 32bit,
Windows 64bit a Linux 64bit. Viz též https://www.viva64.com/en/a/0004/#ID0EVQCK
.
Model pracuje tak, že:
- Není shodný s žádným výše uvedeným, kód se píše pro jakoby pro
model 32 bit s typy size_t
a prtdif_t..
- Explicite se nekontroluje hranice 4 GB, kontrola je implicitní
při allokaci paměti.
- Podle potřeby se pracuje v 64-bitové oblasti.
- Kompilační mód určuje preprocesorová proměnná BITS32
nebo BITS64. Ta se ošetřuje v cl_alway.h, kontroluje se rutinou Check_basic_environment();
při startu programu.
- Linux se nastaven pevně na 64 bit.
Tak všechno pracuje jak třeba, pro Windows
jsou nastaveny projekty v kompilátorech, pro Linux vše běží
automaticky.
Podrobnější popis:
- Logická úroveň programu:
- Nezávisí na paměťovém modelu (32
bit - 64 bit) ani na kompilátoru (MSVC 6.0 - VC 10+, gcc
pro Linux)
- Výchozí stav je 32 bit. Podle
potřeby se v něm formují funkce používající 64 bit proměnné
- poznají se podle použití long64, ulong64 deklarací.
Tyto proměnné umí i MSVC 6.0.
- S pointery se pracuje běžně, nikdy nejsou ukládány do
persistentních struktur (na disk nebo předávány jinému
programu). Příkladem je struktura DB_OBJECT,
která pointer obsahuje.
- Program bude udržován a kompilován pro 32
i 64 bit, časem se předpokládá opuštění kompilace 32
bit.
- Bude použita strategie Windows
s ohledem na Linux, tj.:
- short, WORD 16
bit
- int, long, ULONG, DWORD 32 bit - použije se
tam, kde není obava z překročení 32 bit
- long64, ulong64 64 bit - použije se
tam
- kde je možné překročení 32 bit,
- kde se očekává neomezený nárůst kapacity,
- kde jsou potřebné výpočty v 64
bitech
- velikosti long,
pointer, size_t, ptrdiff_t
atd. určuje kompilátor
- Diskové operace budou používat běžná
volání fopen(), fread(), fwrite(),
ftell(), fseek() a pouze při jasné potřebě
velkého souboru použijí fseek64()
apod.
- 20
issues of porting C++ code to the 64-bit platform
size_t
- viz https://www.viva64.com/en/a/0050/
- Bude použito pro práci s pamětí, tedy pro délky v paměti a
offsety v paměti
- Nebude použito pro počty položek, tam se použije long
- Požití čísla -1 při práci s pamětí je třeba vymýtit:
- Tam kde -1 označovala "délkou se rozumí délka stringu" je
třeba dodat délku stringu.
- Tam kde -1 označovala, že položka je NULL a její délka to
potvrzuje hodnotou -1, je třeba dát za délku 0 a testovat ten
NULL.
ptrdiff_t
Kódování konstant a
literálů
- Je třeba mít jasno, co je co:
- SBCS (single byte code page) - 1B kódová stránka,
příklad CP437 nebo ISO 8859-2 nebo CP1250.
- MBCS (multi byte code page) - 1B-2B kódová stránka
vymyšlená Microsoftem, např. BIG5. Tato kódování jsou dnes
zastaralá a je třeba se jim vyhnout. Bohužel nově Microsoft
označuje jako MBCS i UTF-8 což vnáší do věcí další zmatek čili
máme zmatek2.
- UNICODE (double byte code page) - 2B kódová stránka
vymyšlená UNICODE konzorciem. Toto kódování mění formát C
stringů a proto se příliš neujalo.
- UTF-8 (proměnná délka kódů znaků) - 1B-4B kódová
stránka vymyšlená HTML programátory. Toto kódování nemění
formát C stringů a proto se ujalo pro HTML, JavaScript, Javu,
Python atd. V C kompilátorech Microsoftu se označuje jako MBCS
- viz výše.
- DB (Databázové kódování) - 1B-4B kódování snažící se
zachytit vznik znaku z původních znaků low ASCII. Jeho výhodou
oproti UTF-8 je čitelnost textu a snadnost realizace
morfologie přirozených jazyků.
- Zpočátku o kódování nikdo moc mepřemýšlel, protože se v C
programovaly operační systémy a drivery, takže se vystačilo s
angličtinou a low ASCII znaky.
- Později byly do C zavedeny kódové stránky a situace ještě byla
udržitelná v rámci 1B kódových stránek. To je případ MSVC 6.0.
Navíc je možno do systému vložit český systémový font a MSVC 6.0
pak poslušně píše i české znaky. Při ukládání souborů si MSVC
6.0 nevymýšlí a poslušně vše ukládá tak, jak je má uloženo v
paměti.
V souboru ins_fmi.cpp jsou
popsány podrobnosti:
- k různému zacházení se zdrojovými texty v jednotlivých
kompilátorech,
- a různým interpretacím znaků v jednotlivých operačních
systémech.
- Obtížná situace nastává se zavedením UTF-8 do kompilátorů C:
- Ty se začínají zajímat o obsah literálů a je nebezpečí, že
je budou "tvořivě" upravovat, čímž by ničily práci
programátora.
- Neumožňuje jemnou práci se stringy, která je potřebná pro
úlohy lingvistiky. Například v UTF-8 kódy pro e, é, ě, ë spolu nijak
nesouvisí, v DB kódování všechny začínají znakem e a tak přirozeně tvoří
skupinu znaků příbuzných e.
- Kazí transparentnost mezi C kódem a strojovými instrukcemi.
- Jak je u MS zvykem, vždy bude udržovat chaos, např.:
- Ve programech se nikdy nevzdal SBCS i když již v
roce 1995 prohlašoval, že všechno bude UNICODE.
- S příchodem UTF-8 se mu ulevilo, protože mohl použít svoji
technologii MBCS a tak se zbavit obrovských problémů
svázaných s použitím UNICODE při programování.
- Je proto třeba:
- kompilátory nastavovat na SBCS.
- V literálech používat pro ne-low-ASCII znaky hexadecimální
nebo lépe oktalové kódy, například C\x82erma\x8c\x82ek
nebo C\202erma\201c\202ek,
protože MS pracuje s hexadecimálními kódy záludně.
- Ve výsledku pak všechno funguje hladce:
- slova a jména proměnných jsou (z definice) low ASCII,
- na komentářích moc nezáleží - mohou být míchané české a
anglické, protože s českým systémovým fontem se zobrazí
správně, dokonce i ve Windows 10,
- literály a stringy:
- mohou být v UTF-8:
- pokud přícházejí na vstup,
- pokud jsou určeny přímo pro výstup,
- jinak v DB kódování s tím, že:
- vstupy přicházející v UTF-8 či jiném kódování se
překódovávají do DB kódování,
- výstupy se podle potřeby překódovávají do UTF-8 či
jiného kódování.
Dokud bude možno využívat MSCV 6.0 je mechanizmus práce navržen
takto:
- Programování a ladění:
- Hlavní práce: 32bit, MSVC 6.0 ve Win XP nebo Win7+
- Testování (opravy je třeba přenášet zpět ručně):
- Win 64bit: VisualStudio 10.0+
- Linux 64bit: export pomocí Do_on_source_files()
(zpětný export neexistuje) a potom např. KDevelop
- Linux 32bit: není navrženo, nepředpokládá se, bude
podobné jako Linux 64bit.
- Editace kódu včetně literálů v češtině a dalších jazycích s
diakritikou:
- Pouze v MSVC 6.0, pokud možno Win XP, protože je 1:1,
zatímco VS 10+ a gcc si hrají už s UTF-8
- Opravy nalezené při testování v jiných prostředích se
přenášejí ručně do MSVC 6.0
- Kód z MSVC 6.0 se přenáší do jiných prostředí
prostřednictvím rutiny Do_on_source_files()
Případný přechod na jiný kompilátor by neměl být moc těžký:
- programem (viz rutina Do_on_source_files()) by se měly
najít všechny znaky >= 127 (127 je znak "delete", který se
zobrazuje různě)
- podívat se, tak se tyto různé znaky zobrazují ve vybraném
kompilátoru
- navrhnout překódování - patrně to budou náhrady oktalovými
kódy (v kódu, např. "nůše" jako "nu\207s\202e") a překódování do
UTF-8 (v komentářích)
- překontrolovat, jak to dopadlo
Praktické programování
- Školení ovládání kompilátoru:
- sestavení projektu
- výroba modulů - zakládání, editace, znalost precedence
operátorů - např:
- if ( type == VALUE_TYPE_BINARY
&& (export_params &
EXPORT_LANGUAGE_PATTERNS) != 0 ) vypadá dobře
- if ( type == VALUE_TYPE_BINARY
&& export_params & EXPORT_LANGUAGE_PATTERNS
!= 0 ) je téměř jistě špatně
- omezení repertoáru příkazů - např. z podmíněných stačí for a while
jako zjednodušená varianta for.
- preprocesor - příkazy a flagy v projektu
- systém modulů - cpp + h + def
- syntaktické chyby, přetypování
- ladění
- Technologie práce
- C
- konzolová aplikace
- HTML server
- Programování samo o sobě je činnost neomezeně složitá a téměř
vždy z programátora vymačkává i poslední zbytky sil. Uspět lze
jen tak, že se použijí prostředky pro zvládání složitých
projektů:
- modularizace
- dokumentování modulů
- dělení programu na funkce - jsou pro to mnohé důvody:
- funkce vyjadřuje operátor, tj. ucelenou jednotku
zpracování
- funkce zpřehledňuje program - v rozsáhlé funkci se některé
vnitřní úseky pojmenují a osamostatní
- funkce zamezuje psaní stejných úseků kódu - šlo by to i
pomocí maker, ale ta jsou méně přehledná a mohou mít
vedlejší účinky
- funkce je komunikačním bodem černé skříňky, např. vstupním
bodem DLL
- metoda černých skříněk
- Prostředky kompilátoru:
- konzistence projektu pomocí souborů .H
- globální vyhledávání v souborech
- Editace a ladění - liší se podle prostředí vzhledem k různému zacházení se zdrojovými
texty v jednotlivých kompilátorech (viz též ins_fmi.cpp). Mechanizmus
navržen takto:
- Programování a ladění: MSVC 6.0 ve Win7 nebo Win XP
- Editace literálů v češtině a dalších jazycích s
diakritikou: MSVC 6.0 ve Win XP
- Překlad a ladění Win 64bit: VisualStudio
15.0+
- Překlad a ladění Linux 64bit: export pomocí Do_on_source_files() a potom
např. KDevelop (zpětný
export neexistuje, případné opravy je třeba přenést zpět
ručně)
Vlastnosti a
nedostatky MSVC 6.0
- Does not show \x09 in debugger
- Alt+129 does generate \x81 character
- Changed RESRC1.H - does not compile dependent CPP modules
- Ignores "number == 3;" without warning
- long Check_gotos(void) { long ret_val; if ( ret_val == 1 )
goto ALL_RETURNS; ALL_RETURNS: ret_val = 2; }
cycles because return() at
the routine end is missing - does not report error: function
does not return value
- twins_L1[] = "????()[]{}<>¿?¡!«»"; is messed up while
twins_L1[] = "\x3f\x3f\x3f\x3f()[]{}<>¿?¡!«»"; is ok
- How to make { cout << "Ahoj"; } work in MSVC 6.0? go to
Linker:
- Do projektu:
- Object / library modules: libcimt.lib
- Ignore libraries: libcimtd.lib
- Dokovací okna:
- Nahoře mají knoflík "Expand docked window" s nejasnou
funkcí.
- Klik pravým tlačítkem ukáže dialog stavu okna.
- Stisknutí klávesy Control umožní myší tahat title bar okna a
okno zadokovat podle potřeby.
Velké programy a namespaces
Velkým programem se rozumí program s vnořenými balíky třetích stran.
V takovém případě se může objevit problém duplicitních jmen nebo
jmen neodpovídajících standardu tohoto dokumentu. Aby byla zajištěna
transparentnost systému, je třeba:
- Vycházet z standardního uspořádání hlavního programu v pevném
adresáři D:\111.
- Každý balík umístit do vlastního adresáře.
- Jeho C moduly přejmenovat tak, aby jejích jména začínala
shodnými čtyřmi znaky: 3 písmena zkratka balíku + podtržítko
nebo pomlčka.
- Jeho globální proměnné přejmenovat přidáním prefixu "glo_" a
umístit do zvláštního .h
souboru. Ten se případně může učinit viditelným v modulu INS_MAIN.CPP.
- Tyto moduly se vloží do projektu programu - tak se do něj
dostanou i cesty k nim.
- Cesty k .h souborům
neuvádět v globálním seznamu cest pro kompilátor, nýbrž:
- pokud je .h soubor v
jednom adresáři s modulem, cesta se nemusí uvádět,
- pokud je .h soubor v
jiném adresáři, než modul, cestu zapsat přímo v příkazech #include
- to platí i pro adresář D:\111 - ten se
zpravidla prohledává bez hledání v podadresářích, proto se
nemusí najít všechny výskyty a tedy je třeba proměnné a funkce
z D:\111
používat v jiných modulech velmi obezřetně, čili nejlépe
ne.
- Vstupní body do balíku by se měly uvést ve zvláštním .h souboru uvnitř balíku. Tento
souboru by mě obsahovat pouze potřebné minimum údajů. Hlavní
program by se na něj dostal přes cestu uvedenou v příkazu #include
- Statické a globální proměnné balíku budou tak izolovány od
ostatního kódu.
- Jemnější záležitosti, např.:
- nastavování globálních proměnných balíku se může řešit
předáním datového bloku init funkci balíku,
- přebírání globálním proměnných a funkcí balíkem se může
řešit sdílením .h souborů
hlavního programu
Linux
Dokumentace
- vyrábí se podle potřeby ve více stupních:
- Část popisující funkce a
struktury z hlediska programátora - tato část má
referenční hodnotu a měla by být umístěna ve zdrojovém kódu:
- Pro struktury a definice: je k nim připojena v .H.
- Pro funkce: je k nim připojena v .CPP.
- Část popisující SDK
- měla by být v HTML. Aplikační programátor by měl mít
dostup ke zdrojovému kódu, aby mohl sám řešit chyby a
nekonzistence vzniklé vývojem.
- Část popisující
organizaci modulů v projektu - měla by
být v HTML.
- Část popisující projekt
pro zákazníka - její formát určí zákazník; pokud
zákazník dostává i zdrojový kód, měly by v ní být i odkazy do
dvou nižších úrovní dokumentace.
Formátování kódu -
mělo by být zvoleno a dodržováno pečlivě autorem. Hlavní požadavky
jsou: jednoduchost, srozumitelnost a čitelnost kódu.
Psaní kódu - Požadavkem je:
přehlednost, jednoduchost, srozumitelnost a čitelnost kódu. Tedy
ne "chytré" konstrukce, které jsou jednak zdrojem chyb, jednak
jiný programátor má spoustu práce s jejich luštěním a nakonec je
stejně přepíše .
- Z programového kódu sice lze vyrobit multitáskovou DLL, ale
základní použití se spatřuje v paralelním chodu několika
instancí programu na několikajádrovém procesoru.
- Závorkování a tabulátor jsou takové, jaké jsou - dle mého je
to nejpřehlednější zápis.
- Všechny #define, na které
může existovat dotaz #ifdef
nebo #ifndef, musí být
dostupné zcela globálně, tj. buď v preferencích projektu nebo v
souboru cl_alway.h. Tak se
zajistí, že ve všech .cpp
souborech bude viděn stejně.
- return se používá se
závorkami, protože často pracuje s aritmetickým výrazem nebo
voláním funkce.
- int se používá pouze ve
volání systémových funkcí - jinak se doporučuje long. Mj. dosazení z int do char
nehlásí chybu.
- char se rozumí vždy (je
to tak nastaveno v projektu) unsigned. Je-li třeba opak,
nastavuje se explicitně pro jednotlivé proměnné - důvod: char se
používá téměř vždy pro uložení znaků. použití signed char vede k
rozdílným výsledkům při porovnání přímém (znak po znaku) a
pomocí funkcí strcmp(), memcmp().
- Preferuje se používání for.
- Nepoužívá se do.
- Střídmě se používá while.
- Může se používat goto a
continue tam, kde to vede na
přehledný kód, například goto
ALL_RETURNS, kde za ALL_RETURNS
následuje úklid před opuštěním funkce.
- Nepoužívá se extern v .CPP a neumisťuje se kód do .H.
- Je zakázáno měnit literály za běhu programu - měnit lze pouze
kopii literálu v pracovní proměnné.
- Je dovoleno měnit řetězce v konstantních proměnných (např.
nastavit 0 pro kratší řetězec), pokud během několika (2 - 4
řádků) bude tato změna vrácena zpět
- Pokud se mění vnější chování nějaké funkce, měl by se změnit
i prototyp, aby jiné moduly byly na tuto změnu automaticky
upozorněny při překladu.
- Je-li třeba zachytit změnu ve starém kódu, použije se
poznámka // CHANGED: datum jméno autora
důvod.
- Pro složité moduly je vhodné připravit testovací data a
testovací program (zvláštní main()).
- Při tisku nebo výrobě textového souboru by zpravidla znaky \n (nebo \r\n)
měly být na začátku řádky - ne na konci - text většinou ví
nejlépe, zda chce začínat na nové řádce anebo se přilepit k
předchozímu.
- Ladicí soubory s výpisy by měly mít jméno filename.LOG.
Projekty
Jsou uloženy v adresáři D:\111,
jejich popis je uveden vždy v modulu, ve kterém se nachází vstupní
bod programu.
- PRJ_ALL - univerzální
projekt, který může fungovat mnoha způsoby podle zadání
parametru. Jeho funkce jsou realizovány jako Windowsové i
konzolové aplikace
- PRJ_WWW - projekt
používaný pro běžné práce, jeho způsob práce je určen zadaným
parametrem. Je pouze to konzolové aplikace
Kompilace
Předpokládá se v prostředí MSVC 6.0,
K-Develop, CodeWarrior, VC 10+ a Dev-C++. Nepoužívají se optimalizace kódu - zejména v
MSVC 6.0. Z hlediska přenositelnosti se programové moduly dělí
na:
- moduly s rutinami pro komunikaci s
operačními systémy - prefix je PORT_..., jména modulů jsou
CL_PORT.CPP (úzký výběr kódu), DB_PORT.CPP (zbytek kódu)
- moduly s algoritmy - ostatní. Ty by
neměly obsahovat systémově závislé hlavičkové soubory
(windows.h, ...), modifikaci kódu závislou na operačním
systému (#ifdef WIN32 ...)
.H soubory - doporučuje se nevnořovat
je - předejde se tak vícenásobnému vkládání .H souborů a
všelijakým cyklům. Hlavní modul programu má obsahovat všechny .H
soubory. Tím je dáno jejich bezkonfliktní pořadí, které potom může
být použito v kterémkoli dalším modulu.
Modularizace
Modularizace zjednodušuje a zpřehledňuje
programování a také je oporou dokumentace.
- Všeobecné rutiny jsou ty, které rozšiřují standardní
knihovny
- Jejich .H soubor je dostupný všude
- Nepoužívají .H soubory jiných modulů
- Specializované moduly obsahují speciání funkcionalitu
- Mají jeden interface .H
- Ostatní jejich .H soubory jsou dostupné jen jim
- Interface .H soubory jiných modulů používají pouze, pokud
jsou jejich nadmoduly
- Koncové moduly obsahujífunkce pro uživatele včetně user
interface
- Jejich Interface .H soubory mají formát IT*.H
- Ostatní jejich .H soubory jsou dostupné jen jim
- Mají podmodul pomocných rutin pro UI
- MAIN modul
Konfigurace 'C'
projektů
Je na samostatné stránce.
Globální proměnné
- Dělí se na:
- Globální konstanty - jsou v glo_txts.cpp,
externy v cl_base.h
- Globální kódové tabulky - jsou v glo_cods.cpp, externy v cl_base.h
- Globální proměnné nastavené při startu Databáze -
jsou v glo_vars.cpp,
externy v cl_base.h
- Globální proměnné měněné v průběhu úlohy - jsou
soustředěny v GLO_CB
struktuře v modulu v cl_base.h
- Týká se jich dokumentace o multithreadingu.
- Pojmenovávají se glo_... a jejich jména jsou jednoznačná i
pokud jsou součástí GLO_CB.
Je to tak pro zajištění jejich dobré vyhledatelnosti.
Statické proměnné -
pojmenovávají se static_... - jsou
jen v jednom .CPP, nejsou v .H - patrně jsou zbytečné, je lepší
použít globální
Globální
i statické proměnné
mohou mít nežádoucí vliv při vícenásobném volání funkce. Musí se
tedy používat tak, aby bylo kdykoli možno zkontrolovat jejich
použití - buď vyhledáním v kódu nebo speciálním programem.
Makra - pojmenovávají se
MACRO_... - použijí se tam, kde to zpřehlední kód a kudy
není potřeba procházet při
Error management
Je na samostatné stránce.
Literálový systém
Tento systém slouží pro uložení literálů mimo program do souborů *.CDB:
- Šetří místo, což dnes není tak důležité jako v době omezení na
600 kB.
- Znepřehledňuje a zpomaluje program, proto je dnes lépe
systémové (tedy jednojazyčné) texty umisťovat do programu.
- Hlavním důvodem pro používání tohoto systému jsou soubory
přeložených textů sloužících pro komunikační v rozličných
jazycích:
- Ručním překladem se vytváří vlastní soubory literálů.
Přeloženy nemusí být všechny literály - zejména u systémových
hlášení je lépe zůstat u angličtiny.
- Z nich se potom generuje literálový soubor pro ten jazyk.
Nepřeložené literály se převezmou z anglické verze. Rutina pro
generování souboru .Lxx genereruje také přehled o užitých
číslech literálů.
- Při běhu programu lze jednoduše přepínat mezi těmito
soubory a tím s komunikovat v požadovaném jazyku.
- Soubory *.CDBse
zpracovávají rutinou Do_LIT().
Výstupní zařízení
- glo->glo_Console_runs -
zachycuje zprávy:
- glo_file_LOG - zachycuje
zprávy:
- glo_LOG_FILE_name_0 -
zachycuje zprávy:
- glo_LOG_FILE_name_1 -
zachycuje zprávy:
- glo->glo_output_CB -
zachycuje zprávy:
- pro klienta, tj. výstupní stránky
Debug management
- PrintDebug - závisí na glo_debug_level
- makro DBT... - závisí na PROGRAMMER_MODE
- píše na konzoli
- makro DBU... - závisí na definici glo_debug_level
a glo_debug_depth
- píše do výstupního HTML - tyto proměnné se nastavují v
programu dynamicky, tj.:
- výpisy lze zahajovat a ukončovat podle potřeby
- příkazy výpisů obsahují jméno zasahované funkce a nejmenší
hloubku, při které mají proběhnout. Jména funkcí jsou zadávána
makry (tak mají na konci rozlišovač '_')
- Existuje také Benchmark mechanism
- Project / Settings / -
přepíná se mezi:
- Win32Release - v
parametrech nesmí být _DEBUG
a musí být MT
- Win32Debug -
v parametrech musí být _DEBUG
a snad i musí být MTd
Při správném nastavení linker správně rozlišuje např. mezi Python27.lib a Python27_d.lib
CGI programování v C
Vytvoření každé procedury má tři kroky:
- Vytvoření vstupního bodu někde na HTML stránce, pro tuto
aplikaci je vstupní bod na stránce http://194.108.109.100/c%82love%82k/ve%82da/matematika/index.htm
- je tam odkaz Testovací aplikace
odkazující na /scripts/client.exe?m=form_new&p=105&p1=CS
- číslo 105 je komunikační
identifikátor, CS označuje,
že formulář má být v češtině.
- Vytvoření formuláře s položkami pro zadávání parametrů
procedury (provádí se v modulu ins_fmi.cpp)
- formulář testovací procedury vyrábí (v našem případě) funkce FORM_for_test_application().
Prototyp této funkce musí přijít do vhodného *.H souboru a její volání se dá
do větvicí rutiny v ins_fmi.cpp.
- Vytvoření odpovědi ze parametrů zadaných uživatelem - tuto
odpověď vyrábí funkce Evaluate_test_application().
Prototyp této funkce musí přijít do vhodného *.H souboru a její volání se dá
do větvicí rutiny v ins_fmo.cpp.
Ověžení procedury se dělá takto:
- Spustí se Apache (pokud
již neběží)
- Spustí se vytvořený program v debug módu
- Spustí se browser a:
- klikne se na odkaz pro vyvolání procedury,
- v konzolovém okně se objeví zpráva a browser zobrazí
stránku s formulářem,
- fromulář se vyplní (pokud parametry nejsou přednastaveny)
a odešle,
- v konzolovém okně se objeví zpráva a browser zobrazí
výslednou stránku.
Kopírování kódu
do HTML stránek
Volá se rutina podle vzoru INET_ShowSource(output_CB,
"DB_math.cpp", "Quadratic", START);
- ta podle posledního parametru zapíše heading or footing of code
listing
V kódu se používají dvojice direktiv podle vzoru:
#define INET_SHOW__Quadratic
#undef INET_SHOW__Quadratic
Kopírování
JavaSciptu do HTML stránek
Volá se rutina podle vzoru INET_CopySource(output_CB,
"Ziva_citanka.htm", "CSS2");
V HTML kódu se používají dvojice direktiv podle vzoru:
<script INET_COPY__CSS2
START></script>
<script
INET_COPY__CSS2 STOP></script>
Každý jeden příkaz INET_CopySource()
musí kopírovat celistvé bloky JavaScriptu
vmezené příkazy <script ...> ... </script>.
Různá
pravidla
- Užitečná okna debugeru jsou: "Variables", "Watch", "Call
stack", "Memory".
- Je-li zájem použít modul i jinde, je třeba se domluvit, aby
se našla forma vyhovující všem použitím.
- Na schůzky programátorů se zásadně nosí zdrojové moduly, ne
něco přeloženého.
- Je třeba mít jasno, kdo momentálně vlastní který modul a kdo
tedy vlastní směrodatnou verzi; na počítačích ostatních členů
týmu je kód duplikován a tím se automaticky zálohuje. Na svém
počítači může kdokoli opravovat cokoli, tedy i cizí moduly -
nesdělí-li však tyto opravy vlastníkovi modulu, při příštím
předání modulů se tyto opravy ztratí.
- Je třeba používat jednoduchý postup pro přenos zdrojových
modulů (pro MSVC 6.0: přenášet i společný projekt) tak,
aby se program hladce překládal.
Efekty
týmové práce
- Synergický efekt - shody vedoucí k tomu, že:
- Produktivita týmu je vyšší než součet produktivit
jednotlivých členů.
- Jednotlivé projekty sdílejí zdrojový kód, tím se projekty
stávají kompaktními, lépe udržovatelnými a zdrojový kód je
lépe odladěn a má lepší funkcionalitu.
- Nedořešené kompromisy - vedou k tomu, že:
- produktivita týmu je nižší než součet produktivit
jednotlivých členů a současně se projekt rozkližuje.
- Potom krajním východiskem je rozdělení projektu na zcela
nezávislé moduly nesdílející zdrojový kód a komunikující přes
portabilní rozhraní - příkladem je komunikace mezi moduly s:
- rozdílným zarovnáním struktur,
- rozdílným kódováním znaků,
- rozdílným pojetím základních typů.
- Téměř zákonitě se v týmu programátorů vyskytuje efekt "já o
koze, ty o voze". Ten je způsoben:
- Špatnou komunikací, což se řeší dobrou dokumentací a
dostatkem času na nalezení dobrého řešení.
- Různými názory jednotlivých pracovníků na řešení určitého
problému. Opět je třeba věnovat takové debatě dostatek času,
aby se nejlepší řešení dokázalo objevit.
Výuka
Výuku lze rozdělit na:
- Seznámení s instalací vývojového
prostředí a práci s projektem - to je ten slavný
program "Hallo world".
- Seznámení s
knihovnami, debugerem a dalšími rysy
vývojového prostředí - Sepsání malého
programu se souborovým a konzolovým vstupem a
výstupem.
- Školení hlavních
rysů 'C':
- Pohled na
program jako na popis paměti,
- Internetový
model informačních technologií,
- Integrující role
.H souborů
- Shoda 'C' a
strojového kódu (Assembleru)
- Školení
programování:
- Modularita
- Metodika
programování shora dolů - je to základní přístup k
řešení úloh: úloha se tak dlouho rozkládá na
jednodušší, až každá koncová úloha je řešitelná
příkazy 'C' a knihoven
- Metodika
programování zdola nahoru - je to základní přístup budování
knihoven, při kterém se realizují příkazy tak, aby:
- z názvu a
popisu byla jasná jejich funkce,
- pokrývaly co
největší funkčnost - dobrý pohled je tento:
- jména funkcí
jsou slovesa,
- vstupní a
výstupní data jsou podstatná jména,
- parametry
jsou příslovce a přídavná jména.
- Ujasnění
vlastností programátor začátečníka:
- není zběhlý v
psaní kódu,
- nezná
standardní knihovny,
- nemá osvojeny
nebo vybudovány další knihovny,
- není zběhlý v
rozkladech úloh.
Odkazy
Zdrojové texty
HASHTABLE
program in C
Nalezeno na
http://www.tutorialspoint.com/data_structures_algorithms/hash_table_program_in_c.htm
. Ukázka čisté a efektivní práce. Na tom serveru je i pískoviště
v pro C.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define SIZE 20
struct DataItem {
int data;
int key;
};
struct DataItem* hashArray[SIZE];
struct DataItem* dummyItem;
struct DataItem* item;
int hashCode(int key){
return key % SIZE;
}
struct DataItem *search(int key){
//get the hash
int hashIndex = hashCode(key);
//move in array until an empty
while(hashArray[hashIndex] != NULL){
if(hashArray[hashIndex]->key == key)
return hashArray[hashIndex];
//go to next cell
++hashIndex;
//wrap around the table
hashIndex %= SIZE;
}
return NULL;
}
void insert(int key,int data){
struct DataItem *item = (struct DataItem*) malloc(sizeof(struct DataItem));
item->data = data;
item->key = key;
//get the hash
int hashIndex = hashCode(key);
//move in array until an empty or deleted cell
while(hashArray[hashIndex] != NULL && hashArray[hashIndex]->key != -1){
//go to next cell
++hashIndex;
//wrap around the table
hashIndex %= SIZE;
}
hashArray[hashIndex] = item;
}
struct DataItem* delete(struct DataItem* item){
int key = item->key;
//get the hash
int hashIndex = hashCode(key);
//move in array until an empty
while(hashArray[hashIndex] != NULL){
if(hashArray[hashIndex]->key == key){
struct DataItem* temp = hashArray[hashIndex];
//assign a dummy item at deleted position
hashArray[hashIndex] = dummyItem;
return temp;
}
//go to next cell
++hashIndex;
//wrap around the table
hashIndex %= SIZE;
}
return NULL;
}
void display(){
int i = 0;
for(i = 0; i<SIZE; i++) {
if(hashArray[i] != NULL)
printf(" (%d,%d)",hashArray[i]->key,hashArray[i]->data);
else
printf(" ~~ ");
}
printf("\n");
}
int main(){
dummyItem = (struct DataItem*) malloc(sizeof(struct DataItem));
dummyItem->data = -1;
dummyItem->key = -1;
insert(1, 20);
insert(2, 70);
insert(42, 80);
insert(4, 25);
insert(12, 44);
insert(14, 32);
insert(17, 11);
insert(13, 78);
insert(37, 97);
display();
item = search(37);
if(item != NULL){
printf("Element found: %d\n", item->data);
}else {
printf("Element not found\n");
}
delete(item);
item = search(37);
if(item != NULL){
printf("Element found: %d\n", item->data);
}else {
printf("Element not found\n");
}
}
If we compile and run the above program then it would produce
following result −
Output
~~ (1,20) (2,70) (42,80) (4,25) ~~ ~~ ~~ ~~ ~~ ~~ ~~ (12,44) (13,78) (14,32) ~~ ~~ (17,11) (37,97) ~~
Element found: 97
Element not found
Naivní
parser programu v 'C'
Spíše nepovedené dílo v přelomu 2017/2018, které mělo usnadnit v té
době probíhající zavedení GLO_CB do celého programu. Nakonec se vše
provedlo ručně. Vstupní rutina modulu je Fix_2017_12_25()
Program přečte vstupní soubor a určí zda je to .cpp nebo .h
- Od začátku jej čte a dělí na entity:
- SYNTAX_DEFINE_P #define name
( ...text )
- SYNTAX_DEFINE
#define name text
- SYNTAX_IFDEF
#ifdef ...block
#endif
- SYNTAX_IFNDEF #ifndef ...block
#endif
- SYNTAX_IF
#if
...block
#endif
- SYNTAX_TYPEDEF_P typedef struct [
name
] { ...params } name ;
- SYNTAX_TYPEDEF_1 typedef ...text name ;
- SYNTAX_TYPEDEF_2 typedef struct name name;
- SYNTAX_STRUCT
struct name;
- SYNTAX_EXTERN extern ...text;
- SYNTAX_COMMENT_A /*
...text */
- SYNTAX_COMMENT_S //
...text
- SYNTAX_PROTOTYPE ...text ( ...params ) ;
- SYNTAX_FUNCTION ...text
( ...params
) { ...text
} ;
- SYNTAX_GLOBAL ...text
;
- v textech vyhledá jednoduché dvojice
#ifndef MULTITHREADED - #endif a nahradí je
mezerami včetně předchozích \r\n
(aby nezůstaly volné řádky)
- 1: program vybírá z .cpp
a .h prototypy a funkce -
pamatuje si jméno, druh, parametry
- 2: program vybírá z .cpp
a .h prototypy a funkce -
pro GLO_CB a OUTPUT_CB logicky usoudí, co s
nimi a modikuje texty (parametry a také podle potřeby za jméno
se závorkou vloží "loc, ")
- přitom vyřídí i MULTI