Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C: Jonot pointtereilla

mtjj [30.04.2013 20:26:23]

#

Moi...
Yritän täs ratkoa yhtä ohjelmoinnin lisätehtävää jossa käpistellään jonoa C kielellä. Koodi toimi niin pitkälle, kunnes mennään extract funktioon, jonka jälkeen alkaa tapahtua katastrofia. En vaan löydä syytä, miksi queue hajoaa..
Nähtävästi se koodarin ammatitauti eli koodisokeus iski.. :|


Yleensä koodi printtaa tämän:

 extracting vikarivi  head: vikarivi,  tail: ekarivi
 extracting É7g  head: É7g,  tail: ekarivi
 extracting É7g  head: É7g,  tail: ekarivi
 extracting É7g  head: É7g,  tail: ekarivi

ja pitää pysäyttää jollain ctrl+C:llä. Taas +

ja tässä itse tuotos.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLEN 20

typedef struct qelement *qpointer;

typedef struct qelement {
	char name[ MAXLEN ];
	qpointer next, previous;

} qelement;


qpointer insert(char *name, qpointer queue, qpointer *head, qpointer *tail) {

	qpointer new = (qpointer)malloc(sizeof(qelement));
	strcpy(new->name, name);

	if(queue != (qpointer)NULL) {
		new->next = queue;
		queue->previous = new;
		(*head) = new;

	} else {
		new->next = new;
		(*head) = new;
		(*tail) = new;
	}
	new->previous = new;
	return new;
}


qpointer extract(qpointer queue, qpointer *head, qpointer *tail);

qpointer extract(qpointer queue,  qpointer *head, qpointer *tail) {

	qpointer qtail =(*head);

	if(queue == (qpointer)NULL)
		return (qpointer)NULL;
	else {

		if(queue->previous != queue) {
			free(queue);
			(*head)=NULL;
			(*tail)=NULL;
			return (qpointer)NULL;
		}
		else {
			queue->previous->next = queue->previous;
			if ( *head != (*head)->next )
				*head = (*head)->next;

			free(queue);
			return qtail;
		}
	}
}


