Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ.

Καλημέρα. Ευχαριστώ. Έγραψα μια συνάρτηση, την input_word, κατά την οποία ένας άνθρωπος πληκτρολογεί μια λέξη και η λέξη αμέσως μετά εκτυπώνεται. Όταν εκτελώ το πρόγραμμα εμφανίζεται το μήνυμα λάθους παρακάτω. Σχετικά με τον μεταγλωττιστή Pelles C, δήλωσα το πρόβλημα στην διεύθυνση http://forum.pellesc.de/index.php?topic=6735.0 .

Το μήνυμα λάθους σου λέει ότι "έγραψες πέρα από την εκχωρημένη μνήμη σου" άρα μήπως δεν δέσμευσες αρκετή μνήμη ?

 

 

	/* put letters */
	i = 0;
	while ((ch = getchar()) != '\n') {
		input_w[i] = ch;       ===> 1
		i++;
	}
	input_w[i] = '\0';             ===> 2

	/* allocate memory to put returned letters */
	input_w_p = (char *) malloc(i);

 

Στο 1) κάθε φορά που θέτεις τιμή σε μία θέση του πίνακα, αυξάνεις κατά 1 το i. Στο 2) που θέτεις την τιμή '\0', αυξάνεις το i ?

  • Απαντ. 42
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

Δημοσ.

Με πρόλαβε ο ημίθεος. Στο τέλος του loop, το i σου δείχνει πόσους χαρακτήρες διάβασες, αλλά εσύ πρέπει να κάνεις malloc() i+1 επειδή στο input_word_p πρέπει να βάλεις και το τελικό '\0'.

 

Ένα άλλο πρόβλημα είναι πως στο loop δεν ελέγχεις να μη σου δώσουν παραπάνω από BUFSIZ-1 χαρακτήρες στην είσοδο. Η μόνη συνθήκη που κοιτάς είναι να μην είναι '\n'.

 

Μια τελευταία παρατήρηση είναι πως δεν χρειάζεσαι 2 πίνακες μέσα στη συνάρτηση. μπορείς να κάνεις malloc() απευθείας το string που θα επιστρέψεις, αφού πρώτα φυσικά βάλεις μέσα του τους χαρακτήρες που διάβασες.

 

Αν θες να το έχεις bounded, τότε πρέπει να περνάς το upper bound σαν όρισμα στη συνάρτηση. Για παράδειγμα:

char *input_word( size_t sz )
{
    int c;
    size_t i = 0;
    char *ret = NULL;

    if ( 0 == sz ) {
        return NULL;
    }

    ret = malloc( sz );
    if ( NULL == ret ) {
        return NULL;
    }

    for (i=0; i < sz-1 && '\n' != (c=getchar()) && EOF != c; i++) {
        ret[i] = c;
    }
    ret[i] = '\0';

    return ret;
}
Αν δεν θέλεις να το έχεις bounded, τότε ο κώδικας είναι πιο δύσκολος. Μπορείς να δεις μια υλοποίηση που αυξάνει το μέγεθος του string καθώς διαβάζει απο την είσοδο σε αυτό το ποστ: http://www.insomnia.gr/topic/571709-c-input-char-array/?do=findComment&comment=54050202

 

...Σχετικά με τον μεταγλωττιστή Pelles C, δήλωσα το πρόβλημα στην διεύθυνση http://forum.pellesc.de/index.php?topic=6735.0 .

Ναι, το είδα χτες. Δεν έχει και πολύ κίνηση το φόρουμ της Pelles-C οπότε μην απογοητευτείς αν αργήσουν να απαντήσουν, ή ακόμα κι αν δεν απαντήσουν καθόλου. Θα το δουν πάντως (βασικά θα το έχουν ήδη δει).

Δημοσ.

  Καλημέρα. Ευχαριστώ. Έγραψα το παρακάτω πρόγραμμα το οποίο ελέγχει αν υπάρχουν ή όχι τα ζητούμενα γράμματα μιας προεπιλεγμένης στον κώδικα ή argv[1] λέξης.

/* Checks letters guesses of a selected or random word */

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

/* get_word_masked takes a word, counts its letters
   and returns a word loaded with ch characters */
char *get_word_masked(char *word, char ch_m) {

	char *m_word;		/* masked word */
	int w_len;			/* word length */
	int i;				/* counter */

	w_len = strlen(word);

	/* allocates memory for masked word */
	m_word = (char *) malloc((w_len + 1));
	assert(m_word != NULL);

	/* writes ch characters */
	for(i = 0; i < w_len; i++) {
		m_word[i] = ch_m;
	}
	m_word[w_len] = '\0';

	return m_word;
}

