První pokus o kompilaci, a hned celá stránka errorů

Co máš za IDE? Nejde ti tam nějak zapnout zobrazování všech warningů? Ono to může být dost užitečné, protože tě to upozorní i na potenciálně nebezpečné konstrukce, jako
if(a=b), ty to sice takhle můžeš chtít, ale aby kompilátor měl jistotu že jsi jen nezapomněl druhé rovnítko
==, tak je potřeba mu napsat
if((a=b)), tím z toho přiřazení uděláš současně logický výraz. Pokochej se tím co na mě vysypal GCC:
Kód: Vybrat vše
jaker.c: In function ‘vstupD’:
jaker.c:9:9: warning: implicit declaration of function ‘system’ [-Wimplicit-function-declaration]
jaker.c:17:9: warning: implicit declaration of function ‘getch’ [-Wimplicit-function-declaration]
jaker.c:55:35: warning: comparison between pointer and integer [enabled by default]
jaker.c:57:36: warning: comparison between pointer and integer [enabled by default]
jaker.c:60:25: warning: ‘return’ with no value, in function returning non-void [enabled by default]
jaker.c:26:36: warning: unused variable ‘u’ [-Wunused-variable]
jaker.c:69:13: warning: implicit declaration of function ‘main’ [-Wimplicit-function-declaration]
jaker.c:69:36: warning: multi-character character constant [-Wmultichar]
jaker.c: In function ‘vstupZ’:
jaker.c:112:32: warning: comparison between pointer and integer [enabled by default]
jaker.c:114:33: warning: comparison between pointer and integer [enabled by default]
jaker.c:91:25: warning: unused variable ‘Preklad2’ [-Wunused-variable]
jaker.c:122:32: warning: multi-character character constant [-Wmultichar]
/tmp/ccAo4IT1.o: In function `vstupD':
jaker.c:(.text+0x43): undefined reference to `getch'
jaker.c:(.text+0x192): undefined reference to `getch'
/tmp/ccAo4IT1.o: In function `vstupZ':
jaker.c:(.text+0x1ed): undefined reference to `getch'
/tmp/ccAo4IT1.o: In function `main':
jaker.c:(.text+0x354): undefined reference to `getch'
collect2: error: ld returned 1 exit status
- Příště mi to pošli i s hlavičkou, ať vím jaké knihovny používáš. Doufám že jí tam píšeš a nespoléháš na nějaký automat.
- Komentáře // nebyly v Céčku původně povolené, používá je až norma C99. To není chyba, akorát to pro mě znamená napsat při kompilaci jeden parametr navíc

- Funkci getch() tady na Linuxu nemám, to je DOSová specialita a pěkná prasárna, naneštěstí moc rozšířená. Dá se nahradit getchar(), ale pokud jí použiješ pro čekání, dá se odpálit jen Enterem. Je také dobré předtím napsat uživateli pokyn co má dělat, například "Stiskni Enter", aby neseděl a nečuměl na blikající kurzor. Existuje o tom výborná a dnes už špatně sehnatelná kniha Dialog s počítačem, právě se jedna vyskytuje na Aukru za korunu!
Ukázkové programy jsou v ní sice v BASICu, ale ty popisy okolo jsou slušná sbírka člověčí psychologie a programátorských omylů.
- Totéž platí pro system("cls"), to je příkaz DOSu, u mě neudělá nic. Sice to ničemu nevadí, ale je to dobré vědět.
- A teď chyby. Funkce main() je typu int, takže musí int také vracet, chybí ti na konci return 0;
- Totéž platí pro vstupD(), v case 's' máš return bez hodnoty. Buď musíš vrátit nulu nebo jiné číslo jako chybový kód, je jedno jestli si to pak někdo vyzvedne, na tom už nezáleží. Ale kdyby naopak byla nějaká hodnota očekávaná a ty jsi jí nevrátil, průser by byl na světě, protože by se tam objevilo nějaké náhodné hausnumero!
- A stejně jako deklaruješ main(void), deklaruj i ostatní funkce bez parametrů: vstupD(void), vstupZ(void). V Céčku totiž prázdná závorka v definici funkce znamená libovolný počet parametrů, na rozdíl třeba od C++, kde naopak znamená funkci bez parametrů. Takže vždy void, až budeš používat prototypy funkcí, pochopíš proč.
- Tohle je nesmysl:
Kód: Vybrat vše
fclose(textout);
fclose(textout2);
if (textout== EOF)
printf("Soubor se nepodarilo uzavrit!\n");
if (textout2== EOF)
printf("Soubor se nepodarilo uzavrit!\n");
Nemůžeš porovnávat ukazatel na stream a znak. Od toho jsou návratové hodnoty, a man fclose říká:
Upon successful completion 0 is returned. Otherwise, EOF is returned and errno is set to indicate the error.
Takže funkce fclose() vrací nulu nebo error, správné použití je třeba takhle:
Kód: Vybrat vše
if (fclose(textout))
printf("Soubor se nepodarilo uzavrit!\n");
if (fclose(textout2))
printf("Soubor se nepodarilo uzavrit!\n");
- A zůstává nám už jen pár nepoužitých proměnných a jakási podivnost:
Kód: Vybrat vše
jaker.c: In function ‘vstupD’:
jaker.c:27:36: warning: unused variable ‘u’ [-Wunused-variable]
jaker.c:68:13: warning: implicit declaration of function ‘main’ [-Wimplicit-function-declaration]
jaker.c:68:36: warning: multi-character character constant [-Wmultichar]
jaker.c: In function ‘vstupZ’:
jaker.c:90:25: warning: unused variable ‘Preklad2’ [-Wunused-variable]
jaker.c:119:32: warning: multi-character character constant [-Wmultichar]
Co to return (main('void')) jako mělo znamenat? 
Když je funkce int, tak return musí přece vracet číslo (třeba jako výsledek výrazu, ale číselný), a ty vracíš výsledek volání main() a ještě se znakem 'void' místo prázdné závorky! Tady ti bohatě bude stačit return 0.
- Nepoužité proměnné nejsou žádný problém, ale můžou signalizovat že jsi na něco zapomněl. Jen bacha na to že to co deklaruješ uvnitř vnořeného bloku, také platí jen uvnitř vnořeného bloku, nikoliv v celé funkci. Což může být i výhoda

- No a nakonec mi to při spuštění vyhodí segfault, protože vstupní soubor neexistuje! Žádný tu nemám.
Ty sice testuješ jestli se zavřel stdin který máš otevřený jen pro čtení (to snad ani nejde nezavřít), ale netestuješ jestli se ti vstupní soubory vůbec otevřely... Takže BUM - program spadne.
To uzavírání se testuje proto, že v reálu zapisuješ jen do cache, která se až po zaplnění ukládá celá najednou na disk. Ale pokud je disk plný, nemůže se cache zapsat a k uzavření nedojde, aby se neztratila už vypsaná data (to u vstupu jaksi nehrozí). Ty se musíš rozhodnout co s tím provedeš, když se to stane, protože jako programátor víš co jsi zapisoval, program nebo operační systém to neví, nemůže správně reagovat.
Teď konečně k tomu switch(). V Céčku totiž funguje dost zvláštním způsobem, pochopitelným lépe pokud aspoň trochu ovládáš assembler. Patriarchové ho totiž vytvořili tak, aby se dal používat bez jakýchkoliv omezení, jaká jsou v jiných jazycích.
Třeba v Pascalu je příkaz case(), který dělá podobnou věc. Ale tam funguje jako vícenásobné větvení, prostě když skočíš do kterékoliv větve, tak z ní vypadneš až za koncem toho větvení.
Jenže v Céčku je to úplně jinak, switch() tady funguje jako vícenásobné GOTO do souvislé sekvence příkazů! Proto v ní nemusíš psát složené závorky { } kolem každého case. A teprve na konci té sekvence z ní vypadneš, pokud to chceš udělat dřív, musíš použít
break, tak jak to máš v main(). Nebo return pro návrat tam odkud jsi funkci se switchem volal.
Když to neuděláš, pokračuje se normálně dalším case v pořadí! Stejně jako by se pokračovalo další instrukcí strojového kódu, pokud bys před ní nedal nějaký Jump. Ono se z řady různých důvodů včetně tohohle říká že Céčko není programovací jazyk, ale strukturovaný přenositelný assembler

Bacha na to že v novějších jazycích odvozených z C (Java, C#, Python, PHP...) může switch fungovat zase jinak i když se stejně jmenuje! On totiž ten Céčkový dělá řadě "programátorů" problémy, a tak se různě "vylepšuje". Většina těch rozdílů ale znamená jen to, že se s ním některé věci nedají udělat. V Céčku se dá udělat cokoliv, i šíleně nebezpečná prasárna, ale vždy to musí mít autor pod kontrolou. Tady má prostě programátor absolutní volnost, ale i absolutní zodpovědnost, jazyk za něj nic neudělá, všechno si musí uhlídat sám.
Rozdíl je, jak vidíš, hodně velký. Stejná věc jako nahoře by se v Pascalu dala udělat také, ale výsledný kód by byl o něco delší, a některé věci se nedají udělat vůbec. V tomhle případě je to schválně, protože Pascal je výukový jazyk pro začátečníky, a tak většinu prasáren neumožňuje dělat.
Ono je sice GOTO ve strukturovaném programování skoro sprosté slovo, takže se používá jen ve výjimečných příkazech (prostě zapomeň že existuje, dokud nebudeš psát programy delší než nějakých 10000 řádků), ale ve skutečnosti je zakuklené do jiných příkazů, jako je ten switch, break nebo continue. Je to tak bezpečnější, navíc na první pohled vidíš co se má stát, což z prostého
goto label nepoznáš.
Pohled do historie - ten způsob jakým switch funguje je převzatý z klasického FORTRANu z roku 1956, tady je ještě trochu vylepšený, takže můžeš vybírat podle různých hodnot, ne jen podle pořadového čísla.
No a nakonec ten default, ono je ho lepší mít až nakonec, zejména kvůli přehlednosti, ale také abys dřív vyhodnotil platné možnosti kterých bývá obvykle méně, a teprve potom řešil ty ostatní neplatné. Představ si to tak že překladač má seznam těch hodnot case, a postupně ho prochází, dokud nenajde shodu. Potom skočí někam do těla toho switche, podle toho na kterém místě seznamu se řídící proměnná a seznam shodly. To procházení mu samozřejmě chvíli trvá, pokud ho budeš opakovat v cyklu milionkrát za sekundu, bude to znát, takže je dobré myslet na pořadí vyhodnocování. Nemusí to takhle ale být vždy, protože při optimalizacích s tím kódem překladač občas provádí dost šílené věci, zvlášť když to provádí pro tak šílený křáp jako je x86.
Také je užitečné reagovat i na velká písmena, což tenhle způsob pojetí switche přímo nabízí:
Kód: Vybrat vše
switch (volba) // hlavní menu v main()
{
case 'D': vstupD(); printf("Vypni si CapsLock!\n"); // upozorní troubu na problém a pokračuje dalším case
case 'd': vstupD(); // při stisku d nebo D volá funkci ...
break; // potom vyskočí ze switche ven
case 'Z': vstupZ(); printf("Vypni si CapsLock!\n"); // upozorní troubu na problém a pokračuje dalším case
case 'z': vstupZ(); // při stisku z nebo Z volá funkci ...
break; // potom vyskočí ze switche ven
case 'K': // neudělá nic, rovnou pokračuje dalším case
case 'k': return 0; // ukončí aktuální funkci, tady přímo celý program
default: printf("Zmackni d, z nebo k! \n"); // nakopne toho troubu, aby sundal kočku z klávesnice :-D
}
Switch můžeš do switche samozřejmě vnořit kolikrát chceš, stejně jako to platí pro kteroukoliv jinou programovou strukturu. To je princip strukturovaného programování. Jen musíš dodržet to, že je to blok s jedním vstupem a jedním výstupem, z čehož existuje jediná výjimka, return, který můžeš použít v libovolném místě. Ale protože ten ukončí celou funkci, tak ta se také zvenku jeví jako blok s jedním vstupem a jedním výstupem...
I když by se dalo říct že právě switch tohle také porušuje, protože umožňuje víc vstupů do sekvence příkazů! O těch break nemluvě. Ale zvenku je to opět jeden blok s jedním vstupem a jedním výstupem, takže to platí. Vyzkoušej si tohle:
Kód: Vybrat vše
#include <stdio.h>
int main(void)
{
int cislo;
printf("Kolik sekund: ");
scanf("%d",&cislo);
switch (cislo)
{
case 10: printf("10\n");
sleep(1);
case 9: printf("9\n");
sleep(1);
case 8: printf("8\n");
sleep(1);
case 7: printf("7\n");
sleep(1);
case 6: printf("6\n");
sleep(1);
case 5: printf("5\n");
sleep(1);
case 4: printf("4\n");
sleep(1);
case 3: printf("3\n");
sleep(1);
case 2: printf("2\n");
sleep(1);
case 1: printf("1\n");
sleep(1);
case 0: printf("\aBUM\n");
break;
default: printf("WTF: %d\n",cislo);
}
return 0;
}
Ty asi budeš muset tu hodnotu sleep(1) změnit na sleep(1000), protože v unixových systémech se zadává v sekundách a v dosových v milisekundách. Ale protože je to odvozené od časovače který posílá přerušení 18,2 krát za sekundu, tak to pro nějaké přesné časování stejně nejde použít. Je to jen atrapa jako spousta dalších věcí od M$.