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

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

Δημοσ.

mig1f ευχαριστώ για την βοήθεια. Σχετικά με τον κώδικα του προγράμματος σου γίνεται compile στο vs2008sp1 αν βγάλεις τo stdbool.h και το πρόγραμμα λειτουργεί κανονικά.

 

Βασικά ο κώδικας έχει bugs. Τον παραθέτω παρακάτω σε spoiler (ελπίζω διορθωμένο). Του έβαλα να κάνει και handle το Backspace κατά την είσοδο της μυστικής λέξης, κι επίσης έβαλα κι ένα 3ο όρισμα στην read_string() που είναι boolean και καθορίζει αν θέλουμε να γίνεται ή όχι flush η stdin. Τέλος ο κώδικας είναι πλέον strictly ANSI C89/ISO C90.

 

Σχετικά με το VS2008, αν το χρησιμοποιείς για καθαρή C δεν είναι από τις καλύτερες επιλογές, γιατί όπως είπαμε και παραπάνω δεν υποστηρίζει το πρότυπο C99. Βασικά υποστηρίζει τα μισά αλλά κι αυτά όχι formally. Για παράδειγμα, το <stdbool.h> στο οποίο αναφέρθηκες, εισήχθη κι αυτό στην C99, προσθέτοντας τα keywords bool, true, false (αυτά δεν υπάρχουν στο πρότυπο C89/C90). Το VS δεν έχει <stdbool.h> αλλά σου επιτρέπει να χρησιμοποιήσεις bool, true, false. Δηλαδή με άλλα λόγια, δεν ακολουθεί ούτε το C89/C90 ούτε το C99 πρότυπο.

 

Όπως και να 'χει, αν δεν σε ενδιαφέρει οι κώδικές σου να είναι cross-platform/cross-compiler, ή αν γράφεις μονάχα C89/C90 τότε όλα OK. Αλλιώς είναι καλύτερα να πας σε έναν compiler που ακολουθεί τις επίσημες εξελίξεις της γλώσσες (π.χ. mingw, pelles c).

 

Διορθωμένος (ελπίζω) κώδικας κρεμάλας (C89/C90, Windows):

 

/* ISO C90 */

#include <conio.h>       /* (Windows ONLY): getch() */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define FLUSH_STDIN         1

/* Echoing a backspace to stdout during unbuffered input is done by
 * backsteping once, outputting a space, then backstepping once again.
 */
#define ECHO_BS()                \
do {                             \
	putchar( '\b' );         \
	putchar( ' ' );          \
	putchar( '\b' );         \
	fflush( stdout );        \
} while(0)

#define BEEP(n)                  \
do {                             \
	int N = (n);             \
	while( N-- > 0 ) {       \
		putchar( '\a' ); \
		fflush( stdout );\
	}                        \
} while(0)

/*****************************************//**
 * Read up to sz-1 chars from stdin into an existing c-string s.
 * If terminating char is either '\n' or EOF, it is dropped.
 * If flush is passed as true, any extra chars in stdin are flushed.
 * Return the updated c-string on success, or NULL on error.
 *********************************************
 */
char *read_string( char *s, size_t sz, int flush )
{
	int c = '\0';
	size_t i = 0;

	/* sanity checks */
	if ( NULL == s || 0 == sz ) {
		return NULL;
	}

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

	/* flush any extra chars from stdin */
	if ( flush && '\n' != c && EOF != c ) {
		while ( '\n' != (c=getchar()) && EOF != c ) {
			;  /* void */
		}
	}

	return s;
}

/*****************************************//**
 * (WINDOWS ONLY)
 * Read up to sz-1 chars from stdin into an existing c-string without
 * echoing them to stdout. If chmask is a printable char, use that for
 * echoing to stdout during input (e.g. use '\0' to echo nothing).
 * If terminating char is either '\r' or EOF char, it is dropped.
 * Return the updated c-string on success, or NULL on error.
 *
 * NOTE: Backspace ('\b') is the only non-printable
 *       character that is handled by the function.
 *********************************************
 */
char *read_password( char *pass, size_t sz, int chmask )
{
	int    echo = isprint( chmask );  /* boolean */
	int    c = '\0';
	size_t i = 0;

	/* sanity checks */
	if ( NULL == pass || 0 == sz ) {
		return NULL;
	}

	i = 0;
	while ( i < sz-1 && '\r' != (c=getch()) && EOF != c) {
		/* handle backspace */
		if ( '\b' == c ) {
			if ( 0 == i ) {
				BEEP(1);
				continue;
			}
			if ( echo ) {
				ECHO_BS();
			}
			i--;
			continue;
		}
		/* ignore other non-printable chars */
		if ( !isprint(c) ) {
			continue;
		}
		/* echo mask char during input*/
		if ( echo ) {
			putchar( chmask );
		}
		pass[i++] = c;
	}
	fflush( stdout );
	pass[i] = '\0';

	return pass;
}

