Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C:n syntaksiristiriitoja

Sivun loppuun

JoinTuanJanohon [17.10.2006 12:29:10]

#

Perin harvoin, mutta kuitenkin, C:n kanssa törmää kummallisiin syntaksiristiriitoihin. Esimerkiksi oheinen koodinpätkä on syntaksiltaan oikein, mutta ei vain käänny:

double MunienSuhde(double *munien_maara_eaa, double *munien_maara_jaa)
{
   return 1-*munien_maara_eaa/*munien_maara_jaa;
}

Mitähän muita kummallisuuksia C sisällään pitää?

Tzaeru [17.10.2006 13:23:59]

#

Eipä ole syntaksiltaan oikein ei :) Huomaa, että '/*' on kommentointi, jolla onnistut mallikkaasti kommentoimaan puolet tuostakin rivistä hus veks.

ZcMander [17.10.2006 17:01:05]

#

Niin, huomaahan sen jo koodin värityksestä.

A-P [17.10.2006 17:06:11]

#

Hetken aikaa meni hahmottaa tuota. Käsitin aluksi asterisin kertomerkiksi enkä osoittimeksi. No, joo. Kun käänsin tuon komennolla gcc -Wall -ansi -pedantic esim.c (pelkkä -Wall olisi riittänyt), kääntäjä kertoi heti saman virheen kuin Tzaerukin, eli kommentti alkaa muttei pääty.

Tuossa voisi muutenkin käyttää sulkuja hahmottamisen apuna. Kun rivi kiroitetaan seuraavalla tavalla:

return 1-(*munien_maara_eaa/(*munien_maara_jaa);

Kääntäjä ja koodin ulkopuolinen lukija ovat tyytyväisiä.

Muutenkin osoitinparametrien käyttö on turhaa sillä niiden ominaisuutta ei hyödynnetä. Ja tässä tuskin tarvitsee yrittää saavuttaa minkäänlaista tilansäästö/nopeusetua. Kuseista funktiota kutsuttaessa sen parametrit on annettava muuttujina.

Lisäys: koodi muuten värittyy ensimmäisessä viestissä väärin. Kommentti alkaa vasta jälkimmäisen asteriskin jälkeen, mutta jo ensimmäisen asteriskin jälkeen rivi värjääntyy. (mod. nyt väritys näkyy oikein)

KeKimmo [17.10.2006 17:06:45]

#

Mistä tiedät tuon olevan syntaksiltaan oikein?

thefox [17.10.2006 17:17:31]

#

Ei se ole syntaksiltaan oikein, jos se ei käänny :)

Hauska ongelma sinänsä, että siitä pääsee eroon ihan vain lisäämällä joukkoon muutaman whitespacen, eli

return 1 - *munien_maara_eaa / *munien_maara_jaa;

JoinTuanJanohon [17.10.2006 17:20:30]

#

ZcMander kirjoitti:

Niin, huomaahan sen jo koodin värityksestä.

Ei kysymys ollut värityksestä, vaan jakolaskusta osoittimen osoittamalla arvolla. Vieläkin perimmäisempi kysymys on, että C:ssä operaatiot, ja niiden moninaiset kombinaatiot ovat ahkerasti käytetty. Miksi kuitenkin kommentin merkiksi on valittu suoraan operaatiokombinaatio, joka siis tarkoittaa jakolaskua osoittimen osoittamalla arvolla. Ehkä vastaus on sitten se, että ANSI-komitea on mietintämyssyt päissään päätynyt johtopäätökseen, että jakolasku osoittimen osoittamalla arvolla on turha ja tarpeeton muutoin C:n monipuolisissa operaatioissa.

ezuli [17.10.2006 18:15:51]

#

Jospa samainen komitea on päättänyt, että standardin mukaan operaattorin molemmin puolin lisätään välilyönti.

Schedler [18.10.2006 23:21:51]

#

Eipä C:ssä tai C++:ssa ole paljoakaan painoarvoa annettu koodin luettavuudelle, joten yllätyksiä on taatusti tiedossa. Hyviä esimerkkejä löytyy vuosittaisesta obfuscated C-kisasta :)

Joskus LINT-työkalun Bug of the Week-palstalla oli esimerkki tapauksesta jossa normaalilta näyttävä koodi ei toiminut; syynä oli se, että tietty merkkijono tulkittiin kääntäjän toimesta (aivan oikein) trigrafiksi. Tämä aiheutti ohjelman väärän toiminnan.

Trigrafit ovat siis C:n historian reliikkejä ajoilta jolloin ei voitu olettaa merkistöstä löytyvän. Esimerkki alla:

??=include <stdio.h>

int main(void) ??<
   char str??(5??) = ??<'F', 'o', 'o', '??/n', '??/0'??>;
   printf("%s", str);
   return 0;
??>

Onneksi noita ei enää tarvitse missään käyttää (gcc:kin vaatii -trigraphs -option)...

Yksi omituisuus C:ssä on myös oktaalilukujen merkintätapa, joka toisinaan on aiheuttanut bugeja:

int coeffs[] = {100, 304, 121, 230,
                014, 554, 021, 200};

Metabolix [19.10.2006 01:26:21]

#

Itse joskus pähkäilin, miksei toimi C++:n määrittely
vector<vector<int>> vec2d;
Vikana tässä on siis se, että >> on operaattori. Välin lisääminen tuohonkin auttaa.

Minusta on aivan turha valittaa kielen "tyhmistä" tavoista. Jos ei miellytä, tee itse parempi. Tyhmää se on valittaa, että syntaksi on huono, jos kyse vain on siitä, ettei itse sitä osaa.

Pekka Karjalainen [19.10.2006 14:25:30]

#

JTJ kirjoitti:

Mitähän muita kummallisuuksia C sisällään pitää?

Tämä kääntyy gcc -Wall:in kanssa ilman varoituksia.

#include <stdio.h>

int main (O_o)
{
  int i;
  int f[10]; 0 <: f :> = 1; 1 <: f :> = 1;
  for (i=0;i<8;++i) {
    int apu = i;
    int summa = apu++ <: f :>;
    summa += apu++ <: f :>;
    apu <: f :> = summa;
  }

  for (i=0; i<10; ++i)
    printf ("%2d\n", i <: f :>);

  return 0;
}

Näinnikkään:

lastu30:(~/temp)(68)% gcc testi.c -o testi -Wall
lastu30:(~/temp)(69)% ./testi
 1
 1
 2
 3
 5
 8
13
21
34
55

Mitähän sille pitäisi sanoa, että se varoittasi tuosta O_o:sta puuttuvasti intistä? Joku pedanttinen ANSI -moodi, joka ei hyväksy implisiittisiä int-deklaraatioita?

Tarjosin tätä siis kummallisuutena. Joillekin varmasti ihan vanha hattu (old hat), mutta kun kerran tuli puhe, niin tämä on hyvä jatko keskustelulle. (edit: avaus -> jatko..)

Suosikkikummallisuuteni on kyllä se, että voi sanoa &argc ja sitä kautta käsitellä ohjelman pinoa ajon aikana. Kun sen yhdistää longjmp-kutsuihin, niin johan alkaa tapahtua. Voi esim. saada potkut, jos töissä tarjoaa siitä tuotantokoodina.

Tässä on C++-esimerkki, joka selventää asiaa, jos se on selvennettävissä. En löytänyt tähän hätään C-versiota, joka kuitenkin on saman tapainen perustoteutukseltaan. Tiedosto on PDF.

High-Level Nondeterministic Abstractions in C++
http://www.cs.brown.edu/people/pvh/search.pdf

Se ei pelkästään outo, vaan myös hyödyllinen vekotin. Hieman saattaa olla turvaton olo sitä käyttäessä, mutta siitä en ota vastuuta.

P.S. Minusta on hyvä, että valitetaan kielen "tyhmistä" tavoista. Aina on hyvä kiinnittää huomiota mahdollisiin ongelmakohtiin. Toki vastaus on usein sangen tyly: niin vain on, totu hommaan. Mutta valittakaa ihmeessä, koska on parempi tietää mahdollisista virheistä etukäteen aina kun mahdollista, kuten tuon >>-operaattorin tapauksessa. Eihän tuota tyhmäpäinen ihminen kuten minä arvaa :-)

A-P [19.10.2006 14:46:36]

#

Kopeekka kirjoitti:

Tämä kääntyy gcc -Wall:in kanssa ilman varoituksia.

#include <stdio.h>

int main (O_o)
{
  int i;
  int f[10]; 0 <: f :> = 1; 1 <: f :> = 1;
  for (i=0;i<8;++i) {
    int apu = i;
    int summa = apu++ <: f :>;
    summa += apu++ <: f :>;
    apu <: f :> = summa;
  }

  for (i=0; i<10; ++i)
    printf ("%2d\n", i <: f :>);

  return 0;
}

Ja kuinka se kääntyy...

gcc -Wall -std=c89 testi.c
testi.c: In function 'main':
testi.c:6: error: expected expression before ':' token
testi.c:6: error: expected expression before ':' token
testi.c:9: error: expected expression before ':' token
testi.c:10: error: expected expression before ':' token
testi.c:11: error: expected expression before ':' token
testi.c:15: error: expected expression before ':' token
testi.c:6: warning: unused variable 'f'

ja

cc -Wall -std=c99 testi.c
testi.c: In function 'main':
testi.c:4: warning: type of 'O_o' defaults to 'int'

Eli koodi ei ole C89- eikä C99-standardin mukaista. Kääntäjänä oli GCC versio 4.1.0.

Pekka Karjalainen [19.10.2006 18:16:58]

#

ISO C:n Amendment 1:n token respellingit (olisivatko merkkien vaihtoehtoiset kirjoittamistavat suomeksi?) eivät kuulu 89-standardiin, joten ei ole ihme, että <: ja :> eivät sille kelpaa. 99-standardin mukaan se kääntyy, vaikka sen toimintaa ei taidakaan olla määritelty, koska siinä on ei-standardi main-funktio. Siinä se muistuttaa kaikkia muita minun kirjoittamiani C-ohjelmia, joiden toimintaa ei myöskään ole tarkasti määritelty.

Ehkäpä eräs pointtini oli se, että pelkkä gcc -Wall ei sanonut näistä asioista mitään. Kuka tietää? En tunnusta enää mitään, vaikka uhattaisiin C:n preprosessorilla.

A-P [19.10.2006 18:53:09]

#

Kopeekka kirjoitti:

Ehkäpä eräs pointtini oli se, että pelkkä gcc -Wall ei sanonut näistä asioista mitään. Kuka tietää? En tunnusta enää mitään, vaikka uhattaisiin C:n preprosessorilla.

Ehkäpä yksi ongelma tässä keskustelussa on se, että mitä tarkoitetaan C:llä. Syy sille, että kääntäessäsi komennolla gcc -Wall tiedosto.c et käännä standardia C-kieltä vaan GNU C:tä. C ja GNU C eivät välttämättä ole keskenään yhteensopivia.

thefox [19.10.2006 19:22:34]

#

Metabolix kirjoitti:

Itse joskus pähkäilin, miksei toimi C++:n määrittely
vector<vector<int>> vec2d;
Vikana tässä on siis se, että >> on operaattori. Välin lisääminen tuohonkin auttaa.

Mmm, aika hauska, nimittäin MSVC2005:stä tuo menee läpi tuollaisenaan ilman varoituksia. GCC näköjään antaa virheilmoituksen error: '>>' should be '> >' within a nested template argument list, mikä antaisi olettaa että se on oikeassa, koska sinne on lisätty vielä erillinen virheilmoitus juuri tuota tapausta varten.

EDIT: selvennystä

Schedler [19.10.2006 21:56:44]

#

Niille joita oikeasti kiinnostaa C:n omituisuudet ja määrittelemättömät asiat suosittelen lämpimästi Les Hattonin kirjaa "Safer C".

C:stä on yritetty monesti rakentaa ns. turvallisia subsettejä, siinä kuitenkaan onnistumatta. Hyvänä esimerkkinä MISRA C, joka määrittelynä itsessään on niin epämääräinen että kolmella eri työkalulla saa eri tulokset koodin MISRA C:n mukaisuudesta :)


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta