Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: Zdeno Sekerák 05. 11. 2010, 12:22:08

Název: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Zdeno Sekerák 05. 11. 2010, 12:22:08
Chcem sa opýtať či existuje (poznáte) neaký nástroj ktorý by prezrel zdrojový kód v C/C++ a upozornil na prípadné chyby. Nemyslím lexikálne ani syntaktické ale tie ako zabudnutý break vo switch, nedáná default hodnota, preplnený stack a pod.

Ďakujem za radu.
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Augur 05. 11. 2010, 12:43:19
http://en.wikipedia.org/wiki/List_of_tools_for_static_code_analysis

podivejte se zde.
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Jiri 05. 11. 2010, 13:23:40
zapomenutý break ve switch neni chyba
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Logik 05. 11. 2010, 13:44:54
Zapomenutý break chyba je. To, že někdy je větev bez ÚMYSLNĚ uvedeného breaku je věc druhá. Jelikož je úmyslné neuvedení řidší než opomenutí, má smysl při auditu kódu se na to zaměřit, byť to chce lidskou kontrolu.


PS: V každém případě je switch jedna z chyb v návrhu C/C++, správné by bylo, kdyby se explicitně označovalo pokračování v následující (nebo ještě lépe v dané) větvi a standardně by se to chovalo jako s break.
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: anonymous 05. 11. 2010, 14:31:28
PS: V každém případě je switch jedna z chyb v návrhu C/C++, správné by bylo, kdyby se explicitně označovalo pokračování v následující (nebo ještě lépe v dané) větvi a standardně by se to chovalo jako s break.

to je dosti subjektivni nazor. pokud na c nahlizis jako lowest common denominator assembler (switch/case = ekvivalent jumptable v asm || serie ifu s fallthru), zacinaji veci davat lepsi smysl. asi je dost nestastne ze nekdo povazuje c v dnesni dobe za hll
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Logik 05. 11. 2010, 14:42:23
Proč je teda v C např. for cyklus? Ten taky nemá obdobu v asm.
C prostě není assembler a je mi úplně jedno, jak se co píše v asm. Obě konstrukce jsou sémanticky ekvivalentní a překlad obou z nich je bezproblémový. Proto je imho chyba, pokud návrháři jazyka zvolili tu, která vede k delšímu, méně čitelnému kódu s větší pravděpodobností chyb.
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Radovan 05. 11. 2010, 16:35:59
PS: V každém případě je switch jedna z chyb v návrhu C/C++...
A to bych se hádal, protože zrovna tahle věc je v Céčku úplně fantastická. Tvůj názor zřejmě pramení z nepochopení jeho účelu, on totiž ten switch není primárně určený k větvení, na to je tam if/else a dá se s nimi bohatě vystačit, switch je jeden blok příkazů, do kterého je možné vstoupit na libovolném místě! Prostě to samé jako v BASICu byl příkaz ON GOTO. To že z něj jde také libovolně vyskočit pomocí break je jen bonus, proč ho tam nepoužít, když už v tom jazyce je a umí to.

C opravdu není assembler, je to hodně vylepšený přenositelný makroassembler maskovaný za programovací jazyk ;-) A u toho for v assembleru bych si také nebyl tak jistý, třeba mikroprocesor Z80 má instrukci djnz: http://z80-heaven.wikidot.com/instructions-set:djnz
Samozřejmě for v C umí mnohem víc věcí než jen počítat cykly, stejně tak jako switch se dá použít několika úplně odlišnými způsoby, a u dalších příkazů je to podobné.
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Logik 05. 11. 2010, 19:09:03
Citace
A to bych se hádal, protože zrovna tahle věc je v Céčku úplně fantastická
Jenže vede k chybám. Samozřejmě já netvrdím, že by se měla zcela vypustit. Ale jelikož je standardní (v 90% případů se to tak používá) používat bloky samostatně, tak by měla být defaultní možnost skočit ven, zatímco možnost pokračovat dalším (nebo i libovolným - i to by se často hodilo a nejde to - teda bez goto) blokem by měla být označena nějakým klíčovým slovem.

Citace
switch není primárně určený k větvení, na to je tam if/else a dá se s nimi bohatě vystačit,
Ono vystačit si jde i s goto, že... Jde o to, co je čitelnější forma zápisu a tady switch rozhodně vede...
Navíc úplně nedá, protože switch s rozumnými numerickými hodnotami může překladač přeložit "chytřeji" než posloupnost podmínek.

Citace
...jeden blok příkazů, do kterého je možné vstoupit na libovolném místě...
Jo? IMHO switch znamená přepnout či přepínač. Přepnout znamená buď, anebo, XOR. Takže pokud je toto zamýšlené použití (imho nikoli), tak je naprosto špatně zvoleno klíčové slovo. Navíc, jak jsem psal, toto použití je minoritní. Takže buďto autoři C špatně navrhli tuto konstrukci, nebo špatně zvážili četnost užívání jednotlivých konstrukcí. "Dobře" (ve smyslu tak, aby to vedlo ke stručnému, čitelnému a bezchybnému kódu) to ale rozhodně není.
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: justik 06. 11. 2010, 11:15:27
Pokud by nemuselo byt reseni pouze pro Linux, doporucuji Static Code Analysis tool ve vyssich verzich Visual Studia 2010...  Je zalozeno na FxCop...
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Radovan 06. 11. 2010, 11:34:37
Jenže vede k chybám.
To že auto může vjet do protisměru také vede k nehodám, a přesto se jízdní pruhy neoddělují betonovou bariérou. Ono se sice v 90% případů jezdí vpravo, ale občas se jiná možnost hodí, třeba při předjíždění nebo objíždění rozkopané silnice. Prostě to řízení tak funguje a je k tomu dobrý důvod, jinak by z toho byla autodráha. Dokonce se tahle vlastnost dá použít i k obracení, parkování a jiným více či méně nebezpečným vylomeninám, ale přitom jde pořád jen o změnu úhlu natočení předních kol ;) Řidič sám musí vědět co dělá, nikdo jiný ho neohlídá.
Citace
switch s rozumnými numerickými hodnotami může překladač přeložit "chytřeji" než posloupnost podmínek.
Rozhodně ano, přinejmenším by ten kousek kódu měl být kratší a rychlejší, pokud tedy bude fungovat jako jeden výběr z tabulky adres místo postupného provádění několika podmíněných skoků. Takže jak jsem psal, switch je zakuklený a vylepšený ekvivalent pozdějšího basicového ON n GOTO label1, label2, label3..., což byl jeden z marných pokusů jak samotné používání GOTO trochu ukáznit a aspoň nějak ten špagetový kód strukturovat. (Ale FORTRAN to měl o "sto" let dříve!)
Citace
IMHO switch znamená přepnout či přepínač.
No však jo, přepínač na některý ze vstupních bodů toho bloku 8) Třeba v QBASICu (ne že by ten byl dobrý příklad) je to takhle:

SELECT CASE výraz
  CASE IS >= 10
    větev 1
  CASE 2 TO 5
    větev 2
  CASE 1
    větev 3
  CASE ELSE
    větev 4
END SELECT

Nikde žádný break, prostě SELECT jako výběr jedné z několika větví (a také žádná jiná možnost, aspoň bez použití GOTO). U switche se sice přesně tenhle případ použití uvádí v učebnicích, ale stejně jako většina věcí v C toho umí mnohem víc. Ještě spíš bych řekl že switch umí něco úplně jiného, ale k tomuhle se dá díky své univerzálnosti použít také.
Řekněme, že v "bezpečných" jazycích (jako je Karel ;D) má programátor jen jednu možnost jak to udělat a co smí udělat, nic jiného mu ten jazyk pro jistotu nedovolí. Tady v C má možností mnohem víc, a proto je také jen na jeho zodpovědnosti, jestli a jak si ohlídá že omylem nepoužil zrovna jinou možnost než chtěl, to není syntaktická chyba, spíš logická, jako kdyby místo A+B napsal A-B. Obojí jde a obojí je v tom jazyce správně, tak proč jedno z nich zakazovat jen proto že se používá méně často a "vede k chybám"? Ne, nevede, jen to prostě udělá něco jiného. (Však to odčítání je vlastně také úplně zbytečné, vždycky můžu použít A+(-B), nebo ne? Unární minus to jistí.)
Tohle prostě není Pascal, kde se padá do peřin a ještě navrch umí pofoukat bebíčko. C je jako břitva, a pokud si někdo při holení uřízne nos, je to jen jeho chyba a buďto si měl dávat víc bacha nebo koupit Braun :o
Citace
Navíc, jak jsem psal, toto použití je minoritní.
Ano, častěji se switch používá tak jako ten SELECT, troufl bych si tvrdit že je dost lidí kteří si ani neuvědomují že to jde i jinak (případně to považují za chybu):

                    |
    ----------------+-----------------
   /              SELECT              \
  --+----------+----------+----------+--
    |          |          |          |
+---+---+  +---+---+  +---+---+  +---+---+
| blok1 |  | blok2 |  | blok3 |  | blok4 |
+---+---+  +---+---+  +---+---+  +---+---+
    |          |          |          |
    +----------+----+<----+<---------+
                    |

Ale to přece není důvod k omezování těch ostatních možností, které tam jsou! Ono to totiž ve skutečnosti funguje úplně jinak, protože příkaz switch je úmyslně navržený takhle:

      |
  ----+-----
 /  switch  \
--+--+--+--+--
  |  |  |  |
  |  |  |  |  +---------+
  |  |  |  +--+ příkaz1 |
  |  |  |     +----+----+
  |  |  |          |
  |  |  |     +----+----+
  |  |  +-----+ příkaz2 |
  |  |        +----+----+
  |  |             |
  |  |        +----+----+
  |  +--------+ příkaz3 |
  |           |  break  +--+
  |           +---------+  |
  |                        |
  |           +---------+  |
  +-----------+ příkaz4 |  |
              +----+----+  |
                   |       |
                   +<------+
                   |

To je důvod proč se u každé větve musí psát ten break, aby se dosáhlo toho prvního stavu, zrovna break je tam jaksi navíc a umožňuje ten blok příkazů zase ve vhodném místě podle potřeby přerušit, klidně i podmíněně. Takže je to jeden blok s několika vstupními body, a pomocí break se z toho dá udělat SELECT.
Pro tyhle dvě možnosti by tedy "správně"(?) měla být dvě různá klíčová slova, jenže tvůrci Céčka si byli vědomi toho že po překladu do strojového kódu tam stejně zbydou jen nějaké CMP a JNZ a tak to všechno vohrábli jedním víceúčelovým příkazem, podobně jako celý zbytek tohohle jazyka, aby si nekomplikovali život a kompilátor. To jen potvrzuje názor, že C je nízkoúrovňový systémový jazyk, postavený jen o málo výš než assembler, který se sice moc nehodí na psaní větších programů, ale je dokonalý pro malé a rychlé kostky unixových skládaček: http://cm.bell-labs.com/cm/cs/who/dmr/unixblocks.jpg (http://cm.bell-labs.com/cm/cs/who/dmr/unixblocks.jpg) Přesto ten jazyk má na víc, je otázka jestli na to mají i jeho programátoři.
Čili, defaultní možnost je, stejně jako u VŠECH ostatních příkazů, že se jede dál dokud je kam, a pokud to potřebuji z jakéhokoliv důvodu v libovolném místě přerušit, tak to udělám úplně stejným příkazem jako ve VŠECH ostatních případech - příkazem break. Kdyby to zrovna a jedině u switche bylo opačně, tak by to bylo pro programátora matoucí, navíc proti principům UNIXu - jednotnosti a univerzálnosti.
Nakonec, právě tebou zmíněný příkaz for je nejlepší ukázka toho jak univerzálně K&R uvažovali, protože do něj se dá zadat kromě běžného počítaného cyklu tolik nej(h)různějších možností a činností, že se k němu nakonec ani žádný blok příkazů psát nemusí (což neznamená že to není hrozná prasárna).