int main ( void ) {

	int i;
	qpointer qptr =NULL;
	qpointer head =NULL ;
	qpointer tail =NULL ;

	char teksti[MAX][MAXLEN] = {
		"ekarivi", "tokarivi", "kolmasrivi", "valirivi", "virherivi", "kommenttirivi", "vikarivi"	};

	/* insert into queue */
	printf("\n queue tester \n");
	for (i =0; i<= MAX-1 ; i++) {

		printf("\n insert %d: %s", i , teksti[i]);
		qptr= insert( teksti[i], qptr, &head, &tail);
		printf(" head: %s, ", head->name ? head->name : "no data");
		printf(" tail: %s ",  tail->name ? tail->name : "no data");
	}
    /* code here */

	/* remove from queue */
	printf("\n ***\n");
	while (  qptr != NULL ) {

		printf("\n extracting %s ",qptr->name);
		printf(" head: %s, ", head->name ? head->name : "no data");
		printf(" tail: %s ",  tail->name ? tail->name : "no data");
		qptr = extract(qptr, &head, &tail);
	}

	return(0);

Kiitoksia vaivautuneille..
\\T.

User137 [01.05.2013 06:36:07]

#

Eipä ne ongelmat aina ratkea vain koodia tuijottamalla. Pitää debugata vaikeemmissa tilanteissa.

Äkkiseltään insert()-funktio näyttää erikoiselta. Mitä virkaa "qpointer queue"-parametrilla on jos se on aina NULL? Sama NULL pyörii sitäkautta pääohjelmassa.

muokkaus: Eipä tullu jostain syystä itellä mieleen että jotain muuta kuin NULL pyörähtää paluuarvon kautta -.- No joku jakso kattoa koko koodin tarkemmin.

mtjj [01.05.2013 11:45:29]

#

Moi,

queue on NULL vain silloin, kun jono on tyhjä, jolloin sekä head & tail päivitetään. Seuraavilla kerroilla queue viittaa jonoon, joten viittaukset uuteen elementtiin asetetaan(->next, ->previous, head). Lisäksi insert() palauttaa aina uuden jonon elementin osoitteen (ok, on tuossa yksi virhe, malloc():n palautusarvoa ei testata koskaan, fixed), joten ei sitä NULL:ia juurikaan kierrätetä, jos nyt oikein ymmärsin argumenttisi.

Myönnän, että meni oma aikansa saada hahmotettua... Ja eclipse CDT /gdb debuggeria on käytetty, mut ei oikein sytytä, miksi juuri extract() toimii kuten toimii..


\\T.

jlaire [01.05.2013 13:17:55]

#

Miksi jonon esittämiseen tarvitaan kolme muuttujaa, eikö pelkkä queue riittäisi? Ilmeisesi head osoittaa aina samaan jonon jäseneen kuin queue tai qptr, joten se on täysin turha. Myös tail on aina tämän jäsenen previous, joten sitäkään ei tarvita.

Muuttujien kryptisten nimien takia koodia on muutenkin turhan vaikea ymmärtää.

if(queue->previous != queue) {
	free(queue);
	(*head)=NULL;
	(*tail)=NULL;
	return (qpointer)NULL;
}

Vertailun pitäisi olla ==.

queue->previous->next = queue->previous;

Tässä on logiikkavirhe ja myös queue->next->previous pitäisi asettaa.

Lisäys:

Koodissa on niin paljon virheitä, etten jaksa listata niitä yksitellen. Tässä korjattu versio.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX 7
#define MAXLEN 20

typedef struct qelement *qpointer;

struct qelement {
    char name[ MAXLEN ];
    qpointer next, previous;
};

qpointer insert(qpointer queue, const char *name) {
    qpointer elem = malloc(sizeof(*elem));
    strcpy(elem->name, name);

    if (queue != NULL) {
        elem->next = queue;
        elem->previous = queue->previous;
        queue->previous->next = elem;
        queue->previous = elem;
    } else {
        elem->next = elem;
        elem->previous = elem;
    }
    return elem;
}

qpointer extract(qpointer queue) {
    if (queue == NULL)
        return NULL;

    if (queue->previous == queue) {
        free(queue);
        return NULL;
    }

    queue->previous->next = queue->next;
    queue->next->previous = queue->previous;

    qpointer next = queue->next;
    free(queue);
    return next;
}

void print_queue(const qpointer queue) {
    if (queue == NULL) {
        printf("(empty)\n");
        return;
    }

    printf("%s", queue->name);
    for (qpointer p = queue->next; p != queue; p = p->next) {
        printf(", %s", p->name);
    }
    printf("\n");
}

int main(void) {
    int i;
    qpointer queue = NULL;

    char teksti[MAX][MAXLEN] = {
        "ekarivi", "tokarivi", "kolmasrivi", "valirivi", "virherivi", "kommenttirivi", "vikarivi"
    };

    printf("queue tester\n");
    for (i = 0; i < MAX; i++) {
        printf("insert %d: %s\n", i , teksti[i]);
        queue = insert(queue, teksti[i]);
        printf("queue = ");
        print_queue(queue);
    }

    printf("\n ***\n");
    while (queue != NULL) {
        printf("extracting %s\n", queue->name);
        queue = extract(queue);
        printf("queue = ");
        print_queue(queue);
    }
}

Pekka Karjalainen [01.05.2013 14:11:38]

#

Kappas, poistanpa omani kun jlaire ahkeroi. Kaipa nyt tuli selväksi mikä on jonon ja pinon ero, koska se näkyi alkuperäisessä kysymyksessä olevan vähän epäselvä ohjelman tulostuksen kannalta.

Metabolix [04.05.2013 16:50:41]

#

Mielestäni koodia selkeyttää, jos tehdään ensin funktiot linkitetyn listan käsittelyyn ilman muistinhallintaa ja toteutetaan sitten tarvittaessa erikseen versiot, joissa on free ja malloc. Tällä tavalla funktiot ovat myös joustavammat: niillä voidaan esimerkiksi siirtää alkio listasta toiseen ilman muistin varaamista ja vapauttamista.

jlaire [05.05.2013 01:12:46]

#

Hyvä pointti.

Vastaus

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

Tietoa sivustosta