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

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

Δημοσ.

Έχω αυτόν εδώ τον πολύ απλό κώδικα

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

main()
{
  char title[61];
  char artist[61];
  char  type;
  int  tracks;
  float price;
  int album;

  printf("welcome to the cd database.\n\n");
  /*
  * prompting the user to give the details of the cd
  */
  printf("Please enter the details of the cd...\n");
  printf("Title:");
  scanf("%s",title);

  printf("Artist:");
  fflush(stdin);
  scanf("%s",artist);

  printf("Number of tracks:");
  fflush(stdin);
  scanf("%d",&tracks);


  printf("Album or single(a for album, s for single):");
  //getchar();
  fflush(stdin);           /*προσπαθώ να αδειάσω το buffer αλλά δεν λειτουργεί*/
  scanf("%c", &type);
  album = type == 'a';     /* if they didn't enter 'a' or 's' it assums 's'*/

  printf("Price:");
  fflush(stdin);
  scanf("%f",&price);

  printf("\nThe cd details you entered are:\n");
  printf("=================================\n");
  printf("Title:%s\n", title);
  printf("Artist:%s\n", artist);
  printf("Number of tracks:%d\n",tracks);

  if (album)
      printf("Album\n");
  else
      printf("Single\n");
  printf("Price:%.2f\n",price);
  printf("===================================\n");


}

Το πρόβλημα μου είναι όταν προσπαθώ να πάρω δεδομένα τύπου char από τον χρήστη. Χρησιμοποιώ

το fflush(stdin); για να καθαρίζω το buffer. Ενώ λειτουργεί σε δεδομένα τύπου int και string, δεν λειτουργεί

για δεδομένα τύπου char.

Η ερώτηση μου είναι λοιπόν, γιατί δεν λειτουργεί;

Δημοσ.

Η fflush(stdin) δουλεύει μόνο σε Windows, κι αυτό καταχρηστικά σύμφωνα με τα στάνταρ της γλώσσας.

 

Αντί για scanf(); fflush(stdin); μια quick & dirty λύση είναι να χρησιμοποιήσεις: fgets(); sscanf(); περνώντας ως 1ο όρισμα ένα cstring που είναι πολύ μεγαλύτερο σε μήκος από ότι περιμένεις να σου δώσει ο χρήστης (π..χ. 512 chars).

 

Αν στο κάνει overflow, πάλι θα έχεις πρόβλημα με το line-buffering, αλλά πρέπει να το κάνει επίτηδες ο χρήστης. Αν θες να είσαι καλυμμένος 100% μπορείς να φτιάξεις μια δικιά σου flush_stdin() ρουτίνα και να την καλείς αν η fgets() γίνει overflow (ή μπορείς να φτιάξεις μια δικά σου ρουτίνα τύπου fgets() μόνο για την κύρια είσοδο).

  • Like 1
Δημοσ.

Η fflush(stdin) δουλεύει μόνο σε Windows, κι αυτό καταχρηστικά σύμφωνα με τα στάνταρ της γλώσσας.

 

Αντί για scanf(); fflush(stdin); μια quick & dirty λύση είναι να χρησιμοποιήσεις: fgets(); sscanf(); περνώντας ως 1ο όρισμα ένα cstring που είναι πολύ μεγαλύτερο σε μήκος από ότι περιμένεις να σου δώσει ο χρήστης (π..χ. 512 chars).

 

Αν στο κάνει overflow, πάλι θα έχεις πρόβλημα με το line-buffering, αλλά πρέπει να το κάνει επίτηδες ο χρήστης. Αν θες να είσαι καλυμμένος 100% μπορείς να φτιάξεις μια δικιά σου flush_stdin() ρουτίνα και να την καλείς αν η fgets() γίνει overflow (ή μπορείς να φτιάξεις μια δικά σου ρουτίνα τύπου fgets() μόνο για την κύρια είσοδο).

Ευχαριστω, οπότε επειδή δουλεύω σε linux δεν λειτουργεί η fflush(stdin). Προς το παρόν λύνω το πρόβλημα μου με getchar().

Δημοσ.

Με την getchar() θα έχεις πάλι πρόβλημα αν επιχειρήσεις να διαβάσεις κι άλλο char (θα πάρει ως input το '\n' του προηγούμενου input).

 

Δες ένα all-around παράδειγμα που δουλεύει πάντα (ελπίζω)...

#include <stdio.h>
#define MAX_INPUT   (80+1)

/* ------------------------------------------------
 * Read up to ssize-1 chars from stdin into s, or until EOF is encountered,
 * and nul-terminate s (replacing the trailing '\n' if any).
 * Return either s unchanged if ssize is passed as 0,
 * or NULL if s is passed as NULL.
 */