Ještě mě tak napadá, že třeba účelem ternárního operátoru určitě nebylo primárně tohle použití:

#define KOUPELNA 1
#define ZACHOD 2
#define CHODBA 4
unsigned int vypinace;
...
printf("V koupelně %s.\n", vypinace&KOUPELNA ? "se svítí":" je zhasnuto");
printf("Na záchodě je %s.\n", vypinace&ZACHOD ? "rozsvíceno":"zhasnuto");
printf("V chodbě %s.\n", vypinace&CHODBA ? "projdeš bezpečně":"se přerazíš");

Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: anonymous 06. 11. 2010, 14:30:22
0xe0-0xe2 na x86. http://faydoc.tripod.com/cpu/loopnz.htm

Proto je imho chyba, pokud návrháři jazyka zvolili tu, která vede k delšímu, méně čitelnému kódu s větší pravděpodobností chyb.

to rozhodne. oproti pythonu je to rozdil, protoze tam je rec o hll, stejne tak haxe, java ...
c prolina konstrukcema do asm stejne jako basic.

je to oldschool, ale tenkrat nic moc lepsiho neznali. pokud vim computed goto v basicu, stejne jako ve fortranu ma taky implicitni fallthru (na konci statementu mas goto ven).

a vubec to je secko desne OT :))
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Inkvizitor 06. 11. 2010, 15:01:47
Radovane, programovací jazyk C je tak jednoduchý, že by pro nikoho nebyl problém se naučit, že dalším 'case' prostě vyhodnocováním implicitně končí a dokonce si myslím, že by to bylo intuitivnější. Zvlášť u jazyka, který je "jako břitva" bych to neviděl jako sebemenší problém. Podle mého názoru autoři zkrátka nedomysleli důsledky nebo si zjednodušili návrh kompilátoru (museli by pravděpodobně zavést další klíčové slovo, protože třeba 'continue' by mohlo být opravdu matoucí).
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Logik 07. 11. 2010, 00:07:50
Citace
To že auto může vjet do protisměru také vede k nehodám, a přesto se jízdní pruhy neoddělují betonovou bariérou. Ono se sice v 90% případů jezdí vpravo, ale občas se jiná možnost hodí, třeba při předjíždění nebo objíždění rozkopané silnice.
Proto taky je standardně volant vlevo - tzn. usnadňuje nejčastější použití auta: jízdu vpravo, ale nikterak neznemožňuje jet vlevo. Akorát ty nejlepší auta Ti zavibrují volantem, když uděláš nestandardní krok - přejedeš dělící čáru. Návrh C switch/case mi ale připomíná auto s pravostraným řízením, (plánované do evropy).

Zbytek naprosto nechápu, co tím chceš vyjádřit, já se nepřu o užitečnost této konstrukce - sám ji občas použiju, ale tvrdím, že jelikož to není časté použití, zatímco jednoduchý rozskok ano, tak by měla být explicitně označena možnost pokračovat a ne možnost ukončit.

Inkvizitor:
No mě by tam to continue ani zas tak nevadilo. Zaprvé by to odstranilo disproporci, kdy break se uvnitř switch týká jiného bloku kódu než continue. Zadruhé - break v cyklu znamená: "ukonči tuto iteraci a skoč na konec cyklu". U switch znamená: "ukonči tento blok a skoč na konec switche" - v podstatě se v definici zamění blok za iterace a cyklus za switch. Když tedy "continue" u cyklu znamená: "ukonči tuto iteraci a začni novou iteraci", stejnou substitucí dostávám význam continue u switche: "ukonči tento blok a pokračuj následujícím blokem". Což je přesně to, co by příkaz continue měl umět.
 Pak by jen stačilo zadefinovat, že case znamená ve skutečnosti break; case a jsme doma.
A kdyby např. uměl ještě continue <case>, to by byla lahůdka :-) stejně jako tam chybí možnost breaknout víc cyklů najednou apod....
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Radovan 07. 11. 2010, 09:14:00
Auto, které mi zavibruje volantem ve chvíli kdy se vyhýbám slepici (ať s peřím či bez), bych za nejlepší rozhodně neoznačil ;) Kromě toho existují auta s řízením uprostřed, které by bylo z mnoha důvodů nejvýhodnější. Ovšem tradice a tupost zákazníků...

Rozskok je možná častěji používaný, ale já to vidím tak, že patriarchové tenkrát z toho jazyka prostě vyházeli všechno co nebylo nepostradatelné - a dobře udělali - a místo dvou jednoúčelových příkazů tam dali jen jeden, který umí oboje. A kvůli jednoduchosti kompilátoru i jednoznačnosti výkladu kódu to prostě dělá tak jako všechny ostatní cykly: breakem se dá vyskočit z bloku/cyklu ven.

Naproti tomu continue přeskočí na podmínku pro novou iteraci, což by u switche muselo znamenat že se začne zase od začátku - skokem na switch a novým výběrem některé "větve". Continue <case> je blbost, protože je to úplně to samé jako goto label, a to už tam přece je, a právě od toho tam je! (Stejně jako na breaknutí více cyklů najednou.) Tak na co mu dávat další název jen proto, aby se s ním mohlo omezeně skákat jen uvnitř jednoho bloku kódu? Co by na použití goto řekl Ken Thompson a jeho jazyk B, přímý předchůdce Céčka?

loop :
   while((c=char(fmt,i++) ) != '%') {
      if(c == '*e')
         return;
      putchar(c);
   }
   x = *adx++;
   switch c = char(fmt,i++) {

   case 'd': /* decimal */
   case 'o': /* octal */
      if(x < O) {
         x = -x ;
         putchar('-');
      }
      printn(x, c=='o'?8:1O);
      goto loop;

   case 'c' : /* char */
      putchar(x);
      goto loop;

   case 's': /* string */
      while(c=char(x, j++)) != '*e')
         putchar(c);
      goto loop;
   }
   putchar('%') ;
   i--;
   adx--;
   goto loop;
}

Je to sice něco trochu jiného, ale účel goto je z toho úplně jasný. A B je zase odvozené z BCPL, v jehož manuálu čtu:

switchon E into <block>
where the contains labels of the form:
case <constant> : or
default :
...
The switch is implemented as a direct switch, a sequential search or a hash switch
depending on the number and range of the case constants.

Labels se tam píše, labels! A jediný důvod proč tam jsou je ten, že se na ně může skákat. Ta dvojtečka by měla dost naznačit už sama o sobě, ne? Od běžných labelů se liší jen tím case, aby bylo jasné že na ně může skočit jen nejbližší switch. Kdyby to ale mělo fungovat čistě jako rozskok, muselo by tam být něco takového:

switch(n) {
    case 1 {...}
    case 2 {...}
    default {...}
}

Což je teda pěkná hrůza, vůbec to do stylu toho jazyka nesedí. Takže je to tak že mezi { a } je jeden blok - sekvence příkazů, a ten se vykonává od místa kde do něj vstoupíš až do jeho konce, nebo prvního nalezeného break. Tak to funguje naprosto stejně pro switch, for, while i do, a byla by velká chyba to chtít v jednom z těch případů udělat opačně. Navíc jen proto, aby se omezily možnosti toho příkazu.

Prostě je tam jednou switch(n){příkazy}, kde se podle hodnoty toho n dá určitá část příkazů přeskočit, a nemá smysl nat tím ronit slzy, zvlášť že se s tím dá takhle dělat mnohem víc věcí než kdyby to mělo fungovat jinak, čili, jak napsal Jiri, "zapomenutý break ve switchi není chyba", je to prostě základní možnost, která se dá pomocí breaku rozšiřovat o další funkce.

Inkvizitor: no to je právě ono, programátoři by se museli naučit že to právě a jedině u switche funguje odlišně od všech těch cyklů, a také by se to musel naučit kompilátor. A tehdejší kompilátory musely fungovat v pár kilobajtech paměti ;D Místo toho nevhodného continue by se tam dal použít třeba next, ale to by se třeba mě pletlo s basicovým next, které dělá totéž co continue - začíná novou iteraci. Čili nám zbývá jen ty case inteligentně seřadit, nebo použít jednoduché a rychlé goto.
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Logik 07. 11. 2010, 13:26:35
Citace
Kromě toho existují auta s řízením uprostřed, které by bylo z mnoha důvodů nejvýhodnější. Ovšem tradice a tupost zákazníků...
Nevim, ale tendle argument mi připadá ve stylu, všichni jsou blázni, ale já jsem letadlo... Navíc nepopíráš smysl přirovnání, ale jeho nepodstatnou složku (nejde o to, jestli je to skutečně vhodné nebo ne, ale že se to tak dělá, protože je obecně příjmanej názor, že to je neergonomičtější vzhledem k použití auta).

Citace
Naproti tomu continue přeskočí na podmínku pro novou iteraci...
Jenže ve switchi žádné iterace nejsou. To prostě není cyklus a nejsou tam žádné iterace. Jak jsem v předchozím postu, význam break by šlo krásně krásně konzistentně rozšířit i na ne-cykly.
(myšlenka: switch není cyklus, ale jednotlivé case větve lze brát jako iterace. Pak to sedí daleko lépe, než současný stav, kde continue u switche nemá význam, zatímco break ano).

Citace
Continue <case> je blbost,
break je blbost, continue je blbost. To vše lze nahradit příkazem goto. Jenže všechny tyto konstrukty mají jednu výhodu - jsou o mnoho znaků kratší a zároveň neumožňují skočit někam, kde to bude na 99% špatně. Goto by tak zůstalo opravdu na "hacky" a vyloženě nestandardní konstrukty, což by opět omezilo chybovost.

Citace
"zapomenutý break ve switchi není chyba",
Zaprve zapomenutý break chyba je. Chyba není úmyslně nenapsaný break. Což ukazuje na nevhodnost konstruktu: člověk má ořemýšlet proč něco někde je a ne proč něco někde není. Dobrý jazyk, pokud má daný výraz dva možné významy, tak by měl předpokládat standadní užití a vyžadovat označení nestandardního. jinak to prostě vede k těžko odhalitelným chybám.

Zadruhé diskutuješ z pozice je to takto, takže je to dobře. Čili je to podobný argument, jako kdybych tvrdil - v atari basicu bylo pouze goto, takže je dobře, že je tam pouze goto. Proč si tedy každej, kdo moh, instaloval turbo basic, kde byly cykly?

Citace
Takže je to tak že mezi { a } je jeden blok....
A kdo mi brání se na case dívat jako na oddělovač bloků? Jednořádkovej for cyklus má taky na konci implicitní "continue", i když tam není složená závorka. Zkus trochu vystoupit ze svýho vidění "je to prostě takhle" a připustit i jiné možnosti. Já nad tím neromím slzy, já prsotě konstatuji, že kdyby byl návrh jazyka jiný, tak by docházelo k míň chybám ve zdrojácích. Např. by se nikdo nesháněl po softwaru, kterej bude auditovat všechny case větve neukončené break.

