Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C: Ohjelmat eivät toimi

Sivun loppuun

dartvaneri [03.08.2012 12:59:29]

#

Hei!
Latasin Dev-c++ kääntäjän. Sitten loin tiedoston nimeltään test.c. Kääntäjä toimii muuten hyvin, mutta kun klikkaa "run", niin siihen tullee mustaikkuna, jossa lukee vain "Paina jotain näppäintä jatkaaksesi", eikä "Hello World!". Missähän mahtaa olla vika?

Koodi:

#include <stdio.h>

int main(void) {
    printf("Hello World!\n");
    system("PAUSE");
    return 0;
}

Metabolix [03.08.2012 14:28:36]

#

Olet varmaan tehnyt jotain väärin. Toivottavasti loit projektin etkä vain kooditiedostoa.

En kyllä usko, että monikaan on kiinnostunut ikivanhan ja betaversioksi jääneen IDE:n ongelmien selvittelystä. Lataa jokin nykyaikaisempi kehitysympäristö, vinkkejä on oppaassa.

vesikuusi [04.08.2012 14:31:59]

#

Samaa mieltä Metabolixin kanssa, mutta itse kokeilisin vielä fflush-funktiota ihan varmuuden vuoksi.

Lisäys:

Eli ennen pausea:

fflush (stdout);

dartvaneri [04.08.2012 18:49:46]

#

Okei, asensimpa sitten huvikseni ton visual c++ 2010, toiminut ihan hyvin tähän asti. Tosin pienoinen ongelma tuli, kun yritin tehdä koodia, joka kääntää merkkijonon toistepäin.

#include "stdio.h"
#include "string.h"

int main(void){
    int lenght = 0;
    char string[30] = "example";
    char string2[30];
    char string3[30];
    int i;

	lenght = strlen(string);
	for(i = 0; i < lenght; i++){
		strcpy(string2, string[i]);
		strcat(string2, string3);
		strcpy(string3, string2);
	}
    return 0;
}

Elikkäs olis tarkoitus saada toi merkki jono string käännettyä toiste päin.
Olen myös lisännyt noi kirjastot header tiedostoiksi projektiin.

Heittää tollasta:

Unhandled exception at 0x1027d2d1 (msvcr100d.dll) in test.c: 0xC0000005: Access violation reading location 0x00000031.

Metabolix [04.08.2012 19:22:23]

#

Koodissasi on aivan alkeellisia virheitä: et alusta muuttujia, ja annat yhdessä kohdassa strcpy-funktiolle väärää tyyppiä olevan argumentin. Laita kääntäjän asetuksista kaikki mahdolliset varoitukset käyttöön ja lue ne ja korjaa koodisi.

jlaire [04.08.2012 19:38:26]

#

Lisäksi muuttujan lenght määrittely puuttuu. Kopioi koodi jatkossa juuri siinä muodossa missä olet itse sen kääntänyt.

dartvaneri [04.08.2012 19:41:01]

#

Juu olen kyllä määritellyt sen, mutta kun kopion, en muistanu kopioida noita includeja ja mainia, ja kun lisäsin ne käsin, sieltä jäi näköjään tuo lenght muuttujan alustus pois.

Lisäys:

Metabolix kirjoitti:

ja annat yhdessä kohdassa strcpy-funktiolle väärää tyyppiä olevan argumentin.

tarkoitatko tota

string[i]

?

Epsilon [04.08.2012 19:46:33]

#

Morjesta,

Pari huomiota:

Jos haluat kääntää merkkijonon, niin se tapahtuisi esimerkkisi kontekstissa vaikkapa näin:

int length = strlen(string);
for(int i = 0; i < length; ++i)
    string2[i] = string[length-1-i];
string2[length] = 0; //Lisätään null-terminaattori

dartvaneri [04.08.2012 20:00:58]

#

Mitenkäs sitten jos merkkijono alkaa numerolla yksi?

reino [04.08.2012 21:41:07]

#

Jos käytät C++:ssaa niin mikset käytä sen String objektia ja tee reversausta standardifunktioilla

Epsilon kirjoitti:

  • Noita standardikirjaston headereita ei pitäisi lisätä projektiin käsipelissä. Käännösympäristö tulisi olla konffattu siten, että kääntäjälle on annettu polku, jonka takaa headerit löytyvät.

Mielestäni on kyllä hyvä tapa includata noi headerit jos vaikka joskus et käytä IDE:tä.

Ps. Googleta!

dartvaneri [04.08.2012 22:30:04]

#

Saanu nyt tästä keskustelusta sellaisen käsityksen, että putkan c++ oppaassa on jotain hyödyllisiä tietoja c:tä opiskelevalle?

Metabolix [04.08.2012 22:43:52]

#

dartvaneri kirjoitti:

Mitenkäs sitten jos merkkijono alkaa numerolla yksi?

Jos kuvittelet, että merkkijonon sisältö jotenkin vaikuttaisi tässä tapauksessa siihen, onko koodisi väärin vai oikein, olet todella pahasti hakoteillä. Kaikki luetellut viat ovat siinä silti.

Epsilon kirjoitti:

Lisäksi annat strcpy -funktiolle väärää tyyppiä olevan parametrin, joten siitäkin kääntäjän pitäisi huutaa.

C:ssä muunnos char-arvosta char*-arvoksi onnistuu automaattisesti, mikä onkin minusta jo hyvä syy käyttää jotain muuta kieltä opiskeluvaiheessa, jos tyyppien ymmärtämisessä on jotain vaikeaa.

reino kirjoitti:

Jos käytät C++:ssaa – –

Missään vaiheessa ei ole ollut puhe C++:n käytöstä. Otsikossakin lukee C.

reino kirjoitti:

Mielestäni on kyllä hyvä tapa includata noi headerit jos vaikka joskus et käytä IDE:tä.

Ei kukaan ole käsittääkseni sanonut, ettei otsikoita pitäisi olla ollenkaan. Epsilon varmaan tarkoittaa, että standardikirjastoa käytettäessä olisi parasta olla <otsikko> eikä "otsikko", ja tämä on aivan totta.

dartvaneri kirjoitti:

Saanu nyt tästä keskustelusta sellaisen käsityksen, että putkan c++ oppaassa on jotain hyödyllisiä tietoja c:tä opiskelevalle?

Ensimmäisistä kuudesta osasta suurin osa pätee suoraan myös C:hen, mutta jos et halua lukea C++-opasta, lukisit edes jotain C-opasta. Toisaalta voi myös miettiä, onko mitään varsinaista syytä opetella juuri C:tä; useimpiin tarkoituksiin on käytännöllisempiäkin kieliä.

Epsilon [04.08.2012 23:45:24]

#

Metabolix kirjoitti:

dartvaneri kirjoitti:

Mitenkäs sitten jos merkkijono alkaa numerolla yksi?

Jos kuvittelet, että merkkijonon sisältö jotenkin vaikuttaisi tässä tapauksessa siihen, onko koodisi väärin vai oikein, olet todella pahasti hakoteillä. Kaikki luetellut viat ovat siinä silti.

Taisinpa itse sekoittaa pakkaa puhumalla merkkijonon sisällöstä. Pahoittelen. Jotenkin vain vaikutti siltä, että rapotoitu virheilmoitus ei ollut ihan täysin yhteensopiva tuon esitetyn koodinpätkän kanssa ja menin sitten sitä kommentoimaan. Mutta esitinkin epäilyni vain arveluna enkä mitenkään varmana tietona.

Metabolix kirjoitti:

Epsilon kirjoitti:

Lisäksi annat strcpy -funktiolle väärää tyyppiä olevan parametrin, joten siitäkin kääntäjän pitäisi huutaa.

C:ssä muunnos char-arvosta char*-arvoksi onnistuu automaattisesti, mikä onkin minusta jo hyvä syy käyttää jotain muuta kieltä opiskeluvaiheessa, jos tyyppien ymmärtämisessä on jotain vaikeaa.

Totta. Tietysti tyyppimuunnos on automaattinen, kun puhe on C:stä, joten alkuperäisessä ohjelmalistauksessa kääntymisen estäviä ongelmia on ainoastaan muuttujan lenght esittelyn puuttuminen.

Metabolix kirjoitti:

reino kirjoitti:

Mielestäni on kyllä hyvä tapa includata noi headerit jos vaikka joskus et käytä IDE:tä.

Ei kukaan ole käsittääkseni sanonut, ettei otsikoita pitäisi olla ollenkaan. Epsilon varmaan tarkoittaa, että standardikirjastoa käytettäessä olisi parasta olla <otsikko> eikä "otsikko", ja tämä on aivan totta.

Kyllä, tuota tarkoitin.

dartvaneri [05.08.2012 00:08:25]

#

Metabolix kirjoitti:

..mikä onkin minusta jo hyvä syy käyttää jotain muuta kieltä opiskeluvaiheessa, jos tyyppien ymmärtämisessä on jotain vaikeaa.

