imitheos Δημοσ. 20 Οκτωβρίου 2012 Δημοσ. 20 Οκτωβρίου 2012 Βρήκα τελικά τι παίζει με το θέμα undefined συμπεριφορά σε κλήση συνάρτησης με μη συμβατά ορίσματα που είχα ρωτήσει. Ισχύει όντως αυτό που λέει το βιβλίο αλλά μόνο σε μία τρελή περίπτωση. Όταν το πρότυπο λέει non-prototyped εννοεί τον παλιό K&R τρόπο δήλωσης. > #include <stdio.h> void foo(a, float a, b; { printf("%f\n", a * ; } int main(void) { int a = 3, b = 5; foo(a, ; return 0; } Στο παραπάνω κώδικα, ο clang εμφανίζει κανονικά 15.000 ενώ ο gcc 0.000 και το ideone βαράει segmentation fault. Αυτό που έκανα λάθος εγώ ήταν ότι δήλωνα την συνάρτηση α) με τον κανονικό τρόπο και β) από κάτω από την main οπότε έμπαινε η αυτόματη θεώρηση "απροσδιόριστος αριθμός ορισμάτων και επιστροφή int" και έπαιρνα error για conflicting δηλώσεις. Για να ισχύει το undefined πρέπει η συνάρτηση να έχει δηλωθεί πριν ώστε να φαίνεται και πρέπει να έχει δηλωθεί με τον αρχαίο K&R τρόπο, χέσε μέσα δηλαδή. Για αυτό δεν πήγε το μυαλό μου με τίποτα. Μια και δεν νομίζω να χρησιμοποιεί κανείς K&R δηλώσεις πλέον, δεν ισχύει το undefined. 1
migf1 Δημοσ. 20 Οκτωβρίου 2012 Δημοσ. 20 Οκτωβρίου 2012 Μα αυτό έλεγα πριν όταν μίλησα για visible και μη visible χαρακτήρες . Τελικά είναι εύκολο να προστεθεί σαν "επέκταση". Λέγοντας non-visible characters υποθέτω εννοούμε όσους ελέγχει η iscntrl(), σωστά? Δηλαδή οι... > '\a' (Bell), '\b' (BS), '\n' (NL), '\r' (CR), '\f' (FF), '\t' (HT), '\v' (VT) Δεν θυμάμαι σίγουρα, αλλά πέρα του ότι νομίζω πως είναι platform-specific τελικά, πως προτείνετε να τους διαχειρίζεται η συνάρτηση; Θα μπορούσαμε ας πούμε το Bell να είναι j, το BS να είναι j-- και το CR να είναι j=0 (μαζί με το '\n'). Τι γίνεται όμως με τα FF και VT?
moukoublen Δημοσ. 20 Οκτωβρίου 2012 Δημοσ. 20 Οκτωβρίου 2012 Η βασική μου σκέψη ήταν όσοι χαρακτήρες δεν "εκτυπώνουν" κάτι στην οθόνη, η παρουσία τους να μην αυξάνει τον counter. Έπειτα χαρακτήρες όπως το BS ή το Delete (0x7F) είναι ένα θέμα. Θα μειώνουν; Και βέβαια είναι λίγο δουλίτσα αν πεις οτι το κάνεις για όλο τον extended ASCII. Εκτός αν υπάρχει καμία έτοιμη συνάρτηση στην C που σου δίνει για όλο τον extended ASCII αν ο χαρακτήρας "τυπώνει" κάτι στην οθόνη ή όχι (δεν ξέρω αν το κάνουν οι συναρτήσεις που ανέφερες πριν). Βέβαια μετά οδήγησε στο ερώτημα, έχει νόημα όλος αυτός ο διαχωρισμός; Πρόκειται να εφαρμώσεις την συνάρτηση αυτή σε strings που να περιέχουν και τίποτα άλλο εκτός από απλούς χαρακτήρες (visible - που εκτυπώνουν κάτι) και tabs; Αν ναι, τότε μονο έχει νόημα να μπεις σε τέτοιο κόπο θα έλεγα. EDIT Έριξα μια γρήγορη ματιά στον ASCII Απο 0-31 (Dec) δεν εκτυπώνουν κάτι (control characters) εκτός από τον 9 που είναι το TAB. (βέβαια επιφυλάσσομαι γιατι δε ξέρω πλήρως τι κάνουν σε ένα terminal όλοι) Απο 32-127 (Dec) εκτυπώνουν εκτός απο 127 που είναι το DELETE. Aπο 128-255 (Extended) σχεδόν όλοι εκτυπώνουν κάτι και βασικά απο κει και πέρα νομίζω εξαρτάται και απο την κωδικοσελίδα. Όποτε μπορείς να θεωρήσεις όλο το extended μέρος printable. Δηλαδή εν ολίγοις κάτι σαν αυτό να είναι ο έλεγχος: (που μπορεί να υπάρχει ήδη ως συνάρτηση > int isPrintable(char c) { return !( c<9 || (c>9 && c<32) ); } EDIT2: Οπότε μπορεί να γίνει κάπως έτσι η συνάρτηση > // [...] char tmp = '\0'; for (i=0,j=0; '\0' != s[i] && EOF != (int)(s[i]); i++) { if ( '\n' == s[i] ) { if ( j > maxLineLen ) maxLineLen = j; j = 0; continue; } tmp = s[i]; j += ('\t' == tmp) ? (tabSize - (j % tabSize)) : ( isPrintable(tmp) ? 1 : 0 ); } // [...]
migf1 Δημοσ. 20 Οκτωβρίου 2012 Δημοσ. 20 Οκτωβρίου 2012 Υπάρχουν οι isprint() και isgraph() που είναι και locale aware. Αλλά το θέμα είναι αυτό που είπες, εγώ δεν τη χρειάζομαι όλη αυτή την έξτρα δουλειά για αυτό που θέλω να κάνω (βασικά πρόκειται για menu-entries που θέλω να τα τυπώνω στη κονσόλα μέσα σε χρωματισμένο πλαίσιο, χωρίς να χρησιμοποιώ screen-buffer). Από την άλλη, αν πρόκειται να χρησιμοποιηθεί γενικώς η συνάρτηση και σε άλλα projects, τότε καλό θα ήταν να περιλαμβάνει όλες τις περιπτώσεις, μόνο που δεν ξέρω πως πρέπει να συμπεριφέρεται τελικά. Δηλαδή, για την περίπτωση που την θέλω εγώ χρειάζομαι να υπολογίζει ως maxLineLen οτιδήποτε τυπώνεται/σβήνεται στην οθόνη (και μάλιστα χωρίς να περιέχει άλλους ειδικούς πλην των '\t' και '\n'), αλλά μπορεί σε άλλες περιπτώσεις να μην χρειάζονται το "εκτυπώσιμο" μήκος αλλά το "εσωτερικό"... όπου "εσωτερικό" εννοώ το κάθε escape-char να λογίζεται ως 1 χαρακτήρας.
moukoublen Δημοσ. 20 Οκτωβρίου 2012 Δημοσ. 20 Οκτωβρίου 2012 Μπορείς να βάλεις μια διακλάδωση για το αν θα μετριούνται όλοι ή μόνο οι εκτυπώσιμοι. Κάτι του στύλ: > int s_maxLineLen( const char *s, int tabSize, int countAllChars ) { int i=0,j=0, maxLineLen = 0; if ( !s ) return -1; if ( !*s ) return 0; if ( tabSize < 1 ) tabSize = 8; // count len of largest line in s char tmp = '\0'; for (i=0,j=0; '\0' != s[i] && EOF != (int)(s[i]); i++) { if ( '\n' == s[i] ) { if ( j > maxLineLen ) maxLineLen = j; j = 0; continue; } tmp = s[i]; j += ('\t' == tmp) ? (tabSize - (j % tabSize)) : ( (isPrintable(tmp) || countAllChars) ? 1 : 0 ); } if ( j > maxLineLen ) maxLineLen = j; return maxLineLen; } Μόνο που παραμένει το θέμα με το delete και backspace. Νομίζω η πιο καλή λύση είναι ένα μεγάλο hash table με κάθε χαρακτήρα και το ποσό που θα προσθέτει στο length. Ώστε να βάζει 1, 0 ή -1 ανάλογα το χαρακτήρα (προφανώς αστειεύομαι). Θα ήταν εύκολο (δε ξέρω πως γίνεται σε C) να περνάς σαν παράμετρο μια συνάρτηση της υπογραφής int getCharCost(char) έτσι ώστε να γραφείς και να περνάς δυναμικά τον "αλγόριθμο" του τι να "προσθέτει" κάθε χαρακτήρας; (καλά κουβέντα να γίνεται έτσι, δεν έχει κανένα πρακτικό όφελος αυτό - μάλλον μόνο κόπος είναι);;; Και έτσι στον κώδικα να εισαι κάπως έτσι > j += ('\t' == tmp) ? (tabSize - (j % tabSize)) : getCharCost(tmp);
migf1 Δημοσ. 20 Οκτωβρίου 2012 Δημοσ. 20 Οκτωβρίου 2012 Γίνεται πανεύκολα στη C να περνάς callback συναρτήσεις ως ορίσματα, με function pointers. Το πρόβλημα όμως παραμένει π.χ. στο τι θα μετράς για VT και FF στη ρουτίνα που θες να σου μετράει ότι θα τυπωνόταν στην οθόνη.
moukoublen Δημοσ. 20 Οκτωβρίου 2012 Δημοσ. 20 Οκτωβρίου 2012 Μήπως τελικά δεν έχει καθόλου νόημα να ασχοληθείς με control characters μιας και η δουλειά της συνάρτησής σου είναι να υπολογίζει το μέγιστο μήκος σε ένα σύνολο από προτάσεις (αναγνώσιμες από άνθρωπο). Νομίζω στη θέση σου θα έβαζα απλά να μη προσθέτουν τίποτα οι control characters -απλά για λόγους ασφαλείας, αν τυχόν βρεθούν κατά λάθος μέσα στην πρόταση- και τον υπολογισμό του tab ως έχει. Ναι τώρα που το λες θυμάμαι ότι το είχα κάνει κάποτε για παιχνίδι. Αλλά δε θυμάμαι πολλά. Τώρα ξανακοίταξα στο internet ότι κάπως έτσι int (*getCharCost)(char) το δηλώνεις.
migf1 Δημοσ. 20 Οκτωβρίου 2012 Δημοσ. 20 Οκτωβρίου 2012 Μια που ανέφερες callback συναρτήσεις στη C, η συντριπτική πλειοψηφία π.χ. των GUI έτσι υλοποιείται. Ακόμα και σε κονσόλα είναι πολύ συνηθισμένο αν θες να υλοποιήσεις ένα text-based μενού να έχεις έναν πίνακα του στυλ... > #define VALID_MNCMD(mc) ( (mc) >= MNCMD_0 && (mc) < MAX_MNCMDS ) enum MenuCmd{ MNCMD_ERR = -1, MNCMD_0 = '0', MNCMD_1 = '1', ... MNCMD_9 = '9' MAX_MNCMDS }; typedef struct MenuEntry { enum MenuCmd cmd; char desc[MAXLEN_MNENTRY_DESC+1]; bool (*callback)(void *userdata); }MenuEntry; // ---------------------- void menu_print( MenuEntry menu[] ) { if ( !menu ) return; puts( "MAIN MENU" ); for (int i=0; i < N_MNENTRIES_MAIN; i++) printf( "%c\t%s\n", menu[i].cmd, menu[i].desc ); putchar('\n'); } // --------------------- bool menu_do( MenuEntry menu[], MyData *myData ) { if ( !menu || !myData ) return false; for (; { menu_print( menu ); enum MenuCmd cmd = getchar(); if ( !VALID_CMD(cmd) ) { puts("try again"); continue; } if ( MNCMD_9 == cmd ) break; // exit menu; if ( !menu[ cmd-'0'].callback( myData ) ) // handle an callback return errors here } return true; } // ------------------------------- bool do_load_data( myData *myData ) { if ( !myData) return false; // load my data here return true; } ... // ------------------------------- int main( void ) { ... MenuEntry mainMenu[ N_MNENTRIES_MAIN] = { {'0', "Load", &do_load_data}, {'1', "Print",&do_print_data}, ... {'9', "Exit", NULL}, }; ... menu_do( mainMenu, &myData ); ... } ΥΓ. Δεν το έχω τεστάρει, το έγραψα απευθείας εδώ, στον editor του φόρουμ. Μήπως τελικά δεν έχει καθόλου νόημα να ασχοληθείς με control characters μιας και η δουλειά της συνάρτησής σου είναι να υπολογίζει το μέγιστο μήκος σε ένα σύνολο από προτάσεις (αναγνώσιμες από άνθρωπο). ... Νομίζω στη θέση σου θα έβαζα απλά να μη προσθέτουν τίποτα οι control characters -απλά για λόγους ασφαλείας, αν τυχόν βρεθούν κατά λάθος μέσα στην πρόταση- και τον υπολογισμό του tab ως έχει. Νόημα για την δουλειά που την θέλω έχει έτσι όπως την έχω τώρα, δηλαδή να μετράει τα tabs όπως είπαμε με το modulo, και να μηδενίζει όταν βρίσκει '\n'. EDIT: "Κατέβασα" το mainMenu[] από καθολικό σε τοπικό μέσα στη main(). 1
migf1 Δημοσ. 23 Οκτωβρίου 2012 Δημοσ. 23 Οκτωβρίου 2012 Δεν ξέρω κατά πόσο είναι ενδιαφέρον για το παρόν νήμα (εννοώντας πως ίσως πέσει "βαρύ"), αλλά μιας που πιάσαμε τα function pointers κι επειδή τα χρησιμοποιώ κατά κόρον (και κυρίως επειδή χρειάζομαι ένα διάλειμμα να ξεθολώσω λιγάκι ), παραθέτω έναν όχι βέλτιστο τρόπο να διαβάζουμε τα πεδία ενός struct round από την stdin με λογική παραπλήσια των GUI dialog boxes. Εννοείται πως δεν τα γράφω από το μηδέν, αλλά με επιλεκτικό copy & paste από ένα project με το οποίο ασχολούμαι αυτό τον καιρό. Χρησιμοποιεί function pointers σε 3 επίπεδα (2 callback κι ένα κανονικό) για αυτό και το ποστάρω, σε περίπτωση που φανεί χρήσιμο. Σημειώστε επίσης πως σε κανονικά GUI η υλοποίηση των dialog boxes είναι κατά πολύ πιο σύνθετη, μιας και είναι πολύ πιο generic και με πολύ περισσότερες παραμέτρους και αλληλο-επιδράσεις. Πάντως καλού-κακού, αν αποφασίσετε να διαβάσετε το ποστ, προτείνω να κάνετε μια βαρβάτη κούπα καφέ (όσοι πίνετε) και να 'χετε πρόχειρο και το πακέτο με τα τσιγάρα σας (όσοι καπνίζετε) Η βασική ρουτίνα του... ψευτο-dialog... > ... #define CLRSCR true // do NOT change this ... enum ReadRoundDlgField { RRDLG_FLD_NONE = -1, RRDLG_FLD_GTYPE = 0, RRDLG_FLD_GPLAYERS, RRDLG_FLD_SEAT, ... N_RRDLG_FIELDS }; ... /*********************************************************//** * @par Prototype: * enum UiDialogExitCode ui_dialog_read_round( Round *round, const DBase *dbase ); * * @brief Dialog-like implementation for inputing a round, from stdin. * @param[in,out] round Pointer to the round being inputted. * @param dbase Pointer to the database related to the inputted round. * @return The dialog exit-code, which is assigned any of the following values, * depending on the user actions: * - UI_DLGXC_OK: input finished normally (like OK button) * - UI_DLGXC_CANCEL: input cancelled by the user (like CANCEL button) * - UI_DLGXC_NONE: input cancelled by the program, due to an error ************************************************************* */ enum UiDialogExitCode ui_dialog_read_round( Round *round, const DBase *dbase ) { enum ReadRoundDlgField i = RRDLG_FLD_NONE; // enum UiDialogExitCode xc = UI_DLGXC_NONE; enum UIKeyCommand keyCmd = NOVAL_UI_KEYCMD; err_set_noerror(); // sanity checks if ( !round ) { err_set( ERR_INVPOINTER ); errMSGBOXF( UI_MB_FATAL, "*** %s (%s) ", err_get_msg(), "round" ); return UI_DLGXC_NONE; } if ( !db_is_inited(dbase) ) { errMSGBOXF( UI_MB_FATAL, "*** %s", err_get_msg() ); return UI_DLGXC_NONE; } // the main-loop of the dialog for (i=RRDLG_FLD_GPLAYERS; i < N_RRDLG_FIELDS; i++) { ui_print_round( round, CLRSCR ); switch (i) { case RRDLG_FLD_GPLAYERS: // # of players keyCmd = _dlg_read_round_element( round, CLRSCR, &round_init_gamePlayers, &_cb_makePrompt_rgPlayers, &_cb_askfor_round_gPlayers ); break; case RRDLG_FLD_SEAT: // table seat keyCmd = _dlg_read_round_element( round, CLRSCR, &round_init_seat, &_cb_makePrompt_rSeat, &_cb_askfor_round_Seat ); break; ... default: break; } // act upon the key-command returned after a dialog-field has been inputted switch ( (uint8_t)keyCmd ) { case UI_KEYCMD_ROUND_EXIT: return UI_DLGXC_CANCEL; case UI_KEYCMD_ROUND_SKIPFLD: if ( RRDLG_FLD_ORESULT == i ) break; i = RRDLG_FLD_ORESULT - 1; break; case UI_DLGNK_FIRST: i = RRDLG_FLD_GPLAYERS - 1; break; case UI_DLGNK_LAST: i = N_RRDLG_FIELDS - 2; break; case UI_DLGNK_PREV: i = (RRDLG_FLD_GPLAYERS == i) ? i-1 : i-2; break; case UI_DLGNK_NEXT: if ( N_RRDLG_FIELDS - 1 == i ) i--; break; default: break; } } return UI_DLGXC_OK; } Ο enumerator ReadRoundDlgField στην αρχή, αντιστοιχεί στα fields του struct round, και χρησιμοποιείται για να ξέρουμε σε ποιο field του dialog βρισκόμαστε κατά το input. Προσπερνώντας τα sanity-checks στην αρχή της συνάρτησης, ο εν λόγω enumerator κυριαρχεί στο βασικό loop του dialog, επιτρέποντάς μας να διαχειριζόμαστε ξεχωριστά το input κάθε dialog-field με το switch μέσα στο loop (κανονικά δεν χρειάζεται πεπερασμένο πλήθος iterations του loop, το κάνουμε infinite loop ... εγώ το χρειάζομαι έτσι για λόγους άσχετους με το παράδειγμα). Στο κάθε case του πρώτου switch αντί να έχουμε ξεχωριστές συναρτήσεις για κάθε πεδίο του struct round, χρησιμοποιούμε την generic _dlg_read_round_element() η οποία διαχειρίζεται αυτόνομα μεν, ομοιογενώς δε, το input οποιουδήποτε dialog-field. Οι όποιες διαφοροποιήσεις στην διαχείριση των round fields (τα λέω round elements εγώ στον κώδικα) αντιμετωπίζονται μέσω callback συναρτήσεων που τις περνάμε ως παραμέτρους στα 3 τελευταία ορίσματα της _dlg_read_round_element(). Η συνάρτηση αυτή επιστρέφει ένα key-command, με το οποίο η βασική μας συνάρτηση ουσιαστικά εκτελεί τις υψηλότερου επιπέδου εντολές του user, μέσω του δεύτερου switch. Ως υψηλότερου επιπέδου εντολές χαρακτηρίζω όσες αφορούν το κεντρικό dialog-box (όχι δηλαδή εκείνες που ασχολούνται με την είσοδο του εκάστοτε από τα dialog-fields) Δηλαδή τα DialogExiCodes: UI_DLGXC_OK UI_DLGXC_CANCEL UI_DLGXC_NONE καθώς και τυχόν εντολές μετακίνησης μέσα στο κεντρικό dialog (Dialog Navigation Keys): UI_DLGNK_xxx Οι χαμηλότερου επιπέδου εντολές είναι εκείνες που χρησιμοποιούνται εσωτερικά στις ρουτίνες που διαχειρίζονται το input του κάθε dialog-field. Δηλαδή στις callback συναρτήσεις: _cb_askfor_round_gPlayers() και _cb_askfor_round_Seat() στον παραπάνω κώδικα, οι οποίες περνιούνται ως 3ο όρισμα στην _dlg_read_round_element(). Το κάθε dialog-field έχει δικό του, ξεχωριστό inputting-loop υλοποιημένο ως ξεχωριστή συνάρτηση, με δικές της χαμηλού επιπέδου εντολές (οι 2 _cb_askfor_round_xxx() callback συναρτήσεις της προηγούμενης παραγράφου είναι 2 από αυτές. Όταν ολοκληρώσουν το input του πεδίου, τότε επιστρέφουν στο πάνω επίπεδο μια τιμή (εντολή) εκ των... UI_KEYCMD_ROUND_EXIT UI_KEYCMD_ROUND_SKIPFLD με την δεύτερη να μην επιτρέπεται σε όλα τα dialog-fields (είναι μια ειδική εντολή που μετακινεί την εστίαση σε ένα συγκεκριμένο dialog-field, αλλά δεν επιτρέπεται στα 5 πρώτα πεδία του dialog). Αυτές οι χαμηλού επιπέδου συναρτήσεις "καταλαβαίνουν" επίσης και τις υψηλού επιπέδου εντολές μετακίνησης (Dialog Navigation Keys), αλλά σε αυτή την περίπτωση απλώς τις επιστρέφουν ατόφιες. Ακολουθεί ως παράδειγμα ο κώδικας της χαμηλού επιπέδου συνάρτησης _cb_askfor_round_gPlayers() η οποία υλοποιεί το inputting-loop του πεδίου gamePlayers του struct round... > /*********************************************************//** * @par Prototype: * _cb_askfor_round_gPlayers( const char *prompt, Round *round ); * * @brief Implementation ************************************************************* */ static enum UIKeyCommand _cb_askfor_round_gPlayers( const char *prompt, Round *round ) { char input[MAXINPUT] = {'\0'}; char *cpTrimmed = NULL; enum UIKeyCommand ch = NOVAL_RGPLAYERS; Nibble nplayers = NOVAL_RGPLAYERS; err_set_noerror(); if ( !round ) { err_set( ERR_INVPOINTER ); errMSGBOXF( UI_MB_FATAL, "*** %s (%s) ", err_get_msg(), "round" ); return NOVAL_UI_KEYCMD; } // demand a valid number of game players or a recognised-command char for (; { char *cpTail = NULL; unsigned long try = ULONG_MAX; // read input line from stdin _askfor_string( SKIN_FG_NPLAYERS, SKIN_BG_NPLAYERS, prompt, input, MAXINPUT ); // strip-off leading & trailing blanks from input, // and store the 1st of the remaining chars in ch ch = ( NULL == (cpTrimmed = s_cpTrim(input)) ) ? NOVAL_RGPLAYERS : *cpTrimmed; // first check if a command was given if ( (uint8_t)UI_DLGNK_FIRST == (uint8_t)ch || (uint8_t)UI_DLGNK_LAST == (uint8_t)ch || (uint8_t)UI_DLGNK_PREV == (uint8_t)ch || (uint8_t)UI_DLGNK_NEXT == (uint8_t)ch ) { break; } if ( UI_KEYCMD_ROUND_EXIT == ch ) { break; } if ( UI_KEYCMD_ROUND_SKIPFLD == ch ) { ui_msgboxf( UI_MB_HELP, STRF_RNDREAD_SKIPFLD_SHOULDNT, UI_KEYCMD_ROUND_HINT ); continue; } if ( UI_KEYCMD_ROUND_HINT == ch ) { _hint_rgPlayers_letters(); continue; } // no command given, validate input against valid nplayers range try = strtoul(input, &cpTail, 10); nplayers = (Nibble) try; if ( cpTail == input || '\0' != *cpTail || EINVAL == errno || ERANGE == errno || ULONG_MAX == try || try > MAX_RGPLAYERS || try < MIN_RGPLAYERS ){ nplayers = NOVAL_RGPLAYERS; } // act upon validated nplayers if ( nplayers > MAX_RGPLAYERS || nplayers < MIN_RGPLAYERS) { ui_msgboxf( UI_MB_HELP, STRF_RNDREAD_INVALID_GPLAYERS, UI_KEYCMD_ROUND_HINT ); continue; } round_set_gamePlayers(round, nplayers); break; } return ch; } Προσπερνώντας το housekeeping, βλέπουμε πως η συνάρτηση αυτή διαβάζει το πλήθος των gamePlayers, κι επιστρέφει στον caller της την τιμή UI_KEYCMD_ROUND_EXIT ενώ αγνοεί συνειδητά την UI_KEYCMD_ROUND_SKIPFLD (επειδή το συγκεκριμένο πεδίο δεν επιτρέπεται να γίνει skipped). Σε περίπτωση που ο χρήστης της δώσει ως εντολή κάποιο από τα DialogNavigationKeys, απλώς την επιστρέφει ατόφια. Είναι μαρκαρισμένη στην αρχή του ονόματός της ως _cb_ αφενός επειδή χρησιμοποιείται αποκλειστικά ως callback κι αφετέρου διότι είναι private στο τρέχον file-scope, δηλαδή static (σε αυτές βάζω ως πρόθεμα ένα underbar, παρόλο που γνωρίζω πως αντιτίθεται στις επιταγές των στάνταρ της γλώσσας). Αυτό που επιστρέφει η συνάρτηση, το δέχεται αρχικά η συνάρτηση _dlg_read_round_element(), η οποία με τη σειρά της το περνάει στην βασική συνάρτηση του dialog (στο spoiler, στην αρχή του post). Αυτή τέλος το διαχειρίζεται μέσω του 2ου switch της. Δηλαδή η _dlg_read_round_element() λειτουργεί ως ενδιάμεσο επίπεδο, μεταξύ του χαμηλότερου ( _cb_askfor_round_gPlayers() ) και του υψηλότερου ( ui_dialog_read_round() ). Ακολουθεί ο κώδικας της _dlg_read_round_element() που δείχνει ότι όντως καλεί την _cb_askfor_round_gPlayers() όχι απλά ως callback, αλλά ως callback σε callback... > /*********************************************************//** * @par Prototype: * enum UIKeyCommand _dlg_read_round_element( ...see below... ); * * @brief Dialog-like implementation for the inputing-loop of a single dialog-field, * corresponding to an element (field) of a specified round. * @param[in,out] round Pointer to the round whose element will be inputted. * @param cls Should the dialog being redrawn before attempting the input? * @param cb_init_round_element() Callback function for initializing the round-element * before inputting it. * @param cb_makePrompt_for_element() Callback function for generating an inputting prompt * for the round-element. * @param cb_askfor_round_element() Callback function implementing the inputting-loop * for the dialog-field. * @return The key-command returned by the inputting-loop, which takes the value * NOVAL_UI_KEYCMD in case of an error. * Otherwise it may be either a reserved dialog navigation key (UI_DLGNK_xxx), * or a key-command specific to the inputting-loop, which breaks the loop * (namely: UI_KEYCMD_ROUND_EXIT and in some cases UI_KEYCMD_ROUND_SKIPFLD). * * The caller function (that is the dialog's main function) is responsible * for getting this key-command and then taking appropriate action. ************************************************************* */ enum UIKeyCommand _dlg_read_round_element( Round *round, bool cls, bool (*cb_init_round_element)(Round *round), bool (*cb_makePrompt_for_element)(char *prompt), enum UIKeyCommand (*cb_askfor_round_element)(const char *prompt, Round *round) ) { char prompt[MAXINPUT] = {'\0'}; enum UIKeyCommand keyCmd = NOVAL_UI_KEYCMD; err_set_noerror(); if ( !round || !cb_init_round_element || !cb_makePrompt_for_element || !cb_askfor_round_element ){ err_set( ERR_INVPOINTER ); errMSGBOXF( UI_MB_FATAL, "*** %s (%s OR %s OR %s OR %s) ", err_get_msg(), "round", "cb_init_round_element", "cb_makePrompt_for_element", "cb_askfor_round_element" ); return NOVAL_UI_KEYCMD; } (*cb_init_round_element)(round); ui_print_round( round, cls ); _makePrompt( prompt, cb_makePrompt_for_element ); keyCmd =_askfor_round_element( prompt, round, cb_askfor_round_element ); return keyCmd; } Πιο συγκεκριμένα, το διπλό αυτό callback γίνεται στην γραμμή... > ... keyCmd =_askfor_round_element( prompt, round, cb_askfor_round_element ); ... όπου η (περασμένη ως 5ο όρισμα) callback συνάρτηση cb_askfor_round_element περνιέται εκ νέου ως callback συνάρτηση στην _askfor_round_element()... στο 3ο της όρισμα. Ιδού και ο κώδικας της _askfor_round_element() προκειμένου να ... ολοκληρωθεί το puzzle... > /*********************************************************//** * ************************************************************* */ static enum UIKeyCommand _askfor_round_element( const char *prompt, Round *round, enum UIKeyCommand (*cb_askfor_round_element)(const char *prompt, Round *round) ) { enum UIKeyCommand keyCmd; char input[MAXINPUT] = {'\0'}; char *cpTrimmed = NULL; err_set_noerror(); // sanity checks if ( !round || !cb_askfor_round_element ) { err_set( ERR_INVPOINTER ); errMSGBOXF( UI_MB_FATAL, "*** %s (%s OR %s) ", err_get_msg(), "round", "cb_askfor_round_element" ); return NOVAL_UI_KEYCMD; } for (; { keyCmd = (*cb_askfor_round_element)( prompt, round ); if ( UI_KEYCMD_ROUND_EXIT == keyCmd ) { prompt_for_string( STR_RNDREAD_EXITYESNO, input, MAXINPUT ); cpTrimmed = s_cpTrim(input); if ( NULL == cpTrimmed || 'n' != tolower( (int)(*cpTrimmed) ) ) break; } else { break; } } return keyCmd; } Ακριβώς το ίδιο callback σε callback γίνεται και με το 4ο όρισμα της _dlg_read_round_element(), δηλαδή της callback συνάρτησης cb_makePrompt_for_element(). Δεν καλείται απευθείας, αλλά περνιέται ξανά ως callback συνάρτηση στο 2ο όρισμα της _makePrompt(). Η συνάρτηση που εκτελείται καθορίζεται στην βασική συνάρτηση του Dialog (στην αρχή του ποστ). Τελειώνοντας λοιπόν, δίνω και τον κώδικα της _makePrompt() που δείχνει αυτό το διπλο-κάλεσμα, καθώς και τον κώδικα της _cb_makePrompt_rgPlayers() που φτιάχνει το prompt για τους gamePlayers του round... > /*********************************************************//** * @brief Make a prompt corresponding to the # of players in a round. * @param[in,out] prompt The c-string to hold the prompt. * @return False on error, true otherwise. * @note This is a callback function, passed as an argument * to the function: makePrompt(). ************************************************************* */ static bool _cb_makePrompt_rgPlayers( char *prompt ) { if ( !prompt ) return false; snprintf( prompt, MAXINPUT, STRF_RNDREAD_PROMPT_GPLAYERS, MIN_RGPLAYERS, MAX_RGPLAYERS ); return true; } /*********************************************************//** * @brief General function, to prepare a c-string prompt. * @param[in,out] prompt The c-string to hold the prompt. * @param cb_makePrompt() The callback function that actually prepares the prompt. * @return False on error (e.g. NULL == prompt), true otherwise. ************************************************************* */ static bool _makePrompt( char *prompt, bool (*cb_makePrompt)(char *) ) { return (*cb_makePrompt)(prompt); } Αντίθετα η callback συνάρτηση cb_init_round_element() (3ο όρισμα _dlg_read_round_element() ) καλείται απευθείας ... εδώ έχουμε δηλαδή απλό callback. 1
Star_Light Δημοσ. 25 Οκτωβρίου 2012 Δημοσ. 25 Οκτωβρίου 2012 (επεξεργασμένο) Άν έχουμε έναν δεικτη p σε έναν πινακα array[5] με p = array ή p = & array[0] . Το p + 2 πχ θα δειχνει στο array[i + 2 ] -> array[2] ( i = 0 στο ξεκινημα ενος βροχου) επειδη μεσω της αριθμητικης των δεικτων το p + 2 μεταφράζεται p + 2 * 4 = p + 8 = 8 bytes (Αν ο int ειναι 4 byte σε κάποιο συστημα ) οποτε αν η αρχικη διευθυνση του 1ου στοιχειου ειναι 0 ο δεικτης θα χρειαστει να καλύψει "αποσταση" ιση με 8 bytes πέφτωντας πανω στο στοιχειο array[2]. θΕΛΩ ΝΑ ΠΩ οτι γιατι αν δειχνει στο 1ο στοιχειο ο p ή το 2ο αντιστοιχα και δώσεις την έκφραση p = p + 2; ή p = p + 4; θα δειχνει πλεον ο p στο a[2] ή στο a[5] αντιστοιχα ? Επειδη η έκφραση p = p + 4 ας πουμε ειναι ιση με p + 4 * 4 = p + 16 δηλαδη απο το 1ο στοιχειο θα κινηθει ο δεικτης 16 bytes ? 4 ακεραιους δηλαδη σε μεγεθος? και θα πέσει στο a[5] . υ.γ Δεν ξερω αν ειχε συμβει και σε αλλους αλλα στο κεφαλαιο με τους δισδιαστατους και τους δεικτες σε αυτους εχω μπερδευτει. Επεξ/σία 26 Οκτωβρίου 2012 από Star_Light
moukoublen Δημοσ. 26 Οκτωβρίου 2012 Δημοσ. 26 Οκτωβρίου 2012 Γενικά > TYPE *t; /* ... */ t += 1; /* => t += {1*sizeof(TYPE)}BYTES */ Δεν είναι δηλαδή ξερή αύξηση κατά 1 της διεύθυνσης μνήμης. Αλλά είναι πρόσθεση 1 (μίας) "θέσης - κουτί - πακέτου" σύμφωνα με τον συγκεκριμένο, κάθε φορά, τύπο δεδομένων. > #include <stdio.h> int main(void) { int x[2]; int *p = x; printf("address: %X\n", p); p += 1; printf("address: %X\n", p); return 0; } έχει έξοδο > address: BFCDCB50 address: BFCDCB54 έχει προστεθεί το 4 στη "καθαρή" διεύθυνση μνήμης. 1
migf1 Δημοσ. 26 Οκτωβρίου 2012 Δημοσ. 26 Οκτωβρίου 2012 ... θΕΛΩ ΝΑ ΠΩ οτι γιατι αν δειχνει στο 1ο στοιχειο ο p ή το 2ο αντιστοιχα και δώσεις την έκφραση p = p + 2; ή p = p + 4; θα δειχνει πλεον ο p στο a[2] ή στο a[5] αντιστοιχα ? Επειδη η έκφραση p = p + 4 ας πουμε ειναι ιση με p + 4 * 4 = p + 16 δηλαδη απο το 1ο στοιχειο θα κινηθει ο δεικτης 16 bytes ? 4 ακεραιους δηλαδη σε μεγεθος? και θα πέσει στο a[5] . Ναι. Ίσως σε βοηθήσει κι αυτό: http://x-karagiannis.gr/prg/c-notes/pointers/arithmetic/ υ.γ Δεν ξερω αν ειχε συμβει και σε αλλους αλλα στο κεφαλαιο με τους δισδιαστατους και τους δεικτες σε αυτους εχω μπερδευτει. Δηλαδή; Για παράδειγμα κάτι σαν το παρακάτω σε μπερδεύει; > #include <stdio.h> #include <stdlib.h> #define NROWS 4 #define NCOLS 3 /*********************************************//** * ************************************************* */ void arr_print( int arr[], int nelems ) { if ( nelems > NCOLS ) return; for (int i=0; i < nelems; i++) printf( "%d ", arr[i] ); puts("\b"); return; } /*********************************************//** * ************************************************* */ int main( void ) { int *pRow = NULL; int arr2d[NROWS][NCOLS] = { {0,0,0}, {1,1,1}, {2,2,2}, {3,3,3} }; for (pRow = *arr2d; pRow < arr2d[NROWS]; pRow += NCOLS ) { arr_print( pRow, NCOLS ); } system("pause"); // Windows only exit(0); }
Star_Light Δημοσ. 26 Οκτωβρίου 2012 Δημοσ. 26 Οκτωβρίου 2012 @migf1 ωραιος ο οδηγος σου αλλα εχω μια ένσταση. Και την ιδια ένσταση παρουσιάζει και ο King στο βιβλιο του γιατι γενικα για την αριθμητικη δεικτων έψαξα και στους K&R που τα εξηγει και εκει ωραια: H σημασια του προσθέτω 1 σε κάποιο δείκτη και κατ' επεκταση ολοκληρης της αριθμητικης δεικτών ειναι οτι ο p + 1 δειχνει το επομενο αντικειμενο και ο p + i το i-οστο μετα το p. Και προσθέτω εγω οτι το πόσο θα καταλαμβάνει καθε αντικειμενο / στοιχείο έχει να κάνει με το μέγεθος του τύπου δεδομένων (sizeof(int) αν ειναι τύπου int δηλωμένος) στα οποία δειχνει ο δείκτης . Ο οποιος φυσικα ταιριάζει με τον τύπο δεδομενων που ειναι δηλωμένος ο πινακας. O μεταγλωτιστης θα μεταφράσει το p+1 σαν p + 1 * sizeof(int) . > Εχεις δηλαδη ενα πινακακι με 5 στοιχεια τυπου int και επισης εναν δεικτη που έχεις αρχικοποιησει να δειχνει στο 2ο στοιχειο αυτου του πινακα στο arr[1] ας πουμε αν δώσεις την εντολη p = p + 3; αυτο σημαινει οτι θα τον μετακινησουμε 3 στοιχεία δεξια . Ο δεικτης ξέρει οτι πρέπει να κινηθεί 3 * 4 = 12 bytes (αναλογα το συστημα ειπαμε) πέφτοντας στο arr[4] . Δεν ειναι σιγουρο οτι κερδιζεις ταχυτητα αν αντι για array indexing χρησιμοποιησεις pointer arithmetic επειδη σε ειδα οτι το χεις γραψει μεσα ξεκαθαρα . Αυτο γινοταν στους παλιους μεταγλωτιστες αλλα πλεον δεν ειναι ευκολη η απαντηση σε αυτο. Τουλαχιστον αυτη την εκδοχη δινει ο Κινγκ. Οι K&R μιλανε επισης για ταχυτητα αλλα μην ξεχναμε οτι το βιβλιο που εχω εγω εχει να κανει με την C89. ΤΩρα για τον αλλο κωδικα με τους δεικτες και το πινακακι θα τον δω πιο μετα και θα σου πω. Ξερω παντως οτι ενας δισδιαστατος στην C αντιμετωπιζεται σαν ενας πινακας απο πινακες λογω του τροπου αποθηκευσης σε γραμμικη διαταξη.
migf1 Δημοσ. 26 Οκτωβρίου 2012 Δημοσ. 26 Οκτωβρίου 2012 @migf1 ωραιος ο οδηγος σου αλλα εχω μια ένσταση. Και την ιδια ένσταση παρουσιάζει και ο King στο βιβλιο του γιατι γενικα για την αριθμητικη δεικτων έψαξα και στους K&R που τα εξηγει και εκει ωραια: ... Δεν ειναι σιγουρο οτι κερδιζεις ταχυτητα αν αντι για array indexing χρησιμοποιησεις pointer arithmetic επειδη σε ειδα οτι το χεις γραψει μεσα ξεκαθαρα . Αυτο γινοταν στους παλιους μεταγλωτιστες αλλα πλεον δεν ειναι ευκολη η απαντηση σε αυτο. Τουλαχιστον αυτη την εκδοχη δινει ο Κινγκ. Οι K&R μιλανε επισης για ταχυτητα αλλα μην ξεχναμε οτι το βιβλιο που εχω εγω εχει να κανει με την C89. Έτσι είναι, το γράφω κι εγώ αλλά στην αμέσως επόμενη σελίδα: http://x-karagiannis.gr/prg/c-notes/pointers/pre-post-increaments/ ΤΩρα για τον αλλο κωδικα με τους δεικτες και το πινακακι θα τον δω πιο μετα και θα σου πω. Ξερω παντως οτι ενας δισδιαστατος στην C αντιμετωπιζεται σαν ενας πινακας απο πινακες λογω του τροπου αποθηκευσης σε γραμμικη διαταξη. Όχι ακριβώς όμως. Σε αντίθεση με τους απλούς δείκτες, οι πίνακες δεν μπορούν να χρησιμοποιηθούν ως lvalues.
Star_Light Δημοσ. 26 Οκτωβρίου 2012 Δημοσ. 26 Οκτωβρίου 2012 Να σου πω την αληθεια αυτο με τις αυξομειωσεις το πέρασα αέρα.... δεν μπορω να πω οτι με δυσκολεψε . Γιατι οταν γραφεις για εμπειρους προγραμματιστες ειναι φανερή η μπικτή προς εμένα Δεν μπορω να καταλάβω ποια τιμή δεν χρησιμοποιεί ο compiler και μου δινει warn εδω -> > #include <stdio.h> void double_pointer_f(int **); int main(void) { int x=2 , *p , **pp , y=3 , *q = &y; p = &x; // Δείκτης = δείκτης printf(" x now is : %d " , x); double_pointer_f(&p); printf(" x now is : %d " , x); pp = &p; // δεικτης σε δεικτη = δείκτης σε δεικτη printf(" %d" , **pp); *q++; printf("\n %d " , x ); return 0; } void double_pointer_f(int **p) { **p=4; return ; } Αγνοησε τα άλλα και δες μονο το *q++ και την printf μετα. Να ρωτησω κατι ακομη.... αν έχουμε a[j] = 0 o μεταγλωτιστης παράγει οδηγιες για να γίνουν : 1. Πολλ/σμος του i με το μέγεθος μιας single γραμμής 2. Δινει αυτο το αποτέλεσμα εκει που αναπαριστα ο a 3. ΠΟλλαπλασιαζει το j με το μέγεθος ενος στοιχειου πινακα και 4. βάζει αυτο τον υπολογισμο στην διευθυνση που παραγεται απο το βημα 2. οταν λεει single row σε εναν δισδιαστατο εννοει πως αν εχω πχ τον Α = [ 1 2 3 4 5 6 ] ΑΥΤΟΣ θα αποθηκευθει απο την C σαν 1 2 3 (γραμμη 0 ) 4 5 6 (γραμμη 1) 0 ή 1 γραμμή θα εννοει οταν λεει single row. αΡΑ για το 2ο στοιχειο a[0][1] θα έχουμε 0 * 12 (3 στοιχεια επι 4 bytes) = 12 αν θεωρησουμε 0 την διεθυνση που αναπαριστα ο a θα ειναι 0 + 0 στο βήμα 3 θα είναι ξερω γω 1 * 4 = 4 Aρα 0 + 4 κ.ο.κ καπως ετσι δεν παει αυτο? Επειδη σε αυτο με τους δεικτες και τους 2D arrays παιζει πολυ single row & column. Για αυτο προσπαθω να κανω ενα visualization στο μυαλο μου. p.s Στην άσκηση που δίνεις στην σελιδα σου η απάντηση ειναι nums[10] . Αλλα δεν έχεις προβλημα μιας και δεν θα προσπελάσεις ποτε τα περιεχομενα της ωστε να βγεις εκτος οριων.
Προτεινόμενες αναρτήσεις