U zbytku jsem moc nepochopil vztah (co s tim má společnej jazyk B: opět argumentuješ tím jak to je, zatímco jáse bavím o tom, jak by to bylo dobře - to se jako nesmí následník jazyka vylepšit?), popř. argument, že by to zesložitilo kompilátor (naprosto marginálně).
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Ondy 07. 11. 2010, 14:01:52
Existuje spousta toolu pro statickou analyzu kodu. Z komercnich je hojne pouzivany Fortify 360, z open-source jsem pouzival SPlint (u nej ale nevim jak je to s vyvojem, mam pocit ze aktivita vyvojaru je miziva), hodne zajimavy se mi zda Frama-C. Preji hezky den.
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Radovan 07. 11. 2010, 15:45:16
... protože je obecně příjmanej názor ...
Což nemusí nutně znamenat že je to pravda 8) Tohle je argument typu "to ví přece každý" nebo "říkali to v televizi".
Citace
Jenže ve switchi žádné iterace nejsou.
Ne, nejsou. Break znamená "vypadni ven" z příkazu, continue říká "sjedeme to znovu". Proč nefunguje break i u jednotlivých větví if? To by pak podle tvého vzoru toho switche musel přeskočit na opačnou větev, nemyslíš?
Citace
... jsou o mnoho znaků kratší a zároveň neumožňují skočit někam, kde to bude na 99% špatně.
Goto l1 není delší než continue :P Ne, vážně, jsou tam proto že se vztahují jen k těm čtyřem konkrétním příkazům a nedá se s nimi skákat libovolně, takže to goto tam zůstává jen na skutečné hacky jako přeskok na jiné case, případně vyskočení z více cyklů najednou, což je podle mě také dost minoritní potřeba. Osobně jsem ho v C ještě nepoužil!
Citace
Zaprve zapomenutý break chyba je. Chyba není úmyslně nenapsaný break.
Jenže pro kompilátor to je jiný tvar toho příkazu, neznamená to z jeho pohledu o nic víc než když na požadovaném konci řádku zapomenu napsat \n.
Citace
Proč si tedy každej, kdo moh, instaloval turbo basic, kde byly cykly?
Asi jim ten Atari Basic z nějakých důvodů nevyhovoval, mě se v Sinclair Basicu naopak goto líbilo, obzvlášť lahůdková schopnost provést GOTO x*1000 mi po přechodu na "dokonalejší" PC dost citelně chyběla. (ON GOTO to neznalo, takže jsem si vhodným očíslováním řádků nahradil celý ten switch, aniž bych předtím nějaké C nebo něco podobného kdy viděl, nebo tušil něco o strukturovaném programování.) Ale fakt je že i se samotným goto se vystačit dá, vlastně by v úplně minimálním jazyce stačily jen dvě věci: přiřazení a podmíněný skok. Nic víc není potřeba.
Citace
A kdo mi brání se na case dívat jako na oddělovač bloků?
Ne kdo, ale co, ta dvojtečka přeci. Ta ti říká že je to label a nic víc.
Citace
U zbytku jsem moc nepochopil vztah (co s tim má společnej jazyk B: opět argumentuješ tím jak to je, zatímco jáse bavím o tom, jak by to bylo dobře - to se jako nesmí následník jazyka vylepšit?), popř. argument, že by to zesložitilo kompilátor (naprosto marginálně).
B s tím má společného to, že je to předchůdce C, a jeho předchůdce je zase BCPL, jeho předchůdce CPL, jeho předchůdce... Tak do hloubky to studovat nehodlám, musí ti stačit že v BCPL byl příkaz switchon, který se choval stejně jako switch v C, kde se to vylouplo poprvé netuším. Ale řekl bych že takhle se dostaneme až k assembleru a strojovému kódu, které všichni ti co tyhle jazyky pro svojí potřebu vymýšleli znali a zvládali levou zadní. Prostě to vychází ze samotného principu fungování počítačů a je to tak dodnes.
A s tím vylepšováním to nebude tak žhavé, už v době tvoření toho C to neudělali, a třeba v Javě switch funguje také úplně stejně, i když je to jazyk o pár desetiletí mladší a údajně z něj byly odstraněny konstrukce, které dělaly programátorům v C problémy. Nakonec, když je to tak marginální zesložitění, tak si kompilátor uprav, vyzkoušej si co ti to udělá a uvidíš sám. Nebo si pro sebe vytvoř úplně nový jazyk který ti bude vyhovovat, tyhle vykopávky se už stejně používají příliš dlouho a chtělo by to změnu, něco minimálně tak odlišného jako byl Prolog ::)

Prostě se hádáme zbytečně, máme dva opačné náhledy na jednu věc, já si myslím že ten můj je bližší tvůrcům těch jazyků. Radši prozraď, ať z nás vypadne konečně něco užitečného, co ty používáš na vychytávání svých chyb typu zapomenutého breaku? Pro mě osobně je nejlepší živý člověk, kterému dám svůj program přečíst a sleduji jak hodně se šklebí. To zatím žádný program nahradit nedokáže.
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Logik 07. 11. 2010, 20:36:17
Citace
Tohle je argument typu "to ví přece každý" nebo "říkali to v televizi".
Nepochopil. V tom přirovnání vůbec nejde o tom, jestli to je nebo není dobře. V tom přirovnání jde o to, že se ví, že se jezdí vpravo a tak se tomu to auto přizpůsobí.
Ale u switch konstruktu se ví (statistika), že nejpoužívanější je rozskok se separátníma blokama (za každym break), ale ten konstrukt k tomu přizpůsoben není.

Pokud konstruktéři auta předpokládají, že nejlépe se řídí s volantem vlevo a není tomu tak, je to jejich chyba, ústupek přání zákazníka či obecným zvyklostem (by se to nepletlo). Pokud návrháři jazyka navrhnou switch tak, jak je v C, tak se nepoužívá dobře. To lze objektivně změřit (délka nejčastěji používaných kódů, frekvence chyb). Např. u JAVY byl návrh ústupkem obecným zvyklostem (píše se to takhle). U C (respektive B) nebyly ale ani obecné zvyklosti ani přání zákazníka, zbývá tedy chyba...

Citace
Break znamená "vypadni ven" z příkazu.... Proč nefunguje break i u jednotlivých větví if?
Právě proto, že break NEZNAMENÁ vypadni ven z příkazu, ale z cyklu. A právě proto by šlo krásně sémanticky rozšířit i na switch.

