Vlákno názorů k článku
Programovací jazyk C má nejnižší oblíbenost v TIOBE za posledních 15 let od Quark66 - oblíbenost neoblíbenost.­..těžko říci. Cčko ale musí logicky růst....

  • Článek je starý, nové názory již nelze přidávat.
  • 30. 8. 2016 10:52

    Quark66 (neregistrovaný)

    oblíbenost neoblíbenost.­..těžko říci. Cčko ale musí logicky růst. Embedded deviced, ARM programming (IoT), robotika.. To, že percentuelně klesá je třeba dáno tím, že řada jiných jazyků trochu roste. Třebas pythón a tak. Ale C tu bude 4ever!

  • 30. 8. 2016 11:28

    Petr M (neregistrovaný)

    Bohužel :( Jako embeďák musím říct, že by se šiknul trochu jiný jazyk, některý konstrukce jsou v něm ne jak čtvercový, ale jako trojúhelníkový kolo! :Q

  • 30. 8. 2016 14:48

    hu (neregistrovaný)

    Ja bych chtel v Cecku obdobu pythonich generatoru, dal by se pak snadno delat kooperativni multitasking bez OS.

  • 30. 8. 2016 18:20

    ded.kenedy (neregistrovaný)

    Pokud opravdu moc potrebujes kooperativni multitasking, muzes pouzit getcontext, setcontext, etc.

  • 30. 8. 2016 18:57

    Kiwi (neregistrovaný)

    Já ne. C je skvělé právě svou jednoduchostí. Žádný vysokoúrovňový balast do něj nepatří. Od toho jsou vysokoúrovňové jazyky.

  • 31. 8. 2016 13:12

    SB (neregistrovaný)

    To je jasné, když existovaly jen 2 jazyky - assembler a C a jeden z nich se jako vysokoúrovňový vybrat musel. ;)

  • 31. 8. 2016 13:29

    Kiwi (neregistrovaný)

    To bych netvrdil. V době vzniku C existovala hromada jiných jazyků a spousta z nich byla oproti C opravdu vysokoúrovňová. C byl od začátku navrhován jako vysokoúrovňový přenositelný Assembler s jednoduchým kompilátorem, algolskou syntaxí a bez vestavěných funkcí, které by nějak mohly být vázány na konkrétní OS, ale za to s modifikátory jako auto, register, intenzivním využíváním ukazatelů apod. - tedy jako nižší jazyk.

  • 30. 8. 2016 16:37

    Kiwi (neregistrovaný)

    Co ti brání používat trochu jiný jazyk? A které konstrukce například?

    Mimochodem - místo C se k těmto účelům kdysi prosazoval PL/M, Oberon, Ada či Forth. Jen poslední jmenovaný v této oblasti vedle C (a obligátního Assembleru) získal určité pozice.

  • 31. 8. 2016 7:47

    Petr M (neregistrovaný)

    Třeba knihovny, úpravy existujících projektů,...

    Co by se hodilo nejvíc, tak
    - přetěžování funkcí, protože pamatovat si několik názvů je na pytel
    - možnost vynutit silnější typovou kontrolu (souvisí s přetěžováním funkcí), implicitní přetypování na int je někdy pěkně otravný.
    - namespace, protože PREFIX_funkce() je pěkně otravný a zapouzdřením do struktur program bobtná a leniví
    - výjimky, protože http://www.cplusplus.com/reference/csetjmp/ je pěkně otravný a nefunguje na 100%
    - bitový enumy (nepočítaly by 0, 1, 2, 3,.. ale 1, 2, 4, 8,... bez toho, že bych tam musel dopisovat hodnoty ručně)
    - jasně definovaný pořadí bitů u bitově definované struktury, protože teď je použití třeba u hardwarovýho registru je nemožný
    - Pole prvků menších než 1B (například u komunikačních protokolů, kde je třeba 10 stejných prvků velikosti 4b v poli 8B)
    - jednoduchá možnost inicializace jenom některých položek ve struktuře (protože se jinak prasí RAMka, nebo se to blbě udržuje, nebo nelogicky přes .entity)
    - dědičnost struktur, unií a enumů, protože jinak je to opruz a člověk se nevyhne hackování a chybám během údržby, balení struktur do struktur,...
    - extern omezit jenom na const
    - nějaká obdoba "with" z Pascalu, zvýší to přehlednost a ubere psaní -> vyšší produktivita, kompilátor ví, že si má hodit pointer do registru a nemusí se o něho starat (pokud není volatile)
    - množiny z Pascalu by taky nebyly od věci, mohlo by to zpřehlednit flagy a ušetřit maskování bitů
    - ...

  • 31. 8. 2016 8:34

    karel (neregistrovaný)

    no enum bych nechal od 0, ale mohlo by se zavest ze pokud prvnimu pridelim 1 nasledujici budou inkrementovny takze by to clovek nemusel rozepisovat

  • 31. 8. 2016 9:55

    ced (neregistrovaný)

    Ehm, pokud jakémukoliv prvku v enumu přiřadím hodnotu, následující je o jednu vyšší. Tak je to definováno. Nebo jde o bitový posun?

  • 31. 8. 2016 11:04

    Petr M (neregistrovaný)

    Je to tak. Enum se interně v kompilátoru implementuje takto:

    counter = 0;
    while(unassig­nedItem) {
    item=getUnassig­nedItem();
    if(item.hasEx­plicitValue) {
    counter = item.ExplicitValue;
    }
    assignValue(item, counter++);
    }

    Vyzkoušeno v IAR i v GCC, obojí naAVR, ARM7, ARM9, ARM11, CortexM* a MSP430

  • 31. 8. 2016 10:54

    Petr M (neregistrovaný)

    Na enumu od 0 není nic špatnýho. Pokračování od hodnoty tam je tam je (dle K&R), pokračuje se za poslední explicitní hodnotou. Mě šlo spíš o modelovou situaci:

    Mám nějakou komunikaci. První vrstva je PHY+MAC, tam mám nějaký chyby (timeout,... ) a dám je do enumu. Přidám další vrstvu, řekněme šifrování. Najednou mám další chyby (invalid key,...) a potřebuju ten seznam chyb rozšířit o další hodnoty. Možnosti, co mám:

    1) Nový enum s dalšíma chybama, začít poslední hodnotou z PHY+MAC. Jako výsledek nad šifrovací vrstvou použít jeden z těch dvou (jde to díky explicitní konverzi na in), ale při ladění vidím pro polovinu chyb jenom nic neříkající číslo.
    2) Nový enum, kam přepíšu původní hodnoty a přidám další. Pokud potřebuju vložit další vrstvu mezi, nebo přidat třeba chybu parity, se kterou se nepočítalo, tak musím upravovat na víc místech, ale při ladění vidím výsledek a můžu menší přetypovat na větší (při zachování pořadí). Pokud mám pět enumů navázaných na nějaký s 200 položkama, je to na pos...
    3) Lepičská metoda definování hodnot jako maker (sorry, nechci být za hňupa).
    4) Definovat všechny kódy chyb v jednom enumu v extra hlavičce a skousnout, že všichni mají tušení o všech chybách.
    5) To, co navrhuju:
    enum L2Error {
    enum L1Error,
    ERR_INVALID_KEY,
    ...
    }

    Jenomže nejelegentnější řešení nemá kompilátor vůbec rád... :(

  • 31. 8. 2016 12:46

    ced (neregistrovaný)

    Toto moc nepomůže, protože je to ekvivalentní bodu 4 - všichni budou mít tušení o všech chybách, protože předchozí enum bude definovaný v hlavičkovém souboru, který budeš includovat ...

    V podobných případech někdy používám metodu 3) ale definuji pro to extra datový typ (prostě přetypovávám čísla v #define na ten typ) na mnou vytvořený typ, takže vím, k čemu to patří - hodí se to pro flagy, pokud jich může být nastaveno více. Jestli někoho napadne něco lepšího rád se přiučím, protože to rozhodně není ideální. Nenašel jsem ideální způsob :-(

  • 31. 8. 2016 15:05

    Kiwi (neregistrovaný)

    Nic ve zlém, ale to, co popisuješ, je právě celkově nesprávný návrh a z toho už plynou problémy, s nimiž se musíš vypořádat a můžeš si maximálně vybrat, zda-li nešikovně nebo ještě nešikovněji.

    Každá vrstva produkuje vlastní chyby a vrstva vyšší má ošetřit chyby vrstvy nižší. V případě, že chybu nižší vrstvy nelze transparentně ošetřit, je nutné tuto chybu prezentovat nadřazené vrstvě, ovšem vhodně reinterpretovanou jako chybu vrstvy, jež se s ní jakožto prozatím nejvyšší nedokázala vypořádat. Z logiky vrstvené architektury plyne i logika šíření chyby: tak, jako vyšší vrstva představuje vyšší úroveň abstrakce, měla by i generovaná chyba odpovídat této vyšší úrovni abstrakce. Vyšší vrstva nemá funkčně přeskakovat vrstvu o úroveň níž, protože ta by pro ni měla představovat černou skříňku. A protože to jsou černé skříňky, tak vrstva objednu výš nezná její rozhraní a tedy ani nerozumí jejím chybám.
    Další pojetí je hybridní, tedy že každá vyšší vrstva nemusí nutně komunikovat jen s vrstvou bezprostředně ležící níže, ale může komunikovat s kteroukoli vrstvou ležící níže. Pak ovšem musí rozlišovat i množiny chyb podle vrstvy s níž komunikuje a pro prezentaci chyby, kterou se jí nepodařilo ošetřit, platí to samé, co výše: reinterpretovat jako chybu vrstvy, která ji jako poslední zachytila.
    Jakékoli jiné řešení šíření chyby už má smysl jedině informativní, pro účely logování či ladění; chyba vrstvy volané nepřímo skrze vrstvu ležící v hierarchii výše z logiky věcí nemá být ošetřována vrstvou, jež s ní přímo nekomunikovala.
    A protože logovací/infor­mativní funkce chyby je naprosto odlišná od její statusové funkce, mělo by se to řešit i samostatným logovacím modulem/vrstvou, s nímž budou jednotlivé moduly komunikovat přímo a který se už postará o vhodnou prezentaci posloupnosti objevivších se problémů a jejich lokalizaci tomu modulu, který o to bude mít zájem.

  • 31. 8. 2016 12:35

    Kiwi (neregistrovaný)

    Aj aj! Jen doufám, že nikoho nenapadne do C něco podobného v budoucnu matlat.

    - přetěžování: v žádném případě! I v jazycích, které to umožňují, je to častý zdroj záludných chyb, na což reagují standardy jako MISRA apod. a přetěžování omezují nebo zakazují používat zcela. Nicméně kdo bez toho nemůže žít, může použít C++.
    - silnější typová kontrola: implicitně na to dnes kompilátory reagují varováním, obvykle je možné vynutit i vyšší úroveň varování či použít nástroje typu lint; v tom dnes žádný problém nevidím a opět zde platí, že je možné použít C++, jež má typovou kontrolu přeci jen silnější
    - to je tvůj názor, např. já jsem na prefixy zvyklý - pokud to je vůbec kdy nutné; co nechci zveřejňovat, to udělám jako static, rozhraní modulu mohu opatřit prefixem, pokud je to nezbytné. Zavádět namespace jen kvůli tomu, že se mi nelíbí MP_procName() a chci místo toho psát MP::procName() mi připadá jako zbytečnost. Navíc už vidím, jak lidi sypou do namespace i lokální objekty, aniž by jejich lokálnost explicitně zaručovali... Ne, děkuji. Kdo bez toho nemůže žít, viz výše - C++.
    - výjimky: Ne! Tohle by bylo na mnohem delší debatu a vysvětlování a asi i flame, ale výjimky jsou ve skutečnosti velmi kontroverzní konstrukce, které dělají z chyby nějaký speciální stav, což není. Navíc z podstaty věci dělají program lenivější, protože se implicitně kontrola udělat musí, i když to není nutné (např. je-li chyba ošetřena ve spodních vrstvách a vyšší se již mohou spolehnout na bezchybnost) - a je to mnohem, mnohem větší zátěž pro rychlost programu, než nepřímý skok, ke kterému by vedlo volání funkcí umístěných do struktur, na které si stěžuješ hned v prvním bodě. V jazyku úrovně C nemají výjimky co dělat! Řešení pro ty, kdo bez toho nemohou žít, v pořadí od nejpřijatelnějšího k nejhoršímu: 1. goto, 2. setjmp, 3. C++
    - enum, další z konstrukcí, jejíž smysl je často nesprávně chápán, aneb "problém #define x enum x const int". Výčtový typ je určen k tomu, co napovídá jeho název: k výčtu pro vnitřní potřeby programu. Už použití rovnítka v enumu je nečisté a může být velmi nepřehledné, či nebezpečné (skákání tam a zpátky v rozsazích, do většího enumu stačí přidat hodnotu navíc a zbytek za tím se posune až před první další přiřazení...). Přiřazení by se v enumu vůbec nemělo vyskytovat a když, tak jen u první hodnoty, v dobře a bezpečně navrženém programu by na konkrétní hodnotě výčtových konstant nemělo vůbec záležet. Enum skákající implicitně po mocninách dvou - to už je prasečina na kvadrát, a to doslova. Od toho je #define.
    - bitové pole je ze své podstaty nepřenositelná konstrukce, ačkoli uznávám, že by to bylo velmi příjemné; v embedded oblasti to nebývá problém, protože potřebuje-li někdo operovat na této úrovni, pak je velmi silně navázán na konkrétní platformu a na konkrétní překladač a u toho je již takováto věc definována
    - pole prvků pod bajt: viz výše, ze své podstaty nepřenositelné a potenciální nesnáz při nedělitelnosti bajtu velikostí položky pole
    - částečná inicializace struktur: tady také nevidím důvod, proč to neumožnit; potřeba něčeho takového sice na 90% indikuje bad coding practices, ale to není důvod to zakazovat
    - extenze struktur ve stylu Oberonu: za 23 let, co programuji v C, mi to sice nijak zásadně nechybělo, ale myslím, že by to nebyla špatná vlastnost; ovšem i zde by se dalo napsat UTFC++ pro ty, kdož bez toho nemohou dýchat
    - pouze externí const: tady mi smysl poněkud uniká; v čem tkví pointa?
    - argumenty ohledně lenosti více psát jsou už dávno ve skutečnosti pouze neschopnosti efektivně používat textový editor; tato vlastnost C mně osobně nijak nechybí, ale ničemu by to asi nevadilo; u koho má takováto věc výrazný vliv na jeho produktivitu by se měl vážně zamyslet nad svými coding practices
    - množiny: to mi taky nijak zásadně nikdy nechybělo, ono se to ani v tom Pascalu moc neujalo; ničemu by to asi nevadilo, kromě toho, že by to skoro nikdo nepoužíval, což je asi i důvod, proč to v C není

  • 31. 8. 2016 13:56

    Petr M (neregistrovaný)

    Ono jde o abstrakci. Problém je třeba řešit na úrovni, na které existuje. Když dělám komunikační protokol, tak musím vědět, po jaké jedu lince - např. UART_write(), ... Nenapíšu to dost abstraktně na to, abych nemusel nic měnit. A ovlivní to někdy celkem dost věcí, takže potom jsou na řadě zvěrstva jako psaní wrapperu, struktura s referencema na funkce, balení do maker,... Volbou namespace a automatickým výběrem funkce s odpovídajícím chováním by se hodně věcí vyřešilo.

    Výjimky jsou sice prasárna, ale nepotřebuje tak moc zásobníku ve standardu. Ono třeba udělat několik nezávislých instancí hierarchovckýho stavovýho automatu a v něm mimo stavu reportovat chyby je docela divočina. Co je tam chyba návrhu? Stavový automat, ošetření chyby, nebo absence výjimek (a ne, pokud je každá instance parseru v jiným vlákně pod FreeRTOS, tak setjmp nepomůže).

    S čistotou enumu, nebudem si lhát. Mimo cyklu (kde stejně musím mít napsanou poslední položku známýho jména, abych věděl, kolik jich je, protože něco jako countof(enum) nebo foreach neexistuje), je to jenom svázání symbolických jmen s hodnotou a datovým typem. I v tom blbým Pascalu můžu napsat
    var x: array[mujenum] of Integer;
    a když v C napíšu
    int q[enum mujenum];
    tak mě to pošle do ...
    Vyložebně se enum hodí pro zobrazení symbolickýho jména v debuggeru. Jinak jeho použití kulhá.

    Bitový enum chci právě proto, abych nemusel psát rovnítka v klasickým enumu, pokud ten vnitřní stav je složením několika hodnot (fmRead | fmWrite) A pokud jde o #define, to je věc preprocesoru, bez typové kontroly, bez varování,... takže čím méň, tím líp. Je to univerzální hack na věci, který jazyk neumí ve standardu.

    Bitový pole u hardware je jakž takž OK. V dalším levelu, třeba parser protokolu, to přetáhnu na jinou platformu a jsem v pr...

    Částečná definice struktury, to je dost zásadní věc. Při rozšíření struktury nebo smazání položky, která je inicializovaná na X místech, to háže nepěknou chybu. A někdy je problém na straně architektury ve VHDL u výrobce křemíku. Víš, jak by bylo krásný si třeba udělat instanci periferky jako const struct, inicializovat natvrdo a jenom zkopírovat, místo přiřazování registru po registru? Jenomže ti pitomci tuhle nechají 100B pauzu, támhle je u SPI2 registr, který u SPI1 není a u SPI3 má jinou funkci... A někdy (nemožnost rozšířit strukturu, nepodpora přetěžování) je výhodnější vzhledem ke spotřebě paměti použít jednu strukturu s pár položkama navíc. A na mojí úrovni se prostě nechci starat o to, že tam je potřeba vložit pět libovolných intů jako výplň navíc :(

    Rozšíření struktur je celkem důležitá věc. Pokud mám několik modulů se stejným základem (např. různý komunikační linky) a u jedné chci přidat DMA, tak by pěkně odpadl wrapper, který pro společný funkce jako init() hodí strukturu zabalenou ve struktuře jako argument... Napsat to je práce navíc (i když #define to schová skoro bez zvýšení nároků).

    Pouze externí const je za trest pro všechny, kdo nejsou schopní lokální proměnnou zapouzdřit jako static dovnitř modulu a kdokoliv se dostane bez varování kamkoliv. Aspoň by jednou museli přemýšlet.

    A ta lenost má vliv vždycky. Když musím pro každý modul psát něco jako
    #define write(data, size) UART_Write(han­dleWithDma->uart, data, size)
    pro deset funkcí a zbytečně, tak to tak jako tak stojí čas a editor/IDE to za tebe neudělá...

    Množiny, no ty jsem nepotřeboval už od včerejška :/ Zvyk je železná košila.

  • 31. 8. 2016 13:59

    Petr M (neregistrovaný)

    Btw. hodně věcí je doplněno přes #pragma a podobně, a to jsou věci, který bych měl raděj přímo v jazyku.

    A i některý užitečný věci sice C umí, ale jenom díky rozšíření kompilátoru, třeba v GCC __attribute__((we­ak)) a podobně. Standardizace by neškodila.