char *my_gets( char *s, size_t ssize )
{
	size_t i = 0;
	int    c = '\0';

	/* sanity checks */
	if ( !s || ssize < 1 ) {
		return s;
	}

	/* read chars from stdin */
	for (i=0; i < ssize-1 && '\n' != (c=getc(stdin)) && EOF != c; i++) {
		s[i] = c;
	}

	/* flush overflowed chars, if any */
	if ( s[i] != '\n' ) {			/* EOF or ssize reached without '\n'   */
		while (getchar() != '\n')	/* ... flush remaining chars  */
			;			/* ... ... empty-loop body    */
	}
	s[i] = '\0';

	return s;
}

/* ------------------------------------------------ */
int main( void )
{
    char input[ MAX_INPUT ] = '\0';
    int n, m, c;
    ...
    my_gets(input, MAX_INPUT);
    sscanf(input, "%d", &n);
    ...
    my_gets(input, MAX_INPUT);
    sscanf(input, "%c", &c);
    ...
    my_gets(input, MAX_INPUT);
    sscanf(input, "%d", &m);
    ...

    return 0;
}
Δημοσ.

Άκου τι θα κάνεις. Πρώτον δε θα χρειαστείς τα fflush.

 

Το πρόβλημά σου είναι εδώ:

printf("Number of tracks:");
scanf("%d",&tracks);

printf("Album or single(a for album, s for single):");
scanf("%c", &type);

Μία ενδεικτική εκτέλεση για αυτό το snipet είναι αυτή:

Number of tracks:23<enter>

Album or single(a for album, s for single):Price:

 

Όπως σωστά υπέθεσες ο buffer δεν αδειάζει κι έτσι το newline σου χαλάει την εκτέλεση.

 

Μία απλή λύση είναι να το "πετάξεις" με τη δυνατότητα που σου προσφέρει η scanf.

Μπορείς να γράψεις:

scanf(" %c", &type);

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

έτσι ώστε να απορρίπτει το πρώτο whitespace που θα διαβάσει.

Δοκίμασέ το!

Δημοσ.

...

Δοκίμασέ το!

Και πάλι θα έχει πρόβλημα αν μετά διαβάσει χωρίς scanf, π.χ...

int main(void)
{
	int n, m;
	char c, s[80+1] = {'\0'};

	printf("Number of tracks:");
	scanf("%d",&n);

	printf("Album or single(a for album, s for single):");
	scanf(" %c", &c);

	printf("Title: ");
	fgets(s, 80+1,stdin);
	puts(s);

	printf("Price:");
	scanf("%d", &m);

	getchar();
	return 0;
}
H fgets() παραπάνω (που τη χρησιμοποίησα π.χ. επειδή θέλω να διαβάσω τον τίτλο μονοκόμματα με όλα τα κενά που περιέχει) διαβάζει μόνο το '\n' που άφησε στην stdin η scanf(" %c", &c);

 

ΥΓ. Μπορείς και με την scanf() να διαβάζεις μονκόματα με όλα τα κενά, αλλά πρέπει να ανατρέξεις στο manual (τουλάχιστον εγώ, για να θυμηθώ την regex-like σύνταξη της).

Δημοσ.

Θα κάνω μία χαζή ερώτηση για την C. Αρκετά χαζή, αλλά δε μπορώ να το καταλάβω με τίποτα, ό,τι και να διάβασα, ακόμη και βιντεάκια που είδα.

 

Τι παίζει με τους δείκτες και τον τελεστή &; Ποια η λογική τους; 

 

Π.χ. όταν θέλω να διαβάσω κάτι από το πληκτρολόγιο, γιατί πρέπει να βάλω το & μπροστά από τη μεταβλητή μου;

 

Μάλλον τα έχω μπερδέψει, αλλά προσωπικά έχω καταλάβει ότι ένας δείκτης δείχνει τη θέση μνήμης στην οποία αντιστοιχεί μία μεταβλητή. Αν ισχύει αυτό, δηλαδή ότι δείχνει σε μία θέση μνήμης, γιατί να το χρησιμοποιήσω; Και ειδικότερα, πώς συνδυάζονται δείκτης και τελεστής &;

 

Όσο πιο απλά γίνεται παιδιά. 

 

Ευχαριστώ και συγγνώμη που χρησιμοποίησα το παρόν θέμα!!  :-)

Δημοσ.

Θα κάνω μία χαζή ερώτηση για την C. Αρκετά χαζή, αλλά δε μπορώ να το καταλάβω με τίποτα, ό,τι και να διάβασα, ακόμη και βιντεάκια που είδα.

 

Τι παίζει με τους δείκτες και τον τελεστή &; Ποια η λογική τους; 

 

Π.χ. όταν θέλω να διαβάσω κάτι από το πληκτρολόγιο, γιατί πρέπει να βάλω το & μπροστά από τη μεταβλητή μου;

 

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

 