Citace
Goto l1 není delší než continue (http://forum.root.cz/Smileys/default/tongue.gif)
l1:
...
Goto l1
delší je. A jestli chceš svůj názor obhajovat prasečinama jako dvoupísmenej název identifikátoru...

Citace
Jenže pro kompilátor to je jiný tvar toho příkazu...
Tvrdil jsem snad, že je to syntaktická chyba? Ne.
Dělá to to, co bylo zamýšleno? Ne? Tedy je to chyba.
To, že to není syntaktická ale sémantická chyba je o to hroší -
člověk na ní něpřijde při překladu, ale později.

Citace
Ne kdo, ale co, ta dvojtečka přeci. Ta ti říká že je to label a nic víc.
Takže výraz (1+1) je volání funkce? Jsou tam závorkly a ty přeci znamenají volání funkce.

Citace
Jazyk B...
Znova. To, že to je v B nebo v assembleru tak neznamená, že to v těch jazycích bylo dobře. Takže to není argument. Vždycky se jezdilo na koních a taky to není nejlepší způsob překonávání vzdáleností.
 Ad Java - tendle jazyk dědí C++ syntaxi. Pravda odstraňuje některé nebezpečné věci, ale to je rozdíl oproti tomu "přehodit" význam. V době vývoje C to problém nebyl, protože B nemělo v podstatě uživatelskou základnu a jazyk C jazyk B nahrazoval. Teď už to samozřejmě člověk těžko změní, jen by se mu to pletlo. Ale to neznamená, že nemůže konstatovat, že to je špatně.

Btw. ve všech jazycích, co znám a co nemají C-like syntaxi se case používá bez pokračování dalšími větvemi.

Citace
případně vyskočení z více cyklů najednou, což je podle mě také dost minoritní potřeba. Osobně jsem ho v C ještě nepoužil!
A koliks toho v C napsal? Např. právě u switch příkazu uvnitř cyklu je to vcelku častá potřeba jako jedna z možností ukončit cyklus...

Citace
Prostě se hádáme zbytečně, máme dva opačné náhledy na jednu věc, já si myslím že ten můj je bližší tvůrcům těch jazyků....
Já Ti Tvůj náhled neberu. Já jen tvrdím, že z objektivních hledisek (jako je např. délka kódu u často používaných konstrukcí, odolnost vůči programátorským chybám) není návrh konstrukce switch v jazyce C dobrý. S mým, tvým či K&R pohledem na věc nemá mé tvrzení žádnou souvislost, jak délka kódu, tak frekvence chyb daného typu je veličina objektivně měřitelná :-)
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Radovan 08. 11. 2010, 20:45:17
V tom přirovnání jde o to, že se ví, že se jezdí vpravo a tak se tomu to auto přizpůsobí.
A víš také odkdy se jezdí vpravo? Zase tak dlouho to není, sotva dvě staletí. Prozkoumej pár starých kočárů a povozů, a spočítej si kolik z nich má brzdu (tu kliku) na pravé a kolik na levé straně. A nebo lépe, proč je spojka v autě vlevo i u aut s levostranným řízením, když to u spousty z nich způsobovalo konstrukční problémy? Pokud chceš pochopit proč to tak je, budeš muset prostudovat konstrukci a používání dopravních prostředků za poslední tři tisíce let, s obzvláštním důrazem na období 1890 až 1920.
Pak se vykašleš na nějakou statistiku (která navíc za pár let nemusí platit) a dojde ti, že v C je switch udělaný tak jak je právě proto, že takhle to funguje v assembleru, ne kvůli nějaké četnosti použití, ale proto že před padesáti lety si ti lidé co ho používali potřebovali usnadnit práci a udělali to podle toho co znali a dělali každý den. Vyšší jazyky vycházející z jiných principů potom používají i jiný způsob větvení. Když se ti ten nízkoúrovňový v C tolik nelíbí, proč nepoužíváš třeba C#? Tam je to takhle:

case 2:
    Console.WriteLine("n is an even number.");
    goto case 3;
case 3:

Nedědí snad C# céčkovou syntaxi stejně jako Java? Přesto je to v něm opačně, jako v Pascalu. Ale abych se přiznal, pouhý pohled na tenhle kód mi způsobuje nepříjemné pocity v oblasti žaludku >:( Možná že mix BASICu, Pascalu a C++ na závěr přišlehaný Javou je na mě opravdu moc, asi jako když pejsek s kočičkou pekli dort. A ještě navíc NUTÍ programátora použít goto v místě, kde v C není potřeba napsat nic, to je fakt vylepšení! Ten původní céčkový způsob mi prostě připadá mnohem přirozenější (možná právě proto že je bližší strojům), a to jsem v assembleru nikdy nic pořádného nenaprogramoval. Pokud tedy nepočítám C jako maskovaný assembler, což skutečně je.
A ještě navrch se zeptám, existuje pro C# nějaký nástroj, který odhalí ve switchi zapomenuté goto? To je přece úplně stejná "chyba" jako v C zapomenutý break! To se prostě nedá poznat, protože by musel upozornit na všechny větve které by podle něj mohly být opomenuté, a těch by tam mohl být také pěkných pár tisíc. Prostě jako když při if(a=b) dostanu warning, zatímco při if((a=b)) je všechno v pořádku.
Citace
Právě proto, že break NEZNAMENÁ vypadni ven z příkazu, ale z cyklu.
while() příkaz; nebo while() {složený příkaz}
for() příkaz; nebo for() {složený příkaz}
switch() {složený příkaz}
Tenhle "příkaz" jsem měl na mysli.
Citace
A jestli chceš svůj názor obhajovat prasečinama jako dvoupísmenej název identifikátoru...
On to l1 byl vtip, ale kolik těch labelů chceš v jedné funkci mít? Jeden, dva... I to je víc než dost, a stojí za to se zamyslet jestli to bez nich opravdu nešlo... Navíc, když tam budu mít jeden label a pět goto tak jsem stejně pořád v plusu.
Jo, asi jsem toho opravdu nenaprogramoval dost abych se bez goto neobešel, to je pravda, párkrát jsem si radši vypomohl nějakým flagem, což ale zase vede ke složitější podmínce v několika vnořených cyklech, netroufnu si rozhodnout jestli je to lepší nebo přehlednější než kdybych tam prostě plácnul goto. Každopádně je to ve výsledku delší a pomalejší, ale není tam to strašně škodlivé goto ;)
Něco jiného by bylo, kdyby šlo použít třeba break 3 pro přerušení tří cyklů najednou, případně continue 3 s podobným významem. To bych si dovedl představit jak používat, i když si myslím že by to zase bylo méně přehledné než prosté goto label.

