Kirjoittaja Aihe: C-ohjelma, aloittelija, ongelmia  (Luettu 4155 kertaa)

Regel

  • Käyttäjä
  • Viestejä: 1090
  • Lucid
    • Profiili
C-ohjelma, aloittelija, ongelmia
« : 08.03.08 - klo:14.45 »
Seuraavanlainen "tietokanta" -harjotelma on tekeillä, mutta on joitain ongelmia:
1. Kun ohjelma kysyy ensimmäisen kerran, mitä tehdään, niin ohjeteksti tulee kerran, mutta seuraavalla kierroksella se tulee kahdest(ensimmäinen code-tagi havainnollistaa). Olisiko jotain neuvoja??

2. Miksi kääntäjä valittaa jokaiselle omatekemälleni funktiolle: "tiedosto.c:27: warning: implicit declaration of function ‘poista’"

Koodia: [Valitse]
Lisää(a) | Poista(d) | Näytä kaikki(s) | Lopeta(q)
a
Syötä nimi
 > Regel
Lisää(a) | Poista(d) | Näytä kaikki(s) | Lopeta(q)

Lisää(a) | Poista(d) | Näytä kaikki(s) | Lopeta(q)
a
Syötä nimi
 >

Koodia: [Valitse]
#include <stdio.h>
#include <string.h>
char names[50][20];
int n=0;
char valinta[1];
void main()
{

puts("Lisää(a) | Poista(d) | Näytä kaikki(s) | Lopeta(q)");
fgets(valinta,2,stdin);
printf("\n");
if (valinta[0] == 'a')
{
lisaa();

}

else if (valinta[0] == 's')
{
nayta();
}

else if (valinta[0] == 'd')
{
poista();
}

else
{
}
fflush(stdin);
main();


}

int lisaa()
{
printf("Syötä nimi \n > ");
char name[20];
scanf("%s",name);
strcpy(names[n],name);
n++;
fflush(stdin);
return 0;
}

int poista()
{
int del_id,i;
scanf("%d",&del_id);

for(i=del_id;i<n;i++)
{
strcpy(names[i],names[i+1]);
}
n--;
return 0;
}

int nayta()
{
int i;
for(i=0;i<n;i++)
{
printf("%d %s \n",(i),names[i]);
if (i==(n-1))
{
printf("\n");
}
}
return 0;
}
« Viimeksi muokattu: 08.03.08 - klo:14.48 kirjoittanut Regel »

Tuxer

  • Käyttäjä
  • Viestejä: 657
  • Debian 7.0, Sailfish
    • Profiili
    • opendimension.org
Vs: C-ohjelma, aloittelija, ongelmia
« Vastaus #1 : 08.03.08 - klo:16.38 »
Lainaus
2. Miksi kääntäjä valittaa jokaiselle omatekemälleni funktiolle: "tiedosto.c:27: warning: implicit declaration of function ‘poista’"

Nuo funktiot pitäisi esitellä ensin ennen kuin käyttää:
http://goblin.tkk.fi/c/tutorials/kaantaja_virheilmoitukset.html#w016
"Menehän Tonttu Tomera tarkastamaan ollaanko korsuissa kiltisti."
Fingerpori

Regel

  • Käyttäjä
  • Viestejä: 1090
  • Lucid
    • Profiili
Vs: C-ohjelma, aloittelija, ongelmia
« Vastaus #2 : 08.03.08 - klo:16.47 »
Lainaus
2. Miksi kääntäjä valittaa jokaiselle omatekemälleni funktiolle: "tiedosto.c:27: warning: implicit declaration of function ‘poista’"

Nuo funktiot pitäisi esitellä ensin ennen kuin käyttää:
http://goblin.tkk.fi/c/tutorials/kaantaja_virheilmoitukset.html#w016

Aivan :) Kiitos, 2. ongelma ratkesikin tuolla.

Vieläkin tuo 1. ongelma on.

beh

  • Käyttäjä
  • Viestejä: 154
    • Profiili