Όσο πιο απλά γίνεται παιδιά. 

 

Ευχαριστώ και συγγνώμη που χρησιμοποίησα το παρόν θέμα!!  :-)

 
int x = 2;
int *p = &x; 
 

Η p ειναι τώρα δεικτης και δειχνει στην x. Με *p μπορεις να τροποιήσεις την τιμή της x επειδη το *p ειναι ισοδυναμο με το x ενω το p με το &x. 

 

Αν *p = 3; τοτε η τιμή της x τωρα θα ειναι 3 και οχι 2. Ο τελεστής & δινει την διεύθυνση μνήμης για μια μεταβλητή.

 

Με την scanf διαβάζεις εναν ακεραιο απο το πληκτρολογιο και τον βάζεις στην διεύθυνση που πρέπει , έχεις δεσμευσει πιο πριν μια κατανομασμένη θέση μνήμης πχ x και λες στην scanf να βάλει την τιμή που διάβασε σε αυτη την τοποθεσια μνήμης. Μετα μπορεις να εκτυπώσεις αυτη την τιμή με μια άμεση αναφορά (το identifier της μεταβλητής που ειναι το ονομά της  το x δηλαδη) ή με μια έμμεση αναφορά το *p. 

 

Μπορεις να μας γράψεις την έκφραση που δίνει την διεύθυνση του ιδιου του δείκτη?

  • 3 εβδομάδες αργότερα...
Δημοσ.

Έχω αυτόν τον κώδικα 

main()
{
    char name[51];
    char name2[51];
    int i=0;

    printf("Please enter your name: ");
    scanf("%s",name);
    
   while(name[i]!= '\0')
        {
        name2[i++]  = name[i];
        }
         puts(name2);
    printf("the name is %s",name2);
}

προπαθώ να αντιγράψω το ονομα που θα μου δινει ο χρήστης  σε ενναν πινακα και μετα απο τον πινακα να το αντιγρψω σε ενα δευτερο πινακα. το προβλημα βρισκεται οταν βαζω το ονομα στον πρωτο πινακα και τον μεταφερω στον δευτεορ πινακα τοτε εξαγωντας το ονομα απο τον δευτερο, παρακαμπτει τον πρωτο χαρακηρα. Τι δεν λειτουργει σωστα;


Επισης υπάρχει και αυτή η λύση

main()
{
    char name[51];
    char name2[51];
    int i=0;

    printf("Please enter your name: ");
    scanf("%s",name);

    while(name[i]!= '\0')
        {
        name2[i]  = name[i];
        i+=1;
        }

         puts(name2);
    printf("the name is %s",name2);
}

που ομως πεταει σκουπιδια μετα το ονομα

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

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

 

int main( void )
{
    char name[51]  = {'\0'};
    char name2[51] = {'\0'};

    printf("Please enter your name: ");
    scanf("%50s", name);

    for (int =0; i < 50 && name[i] != '\n'; i++) {
        name2[i]  = name[i];
    }

    puts(name);
    puts(name2);

    return 0;
}
EDIT: http://www.insomnia.gr/topic/513504-%CE%B1%CE%BA%CE%B1%CF%84%CE%B1%CE%BD%CF%8C%CE%B7%CF%84%CE%B7-%CF%83%CF%85%CE%BC%CF%80%CE%B5%CF%81%CE%B9%CF%86%CE%BF%CF%81%CE%AC-c/?do=findComment&comment=52952130 Επεξ/σία από migf1
Δημοσ.

Έχω αυτόν τον κώδικα 

main()
{
    char name[51];
    char name2[51];
    int i=0;

    printf("Please enter your name: ");
    scanf("%s",name);
    
   while(name[i]!= '\0')
        {
        name2[i++]  = name[i];
        }
         puts(name2);
    printf("the name is %s",name2);
}

προπαθώ να αντιγράψω το ονομα που θα μου δινει ο χρήστης  σε ενναν πινακα και μετα απο τον πινακα να το αντιγρψω σε ενα δευτερο πινακα. το προβλημα βρισκεται οταν βαζω το ονομα στον πρωτο πινακα και τον μεταφερω στον δευτεορ πινακα τοτε εξαγωντας το ονομα απο τον δευτερο, παρακαμπτει τον πρωτο χαρακηρα. Τι δεν λειτουργει σωστα;

 

Σε αυτή τη περίπτωση η συμπεριφορά αυτή οφείλεται στο γεγονός ότι αυξάνεις το i πριν κάνεις την ανάθεση.

Με την γραμμή,

name2[i++] = name[i];

γίνονται οι εξής κινήσεις με αυτή τη σειρά: Αύξηση κατά 1 του i και φόρτωση του name[i_new] στο name2[i_new].