/* letters_check checks letters guesses of a word */
void letters_check(char *word_p) {

	char word[BUFSIZ] = {'\0'};				/* word */
	char wr_letters[BUFSIZ] = {'\0'};		/* wrong letters */
	char *masked_word;						/* masked word and valid letters */
	int oppor_num;							/* opportunities */ 
	int input_ch;							/* input character */
	char ch_pos;							/* character position */
	int i;									/* counter */

	/* sets word, masked word, opportunities and counter values */
	strcpy(word, word_p);
	masked_word = get_word_masked(word, '_');
 	oppor_num = 10;
	i = 0;

	printf("Proteinetai ena h perissotera grammata\n");
	/* loop gets and checks input character */
	while (input_ch = getchar()) {

		/* correct letter */
		if (ch_pos = strchr(word, input_ch) != NULL) {
			if (ch_pos = strchr(masked_word, input_ch) != NULL) {
				printf("To gramma %c yparxei kai to exete hdh proteinei\n", input_ch);
				printf("%s\n", masked_word);
				continue;
			}
			for (i = 0; i < strlen(word); i++) {
				if (word[i] == input_ch)
					/* write letter to masked word */
					masked_word[i] = word[i];
			}
			printf("Brhkate to gramma %c\n", input_ch);
			printf("%s\n", masked_word);
			if (strcmp(masked_word, word) == 0) {
				printf("Brhkate ola ta grammata\n");
				break;
			}
		}

		/* wrong letter */
		else if (input_ch != '\n') {
			if (ch_pos = strchr(wr_letters, input_ch) != NULL) {
				printf("To gramma %c den yparxei kai to exete hdh proteinei\n", input_ch);
				continue;
			}
			for (i = 0; i < BUFSIZ; i++) {
				wr_letters[i] = input_ch;
			}
			printf("To gramma %c den yparxei\n", input_ch);
			oppor_num--;
			if (oppor_num == 0) {
				break;
			}
			printf("Exete akoma %d eykairies\n", oppor_num);
		}
	}
	
	/* reallocates memory */
	memset(word, 0, sizeof(word));
	memset(wr_letters, 0, sizeof(wr_letters));
	free(masked_word);
}

void main(int argc, char *argv[]) {

	if (argc == 1)
		letters_check("iwdio");
	else
		letters_check(argv[1]);	
}

  Ένας από τους προβληματισμούς μου είναι για το σημείο στον κώδικα στην συνάρτηση letters_check, στην μέση του βρόγχου while, που δηλώνω την εντολή