Vs: C-ohjelma, aloittelija, ongelmia
« Vastaus #3 : 08.03.08 - klo:17.24 »
Enpä osaa vastata kysymykseesi, mutta valikon voisit tehdä myös ehkä hieman paremmin. Laita tuo nykyinen valikko ikuisen while loopin sisään, jolloin ei tarvitse kutsua mainia rekursiivisesti (joka ei kai ole kovin hyvä systeemi).
whilestä pääset pois kun laitet testin epätodeksi kun käyttäjä valitsee q:n.

Ja tuota If-elseä siistimmän ratkaisun voit tehdä switch-rakenteella

Regel

  • Käyttäjä
  • Viestejä: 1090
  • Lucid
    • Profiili
Vs: C-ohjelma, aloittelija, ongelmia
« Vastaus #4 : 08.03.08 - klo:17.39 »
Nyt on while-loopissa, niin ei tarvitse rekursiivisesti kutsua, pitänee kokeilla vielä tuota switch-juttua :)


mgronber

  • Käyttäjä
  • Viestejä: 1458
    • Profiili
Vs: C-ohjelma, aloittelija, ongelmia
« Vastaus #5 : 08.03.08 - klo:17.46 »
1. Kun ohjelma kysyy ensimmäisen kerran, mitä tehdään, niin ohjeteksti tulee kerran, mutta seuraavalla kierroksella se tulee kahdest(ensimmäinen code-tagi havainnollistaa). Olisiko jotain neuvoja??

Funktion lisaa() sisällä luet tekstin scanf():n avulla ja se jättää puskuriin rivinvaihdon joka luetaan vasta main():n "pääsilmukassa" (jota ei tuossa varsinaisesti ole). Tiivistetysti minä tekisin tuon seuraavasti.

Koodia: [Valitse]
int lisaa() {
    char syote[20];
    printf("Syötä nimi \n > ");
    fgets(syote, sizeof(syote), stdin);
    sscanf(syote, "%s", names[n++]);
    return 0;
}

Tuosta puuttuu edelleen virheentarkastukset jotka puuttuivat alkuperäisestäkin koodista eli tuossa pitäisi tarkistaa ettei taulukko ole vielä täynnä.

Oletan että seuraavaksi opettelet muistin dynaamisen varaamisen. Viimeistään siinä vaiheessa kannattaa asentaa valgrind ja testata ohjelmaa sen avulla koska silloin muistivirheet on helpompi saada kiinni.

En sen enempää toista nimimerkin beh sanomaa, mutta rekursiivinen main:n kutsuminen ei ole hyvä ajatus vaan se on syytä korvata silmukalla. C ei ole funktionaalinen kieli ja jos sellaista kaipaat niin valitse Haskell, OCaml, Lisp tai jokin muu vastaava :)

Sellainen virhe vielä on että main():n tulee palauttaa int. Määrittely "void main()" on siis väärin.

Regel

  • Käyttäjä
  • Viestejä: 1090
  • Lucid
    • Profiili
Vs: C-ohjelma, aloittelija, ongelmia
« Vastaus #6 : 08.03.08 - klo:19.49 »
Kiitoksia neuvoista :) Yritän tässä alkuun päästä eroon pahimmista virheistä.

Nyt kun tein korjauksia koodiin, niin tuntuu, että se meni entistä enemmän sekaisin :S
lisaa-funktio ei tunnu toimivan. nayta puolestaan vaikuttaisi toimivan.
Kun valitsee 1 (lisaa), niin tulee alkuvalikko syötteen jälkeen uudelleen. Kun syöttää nimen, tulee syötelaatikko uudelleen. :S Tämä on jotain mystistä. scanf:llä sain toimimaan, eli jotain häikkää lienee fgets:n käytössä?

Tuntuisi, ettei ohjelma jää odottamaan tuossa lisaa-funktion fgets:issä käyttäjän syötettä, vaan rynnistää siitä ohi  ???

Koodia: [Valitse]
Lisää(1) | Poista(2) | Näytä kaikki(3) | Lopeta(4)
1

Syötä nimi
 > Ohi mennäään jo
Lisää(1) | Poista(2) | Näytä kaikki(3) | Lopeta(4)
arnold

Syötä nimi
 > Ohi mennäään jo
