Výjimka je na řešení cyb v hodnotách, asset na hledání chyb v kódu.
Pokud mám kus kódu, který požaduje existující adresář, napíšu, co má dělat. Pak řeším, že není platná cesta, nebo že adresář neexistuje. Buďto dám na začátek test s return DIR_NOT_EXIST, nebo výjimku. Je to vstup zvenčí, kde může být hodnota, která se liší od toho, s čím můžu pracovat - výjímečná situace, která by správně neměla nastat. A může nastat v závislosti na použití, takže musí být i v release. Můžu to chytnout, hodit chybovou hlášku nbebo ten adresář bytvořit a prubnout to znovu,...
Oproti tomu aserce hlídá, že kód dělá to, co má. Pokud napíšu funkci pro odmocninu, tak z definice je jasný, že na výstupu bude kladný číslo menší než argument. To dám do assertu. V produkční verzi by to jenom zabíralo místo a brzdilo, ale během ladění mě to může praštit, že co počítám není odmocnina, ale nesmysl. A samozřejmě si musím hlídat i vstup, pokud jsem na reálné množině, tak musí být >=0. Tam je to na výjimku u knihovní funkce (volání odkudkoliv kdykoliv), ale pokud je to soukromá metoda, můžu tam dát assert a dostanu kopanec "pozor, rveš tam něco, co tam nepatří". A vím, že se na to mám zaměřit a fixnout to, než začne řvát zákanzík...
Mimo to existuje i compile time asserce, která nedovolí zkompilovat kód, pokud je v něm měco blbě (třeba se liší velikost pole od počtu hodnot v enumu, který je indexuje,...). V nejjednoduší formě
#ifndef KONSTANTA
#error KONSTANTA nedefinována
#endif
A vůbec nejhloupější řešení chyby je to, že programátor řeší blbý hodnoty na vstupu stylem "hodnota neodpovídá, padělám ji". To se pak chyba prakticky nedá najít.