Toisaalta voi myös miettiä, onko mitään varsinaista syytä opetella juuri C:tä; useimpiin tarkoituksiin on käytännöllisempiäkin kieliä.

Se yksi syy, minkä takia minäkin opiskelen c:tä, on mikroprosessorit, niiden ohjelmointiin tarvitsee c:tä. Ja vaikka sitä meillä koulussakin opetetaan, niin en näe mitään syytä olla opiskelematta sitä lomallakaan, sillä vaikka voi sanoa, etten osaa c:tä ollenkaan, osaan sitä kuitenkin enemmän, kuin opettajamme.

Lisäys:

Ilmeisesti en saa käyttää tätäkään funktiota näin:

if(!strcmp(binNum2[i], "1")){

Eli siis tuota

 binNum2[i]

merkkiä en voi käyttää ton vertailu funktion sisällä. Jos näin on, niin miten kyseinen pitäisi toteuttaa?

Epsilon [05.08.2012 10:41:21]

#

dartvaneri kirjoitti:

Lisäys:

Ilmeisesti en saa käyttää tätäkään funktiota näin:

if(!strcmp(binNum2[i], "1")){

Eli siis tuota

 binNum2[i]

merkkiä en voi käyttää ton vertailu funktion sisällä. Jos näin on, niin miten kyseinen pitäisi toteuttaa?

Lyhyt selitys tässä, lue tosiaan tarkemmin jostain C (tai C++) -oppaasta:

C:ssä tyyppi char esittää kokonaislukua, joka on standardin mukaan määritelty vähintään 8-bitin, eli yhden tavun, mittaiseksi etumerkilliseksi tai etumerkittömäksi. Useimmissa kääntäjissä char on toteutettu 8-bittisenä etumerkillisenä (signed) kokonaislukuna. Tällöin käytännössä yhteen char-tyyppiseen muuttumaan mahtuu yksi kokonaisluku lukualueelta -128..127. (Tai lukualueelta 0..255, jos kyseessä on unsigned char.)

Perinteisesti noita char-tyyppisiä muuttujia käytetään myös merkkien tallettamiseen. Yhteen char-tyyppiseen muuttujaan talletetaan yksi merkki, joka on koodattu lukuarvoksi välillä -128..127. En totta puhuen juuri nyt muista / jaksa kaivaa, mitä standardi sanoo käytetyistä merkistöistä (ehkä Metabolix tai joku muu vetäisee tiedon ulkomuistista), eli siitä, miten merkit koodataan luvuiksi. Käytännössä ainakin hyvin usein on käytössä sellainen merkistö, jossa lukuarvot 0..127 vastaavat ascii-merkkejä. Eli googleta esimerkiksi "ascii chart" ja saat eteesi taulukon, jossa on kerrottu ascii-merkistön mukaiset vastaavuudet lukuarvojen 0..127 välillä oleville luvuille ja kirjainmerkeille.

Yksittäisiä merkkejä voit tallettaa char-tyyppisiin muuttujiin seuraavasti:

char c1;
char c2
c1 = 'A'; //Asettaa merkin arvoksi lukuarvon 65, joka vastaa merkkiä A (ks. ascii-taulukko)
c2 = 66; //Asettaa merkin arvoksi lukuarvon 66, joka vastaa merkkiä B.

Koska merkit char-tyypin muuttujat ovat tavallisia 8-bittisiä kokonaislukuja, voit vertailla niitä kuten mitä tahansa integer-muuttujia. Eli edellisen esimerkin perustella voisit tehdä vaikka seuraavia vertailuita:

c1 == 'A' //Arvoksi tulee tosi
c1 == 65  //Arvoksi tulee tosi
c1 == c2  //Arvoksi tulee epätosi
c2 == 66  //Arvoksi tulee tosi
c2 == 'B' //Arvoksi tulee tosi

C:n (standardin mukaiset, itse voi tietysti tehdä toisenlaisia) merkkijonot puolestaan muodostuvat muistissa peräkkäin olevista merkeistä. Merkkijono päättyy aina nollamerkkiin (eli sellaiseen char:iin, jonka arvo on 0). Tällaisia merkkijonona käsitellään C:ssä osoittimien kautta. (Jos et tiedä, mikä osoitin on, niin suosittelen edelleen sitä C-manuaalia.) Käytännössä tämä tarkoittaa sitä, että kokonaisten merkkijonojen välittämisen ja kopioinnin sijaan annetaan vain merkkijonon alkuosoite muistissa char * -tyyppisen muuttujan kautta. Tuon osoitteen osoittamassa paikassa muistia tulee sitten olla peräkkäin merkkijonon merkit ja viimeiseksi nollamerkki, jonka havaitsemisen jälkeen merkkijono todetaan päättyneeksi.

Merkkijonoja voit luoda esimerkiksi seuraavasti:

char * str1;
char * str2;
char str3[30] = "123"; //Luo 30 mittaisen arrayn charreja, jonka neljä ensimmäistä merkkiä ovat '1', '2', '3' ja 0, eli varsinainen merkkijono on kolmen merkin mittainen.
str1 = "example"; //str1 osoittaa kääntäjän varaamaan vakiomerkkijonoon "example", joka koostuu sanan merkeistä ja taas lopussa on nollamerkki
str2 = malloc(30); //Varaa 30 tavua muistista, muista vapauttaa free:llä
str2[0] = 'A'; //Asettaa merkkijonon ensimmäiseksi merkiksi 'A'
str2[1] = 0; //Ja loppuun nollamerkki.

Nuo esimerkissäsi käyttämät standardikirjaston str* -funktiot olettavat, että niille annetut char*-parametrit ovat noita standardin mukaisia nollaan päättyviä merkkijonoja. Funktiot on muutenkin tarkoitettu kokonaisten merkkijonojen vertailuun/käsittelyyn, joten niitä ei oikeastaan voi kätevästi käyttää yksittäisten merkkien käsittelyyn, mitä ilmeisesti yrität tehdä. Yksittäisiä merkkejä voi parhaiten käsitellä suoraan pointterin kautta:

char * str = ... //Tämä on asetettu osoittamaan johonkin kelvolliseen merkkijonoon
if(str[2] == 'A') //Vertaa, onko kolmas merkki merkkijonossa 'A'
   tee_jotain();

//Vaihdetaan merkkijonon kahden ensimmäisen merkin paikka
char temp;
temp = str[0];
str[0] = str[1];
str[1] = temp;

Toivottavasti tämä selitys selvensi enemmän kuin sekoitti. =)

Krist [08.08.2012 22:15:11]

#

dartvaneri kirjoitti:

Metabolix kirjoitti:

..mikä onkin minusta jo hyvä syy käyttää jotain muuta kieltä opiskeluvaiheessa, jos tyyppien ymmärtämisessä on jotain vaikeaa.

Toisaalta voi myös miettiä, onko mitään varsinaista syytä opetella juuri C:tä; useimpiin tarkoituksiin on käytännöllisempiäkin kieliä.

Se yksi syy, minkä takia minäkin opiskelen c:tä, on mikroprosessorit, niiden ohjelmointiin tarvitsee c:tä. Ja vaikka sitä meillä koulussakin opetetaan, niin en näe mitään syytä olla opiskelematta sitä lomallakaan, sillä vaikka voi sanoa, etten osaa c:tä ollenkaan, osaan sitä kuitenkin enemmän, kuin opettajamme.

Lisäys:

Ilmeisesti en saa käyttää tätäkään funktiota näin:

if(!strcmp(binNum2[i], "1")){

Eli siis tuota

 binNum2[i]

merkkiä en voi käyttää ton vertailu funktion sisällä. Jos näin on, niin miten kyseinen pitäisi toteuttaa?

int strcmp(const char*, const char*);

eli nyt jos binNum2 on tyyppiä char* eli joku merkkijono, verrataan sitä:
if (!strcmp(binNum2, "1"))

tämä testaa onko KOKO jono sama kuin "1", eli "12" esim ei ole sama asia.

Jos haluat vain testata ensimmäisen merkin samuutta:
if (binNum2[0] == '1')

'merkki' ja "merkki" ero on siinä että esimmäinen on pelkkä yksi vakio kirjain, toinen on kirjain + null-terminointi (eli effektiivisesti kaksikirjaiminen merkkijono).


PS: Potkikaa se opettaja sieltä pois.

Ja vielä tapaus jossa binNum2 on taulukko merkkijonoja: char** binNum;
silloin strcmp(binNum2[ i ], "1") vertaa indeksin monetta merkkijonoa merkkijonoon "1"...

Mitä tässä nyt oli haettu? Muuttamalla hieman asioita voidaan saavuttaa paljon erillaisia asioita, se on yksi C:n vahvuuksista mutta myös heikkouksista/vaikeudesta oppia.


Sivun alkuun

Vastaus

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

Tietoa sivustosta