P.S. Pod většinu toho co jsem tu od tebe četl bych se klidně podepsal, a nerad bych se s tebou o tuhle jednu věc nekonečně hádal jako s Laelem, se kterým jsem se naopak neshodl téměř na ničem, takže prostě končím tím jak to chápu a řeším já, abych tam ten break nezapomněl napsat:

1. Každá větev switche je ukončená breakem, stejně jako každá funkce je ukončená returnem.
2. Pokud nechci aby větev skončila nebo funkce vracela hodnotu, tak tam break nebo return nepíšu.

Toť vše, prostě to ve všech případech beru stejně, podle té unixové jednotnosti, tak jako tebou jinde jmenovaný design "vše je soubor".
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Logik 09. 11. 2010, 01:49:24
Ad historie. V asembleru je to natolik odlišné, že by to problémy nezpůsobovalo. Dokladem toho je, že co vím žádný jazyk se syntaxí neodvozenou od C (ať už jde o pascal, python, ruby, plsql, visual basic, fox pro, eifell, perl, bash, C#) nemá příkaz switch s automatickým propadem do další větve. IMHO už to zcela jasně ukazuje, že ta konstrukce v C nebyla volena štastně, když to všichni ostatní mají jinak.

Citace
Prostě jako když při if(a=b) dostanu warning, zatímco při if((a=b)) je všechno v pořádku.
Ale vždyť to je přesně ono. Normální je nedělal v ifu přiřazení a jazyk by měl být navržen tak, aby varoval, když děláš něco nestandardního. Ty dvojité závorky tu slouží jako upozornění: ano, vím, že dělám něco nestandardního, něco, co na daném místě není běžné a co může být typo chyba či opomenutí. A proto to explicitně označuji. Pomíjím tedy možnost, že člověk udělá dvojité závorky jen tak, to je u mě čuně a toho nezachrání nic :-)

 Stejně tak se v majoritním případě používá case jako rozskok, pokračování do dalšího case je minoritní, a často se dělá chyba v opomenutí break. Proto by měl být jazyk, stejně jako u = a == v ifu, navržen tak, aby standardní zápis vyhodil alespoň varování a umožnil ho explicitně označit za správný (např. formulkou continue, nebo jako v C# goto case x, i když to se mi také moc nelíbí).


Ad C#: Právě proto, že zapomenuté breaky způsobovaly chyby, není v C# legální na konec neuvést nic, vždy tam musí být goto nebo break.  Asi jsi se tedy chtěl zeptat, jak se to dělá v perlu, kde lze explicitně přeskočit na další pomocí continue a break je implicitní. No právě pro lidi je přirozené, že větev dalším case končí, takže jim nedělá problém označit explicitně přeskok na jinou větev. Stejně jako by dělali chyby, pokud nějaký jazyk by zaved prioritu sčítání před násobením, tak dělají chyby, když C zavedlo prioritu pokračování před ukončením. Prostě jedna možnost přirozená je, druhá nikoli. 

Co se týče C#, ideální to imho také není, je to ale výsledek toho, že se návrháři C# (asi správně) neodvážili u C like jazyka změnit smysl příkazu. Neboť to by způsobilo přesně to, v čem je problém switche v C: člověk automaticky napíše něco co čeká, že se chová tak a ono by se to chovalo jinak.
Zas si ale byli vědomy velké problematičnosti toho konstruktu a tedy přidali bezpečností obstrukci. To, že v podstatě explicitně oproti Javě a C nutí člověka psát "zbytečnosti" jen více jen dokládá, že to problém je. Takže C# řešení je z nouze ctnost.


Citace
Tenhle "příkaz" jsem měl na mysli.
 
Argumentuješ kruhem. Pokud se bavíme o správné sémantice "break" v konstruktu switch, nemůžeš argumentovat sémantikou break v konstruktu switch. A pokud vezmeš všechny ostatní případy, kdy je break použito, vždy jde o cyklus. Naopak ve všech ostatních "necyklových" konstrukcích (if, funkce, blok) příkaz break použít nelze.
  Z toho plyne, že break v příkazu switch má nestandardní a posunutou sémantiku (to dokládá i to, že jedině ve switch nelze užít continue na místě break.) Proto nevidím žádný problém, kdyby ta sémantika byla posunuta trochu jinak.
  Ono právě to, že nejde jednoduše říct - např. break ukončuje cyklus, ale musíš dát seznam příkazů, které ukončuje, ukazuje na to, že není definováno "konzistentně" (rozuměj u všech příkazů stejně - viz if).
 
Citace
1. Každá větev switche je ukončená breakem, stejně jako každá funkce je ukončená returnem.
2. Pokud nechci aby větev skončila nebo funkce vracela hodnotu, tak tam break nebo return nepíšu.
Rozdíl je ten, že případ 2, pokud nastane omylem, pro funkci kompilátor odchytí (function should return value), ale pro switch nikoli. Pokud se opravdu umíš pohlídat, že nikdy nezapomenš break, tak Tě to ctí. Z praxe se ale ukazuje, že ostatní s tím problémy mají :-)... jak ukazuje i úvodní dotaz v threadu (admini, nejde tu polemiku přesunout do samostatného threadu? :-)).
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Radovan 09. 11. 2010, 04:38:28
Ad historie. V asembleru je to natolik odlišné...
C:

switch()
{
case 1: příkaz 1
        příkaz 2
case 2: příkaz 3
        příkaz 4
        break
case 3: příkaz 5
        příkaz 6
}
příkaz 7

ASM:

jumptable...
label1: instrukce 1
        instrukce 2
label2: instrukce 3
        instrukce 4
        jmp dolu
label3: instrukce 5
        instrukce 6
dolu:   instrukce 7

Neřekl bych že se to nějak výrazně liší ;-) Koukl jsem se na ten Perl (zkráceně z Wikipedie):

use feature 'switch';
given ($foo) {
    when (undef) {...;}
    when ("foo") {...;}
    when ([1,3,5,7,9]) {...; continue; # Fall through}
    when ($_ < 100) {...;}
    when (\&complicated_check) {...;}
    default {...;}
}

Jo, takhle to beru, však jsem to s těmi závorkami navrhoval. Perl neznám, takže mě těší že nejsem první koho to napadlo, aspoň vidím že to nebyla blbost. Akorát u toho continue mi to úplně nesedí, takhle mi není jasné jestli se při něm vyhodnotí ($_ < 100) jak bych očekával, nebo se automaticky přeskočí rovnou do další větve. Předpokládám že se ta podmínka vyhodnotí, protože v Perlu to funguje jako pomalá série ifů a nezávislých bloků, zatímco v C je to rychlá jumptable do jednoho bloku.
Citace
Normální je nedělal v ifu přiřazení...
Jenže v C to normální je (přiřazení je výraz), a s tím spousta dalších podivných věcí. Pro mě ty vnitřní závorky neznamenají nic nestandardního, ale jasně říkají překladači že je mezi nimi výraz, kterého výsledek má vyhodnotit. No, stejně je tam obvykle dopíšu až po tom warningu :P Jenže ten také dostanu jen když ho chci a zadám -Wall, jinak je to kompilátoru úplně fuk. (Osobně jedině -Wall -pedantic, nikdy jinak.)
Citace
Takže C# řešení je z nouze ctnost.
Spíš splácanina páté přes deváté, tedy maloměkký standard. Navíc to nejsou jen obstrukce ale navrch spousta omezení, co všechno se tam nesmí použít, prostě ti hodně bere ale nedává nic výměnou. Psát break i v poslední větvi se doporučuje v každé učebnici C pro začátečníky, asi proto aby si vtloukli do hlavy že v tomhle jazyce se prostě psát musí.
Osobně bych spíš uvítal, kdyby překladač ohlídal neinicializované proměnné, protože u tohohle žádnou hlášku nedostanu:

int a,b;

if((a=b))
    printf("OK\n");

Přitom je to očividná díra, nebylo by lepší kdyby se KAŽDÉ proměnné už při deklaraci MUSELA přiřadit nějaká hodnota? Možná že by se tím zabránilo víc chybám a vráskám než implicitním breakem, každopádně jsem s tím už párkrát narazil, zatímco break jsem fakt nezapomněl. Napsal jsem ho i tam kde jsem ho nechtěl ;D
Mimochodem, třeba BASIC, až na ty nejprimitivnější verze, implicitně přiřadí každé nové proměnné nulu, což také není dobrá vlastnost. Jak se říká, nikdy nespoléhej na to co by měl udělat někdo jiný, jako s tím breakem.
Citace
Argumentuješ kruhem.
 
Nikoliv, v kruhu se motáme. Viz ten Perl.
Takže výsledek je stále tentýž, C a pár dalších prehistorických jazyků má implicitní fall-through, modernější jazyky ho nemají. Tenkrát se to tak dělalo (a každý očekával že to tak bude fungovat), pokud musíš za každou cenu pořád jezdit Pragovkou z roku 1937, nediv se že sedíš u chodníku a řadíš levou rukou. Spíš buď ještě rád že nemusíš řadit pákou zvenku auta, jako o dalších dvacet let dříve.
Název: Re: Audit zdrojového kódu v C (prípadne v C++)
Přispěvatel: Logik 09. 11. 2010, 12:34:49
Citace
Jenže v C to normální je (přiřazení je výraz)\
Na daném místě to je z 95% chyba. Proto má před tou chybou jazyk chránit. Je úplně jedno, že je to normální C výraz. Je to stejné, jako některé konstruktory v C++ se označují jako explicit, aby se odchytila náhodná nechtěná konverze. Taky by to šlo i bez toho, ale pokud ta konverze běžně nemá smysl, je chyba ten konstruktor tak neoznačit.

Jinak co vím, tak snad kompilátory varují před použitím undefined proměnných, aspoň ty, co jsem používal já... ale je fakt, že u GCC teď nevim....

Citace
Takže výsledek je stále tentýž, C a pár dalších prehistorických jazyků má implicitní fall-through, modernější jazyky ho nemají.
No ale to v podstatě znamená, že návrh C nebyl dobrý a proto ho v novějších jazycích změnili, ne? Kdyby to bylo dobře, tak by to v modernějších (no, modernějších, takový pascal je z stejné doby jako C a nemá to :-)) jazycích zůstalo stejně, ne?

PS: jinak v C a Assembleru vidím dosti podstatný rozdíl: v asm je to jumptable (čili seznam návěstí), v C je to switch (volba). Takže když si to přečte laik nezatížený programováním, tak u jumpu si řekne - program skočí tam a pokračuje. U volby si řekne - program zvolí jednu z možností. V C je to prostě neintuitivní - a to vede k chybám.
Jumptable taky funguje jinak, jde prostě o indexovaný skok, v C specifikuješ hodnoty.
Takovej pascal - vzniknul plus mínus stejně - to má ještě o něco podobnější asm a má to bez problémů jinak.