Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: viallinen pätkä

Sivun loppuun

Touho [24.08.2004 21:36:56]

#

void Random(Piece *piece)
{
	int random1;
	int random2;

	do
	{
		random1 = 1 + rand() % 3;
		random2 = 1 + rand() % 3;
	} while (!kaytetty[random1][random2]);

	kaytetty[random1][random2] = 1;

	piece->blockx = random1;
	piece->blocky = random2;
}

tuon funktion ansiosta ohjelma kaatuu heti käynnistämisen jälkeen. voi toki olla, että jossain muualla koodissa esiintyy virhe ja heijastuu tähän, mutta toivotaan, että se vika löytyy tästä. jos poistan tämän funktion ja kaikki paikat, jossa tätä kutsutaan, ohjelma toimii moitteettomasti.

EDIT: Kutsuminen tapahtuu tällälailla:

Random(pic[x][y]);

Kääntämisen aikaisia ongelmia ei ole.

FooBat [24.08.2004 22:07:53]

#

Onko 'kaytetty'-taulukko alustettu tai edes luotu ennen tuota funktiota?

Metabolix [24.08.2004 22:08:13]

#

Käytäpä kooditageja, kiitos.
Olisi myös kiva tietää, miten on määritelty:
pic
kaytetty

Veikkaisin että vika on seuraava: Jos taulukko on esim:

Piece palat[3];

niin alkioita ovat 0, 1 ja 2. Tuolla näyttäisi olevan "1 + rand() % 3", josta tulee 1, 2 tai 3.

FooBat: Alustamisellahan ei tuossa ole käytännön merkitystä, ja jos sitä ei olisi luotu, kääntäjä ilmoittaisi asiasta. Oletan siis että luominen tapahtuu jo määrittelyssä ("bool kaytetty[3][3];" , ei "bool **kaytetty;")

Touho [24.08.2004 22:18:09]

#

int kaytetty[3][3];

alustettu globaaliksi.

  Piece *pic[3][3];

  for (x = 1;x<5;x++){
	  for (int y;y<5;y++){
		  Random(pic[x][y]);
	  }
  }

Metabolix [24.08.2004 22:24:26]

#

Kooditagi:

[koodic]
Koodia
Koodia
Koodia
[/koodic]

Ongelma on juuri tuo, jonka äsken mainitsin: Taulukon alkiot ovat 0, 1 ja 2, mutta satunnaislukusi on väliltä 1 - 3, jolloin voidaan osoittaa taulukon ulkopuolelle.
Tämä on siis ratkaisu:
Muuta tämä:

random1 = 1 + rand() % 3;
random2 = 1 + rand() % 3;

Tällaiseksi:

random1 = rand() % 3;
random2 = rand() % 3;

FooBat [24.08.2004 22:48:18]

#

Alustamisella on se merkitys, että C-kääntäjä ei takaa, että uudet taulukot sisältäisivät vain nollaa. Usein asia on aivan päinvastoin. Ei se kyllä ohjelmaa tuossa tapauksessa saisi kaatumaan, mutta silmukka keskeytyisi ennen aikojaan.

Tässä tapauksessa ongelma tosiaa taitaa olla se, että indeksit alkavat C:ssa nollasta.

ezuli [25.08.2004 15:29:57]

#

 Piece *pic[3][3];