Έτσι αν name = "john", τότε στην πρώτη επανάληψη name2[1] = name[1] = 'j'.

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

 

Τώρα το γεγονός ότι στο πρώτο πρόγραμμα μετά τη δουλειά που έκανες, το νέο string δεν ακολουθούνταν από "σκουπίδια" είναι κάτι το τυχαίο. Τα strings στη C είναι NULL terminated, πράγμα που σημαίνει ότι στο τέλος της χρήσιμης πληροφορίας μπαίνει ένας χαρακτήρας '\0'. Μετά από αυτόν ακολουθούν "σκουπίδια". Γι' αυτό και στην επανάληψη ψάχνεις τους χαρακτήρες του string μέχρι να συναντήσεις '\0'. Στην περίπτωσή μας τώρα εσύ θέλεις να τερματίσεις την επανάληψη μόλις διαβάσεις τον NULL character. Στο σημείο όμως στο οποίο κάνεις αύξηση του i, στο name2 αντιγράφεται στο τέλος και ο χαρακτήρας NULL από το name. Στην ουσία κοιτάς ένα σημείο μπροστά.

 

Στο δεύτερο κώδικά σου κάτι τέτοιο δε συμβαίνει. Οπότε εύκολα καταλαβαίνεις πως η επανάληψη τερματίζεται πριν αντιγράψεις και το NULL στο name2. Μετά όταν κάνεις την εκτύπωση του name2, εκτυπώνεται το string μέχρι να βρεθεί NULL, το οποίο βρίσκεται στο 51ο χαρακτήρα. Άρα διαβάζονται και όλα τα σκουπίδια, μέχρι εκείνο το σημείο.

 

Υπάρχουν δύο λύσεις:

  • αρχικοποιείς τα strings σου με NULL, όπως σου έδειξε ο migf1, είτε
  • όταν εξέλθεις της επανάληψης θέτεις name2 = '\0'.

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

Δημοσ.

Σε αυτή τη περίπτωση η συμπεριφορά αυτή οφείλεται στο γεγονός ότι αυξάνεις το i πριν κάνεις την ανάθεση.

Με την γραμμή,

name2[i++] = name[i];
γίνονται οι εξής κινήσεις με αυτή τη σειρά: Αύξηση κατά 1 του i και φόρτωση του name[i_new] στο name2[i_new].

Έτσι αν name = "john", τότε στην πρώτη επανάληψη name2[1] = name[1] = 'j'.

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

 

Γιατί αυξάνεται κατά ένα και μετά έχουμε παντού τον i_new ? Τον postfix τελεστή χρησιμοποίησε όχι τον prefix. Επίσης τον έχει στην αριστερή πλευρά της ανάθεσης οπότε μια λογική ερμηνεία της λειτουργίας θα ήταν ότι διαβάζεται το περιεχόμενο της μνήμης name, έπειτα αυτό τοποθετείται στην διεύθυνση name2 και μετά λόγω postfix αυξάνεται το i, δηλαδή αυτό που ήθελε ο OP.

 

Το λογικό όμως δεν ισχύει εδώ γιατί πέφτουμε σε αόριστη συμπεριφορά και μπορεί το αποτέλεσμα να είναι ό,τι θέλει ο compiler. Ίσως να μη το θυμάμαι καλά αλλά νομίζω δεν μπορείς να αλλάζεις την μεταβλητή στις δύο πλευρές της ανάθεσης (παρόμοιο δηλαδή με το i = i++ + i++)

Δημοσ.

Το λογικό όμως δεν ισχύει εδώ γιατί πέφτουμε σε αόριστη συμπεριφορά και μπορεί το αποτέλεσμα να είναι ό,τι θέλει ο compiler. Ίσως να μη το θυμάμαι καλά αλλά νομίζω δεν μπορείς να αλλάζεις την μεταβλητή στις δύο πλευρές της ανάθεσης (παρόμοιο δηλαδή με το i = i++ + i++)

 

Σωστά! Ο compiler παραπονιέται για το postfix increment του i στο name2[i++]:

warning: operation on ‘i’ may be undefined [-Wsequence-point]

οπότε τα αποτελέσματα δεν είναι αυτά που θέλουμε.

Δημοσ.

Να σημειώσω κι εγώ πως έχει λάθος το σνίπετ που πόσταρα παραπάνω... εκ παραδρομής έχω γράψει '\n' αντί για το σωστό '\0' στη συνθήκη του loop.

 

(έχω επίσης διπλο-τριπλο-ελέγχους οι οποίοι στη συγκεκριμένη ερώτηση δε χρειάζονται... αλλά μπορεί να αποδειχτούν χρήσιμοι αν ο κώδικας γίνει συνάρτηση κάποια στιγμή)

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

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

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

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

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

Σύνδεση

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

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