/*****************************************//**
 *
 *********************************************
 */
int main( void )
{
	char input[BUFSIZ]  = {'\0'};  /* for generic input from stdin */
	int  len;                      /* length of inputed secret word */
	char inbuf[BUFSIZ]  = {'\0'};  /* the secret word */
	char outbuf[BUFSIZ] = {'\0'};  /* the masked secret word */
	char nobuf[BUFSIZ]  = {'\0'};  /* c-string holding wrong guesses */
	int  noidx    = 0;             /* count of wrong guesses (also indexer of nobuf) */
	int  yescount = 0;             /* count of correct guesses */
	int  i;                        /* generic indexer/counter */
	int  turns;                    /* count of guesses */

	/* prompt for the secret word (inbuf), repeat until it is valid */
	for (; {
		printf( "Dwse mystikh lejh: " );
		fflush( stdout );
		read_password( inbuf, BUFSIZ, '*' );
		putchar( '\n' );

		/* populate outbuf with so many dashes as inbuf's length */
		for (i=0; '\0' != inbuf[i]; i++) {
			outbuf[i] = '-';
		}

		/* remember length of inputed inbuf */
		len = i;

		/* validate inputed inbuf */
		printf( "H leksi exei %d grammata\n", len );
		if ( 0 == len ) {
			puts( "Akyrh lejh, prospathise jana\n" );
			continue;
		}
		break;
	}

	/* game loop */
	turns = 10;
	yescount = noidx = 0;
	for (; {
		int found = 0; /* boolean false */

		printf( "\n     Mystikh lejh: %s\n", outbuf );
		printf( "Lathos mantepsies: %s\n", nobuf );
		printf( "        Apomenoyn: %d prospatheies\n\n", turns );

		/* Prompt for a letter (input[0]).
		 * Repeat if it is invalid, or already played.
		 */
		printf( "Dwse gramma: " );
		fflush( stdout );
		read_string( input, BUFSIZ, FLUSH_STDIN );
		/* invalid letter? */
		if ( !isalpha(input[0]) ) {
			BEEP(1);
			puts( "[ AKYRO GRAMMA, PROSPATHISE JANA ]\n" );
			continue;
		}
		/* already played letter? */
		if ( strchr(nobuf, input[0]) || strchr(outbuf, input[0]) ) {
			BEEP(1);
			puts( "[ HDH PAIGMENO GRAMMA, PROSPATHISE JANA ]\n" );
			continue;
		}

		turns--;

		/* Search for all instances of the inputed letter inside inbuf.
		 * If found, update yescount and copy the letter into outbuf.
		 */
		found = 0;  /* false */
		for (i=0; i < len; i++) {
			if ( input[0] == inbuf[i] ) {
				outbuf[i] = input[0];
				yescount++;
				found = 1; /* true */
			}
		}

		/* winner? (count of correct guesses equals length of secret word) */
		if ( yescount == len ) {
			BEEP(2);
			puts( "Kerdises" );
			break;
		}
		/* letter not found in inbuf, copy it to nobuf & continue */
		if ( !found ) {
			printf( "To %c den yparxei sth lejh.\n", input[0] );
			nobuf[ noidx++ ] = input[0];
		}

		/* loser? (run out of turns) */
		if ( 0 == turns ) {
			puts( "Exases" );
			break;
		}

	}

	printf( "\nMystikh lejh: %s\n", inbuf );

	return 0;
}

 

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

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

Δημοσ.

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

 

Σχετικά με τους C compilers έβαλα το Dev-C++ 5.11 (TDM-GCC 4.9.2 32 / 64-bit) και υποστηρίζει το VLA που αναφέρθηκες.

 

Το PellesC 64-bit σε Windows 8 κατά την δημιουργία νέου έργου δεν εμφανίζει κάτω την διαδρομή του φακέλου με τα 2 κουμπιά. Αυτό γίνεται ακόμα και όταν σύρω με τον δείκτη του ποντικιού τις κάτω δεξιά μικρές μπάρες προκειμένου να μεγαλώσει το μέγεθος του παράθυρου για να εμφανιστούν.

 

Pelles_C_8_00_64_bit_N.png

Δημοσ.

  Έγραψα την παρακάτω συνάρτηση για την περίπτωση που ο χρήστης στην αρχή του προγράμματος πατάει μόνο Enter, και επιλέγει να μην πληκτρολογήσει οτιδήποτε άλλο, να επιλέγεται μια λέξη τυχαία ανάμεσα σε 10 αποθηκευμένες λέξεις, ως η λέξη που έχει για να μαντέψει τα γράμματα της. Έκανα μια προσπάθεια να αντιγράψω την επιστρεφόμενη λέξη t_words[r_num] της συνάρτησης στο inbuf, άλλα δεν λειτούργησε.

#include <time.h>

/* Set and return a random word between 10 words */
char *get_rand_word( void ) {
	
	/* 10 words */
	static char *t_words[] = { "anthropos",
							   "ypologisths",
							   "diadiktyo",
							   "tetradio",
							   "dentro",
							   "omikron",
							   "ygeia",
							   "kitrino",
							   "pente",
							   "oyranos" };
							   
	int r_num;	/* number of random word */
	
	/* set random number */
	srand( time(NULL) );
	r_num = rand() % 10;

	/* return word */
    return t_words[r_num];
}
Δημοσ.

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

Είναι ένα από τα bugs του αρχικού κώδικα που πόσταρα. Στον διορθωμένο κώδικα που πόσταρα μετά, το έχω φτιάξει το συγκεκριμένο (μόλις το τσέκαρα ξανά)

 

Σχετικά με τους C compilers έβαλα το Dev-C++ 5.11 (TDM-GCC 4.9.2 32 / 64-bit) και υποστηρίζει το VLA που αναφέρθηκες.

Ναι, το TDM-GCC toolchain είναι ένα διαφορετικό "πακετάρισμα" των mingw και mingw-w64 toolchains.

 

Το PellesC 64-bit σε Windows 8 κατά την δημιουργία νέου έργου δεν εμφανίζει κάτω την διαδρομή του φακέλου με τα 2 κουμπιά. Αυτό γίνεται ακόμα και όταν σύρω με τον δείκτη του ποντικιού τις κάτω δεξιά μικρές μπάρες προκειμένου να μεγαλώσει το μέγεθος του παράθυρου για να εμφανιστούν.

 

 

Pelles_C_8_00_64_bit_N.png

 

Νομίζω πρέπει να το αναφέρεις στο forum της Pelles-C (ενδεχομένως στα bugs).

 

Στο μεταξύ, αν έχεις μεγαλώσει το dpi, δοκίμασε να το επαναφέρεις στο default scaling (100%).

 

Μπορείς επίσης να δοκιμάσεις να μεγαλώσεις το αρχικό μέγεθος του "προβληματικού" dialog-box, στο .dll της ελληνικής μετάφρασης (δες παρακάτω), αν και αυτό θα βοηθήσει μονάχα αν δεν το αλλάζει δυναμικά με κώδικα ο Pelle. Βασικά εφόσον λες πως δεν μεγαλώνει ούτε όταν προσπαθείς να το μεγαλώσεις από την κάτω δεξιά γωνία του, μάλλον θα είναι κάποιο bug ή κάποια ασυμβατότητα του κώδικα με τα Win 8/8.1

 

Αν θες να δοκιμάσεις να το αλλάξεις στο dll, η διαδικασία είναι η εξής:

 

1. Κράτα backup και μετά άνοιξε με το IDE της Pelles-C το αρχείο: C:\Program Files\PellesC\Bin\Intl\rsrc0008.dll

 

2. Στο δέντρο πόρων που θα εμφανιστεί, άνοιξε το κλαδί: Διάλογος και κατόπιν άνοιξε με διπλό-κλικ το dialog-box: #1013

 

3. Μεγάλωσε λίγο το ύψος του dialog-box, σώσε και κλείσε το IDE.

 

Αν θέλεις να το κάνεις στην αγγλική μετάφραση, τότε αντί για το rsrc0008.dll κάνε edit το rsrc0009.dll

Έγραψα την παρακάτω συνάρτηση για την περίπτωση που ο χρήστης στην αρχή του προγράμματος πατάει μόνο Enter, και επιλέγει να μην πληκτρολογήσει οτιδήποτε άλλο, να επιλέγεται μια λέξη τυχαία ανάμεσα σε 10 αποθηκευμένες λέξεις, ως η λέξη που έχει για να μαντέψει τα γράμματα της. Έκανα μια προσπάθεια να αντιγράψω την επιστρεφόμενη λέξη t_words[r_num] της συνάρτησης στο inbuf, άλλα δεν λειτούργησε.

 

 

#include <time.h>

/* Set and return a random word between 10 words */
char *get_rand_word( void ) {
	
	/* 10 words */
	static char *t_words[] = { "anthropos",
							   "ypologisths",
							   "diadiktyo",
							   "tetradio",
							   "dentro",
							   "omikron",
							   "ygeia",
							   "kitrino",
							   "pente",
							   "oyranos" };
							   
	int r_num;	/* number of random word */
	
	/* set random number */
	srand( time(NULL) );
	r_num = rand() % 10;

	/* return word */
    return t_words[r_num];
}

 

Η συνάρτηση είναι εντάξει (με εξαίρεση ίσως το srand(), που δεν χρειάζεται να το κάνεις μέσα στη συνάρτηση. Κάνε το μια μόνο φορά στην αρχή της main() ).

 

Οπότε αν υπάρχει πρόβλημα, πιθανότατα οφείλεται στον κώδικα που χρησιμοποιείς για να αντιγράψεις στο inbuf το c-string που επιστρέφει αυτή η συνάρτηση.

 

ΥΓ. Πριν από 3-4 χρόνια είχα γράψει μια "κρεμάλα" η οποία διάλεγε λέξεις είτε από το μικρό λεξικό του iSpell για τα ελληνικά, είτε από το αγωνιστικό λεξικό του Scrabble για τα αγγλικα, χρησιμοποιώντας (αν θυμάμαι καλά) απλά συνδεδεμένη λίστα. Αν σε ενδιαφέρει, μπορείς να τη βρεις εδώ: http://x-karagiannis.gr/prg/c-prog/c-games/hangman/

Δημοσ.

  Το dpi scalling πρέπει να δημιουργεί το πρόβλημα γιατί σύμφωνα με την οδηγία σου επαναφέροντας το στο προεπιλεγμένο, τα περιεχόμενα του παράθυρου του Νέου έργου του PellesC εμφανίζονται σωστά.

 

  Ωραίο το hangman.

Δημοσ.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */

int main(int argc, char *argv[]) {
    int i,j,l,t;//diktes
    j=0;
    t=0;
    int z=10;//zoes
    char buffer[100];
    puts("dose leksi");
    scanf("%s",&buffer);
    l=strlen(buffer);
    char *pin1,pin2[l],g[1];
    pin1=malloc((l)*sizeof(char));
    strcpy(pin1,buffer);
    for(i=0;i<l;i++){
        pin2='.';
    }
    while(1){
        puts("dose ena gramma kai na to thamase min to ksanadoseis tha vgei lathos");
        scanf("%s",g);
        for(i=0;i<l;i++){
            if(g[0]==pin1){
                pin2=g[0];
                j=j+1;
            }
            else{
                t=t+1;    
            }
        }
        if(j==l){
            puts("kerdises");
            break;
        }
        else if(j<l){
            puts("h leksi sou tora einai etsi");
            printf("%s\n",pin2);
        }
        if(t==l){
            z=z-1;
            printf("exeis %d zoes\n",z);
        }
        if(z==0){
            puts("exases axriste");
            break;
        }
        t=0;
    }    
    return 0;
}

Δημοσ.

  vaggos_ece αν θες πρόσθεσε στον κώδικα του προγράμματος σου την γραμμή

pin2[l] = '\0';

μετά από το πρώτο for loop, για να μη σου εμφανίζονται τα σύμβολα μετά την λέξη.

Δημοσ. (επεξεργασμένο)

  Έγραψα μια συνάρτηση που παίρνει ως όρισμα μια λέξη και επιστρέφει μια άλλη λέξη με το ίδιο μήκος χαρακτήρων αποτελούμενη ωστόσο από χαρακτήρες '_' . Έχω κάποιες επιφυλάξεις για τον κώδικα αλλά και για το αν χρησιμοποιώ την συνάρτηση εκχώρησης μνήμης αλλά και την free σωστά.

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

/* this function takes a word, counts its letters
   and returns a word loaded with underscore characters
 */

char *get_word_masked( char *word ) {

    int w_len = strlen(word);    /* length of given word */
    int i;                /* counter */
    char *m_word_p;            /* pointer for masked word dynamic allocation */

    /* memory allocation for masked word */
    m_word_p = (char *) malloc( (w_len + 1) * sizeof(char) );
    if( m_word_p == NULL ) {
        printf("Memory allocation failed\n");
        exit(1);
    }

    /* write underscore characters to masked word array */
    for( i = 0; i < w_len; i++ ) {
        m_word_p[i] = '_';
    }
    m_word_p[w_len] = '\0';

    return m_word_p;
}

int main( void ) {
    
    char *m_word = get_word_masked( "metaglwttisths" );

    printf( "%s\n", m_word );

    free(m_word);
    return 0;
}
Επεξ/σία από cvb
Δημοσ.

  Ευχαριστώ. Αναρωτιέμαι πως να επιστρέψω την *m_word_p αλλά και να κάνω στον ίδιο δείκτη deallocation την εκχωρημένη μνήμη με κάποιον σωστό τρόπο.

Δημοσ.

Ευχαριστώ. Αναρωτιέμαι πως να επιστρέψω την *m_word_p αλλά και να κάνω στον ίδιο δείκτη deallocation την εκχωρημένη μνήμη με κάποιον σωστό τρόπο.

Δεν μπορείς.

 

int main( void ) {
	
	char *word = "metaglwttisths";
	char *m_word = get_word_masked( word );

.....

}
Εφόσον μετά από το παραπάνω κομμάτι θέλεις να μεταχειριστείς την m_word, θα πρέπει αυτή να δείχνει κάπου. Γιατί λοιπόν θέλεις να την κάνεις free μέσα στην συνάρτηση ?
Δημοσ.

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

Δημοσ.

Το dpi scalling πρέπει να δημιουργεί το πρόβλημα γιατί σύμφωνα με την οδηγία σου επαναφέροντας το στο προεπιλεγμένο, τα περιεχόμενα του παράθυρου του Νέου έργου του PellesC εμφανίζονται σωστά.

Κατά την άποψή μου παραμένει bug όμως. Το dialog-box θα έπρεπε να προσαρμόζεται στο τρέχον dpi (εκτός αν έχει ήδη εξαντληθεί το ύψος της ανάλυσης). Οπότε νομίζω έχει νόημα να το αναφέρεις στο φόρουμ της Pelles C.

 

Ωραίο το hangman.

Ευχαριστώ. Έχει όμως κι αυτό τα προβλήματά του. Για παράδειγμα στη γραμμή 486 που επιχειρεί να επιλέξει τυχαία τη μυστική λέξη από την φορτωμένη linked-list, η επιλογή περιορίζεται στο εύρος [0, RAND_MAX], διότι σε αυτό το εύρος λειτουργεί η rand().

 

Το πρότυπο εγγυάται πως το RAND_MAX θα είναι τουλάχιστον 32767, αλλά για παράδειγμα το αγγλικό λεξικό που χρησιμοποιώ περιέχει σχεδόν 173000 λέξεις (και το ελληνικό περίπου 76500 λέξεις). Και στις 2 περιπτώσεις η απόσταση από το 32767 είναι μεγάλη.

 

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

 

Κάτι σαν το παρακάτω δηλαδή:

...
	long int len  = wlist.len;  // just for brevity later on
	long int pick = 0;
	if ( len < RAND_MAX ) {
		pick = rand() % len;
	}
	else {
		/* Split available range (len) in RAND_MAX parts,
		 * then pick a random part, and finally pick a
		 * random number inside the previously chosen part.
		 */
		long int parts = len / RAND_MAX;
		long int part  = rand() % parts;
		pick = (part * RAND_MAX) + rand();
	}
...
Αν το εύρος λέξεων (len) είναι μικρότερο από RAND_MAX, τότε χρησιμοποιούμε την κλασική κλήση της rand(), αλλιώς κάνουμε ότι περιέγραψα παραπάνω.

 

Βέβαια και πάλι υπάρχουν θέματα (π.χ. το len % RAND_MAX κομμάτι του εύρους μένει πάντα εκτός, ή π.χ. αν το len είναι τεράστιο και len / RAND_MAX > RAND_MAX πάλι μένουν κομμάτια του εύρους εκτός διαδικασίας στην μετέπειτα επιλογή τυχαίου κομματιού) αλλά σίγουρα είμαστε καλύτερα από το να κάναμε ένα απλό rand() % len

 

Τέλος, αν ξανάγραφα τον κώδικα σήμερα, τότε για το φόρτωμα των λέξεων στην μνήμη μάλλον θα χρησιμοποιούσα τελείως δυναμικό πίνακα από cstrings, αντί για linked-list, με alloc-ahead προσέγγιση στη δέσμευση της μνήμης...

 

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>

/*
 * The dominant data-type (Array of Strings).
 * The actual array (**strings) is a NULL terminated series of char-pointers.
 * Max possible capacity of the array is SIZE_MAX-1 char pointers (SIZE_MAX is
 * used for flagging indexing errors).
 */
typedef struct ArrStr ArrStr;
struct ArrStr {
	char   **strings; /* dynamic array of strings */
	size_t len;       /* count of already populated char pointers (strings) */
	size_t capacity;  /* count of total allocated char pointers */
};

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

/*************************************//**
 * Duplicate a c-string s.
 * The caller is responsible for freeing up the returned duplicate.
 *****************************************
 */
char *s_dup( const char *src )
{
	char   *s = NULL;
	size_t sz = 0;

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

	sz = strlen( src ) + 1;
	if ( NULL == (s = malloc(sz)) ) {
		return NULL;
	}

	return (char *) memcpy( s, src, sz );
}

/*************************************//**
 * Free mem of all populated strings in the given arrstr.
 * (no sanity checks)
 *****************************************
 */
static inline void _release_strings( ArrStr *arrstr )
{
	if ( NULL == arrstr->strings ) {
		return;
	}
	char **cpp = arrstr->strings;
	while ( *cpp ) {
		free( *cpp++ );
	}
	free( arrstr->strings );
	arrstr->strings = NULL;
}

/*************************************//**
 *
 *****************************************
 */
bool arrstr_append_string( ArrStr *arrstr, char *s )
{
	/* sanity checks */
	if ( NULL == arrstr || NULL == s ) {
		DBG_ERRMSG( "NULL pointer argument" );
		return false;
	}
	if ( 0 == arrstr->capacity ) {
		DBG_ERRMSG( "capacity MUST be greater than 0" );
		return false;
	}

	/*
	 * empty arrstr->strings ?
	 */
	if ( NULL == arrstr->strings ) {
		/* alloc ahead arrstr->capacity char pointers */
		if ( 1 == arrstr->capacity ) {
			arrstr->capacity = 2;
		}
		arrstr->strings = calloc( arrstr->capacity, sizeof(char *) );
		if ( NULL == arrstr->strings ) {
			DBG_ERRMSG( "alloc-ahead failed" );
			return false;
		}

		/* duplicate s into arrstr->strings[0] */
		arrstr->strings[0] = s_dup( s );
		if ( NULL == arrstr->strings[0] ) {
			DBG_ERRMSG( "s_dup() failed on 1st string" );
			return false;
		}

		/* update strings counter */
		arrstr->len = 1;

		return true;
	}

	/*
	 * non-empty arrstr->strings ?
	 */

	/* if needed, alloc ahead 2 * arrstr->capacity char pointers & set them to NULL */
	if ( arrstr->len == arrstr->capacity - 1 ) {
		size_t cap = arrstr->capacity;
		if ( SIZE_MAX-1 == cap ) {
			DBG_ERRMSG( "cannot alloc-ahead (out of memory)" );
			return false;
		}
		cap = (cap >= SIZE_MAX - 2 * cap) ? SIZE_MAX-1 : 2 * cap;
		char **try = realloc( arrstr->strings, cap * sizeof(char *) );
		if ( NULL == try ) {
			DBG_ERRMSG( "realloc() failed" );
			return false;
		}
		arrstr->strings  = try;
		arrstr->capacity = cap;
		for (size_t i = 1 + arrstr->len; i < arrstr->capacity; i++) {
			arrstr->strings[i] = NULL;
		}
	}
	/* duplicate s into arrstr->strings[ arrstr->len ] */
	arrstr->strings[ arrstr->len ] = s_dup( s );
	if ( NULL == arrstr->strings[ arrstr->len ] ) {
		DBG_ERRMSG( "s_dup() failed" );
		return false;
	}
	/* update strings counter */
	(arrstr->len)++;

	return true;
}

/*************************************//**
 *
 *****************************************
 */
char *arrstr_get_random_string( const ArrStr *arrstr )
{
	if ( NULL == arrstr ) {
		DBG_ERRMSG( "NULL pointer argument" );
		return NULL;
	}

	if ( 0 == arrstr->len || NULL == arrstr->strings ) {
		DBG_ERRMSG( "There are no populated strings." );
		return NULL;
	}

	size_t len = arrstr->len;
	size_t r   = 0;
	if ( len < RAND_MAX ) {
		r = rand() % len;
	}
	else {
		/* Split available range (len) in RAND_MAX parts,
		 * then pick a random part, and finally pick a
		 * random number inside the previously chosen part.
		 */
		size_t parts = len / RAND_MAX;
		size_t part = rand() % parts;
		r = (part * RAND_MAX) + rand();
	}

	return arrstr->strings[ r ];
}

/*************************************//**
 *
 *****************************************
 */
void arrstr_free( ArrStr **arrstr )
{
	/* sanity check */
	if ( NULL == arrstr ) {
		DBG_ERRMSG( "NULL pointer argument" );
		return;
	}

	if ( NULL == *arrstr ) {
		return;
	}

	_release_strings( *arrstr );
	free( *arrstr );
	*arrstr = NULL;
}

/*************************************//**
 *
 *****************************************
 */
ArrStr *arrstr_new( size_t capacity )
{
	/* sanity checks */
	if ( 0 == capacity || SIZE_MAX == capacity ) {
		char msg[ BUFSIZ ] = {'\0'};
		snprintf( msg, BUFSIZ, "capacity must be in [0, %u]", SIZE_MAX-1 );
		DBG_ERRMSG( msg );
		return NULL;
	}

	ArrStr *ret = malloc( sizeof(*ret) );
	if ( NULL == ret ) {
		DBG_ERRMSG( "malloc() failed" );
		return NULL;
	}

	ret->strings  = NULL;
	ret->len      = 0;
	ret->capacity = capacity;

	return ret;
}

/*************************************//**
 *
 *****************************************
 */
void arrstr_dump( const ArrStr *arrstr )
{
	if ( NULL == arrstr ) {
		DBG_ERRMSG( "NULL pointer argument" );
		return;
	}

	printf( "->strings @ 0x%x", arrstr->strings );
	fflush( stdout );
	if ( NULL == arrstr->strings ) {
		putchar( '\n' );
	}
	else if ( 1 == arrstr->len ){
		printf( " = { \"%s\" }\n", arrstr->strings[0] );
	}
	else {
		printf(
			" = { \"%s\", ..., \"%s\" }\n",
			arrstr->strings[0],
			arrstr->strings[ arrstr->len-1 ]
			);
	}
	
	printf( "->len: %zu\n", arrstr->len );
	printf( "->capacity: %zu\n", arrstr->capacity );
	putchar( '\n' );
}

/*************************************//**
 *
 *****************************************
 */
int main( void )
{
	srand( time(NULL) );

	ArrStr *words = arrstr_new( 4096 );

	FILE   *fp = fopen( "_el_ispell_small.dic", "r" );
	char   line[ BUFSIZ ] = {'\0'};
	while ( NULL != fgets(line, BUFSIZ, fp) ) {
		char *cp = strchr( line, '\n' );
		if ( NULL != cp ) {
			*cp = '\0';
		}
		if ( !arrstr_append_string(words, line) ) {
			goto exit_failure;
		}
	}
	fclose( fp );

	arrstr_dump( words );
	puts( arrstr_get_random_string(words) );

	arrstr_free( &words );

	return EXIT_SUCCESS;

exit_failure:
	arrstr_free( &words );
	return EXIT_FAILURE;
}

 

To ArrStr mini-interface δεν το έγραψα τώρα (εκτός από τη συνάρτηση: arrstrt_get_random_string() που την έγραψα για αυτό το παράδειγμα). Χρησιμοποίησα μόνο όσες συναρτήσεις χρειάστηκαν για αυτό το παράδειγμα, από ένα μεγαλύτερο ArrStr interface που είχα γράψει πολύ παλιαότερα κι ενίοτε χρησιμοποιώ σε διάφορα projects.

 

Αλλά ο κώδικας είναι C99 και μάλλον δεν θα γίνεται καν compile με VS. Οπότε αν σε ενδιαφέρει να τον δοκιμάσεις, χρησιμοποιήσε κάποιον άλλον compiler. Τέλος, όπως πάντα, οι κώδικες που ποστάρω ως παραδείγματα ενδέχεται να περιέχουν αβλεψίες ή/και bugs.

Δημοσ.

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

Ωραίος :)

 

Θα μπορούσες επίσης να περνάς σαν όρισμα τον χαρακτήρα που θα χρησιμοποιηθεί ως "mask". Δίνεις έτσι την ευκαιρία σε όποιον χρησιμοποιεί τη συνάρτηση να επιλέγει με ποιον χαρακτήρα θα κάνει "mask". Αν σου περάσουν μη-εκτυπώσιμο χαρακτήρα, μπορείς να τον κάνεις default σε '-'.

 

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

 

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

 

Η άλλη "σχολή" προτιμά να αφήνει στον προγραμματιστή να διαλέξει πως θέλει να διαχειριστεί το λάθος.

 

Ο κώδικας της συνάρτησής σου ανήκει στην 1η κατηγορία, διότι τερματίζει άμεσα το πρόγραμμα μέσα από τη συνάρτηση όταν αποτύχει το malloc(). Αν ήταν στην 2η κατηγορία, τότε σε περίπτωση λάθους η συνάρτηση θα επέστρεφε "τιμή-λάθους" στον caller, αφήνοντας στη δική του ευχέρεια το τι θα κάνει στη συνέχεια.

 

Είτε στην μια είτε στην άλλη περίπτωση, και ειδικά αν η συνάρτηση προορίζεται για χρήση από τρίτους (αλλά και από σένα όταν κάνεις debug) , καλό είναι να γράφει η συνάρτηση στο stderr stream πληροφορίες για το σφάλμα.

 

Αν χρησιμοποιείς την assert() τότε τις πληροφορίες αυτές τις γράφει η assert(). Αν όχι, μπορείς να βάλεις να τις γράφει η συνάρτησή σου. Οι περισσότεροι compilers (αν όχι όλοι) σου παρέχουν τα preprocessor symbols: __FILE__, __func__ και __LINE__, για αυτή τη δουλειά (αν και το __func__ αν το θυμάμαι καλά είναι προσθήκη της C99).

 

Στον κώδικα που ακολουθεί επιχειρώ να δείξω μια εκδοχή της συνάρτησης σύμφωνα με όσα περιέγραψα παραπάνω. Ανήκει στην 2η "σχολή", οπότε σε περίπτωση λάθους επιστρέφει NULL στον caller, αφού πρώτα γράψει στο stderr stream το αρχείο, τη συνάρτηση και τη γραμμή στην οποία παρουσιάστηκε το σφάλμα, καθώς και μια σύντομη περιγραφή του σφάλματος (αυτά τα κάνει το macro DBG_ERRMSG() που ορίζω στην αρχή του αρχείου, για λόγους δόμησης και συντομίας στον μετέπειτα κώδικα... σημείωσε πως πρέπει υποχρεωτικά να είναι inlined μέσα στη συνάρτηση αυτές οι πληροφορίες, εξού και η χρήση macro αντί για συνάρτηση).

/* ISO C90 */

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

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

/*****************************************//**
 * 
 *********************************************
 */
char *new_masked_word( const char *word, char chmask )
{
	int sz, i;
	char *ret = NULL;

	/* sanity checks */
	if ( NULL == word ) {
		DBG_ERRMSG( "NULL pointer argument" );
		return NULL;
	}

	if ( !isprint(chmask) ) {
		chmask = '-';
		puts( "\tNOTE: Masking character defaulted to '-'" );
	}

	/* alloc mem for masked word */
	sz = 1 + strlen( word );
	ret = calloc( sz, 1 );
	if ( NULL == ret ) {
		DBG_ERRMSG( "calloc() failed" );
		return NULL;
	}

	/* fill ret with chmask chars*/
	for (i=0; i < sz-1; i++) {
		ret[i] = chmask;
	}

	return ret;
}

/*****************************************//**
 *
 *********************************************
 */
int main( void )
{
	char *masked = new_masked_word( "123", '\0' );
	if ( NULL == masked ) {
		return EXIT_FAILURE;
	}
	puts( masked );
	free( masked );

	return EXIT_SUCCESS;
}
Δημοσ.

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

/* Waits a typed word and prints it */

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

/* waits a typed word and returns it */
char *input_word(void) {
	
	char *input_w_p;		/* returned letters */
	char input_w[BUFSIZ];	        /* typed letters */
	char ch;			/* letter */
	int i;				/* letter counter */

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

	/* allocate memory to put returned letters */
	input_w_p = (char *) malloc(i);
	assert(input_w_p != NULL);
	strcpy(input_w_p, input_w);

        /*  tests
	    printf("%s\n", input_w);
	    printf("%s\n", input_w_p);
         */

	return input_w_p;
}

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

	printf("%s\n", input_w_p);
	free(input_w_p);

	getchar();
	return 0;
}

image.jpg

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

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

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

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

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

Σύνδεση

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

Συνδεθείτε τώρα

  • Δημιουργία νέου...