for (x = 1;x<5;x++){
for (int y;y<5;y++){
Random(pic[x][y]);

Y:ltä puuttuu alustus ja eikös tuossa täytetä [0..2][0..2] taulukkon [1..4][?..4] kohdat?

Touho [25.08.2004 16:31:31]

#

Nyt olen muuttanut ohjelman taulukot oikein, mutta se kaatuu vieläkin. (vähän erilailla)

koko koodi on tässä:

#include <SDL/SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

SDL_Surface *back;
SDL_Surface *image;
SDL_Surface *screen;

int picx = 80;
int picy = 140;
int resx = 200;
int resy = 200;
int x_maara = 4;
int y_maara = 4;
int kaytetty[4][4];
int xpos = 1;
int ypos = 1;

int placex[4];
int placey[4];   //liikaa globaaleja muuttujia...


class Piece
{
public:
    Piece(int homeblockx, int homeblocky) {homex = homeblockx; homey = homeblocky;}
    int blockx;
    int blocky;
    int homex;
    int homey;
};


void DrawIMG(SDL_Surface *img, int x, int y)
{
  SDL_Rect dest;
  dest.x = x;
  dest.y = y;
  SDL_BlitSurface(img, NULL, screen, &dest);
}

void DrawIMG(SDL_Surface *img, int x, int y,
                                int w, int h, int x2, int y2)
{
  SDL_Rect dest;
  dest.x = x;
  dest.y = y;
  SDL_Rect dest2;
  dest2.x = x2;
  dest2.y = y2;
  dest2.w = w;
  dest2.h = h;
  SDL_BlitSurface(img, &dest2, screen, &dest);
}

int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect,
                        SDL_Surface *dst, SDL_Rect *dstrect);

void DrawScene()
{
    DrawIMG(back, 0, 0);
    DrawIMG(image, picx, picy);
    SDL_Flip(screen);
}

void Random(Piece *piece)
{
    int random1;
    int random2;

    do
    {
        random1 = rand() % 3;
        random2 = rand() % 3;
    } while (!kaytetty[random1][random2]);

    kaytetty[random1][random2] = 1;

    piece->blockx = random1;
    piece->blocky = random2;
}

int main(int argc, char *argv[])
{
    if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0)
    {
        printf("Error to Init yms.. %s\n", SDL_GetError());
        exit(1);
    }
    atexit(SDL_Quit);

    screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE|SDL_DOUBLEBUF);
    if (screen == NULL) {
        printf("Unable to set video mode.. sry .. &s\n", SDL_GetError());
        exit(1);
    }
  back = SDL_LoadBMP("bg.bmp");
  image = SDL_LoadBMP("image.bmp");


  Piece *pic[4][4];

  for (int i=0;i<4;i++){
      for (int ii=0;ii<4;ii++){
          Random(pic[i][ii]);
      }
  }

  for (i=0;i<x_maara;i++)
      placex[i] = picx+i*(resx/4);

  for (i=0;i<y_maara;i++)
      placey[i] = picy+i*(resy/4);


  DrawIMG(back, 0, 0);
  DrawIMG(image, picx, picy);
  SDL_Flip(screen);

  Uint8* keys;
  int done=0;

  while(done == 0)
  {
    SDL_Event event;


    while ( SDL_PollEvent(&event) )
    {

      if ( event.type == SDL_QUIT )  {  done = 1;  }

      if ( event.type == SDL_KEYDOWN )
      {
        if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }
      }

    }


    keys = SDL_GetKeyState(NULL);
    if ( keys[SDLK_UP] && ypos != 0 ) { ypos -= 1; }
    if ( keys[SDLK_DOWN] && ypos != y_maara-1 ) { ypos += 1; }
    if ( keys[SDLK_LEFT] && xpos != 0 ) { xpos -= 1; }
    if ( keys[SDLK_RIGHT] && xpos != x_maara-1 ) { xpos += 1; }
    if ( keys[SDLK_RETURN] || keys[SDLK_SPACE] ) {done = 1;}

    DrawScene();
  }
    return 0;
}

ja binääri (kuvien kera): http://zux.sjr.fi/moks/Col.game.zip

voiko se johtua Piece*:n alustamisesta?

Metabolix [25.08.2004 16:33:37]

#

Olisit heti sanonut että se jumittuu; se on aivan eri asia kuin kaatuminen.

Tässä ongelmasi:

for (int i=0;i<4;i++)
{
  for (int ii=0;ii<4;ii++)
  {
    Random(pic[i][ii]);
  }
}

Kutsut tuossa Randomia 16 kertaa (0, 1, 2, 3 moolemmilla muuttujilla) mutta Randomin sisäinen arvonta voi antaa 3 * 3 = 9 erilaista arvoa, jolloin 10. kerralla kaikki mahdolliset arvot on käytetty ja kaytetty[random1][random2] on aina tosi. Tässä siis ohjelmasi jumittuu.
Noihin siis kolmosen paikalle nelonen Random-funktiossa:

random1 = rand() % 4;
random2 = rand() % 4;

Touho [25.08.2004 17:13:39]

#

Se silti jumittuu. (ennen se kaatu ja nyt jumittaa)

void Random(Piece *piece)
{
    int random1;
    int random2;

    do
    {
        random1 = rand() % 4;
        random2 = rand() % 4;
    } while (!kaytetty[random1][random2]);

    kaytetty[random1][random2] = 1;

    piece->blockx = random1;
    piece->blocky = random2;
}

muuten täysin sama ohjelma, kuin edellä mainittu.

Metabolix [25.08.2004 17:22:20]

#

Mitäpä jos opettelisit hieman perusasioita kuten kääntäjän käyttöä? Sitten voisit laittaa tuonne Breakpointteja ja katsoa, mihin ohjelma jumittuu.
Ja nytten huomasin sen virheenkin: "while (!kaytetty[random1][random2])": ota tuo huutomerkki pois. Nyt nimittäin toistat looppia niin pitkään kuin valittu kohta ei ole käytetty.

FooBat [25.08.2004 17:40:03]

#

Tai käytä klassista printf-debuggausta :)
Kirjoita

printf("foo X\n");

vähän joka väliin mikä voi mennä pieleen.
Tuolla pitäisi pystyä haarukoimaan paikka mihin
ohjelma jää jumiin tai kaatuu.

Metabolix [25.08.2004 17:52:51]

#

FooBat: On parempi käyttää Breakpointteja, koska silloin voi (ainakin kunnon kääntäjissä) valvoa muuttujien arvoja. Voihan sellaisen toki laittaa printtautumaan ruudullekin, mutta se on yksinkertaisesti huonompi tapa. Sitä paitsi, graafisissa ohjelmissa ei ole kovin mukavaa printtailla ruudulle ylimääräistä tavaraa.

FooBat [25.08.2004 19:35:43]

#

Metabolix: En sanonutkaan, että se olisi parempi tapa (Siitä syystä se hymiö). Se nyt vaan sattuu olemaan tapa, jota harvinaisen moni ohjelmoija käyttää selvittäessään yksinkertaisia ongelmatapauksia.

Jos ohjelmoi linuksilla kannatta kokeilla Valgrind:ia tai vastaavaa ohjelmaa, jotka löytävät virheelliset muistiviittaukset ja alustamattomat muuttujat hetkessä.

Antti Laaksonen [25.08.2004 22:44:55]

#

Itsellänikin on tapana etsiä ohjelmista virheitä juuri välitulostusten avulla. Se on helppo ja tehokas tapa ja toimii ohjelmointiympäristöstä riippumatta.

hunajavohveli [26.08.2004 19:45:35]

#

Minäkin olin käyttänyt tuota tapaa ainakin QB:llä, kun en vielä tiennyt, mikä kumma se Immediate on. :)
Käytän kyllä tuota tapaa edelleenkin melko usein. Se tuntuu jotenkin luontevammalta.

Metabolix [26.08.2004 20:12:31]

#

No joo... Onhan se ihan käyttökelpoinen joissakin tilanteissa; riippuu täysin, minkä asteista debuggausta tarvitsee. Itselläni on yleensä niin monta asiaa, joita pitää seurata, että ei niitä saa mitenkään iskettyä ruudulle; vaihtoehdot ovat joko yksi breakpointti tai hyvin mutkikas muuttujajärjestelmä jotta saan arvot esille vielä ruudun piirtovaiheessakin. Vielä kun nämä arvot muuttuvat jatkuvasti, on mukavampi tietää ne tietyllä hetkellä.

thefox [26.08.2004 22:59:06]

#

Kyllä kunnollinen debuggaus on opettelun arvoinen asia :) Tuosta voisi kirjoittaa vaikka oppaan. Breakpointit, single-step-mahdollisuus ja watchit yms. löytyvät muuten myös mm. QBasicistä.

Metabolix: breakpointit (tai muuttujien arvojen seuraaminen) ei kylläkään ole kääntäjäkohtainen juttu, vaan debuggerikohtainen :) Toisinsanoen, kun vain työntää binääriin oikeanlaiset debuggausinfot, niin debuggerina voi käyttää sitä mikä itseään eniten miellyttää.

Windowsilla muistiongelmien löytämiseen voi käyttää NuMegan BoundsCheckeriä, tosin se on maksullinen.

T.M. [26.08.2004 23:03:33]

#

Itse teen yleensä niin että laitan monirivisen kommenttialun johonkin kohtaan, ja sitä ennen tulostan jonkun muuttujan arvon. Loppuun en jaksa laittaa kommentin lopetusta, vaikka PHP siitä valittaakin, tosin vain yhden kerran (outoa muuten...)

Tämänkin olisi toki voinut tehdä käyttämällä avuksi die() funktiota, mutta minusta tuo kommenttijuttu tuntuu jotenkin turvallisemmalta :) (ja helpommalta)

Metabolix [27.08.2004 18:04:29]

#

fawkz kirjoitti:

lainaus:

- - ei kylläkään ole kääntäjäkohtainen juttu, vaan debuggerikohtainen - -

No ruvetaanpa nyt saivartelemaan. Siinä vaiheessa kun käyttää kunnollista kääntäjää, esim. VC++, ei ole debuggerissa yleensä mitään vikaa, jonka takia sitä pitäisi vaihtaa. Muutenkin, tuskin kovinkaan monella riittää intressejä vääntää Debug Infoaan toiseen debuggeriin.

jcd3nton [27.08.2004 23:02:53]

#

Jep. Jotkut vaan ei älkkää kun ne käyttää huonompia(mielipide, ei nyt siitä sitten aleta jauhamaan...) ilmais kääntäjiä. Itse ainakin koen tuon VC++:n debug systeemin hyödylliseksi koska sillä on helppo paikantaa vaikkapa jumiutuminen jos ei syntaksivirhettä koodista löydy.

thefox [29.08.2004 02:13:52]

#

Metabolix kirjoitti:

Muutenkin, tuskin kovinkaan monella riittää intressejä vääntää Debug Infoaan toiseen debuggeriin.

Miten niin ei? :) Ei debuggausinfon sisällyttäminen binääriin vaadi yleensä kuin muutamaa lisävalitsinta kääntäjälle ja näin voidaan esim. MASM:lla assembloituja hommia debugata paljon kätevämmin. Monesta kääntäjäpaketista kunnollinen debuggeri puuttuu, eihän sitä löydy esim. MSVC++:n ilmaisistakaan versioista.


Sivun alkuun

Vastaus

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

Tietoa sivustosta