else if (input_ch != '\n') {

προκειμένου να εκτελεστεί ο κώδικας που ακολουθεί μετά.

 

  Υποτίθεται, αν και η δήλωση λειτουργεί, ότι από εκεί και κάτω, ως το τέλος του βρόγχου, ελέγχονται τα γράμματα για την περίπτωση κατά την οποία δεν εντοπίζονται στην λέξη, ωστόσο η εντολή ορίζει να εκτελεστεί το τμήμα κώδικα που ακολουθεί αν ο χαρακτήρας input_ch δεν είναι χαρακτήρας νέας γραμμής. Δεν καταλαβαίνω γιατί λειτουργεί σωστά. Επίσης μετά τον βρόγχο δεν είμαι σίγουρος αν χρειάζεται να μηδενίσω τα περιεχόμενα των δύο πίνακων με την συνάρτηση memset .

 

  Σχετικά με τον κώδικα της input_word της προηγούμενης ανάρτησης, παραθέτω τον κώδικα της βελτιωμένο και σύμφωνο ελπίζω με τα σχόλια σας.

/* Prints typed word */

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

/* input_word gets and returns typed word */
char *input_word(void) {
	
	char ch;				/* character */
	char input_w[BUFSIZ];	/* typed word */
	char *input_w_p;		/* returned word */
	int i;					/* counter */

	/* puts characters */
	i = 0;
	while ((ch = getchar()) != '\n' && i < sizeof(input_w)) {
		input_w[i] = ch;
		i++;
	}
	input_w[i++] = '\0';

	/* puts typed word to allocated memory, deletes typed word memory */
	input_w_p = (char *) malloc(i);
	assert(input_w_p != NULL);
	strcpy(input_w_p, input_w);
	memset(input_w, 0 , sizeof(input_w));

	return input_w_p;
}

int main(void) {
	
	char *input_w_p	= input_word();

	puts(input_w_p);
	free(input_w_p);

	getchar();
	return 0;
}

  Το πρόγραμμα notepad του λειτουργικού Windows 8 64-bit πρέπει να έχει κάποιο πρόβλημα με την στοίχιση των σχολίων, γιατί αυτά έχουν διαφορετική διάταξη όταν ανοίγεται το αρχείο όπου περιέχονται με τον διορθωτή του περιβάλλοντος VS2008 .

 

  Σχετικά με τα αρχεία λιστών λεξικών. Που μπορεί να τα βρεί ένας άνθρωπος αυτά, ποια να επιλέξει, και ποια είναι κατάσταση με τις άδειες και τα δικαιώματα χρήσης την χρήση αυτών των αρχείων; Ευχαριστώ και πάλι. Καλή συνέχεια σε όλους.

Δημοσ.

Καλημέρα,

 

δεν έχω πολύ χρόνο στη διάθεσή μου αυτή τη στιγμή, οπότε θα περιοριστώ σε ότι είναι πολύ οφθαλμοφανές. Για τα υπόλοιπα θα επανέλθω αργότερα πιο αναλυτικά, αν δεν με προλάβει κάποιος άλλος.

 

Επιχειρώντας να κάνω compile τον 1ο σου κώδικα με mingw32-gcc 4.8.1 (πάντα με ενεργοποιημένα όλα τα warnings, δηλαδή: gcc -std=c99 -g3 -Wall -Wextra -ansi -pedantic ...) παίρνω τις ακόλουθες προειδοποιήσεις:

test.c: In function 'letters_check':
test.c:50:2: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
  while (input_ch = getchar()) {
  ^
test.c:53:3: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
   if (ch_pos = strchr(word, input_ch) != NULL) {
   ^
test.c:54:4: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
    if (ch_pos = strchr(masked_word, input_ch) != NULL) {
    ^
test.c:59:18: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    for (i = 0; i < strlen(word); i++) {
                  ^
test.c:74:4: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
    if (ch_pos = strchr(wr_letters, input_ch) != NULL) {
    ^
test.c: At top level:
test.c:96:6: warning: return type of 'main' is not 'int' [-Wmain]
 void main(int argc, char *argv[]) {
      ^
Η πιο χτυπητή ανάμεσά τους είναι η τελευταία, η οποία σου υπενθυμίζει πως η main() πρέπει να επιστρέφει int (και όχι void που την έχεις εσύ).

void main(...) είναι αποδεκτό μόνο στην στάνταρ C++, όχι στη στάνταρ C. Επίσης, διόρθωσε αν θέλεις και τις υπόλοιπες.

 

Σε ότι αφορά τον 2ο κώδικα, όπως σου έγραψα και στο προηγούμενο ποστ μου, για αυτό που θες να κάνει η συνάρτηση δεν χρειάζεται 2 buffers (strings). Σου έγραψα και κώδικα εκεί για να δεις πως μπορείς να το κάνεις με ένα μόνο buffer, χωρίς μάλιστα να περιορίζεις το μέγιστο μήκος του με fixed σταθερά, αλλά να το περνάς σαν όρισμα στην συνάρτηση. Στη δική σου (μεταξύ διάφορων άλλων) περιορίζεις εσωτερικά μέσα στη συνάρτηση το μέγιστος μήκος του επιστρεφόμενου string σε BUFSIZ.

Ξανά δες αν θέλεις τη συνάρτηση που σου έχω γράψει, κι όπου έχεις απορία πόσταρε να τη δω αργότερα που θα ευκαιρήσω πάλι (ή μπορεί να σου τη λύσει κάποιο άλλο παιδί πριν από μένα).

Δημοσ.

αμα θέλετε τον δικου μου κωδικα στειλτε inbox πηρα 10/10.

Οι συμβουλές είναι καλύτερες από ένα ξερό κώδικα γιατί θα μάθει κάτι και ο OP και κάποιος που θα διαβάσει στο μέλλον το μήνυμα.

 

Καλημέρα. Ευχαριστώ. Έγραψα το παρακάτω πρόγραμμα το οποίο ελέγχει αν υπάρχουν ή όχι τα ζητούμενα γράμματα μιας προεπιλεγμένης στον κώδικα ή argv[1] λέξης.

Καταρχάς, δες τι σου έδωσε ο migf1 για την main και τα υπόλοιπα. Ένα πράγμα που προτείνουμε συνέχεια αλλά πολύ λίγος κόσμος το κάνει είναι πάντα να ενεργοποιούνται οι έλεγχοι του compiler. Όπως έδειξε ο migf1, σε gcc, clang, κτλ ένα απλό -Wall θα σε βοηθήσει να διώξεις ένα κάρο χαζά λάθη. Σε Visual Studio υπάρχει αντίστοιχη ρύθμιση που ενεργοποιεί τους ελέγχους.

 

 

char *get_word_masked(char *word, char ch_m) {

	/* allocates memory for masked word */
	m_word = (char *) malloc((w_len + 1));
	assert(m_word != NULL);

 

Άσχετο φυσικά με την παρούσα άσκηση αλλά όπως είπαμε πριν για τα warnings, να χρησιμοποιείς τον compiler. Εδώ και χρόνια οι compilers είναι πάρα πολύ έξυπνοι και μπορούν να βοηθήσουν τον προγραμματιστή. Έτσι μια πρακτική είναι να παρέχεις στον compiler όσο το δυνατόν περισσότερη πληροφορία μπορείς για να του επιτρέψεις να σε βοηθήσει.

 

Τι περισσότερη πληροφορία μπορείς να δώσεις στον compiler στο παραπάνω τμήμα ? Πρώτον, αφαίρεσε το (char *) cast εκτός και αν χρησιμοποιείς C++ compiler για την μεταγλώττιση. Σε C δεν είναι απαραίτητο και αν δεν υπάρχει, ο compiler μπορεί να σε προστατεύσει (από μια ομολογουμένως σπάνια περίπτωση). Δεύτερον, μάθε να χρησιμοποιείς το const. Οπουδήποτε δεν έχεις σκοπό να αλλάξεις μια μεταβλητή και ειδικότερα στην δήλωση συναρτήσεων, δήλωνε την ως const. Μέσα στην συνάρτηση get_word_masked αλλάζεις πουθενά το word ? Αν όχι, τότε "const char *word".

 

Ένα άλλο πράγμα είναι να αποφεύγεις τα assert. Το assert χρησιμεύει για να δηλώσεις ότι κάτι πρέπει οπωσδήποτε να πληροί μια συνθήκη. Αυτό σε βοηθάει κατά την ανάπτυξη του κώδικα που πολλά πράγματα μπορούν να αλλάξουν και κάνει το debugging πιο εύκολο (για αυτό άλλωστε και υλοποιείται μόνο σε debug builds). Το assert δεν φτιάχθηκε για error control και δυστυχώς σε πολλούς οδηγούς βλέπεις να χρησιμοποιείται για αυτό το σκοπό.

 

% cat assert.c 
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main(void)
{
	size_t i = -1;
	int *p = malloc(i);
	assert(p);

	puts("Hello");

	return 0;
}
Δες για παράδειγμα τον παραπάνω κώδικα που χρησιμοποιεί το assert για να ελέγξει τον δείκτη, όπως κάνεις και εσύ. Προσπαθώ να δεσμεύσω επίτηδες ένα τεράστιο πόσο μνήμης ώστε να αποτύχει η κλήση της malloc. Ας δούμε τι θα κάνει.

 

% cc -Wall -o a assert.c 
% cc -Wall -DNDEBUG -o noa assert.c  

% ./a
a: assert.c:9: main: Assertion `p' failed.
zsh: abort      ./a

% ./noa
Hello
Στην περίπτωση που δεν ορίσουμε το NDEBUG και είναι ενεργοποιημένο το assert, το πρόγραμμά μας κρασάρει επειδή απέτυχε το assert. Σκέψου να μην είναι η αρχή του προγράμματος και να κρασάρει αφήνοντας την δουλειά στη μέση.

 

Αν ορίσουμε το NDEBUG macro, λέμε ότι δεν θέλουμε debugging οπότε το assert είναι σαν να μην υπάρχει και ο κώδικάς μας συνεχίζει και εμφανίζει κανονικά το Hello οπότε θα προσπαθήσει να χρησιμοποιήσει σε κάποιο σημείο τον δείκτη, ο οποίος όμως δεν δείχνει πουθενά και θα κρασάρει.

 

Άρα δηλαδή δεν μας χρησιμεύει σε καμμία περίπτωση. Αντί για το assert, το δόκιμο είναι να βάλεις ένα σωστό έλεγχο με if (ή να μην βάλεις καθόλου σε απλές ασκήσεις).

 

 

	/* writes ch characters */
	for(i = 0; i < w_len; i++) {
		m_word[i] = ch_m;
	}
	m_word[w_len] = '\0';

 

Εδώ, αντί για το if δεν θα μπορούσες να χρησιμοποιήσεις πάλι memset ?

 

 

/* letters_check checks letters guesses of a word */
void letters_check(char *word_p) {

	char word[BUFSIZ] = {'\0'};
	char wr_letters[BUFSIZ] = {'\0'};
	char *masked_word;
.....
	/* reallocates memory */
	memset(word, 0, sizeof(word));
	memset(wr_letters, 0, sizeof(wr_letters));
	free(masked_word);

 

Να προσέχεις πού χρησιμοποιείς τον τελεστή sizeof. Οι μεταβλητές word, wr_letters έχουν οριστεί σαν πίνακες οπότε είσαι εντάξει αλλά αν πήγαινες να τον χρησιμοποιήσεις με τις word_p και masked_word που είναι δείκτες θα είχες πρόβλημα. Το λέω απλά να το έχεις στο νου σου.

 

 

 

	strcpy(word, word_p);

 

Στον παρόντα κώδικα δεν έχει σημασία αλλά γενικά να αποφεύγεις τις συναρτήσεις που δεν μπορείς να δώσεις όρια, όπως συγκεκριμένα η strcpy.

 

 

		if (ch_pos = strchr(word, input_ch) != NULL) {

 

Μην χρησιμοποιείς πολύπλοκες εκφράσεις γιατί μπερδεύεσαι και θα κάνεις λάθος και επίσης μπερδεύεις και τον compiler. Η συγκεκριμένη γραμμή έχει πολλαπλά λάθη.

 

Καταρχάς, το ch_pos δεν το χρησιμοποιείς πουθενά οπότε δεν χρειάζεται να το βάλεις. Δεύτερον ο τελεστής != έχει μεγαλύτερη προτεραιότητα από τον τελεστή ανάθεσης = οπότε πρώτα γίνεται η σύγκριση με το NULL και στην μεταβλητή ch_pos μπαίνει η τιμή αυτή. Έτσι δηλαδή η ch_pos παίρνει μόνο τιμές 0 και 1 και δεν δείχνει την θέση μέσα στο string.

 

test.c:53:3: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
   if (ch_pos = strchr(word, input_ch) != NULL) {
Αυτό είναι το μήνυμα λάθους του compiler όπως σου το έδωσε ο migf1. Όπως βλέπεις, λόγω της πολύπλοκης έκφρασης, ο compiler σου λέει απλά να βάλεις παρενθέσεις γιατί δεν ξέρει τι θέλεις να κάνεις.

 

test.c:53:3 προειδοποίηση: assignment makes integer from pointer without a cast
   ch_pos = strchr(word, input_ch);
Αν βάλεις τις παρενθέσεις (ή καλύτερα γράψεις την ανάθεση μόνη της σε άλλη γραμμή), παίρνεις το σωστό μήνυμα λάθους που είναι ότι προσπαθείς να δημιουργείς δείκτη χωρίς cast και αυτό γιατί χρησιμοποιείς λάθος τύπο μεταβλητής

 

NAME

strchr, strrchr, strchrnul - locate character in string

 

SYNOPSIS

#include <string.h>

 

char *strchr(const char *s, int c);

Όπως βλέπεις, η strchr επιστρέφει ένα δείκτη που δείχνει στο γράμμα που βρήκε και όχι αριθμητικά την θέση. Αυτό δεν μπορούσε να στο πει πριν ο compiler γιατί τον μπέρδεψες και δεν ήξερε τι θέλεις να κάνεις.

 

 

 

		/* wrong letter */
		else if (input_ch != '\n') {

 

Δεν καταλαβαίνω ποιο είναι το πρόβλημά σου εδώ. Γιατί να μην τρέχει δηλαδή ? Εφόσον έχει περάσει το πρώτο if και δεν έχει μπει μέσα σημαίνει ότι δεν έχεις σωστό γράμμα. Έρχεται εδώ και τρέχει για όλες τις περιπτώσεις εκτός από newline. Η περίπτωση "λάθος γράμμα" εμπεριέχεται στο "όλες οι περιπτώσεις εκτός από newline" οπότε μπαίνει μέσα στο σώμα του else if και τρέχει κανονικά. Η συνθήκη σου τρέχει για μεγάλο εύρος περιπτώσεων οπότε ίσως να τρέξει και σε περιπτώσεις που δεν θέλεις αλλά γιατί να μην τρέχει για το λάθος γράμμα ?

 

Το Visual Studio όπως και κάθε IDE έχει ενσωματωμένο debugger με τον οποίον μπορείς να προσθέσεις "watches" δηλαδή μεταβλητές των οποίων θέλεις να παρακολουθείς την τιμή και να τρέξεις γραμμή - γραμμή το πρόγραμμά σου. Με αυτό τον τρόπο μπορείς να δεις πότε μπαίνει στο σώμα του else if και πότε όχι. Επίσης θα μπορούσες να παρατηρήσεις και ότι το ch_pos παίρνει μόνο τιμή 1 και 0.

 

Με λίγα λόγια, σου παρέχονται ένα κάρο εργαλεία που σου ευκολύνουν την δουλειά σε ένα τεράστιο ποσοστό. Μάθε να τα χρησιμοποιείς και θα λυθούν όλα σου τα προβλήματα.

Δημοσ.

Λοιπόν, κοίταξα λίγο πιο διεξοδικά τον κώδικα της συνάρτησης letters_chek().

 

Αυτό που παρατήρησα κατά την εκτέλεση της είναι α) πως οι σωστές μαντεψιές δεν αφαιρούνται από τον μετρητή προσπαθειών και β) πως αν χάσει ο παίκτης (τελειώσουν δλδ οι προσπάθειες) η μυστική λέξη δεν φανερώνεται.

 

Σε ότι αφορά τον κώδικα τώρα, και συμπληρωματικά στα όσα πολύ σωστά σημείωσε ο ημίθεος, η παρακάτω προειδοποίηση:

test.c:59:18: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
for (i = 0; i < strlen(word); i++) {
 

βγαίνει επειδή το i έχει οριστεί ως int, ενώ η τιμή επιστροφής της strlen() είναι size_t. Στη προκειμένη περίπτωση δεν υπάρχει πρόβλημα, γιατί το (signed) int i το χρησιμοποιείς μόνο με θετικές τιμές κι επειδή η τιμή επιστροφής της strlen() είναι εξαιρετικά απίθανο (έως αδύνατον) να είναι μεγαλύτερη από INT_MAX. Γενικώς όμως είναι φρόνιμο να αποφεύγεις συγκρίσεις μεταξύ ανόμοιων τύπων.

 

Αν θες να εξαναγκάσεις τέτοια σύγκριση, μπορείς να κάνεις cast τον έναν από τους 2 όρους της σύγκρισης στον επιθυμητό τύπο, π.χ.:

for (i=0; i < (int) strlen(word); i++) {
αλλά ακόμα καλύτερα είναι να έχεις ορίσει εξαρχής το i ως size_t.

 

Άσχετα τώρα με την προειδοποίηση του κομπάιλερ, στον παραπάνω κώδικα η strlen() καλείται σε κάθε επανάληψη της λούπας. Δηλαδή υπολογίζεις ξανά και ξανά το μήκος του word χωρίς να υπάρχει λόγος. Μπορείς λοιπόν να το υπολογίσεις μια μόνο φορά πριν από τη λούπα, και να το βάλεις σε μια μεταβλητή που θα χρησιμοποιήσεις στη συνθήκη της λούπας:

size_t len = strlen( word );
for (size_t i=0; i < len; i++) {
Προφανώς πρόκειται για micro-optimization στην συγκεκριμένη άσκηση, αλλά καλό είναι να το έχεις υπόψη σου, γιατί σε speed-critical εφαρμογές μπορεί να σου κάνει αρκετά έως πολύ μεγάλη ζημιά (ανάλογα πόσο μεγάλο είναι το word και με το αν η λούπα βρίσκεται ή όχι nested μέσα σε άλλες λούπες).

 

Με το ίδιο σκεπτικό, καλό είναι να περιορίζεις όσο το δυνατόν κλήσεις της συνάρτησης strcmp(), ειδικά όταν τις έχεις μέσα σε λούπες.

 

Στο τέλος του ποστ σου παραθέτω μια δική μου υλοποίηση της αλγοριθμικής σκέψης που έχεις χρησιμοποιήσει στην letters_check(), όπου (μεταξύ άλλων μικρο-αλλαγών/διορθώσεων) έχω καταργήσει τελείως την strcmp(). Έχω αντικαταστήσει τη λειτουργικότητά της από μια μεταβλητή yescount, η οποία αυξάνεται μετά από κάθε σωστή μαντεψιά και συγκρίνεται με το μήκος της μυστικής λέξης (το οποίο μήκος υπολογίζεται μια μόνο φορά σε μια μεταβλητή len, έξω από τη λούπα).

 

Ελπίζω να είναι προφανές πως ειδικά μέσα σε λούπα μια συνθήκη του στυλ:

if ( len == yescount ) {
είναι κατά πολύ ελαφρύτερη μιας συνθήκης του στυλ:

            if (strcmp(masked_word, word) == 0) {
Ένα ακόμα σημείο που θέλει προσοχή (το επισήμανε και ο ημίθεος, αλλά σε διαφορετικό πλαίσιο) είναι το παρακάτω:

	char wr_letters[BUFSIZ] = {'\0'};    /* wrong letters */
...
for (i = 0; i < BUFSIZ; i++) {
	wr_letters[i] = input_ch;
}
...
Η λούπα σου κάνει overwrite με τον χαρακτήρα input_ch και την τελευταία θέση του cstring wr_letters, δηλαδή τη θέση: wr_letters[bUFSIZ-1]. Αυτή όμως η θέση πρέπει να περιέχει τον μηδενικό χαρακτήρα '\0', διαφορετικά δεν έχεις πια cstring κι άμα πας να το τυπώσεις θα δημιουργηθεί buffer overflow. Γενικώς διαπίστωσα πως μάλλον έχεις μια δυσκολία στη κατανόηση της διαφορά μεταξύ τρέχοντος & μέγιστου μήκους ενός cstring. Στο link της υπογραφής μου θα βρεις μερικές σημειώσεις περί strings στη C, οι οποίες ελπίζω να σε βοηθήσουν να τα ξεκαθαρίσεις.

 

Παραθέτω σε spoiler τη δική μου υλοποίηση. Είναι λίγο μεγαλύτερη, αλλά κάνει & ελέγχει περισσότερα πράγματα. Έχω βάλει σχόλια στα σημεία που θεώρησα πως θα κάνουν τον κώδικα πιο ευανάγνωστο. Αν όχι, πόσταρε στο νήμα να το δούμε μαζί. Παρεμπιπτόντως, το σημείο στο οποίο αντιγράφω τις σωστές μαντεψιές μέσα στην masked λέξη, αν το βρίσκεις αχρείαστα πολύπλοκο αγνόησέ το και κάνε το όπως το έχεις εσύ (δηλαδή με indexers αντί για pointer arithmetic & strchr()).

 

 

#define DBG_ERRMSG( msg )                                    \
do {                                                         \
	fprintf(stderr, "\n*** ERROR [ %s | %s():ln %d ]\n", \
		__FILE__, __func__,  __LINE__                \
		);                                           \
	fprintf( stderr, "*** %s\n", (msg) );                \
} while(0)

/*****************************************//**
 * Play the hangman game. Return false on error.
 *********************************************
 */
bool do_hangman( char *secret )
{
	bool ret = true;             /* to be returned by the function */
	int  c = '\0';               /* generic char */
	char *cp   = NULL;           /* generic pointer to char */
	char *mask = NULL;           /* cstring to be used as secret's mask */
	char nobuf[BUFSIZ] = {'\0'}; /* cstring holding wrongly guessed letters */
	int  noidx = 0;              /* nobuf's indexer */
	size_t  yescount = 0;        /* counter of correctly guessed letters */
	size_t  len = 0;             /* length of secret word */
	int  tries = 10;             /* tries left before game ends */

	/* sanity check */
	if ( NULL == secret ) {
		DBG_ERRMSG( "NULL pointer argument" );
		return false;
	}

	/* create mask from secret */
	mask = new_masked_word( secret, '-' );
	if ( NULL == mask ) {
		DBG_ERRMSG( "new_masked_word() failed" );
		return false;
	}

	len = strlen( secret );      /* to be compared with yescount */
	noidx = 0;                   /* count of wrong guesses (nobuf's indexer) */
	while ( tries ) {

		/* non-printable chars are ignored */
		if ( !isprint(c=getchar()) ) {
			continue;
		}
		/* non-letter chars are also ignored but the player is informed */
		if ( !isalpha(c) ) {
			printf( "Το %c δεν είναι γράμμα, προσπαθήστε ξανά...\n\n", c );
			continue;
		}

		/*
		 * c is already guessed if it exists in either mask or nobuf
		 */
		if ( NULL != strchr(mask, c) ) {
			printf( "Το %c υπάρχει, αλλά το έχετε ήδη προτείνει\n", c );
			continue;
		}
		if ( NULL != strchr(nobuf, c) ) {
			printf( "Το %c δεν υπάρχει, αλλά το έχετε ήδη προτείνει\n", c );
			continue;
		}

		/*
	 	 * at this point, c has NOT been already guessed
		 */

		/* search for c into secret */
		cp = strchr( secret, c );

		/* is c a wrong guess? */
		if ( NULL == cp ) {
			if ( noidx == sizeof(nobuf) - 1 ) {
				DBG_ERRMSG( "nobuf's internal mem exhausted!" );
				ret = false;
				break;
			}
			nobuf[noidx++] = c;
			nobuf[noidx] = '\0';
			printf( "Το %c δεν υπάρχει\n", c );
		}
		/* is c a correct guess? */
		else {
			printf( "Βρήκατε το γράμμα %c\n", c );

			/* show all occurrences of c in the mask & update yescount */
			while ( NULL != (cp=strchr(cp,c)) ) {
				mask[ (size_t)(cp-secret) ] = c;
				cp++;
				yescount++;
			}

			puts( mask );

			/* is the secret word found? */
			if ( len == yescount ) {
				puts( "Κερδίσατε!" );
				break;
			}
		}

		/* update tries */
		if ( --tries > 0 ) {
			printf( "Εναπομείνασες ευκαιρίες: %d\n\n", tries );
		}
	}

	if ( ret && len != yescount ) {
		puts( "Χάσατε!" );
	}

	free( mask );

	return ret;
}

 

 

...Το πρόγραμμα notepad του λειτουργικού Windows 8 64-bit πρέπει να έχει κάποιο πρόβλημα με την στοίχιση των σχολίων, γιατί αυτά έχουν διαφορετική διάταξη όταν ανοίγεται το αρχείο όπου περιέχονται με τον διορθωτή του περιβάλλοντος VS2008.

Το πρόβλημα το έχει ο editor του παρόντος φόρουμ. Όταν πας να γράψεις κώδικα, μέσα σε code-tags, δοκίμασε να έχεις κλειστό το διακοπτάκι πάνω αριστερά.

 

Σχετικά με τα αρχεία λιστών λεξικών. Που μπορεί να τα βρεί ένας άνθρωπος αυτά, ποια να επιλέξει, και ποια είναι κατάσταση με τις άδειες και τα δικαιώματα χρήσης την χρήση αυτών των αρχείων; Ευχαριστώ και πάλι. Καλή συνέχεια σε όλους.

Κατέβασε από τη σελίδα μου το hangman και πάρε τα από εκεί (τα ελληνικά είναι από το μικρό λεξικό του ispell).
Δημοσ.

  Καλησπέρα. Ευχαριστώ. Είστε καλά καταρτισμένοι. Διόρθωσα σημεία του κώδικα σύμφωνα με τα σχόλια σας και τον παραθέτω. Μία ερώτηση είναι αν υπάρχει κάποιο κείμενο με πληροφόρηση σχετικά με τα πρότυπα κωδικοποίησης χαρακτήρα που χρησιμοποιούν τα λειτουργικά Windows και Linux. Για παράδειγμα σχετικά με τα προγράμματα για την κονσόλα των Windows, είναι απαραίτητο να περιλαμβάνω στο αρχείο κώδικα την κεφαλίδα windows.h και να χρησιμοποιώ τις συναρτήσεις της προκειμένου να τυπώσω ελληνικούς ή και μη αμερικάνικους χαρακτήρες; Eναλλακτικά θα πρέπει να αλλάζω το code page, αν επιλέγω να μη χρησιμοποιήσω το αρχείο κεφαλίδας;

/* Checks letters guesses of a selected or random word */

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

/* get_word_masked takes a word, counts its letters
   and returns a word loaded with ch characters */
char *get_word_masked(const char *word, char ch_m) {

	char *m_word;		/* masked word */
	int w_len;			/* word length */

	w_len = (int) strlen(word);

	/* allocates memory for masked word */
	m_word = (char *) malloc((w_len + 1));
	if (m_word == NULL) {
		fprintf(stderr, "Memory allocation failed\nError: %s %d\n", __FILE__, __LINE__);
		exit(EXIT_FAILURE);
	}

	/* writes ch characters */
	memset(m_word, ch_m, w_len);
	m_word[w_len] = '\0';

	return m_word;
}

/* letters_check checks letters guesses of a word */
void letters_check(char *word_p) {
	
	int input_ch;							/* input character */
	int w_len;								/* word length */
	int wr_len;								/* wrong letters length */
	int i;									/* counter */
	int oppor_num;							/* opportunities */
	char wr_letters[BUFSIZ] = {'\0'};		/* wrong letters */
	char *masked_word;						/* masked word and valid letters */

	/* sets word length, counter, opportunities and masked word values */
	w_len = (int) strlen(word_p);
	i = 0;
	oppor_num = 10;
	masked_word = get_word_masked(word_p, '_');

	printf("Proteinetai ena h perissotera grammata\n");
	/* loop gets and checks input character */
	for (; {
		input_ch = getchar();

		/* correct letter */
		if (strchr(word_p, input_ch) != NULL) {
			if (strchr(masked_word, input_ch) != NULL) {
				printf("To gramma %c yparxei kai to exete hdh proteinei\n", input_ch);
				printf("%s\n", masked_word);
				continue;
			}
			for (i = 0; i < w_len; i++) {
				if (word_p[i] == input_ch)
					/* overwrite letter to masked word */
					masked_word[i] = word_p[i];
			}
			printf("Brhkate to gramma %c\n", input_ch);
			printf("%s\n", masked_word);
			if (strcmp(masked_word, word_p) == 0) {
				printf("Brhkate ola ta grammata\n");
				break;
			}
		}

		/* wrong letter */
		else if (input_ch != '\n') {
			wr_len = (int) strlen(wr_letters);
			if (strchr(wr_letters, input_ch) != NULL) {
				printf("To gramma %c den yparxei kai to exete hdh proteinei\n", input_ch);
				continue;
			}
			for (i = 0; i < wr_len; i++) {
				wr_letters[i] = (char) input_ch;
			}
			wr_letters[wr_len] = '\0';
			printf("To gramma %c den yparxei\n", input_ch);
			oppor_num--;
			if (oppor_num == 0) {
				break;
			}
			printf("Exete akoma %d eykairies\n", oppor_num);
		}
	}
	
	/* reallocates memory */
	memset(wr_letters, 0, sizeof(wr_letters));
	free(masked_word);
}

int main(int argc, char *argv[]) {

	if (argc == 1)
		letters_check("iwdio");
	else
		letters_check(argv[1]);

	return 0;
}

/* Prints typed word */

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


/* input_word gets and returns typed word */
char *input_word(void) {
	
	char ch;				/* character */
	char input_w[BUFSIZ];	/* typed word */
	char *input_w_p;		/* returned word */
	int i;					/* counter */
	int w_len;				/* word length */

	/* puts characters */
	i = 0;
	while ((ch = getchar()) != '\n' && i < sizeof(input_w)) {
		input_w[i] = ch;
		i++;
	}
	input_w[i++] = '\0';

	/* puts typed word to allocated memory, deletes typed word memory */
	input_w_p = (char *) malloc(i);
	if (input_w_p == NULL) {
		fprintf(stderr, "Memory allocation failed\nError: %s %d\n", __FILE__, __LINE__);
		exit(EXIT_FAILURE);
	}
	w_len = (int) strlen(input_w);
	strncpy(input_w_p, input_w, w_len);
	input_w_p[w_len] = '\0';
	memset(input_w, 0 , sizeof(input_w));

	return input_w_p;
}

int main(void) {
	
	char *input_w_p	= input_word();

	puts(input_w_p);
	free(input_w_p);

	getchar();
	return 0;
}
Δημοσ.

Καλησπέρα. Ευχαριστώ. Είστε καλά καταρτισμένοι. Διόρθωσα σημεία του κώδικα σύμφωνα με τα σχόλια σας και τον παραθέτω.

 

	w_len = (int) strlen(word);
	w_len = (int) strlen(word_p);
			wr_len = (int) strlen(wr_letters);

 

Το θέμα δεν είναι να κάνεις στα τυφλά τις διορθώσεις που είπαμε απλά επειδή το είπαμε εμείς "που είμαστε καταρτισμένοι". Εμείς μπορεί να είμαστε βλαμμένοι και να λέμε μπούρδες.

 

Για παράδειγμα, το να σημειώσεις κάπου σαν tip "όταν έχουμε strlen βάζουμε (int) cast" και να το βάζεις πάντα στους κώδικές σου (ζητώ συγγνώμη αν κάνω λάθος αλλά μου δόθηκε η εντύπωση ότι το έβαλες παντού μόνο επειδή το είπε ο migf1, χωρίς να καταλαβαίνεις γιατί και αν χρειάζεται), προσωπικά το θεωρώ πολύ πιο επικίνδυνο από πριν που δεν το ήξερες και δεν το έβαζες.

 

Το θέμα είναι να καταλάβεις τους λόγους που βρίσκονται πίσω από την κάθε σύστασή μας και έπειτα να κρίνεις αν η σύστασή μας έχει νόημα ή όχι. Αν δεν καταλαβαίνεις κάποια σύσταση, τότε δεν την τηρείς στα τυφλά αλλά μας λες και την αναλύουμε καλύτερα.

  • Like 2
Δημοσ.

@cvb:

Σχετικά με τον νέο κώδικα, νομίζω σου έχουμε δώσει στο νήμα πολλές πληροφορίες αλλά και πολύ κώδικα έτσι ώστε να μπορείς πλέον να συγκρίνεις μόνος σου τις δικές μας υλοποιήσεις με τις δικές σου και να αποφασίζεις τις θα κάνεις.

Βλέπω για παράδειγμα πως στην input_word επιμένεις να χρησιμοποιείς 2 buffers (strings), ένα statically allocated ki ένα dynamically allocated, και μάλιστα περιορίζοντας το μέγιστο μήκος του dynamic στο μέγιστο μήκος του static, παρόλο που σου έχω εξηγήσει πως είναι είναι περιττό, και σου έχω δώσει αναλυτικό κώδικα για το πως γίνεται με ένα μόνο dynamically allocated buffer. Οπότε από εδώ και πέρα δεν έχει νόημα να ανακυκλώνουμε τους εαυτούς μας με τα ίδια πράγματα :P

Να σημειώσω μόνο πως συμφωνώ με το τελευταίο σχόλιο του ημίθεου. Το casting όταν είναι συνειδητό και αιτιολογημένο (ιδανικά με inline σχόλιο) προσωπικά δεν το θεωρώ κακή πρακτική. Σου ξανά έγραψα όμως πως το καλύτερο από όλα είναι να ορίσεις εξαρχής το wr_len ως size_t.

Σχετικά με την Pelles-C, έστησα πριν μερικές μέρες Windows 8.1 Pro στο desktop pc μου και δοκίμασα αυτό που είπες. Σε μένα το πρόβλημα παρουσιάζεται όταν βάλω το dpi-scaling στo 150%. Στο 125% είναι όλα ΟΚ, όπως δείχνει η παρακάτω εικόνα (σε spoiler):

Εικόνα:

3oJrmCF.png?1


Σημείωσε όμως πως εγώ έχω την ανάλυση στα 1920x1080. Σημείωσε επίσης πως όταν παρουσιάζεται το πρόβλημα, δεν παρουσιάζεται μονάχα στο ide της Pelles-C αλλά και σε άλλες εφαρμογές.

Σχετικά με τα ελληνικά μέσα από στάνταρ C στην κονσόλα των Windows, είχα γράψει ένα τεράστιο σεντόνι, αλλά τελικά αποφάσισα να το ποστάρω σε ξεχωριστό νήμα. Θεωρώ πως θα είναι πιο χρήσιμο έτσι (θα το ανεβάσω σε λίγο).
 

Δημοσ.

Γεια. Ευχαριστώ. Σχετικά με τον μεταγλωττιστή Pelles C υπάρχει και στον υπολογιστή που χρησιμοποιώ παρόμοιο πρόβλημα με dpi στο 150. Αλλοιωμένα διπλανά παράθυρα δεν θυμάμαι να είδα. Το Add-In SDK σε τι χρειάζεται;

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...