Lisää(1) | Poista(2) | Näytä kaikki(3) | Lopeta(4)
3

0
 
1 arnold
 

Lisää(1) | Poista(2) | Näytä kaikki(3) | Lopeta(4)

Koodia: [Valitse]
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


char names[50][20];
int n=0;


int nayta()
{
int i;
for(i=0;i<n;i++)
{
printf("%d %s \n",(i),names[i]);
if (i==(n-1))
{
printf("\n");
}
}
return 0;
}

int poista()
{
int del_id,i;
scanf("%d",&del_id);

for(i=del_id;i<n;i++)
{
strcpy(names[i],names[i+1]);
}
n--;
return 0;
}

int lisaa()
{

    char syote[20];
    printf("Syötä nimi \n > ");
    fgets(syote,sizeof(syote),stdin);
    strncpy(names[n],syote,20);
    n++;
    puts("Ohi mennäään jo");
    return 0;

}


int main()
{
int valinta;
int y=1;
while (y==1)
{
printf("Lisää(1) | Poista(2) | Näytä kaikki(3) | Lopeta(4) \n");
scanf("%d",&valinta);
printf("\n");
switch (valinta)
{
case 1:
{

lisaa();
break;
}
case 2:
{

poista();
break;
}
case 3:
{

nayta();
break;
}
case 4:
{

exit(1);
}
}
fflush(stdin);



}
return 0;

}

Edit: Ja kieltähän en vaihda, kun tätä C:tä tai sen johdannaisia tuntuuvat yliopistossa suosivan.

Edit2: Mutta jos fgets -> scanf, niin ohjelma toimii ??? Ei ymmärrä..
« Viimeksi muokattu: 08.03.08 - klo:20.53 kirjoittanut Regel »

janne

  • Käyttäjä
  • Viestejä: 5150
    • Profiili
Vs: C-ohjelma, aloittelija, ongelmia
« Vastaus #7 : 11.03.08 - klo:23.42 »
Kiitoksia neuvoista :) Yritän tässä alkuun päästä eroon pahimmista virheistä.

en juuri nyt jaksa perehtyä kamalasti tähän, mutta (liittymättä varsinaisiin ongelmiisi) seuraavat jutut pistivät vieläkin silmään...

Koodia: [Valitse]

int nayta()
{
...
return 0;
}

int poista()
{
...
return 0;
}

int lisaa()
{
...
    return 0;

}

funkitioiden paluuarvo ei kerro mitään hyödyllistä, eikä sitä tarkisteta tai ylipäätään käytetä mihinkään. funktioiden paluuarvon tyyppi voisi aivan hyvin olla void ja return-lauseet voisi jättää pois.
   
Koodia: [Valitse]
int main()
{
int valinta;
int y=1;
while (y==1)
{

ihan tarkalleen ottaenhan tuo looppi on do-while tyyppinen, koska y:n arvoa ei ole tarpeen tarkistaa ennen ensimmäistä kierrosta.

Koodia: [Valitse]

case 4:
{

exit(1);
}
}
fflush(stdin);



}
return 0;
}

ja kaiken kaikkiaan minun mielestäni olisi kauniimpaa lopettaa looppi jos käyttäjä valitsee 4:n. tarkoitan siis y:n muuttamista epätodeksi, jolloin loopista poistuttaisiin normaalia kautta ja pääohjelma suoritettaisiin loppuun. tämä mm. seuraavista syistä...

- rivi return 0 on tässä yhteydessä täysin turha koska suoritus ei voi koskaan päätyä sinne
- exit(1) palauttaa koko ohjelman paluuarvoksi 1 joka tarkoittaa virheellistä suorituksen päättymistä
- yleensä on siistimpää ja selkeämpää jos funktiossa on vain yksi poistumiskohta
- yleensä on siistimpää ja selkeämpää jos sovelluksessa on mahdollisimman vähän poistumiskohtia

Edit: Ja kieltähän en vaihda, kun tätä C:tä tai sen johdannaisia tuntuuvat yliopistossa suosivan.

ei siinä mitään. muista vaan huolehtia ettet kirjoita ohi varatun muistialueen ;)
Janne