capoelo Δημοσ. 31 Μαΐου 2012 Δημοσ. 31 Μαΐου 2012 #include<stdio.h> #include<stdlib.h> #include<string.h> int main(void) { char str1[]="Pointers are fun to use"; char str2[80],*p1,*p2; p1=str1+strlen(str1)-1; p2=str2; while(p1>=str1) *p2++=*p1--; *p2='\0'; /*το str2 τερματίζει με null*/ printf("%s %s\n",str1,str2); system("Pause"); return 0; } Η ερώτηση μου είναι η εξής:η εντολή στην οποία έχω βάλει το σχόλιο,χρησιμεύει για όταν το p1 γίνει μικρότερο απο το str1,να περάσουμε το null στην τιμή που θα έχει εκείνη τη στιγμή το p2; Αν τη παραλείψω θα έχω πρόβλημα;
gon1332 Δημοσ. 31 Μαΐου 2012 Δημοσ. 31 Μαΐου 2012 Δοκίμασε να τρέξεις το πρόγραμμα χωρίς την εντολή αυτή. Δες τι σου βγάζει. Τότε θα καταλάβεις την χρησιμότητα του '\0' στα strings Αν παραλείψεις αυτή την εντολή μπορείς να δεις παρακάτω την τελική τιμή της( μέσω gdb): >(gdb) p str2 $5 = "esu ot nuf era sretnioP\277\352\360\377\277\302\000\000\000\001\000\000\000\377\262\360\000\000\000\000\000\000\000\312\000\364\237\004\b\001\000\000\000!\203\004\b\030\371\022\000m\202\004\b\364\237\004\ba\205\004\b\200\203\004\b" Όπως παρατηρείς μέχρι να παρεμβληθεί τυχαία κάποιο '\0' έχουν αποθηκευτεί στο str2 άλλοι 6 άχρηστοι χαρακτήρες. Αυτοί προϋπήρχαν, απλά η C, για να καταλαβαίνει τους οφέλημους χαρακτήρες ενός string, scan-άρει μέχρι να εντοπίσει το '\0'. Αν το παραλείψεις, τότε στην καλύτερη να υπάρχει κάπου τυχαία, όπως εδώ. Στη χειρότερη, θα σου σκάσει το πρόγραμμα με segmentation fault.
defacer Δημοσ. 31 Μαΐου 2012 Δημοσ. 31 Μαΐου 2012 Η εντολή αυτή τρέχει μόνο μία φορά, μετά το while (αφού είναι εκτός του loop). Νομίζω πως εσύ είχες κάτι άλλο στο μυαλό αφού λες "εκείνη τη στιγμή". Όταν λοιπόν τελειώσει το loop και το str1 έχει αντιγραφεί στο str2 με τη γραμμή που λες βάζεις κι ένα null στο τέλος για να έχεις στο str2 ένα κανονικό C-style string. Αν την παραλείψεις τότε αυτό που έχεις δεν είναι C-style string αλλά ένα μάτσο bytes που τυχαίνει να περιέχουν κείμενο, και σε περίπτωση που δοκιμάσεις να χρησιμοποιήσεις το str2 σα string (όπως κάνεις παρακάτω με την printf) θα υπάρξει πρόβλημα. Το πρόβλημα είναι ότι η όποια function πάει να βρει το τέλος του str2 θα συνεχίσει να ψάχνει μετά το "κανονικό" του τέλος μέχρι να βρει ένα 0 byte. Αρχικά θα ψάξει σε όσους από τους 80 χαρακτήρες έτυχε να περισσέψουν (by the way, το πρόγραμμα έχει θέμα αν strlen(str1) >= 80) οι οποίοι θα έχουν "τυχαίες" (τεχνικά η σωστή λέξη είναι "απρόβλεπτες")τιμές εφόσον το str2 είναι local μεταβλητή. Σε περίπτωση που δεν βρει κάτι τέτοιο θα συνεχίσει να ψάχνει στο stack μέχρι τη στιγμή που όντως θα βρεί ένα 0 (το πότε θα γίνει αυτό εξαρτάται), με αποτέλεσμα το τελικό "string" να έχει λιγότερα ή περισσότερα σκουπίδια στο τέλος του.
capoelo Δημοσ. 12 Ιουλίου 2012 Μέλος Δημοσ. 12 Ιουλίου 2012 Έχω ερώτηση:εάν προσπαθήσω να περάσω μία char τιμή σε έναν δείκτη int,θα λειτουργήσει κανονικά διότι ο int έχει μεγαλύτερο μέγεθος απο char.Το πρόβλημα θα παρουσιαστεί μόνο αν προσπαθήσω να περάσω μεγαλύτερη τιμή σε μικρότερο τύπο δεδομένων.Σωστά;
migf1 Δημοσ. 12 Ιουλίου 2012 Δημοσ. 12 Ιουλίου 2012 Έχω ερώτηση:εάν προσπαθήσω να περάσω μία char τιμή σε έναν δείκτη int,θα λειτουργήσει κανονικά διότι ο int έχει μεγαλύτερο μέγεθος απο char.Το πρόβλημα θα παρουσιαστεί μόνο αν προσπαθήσω να περάσω μεγαλύτερη τιμή σε μικρότερο τύπο δεδομένων.Σωστά; Και ναι και όχι, γιατί δεν διευκρινίζεις τι εννοείς όταν λες να εκχωρήσεις έναν char σε έναν int *. Μπορεί να εννοείς οποιοδήποτε από τα παρακάτω... > int *pint = NULL; pint = 'c'; // εδώ δίνεις ως διεύθυνση των περιεχομένων του pint το ascii-code του 'c' ή *pint = 'c'; // εφικτό μονάχα αν έχει γίνει πρώτα allocated μνήμη για τον pint τα οποία btw είναι και τα 2 λάθος (είτε άμεσα είτε έμμεσα). Αν όμως εννοείς π.χ.... > char c = 'A'; int i = 30000; i = c; // no problem c = i; // problem τότε ισχύει αυτό που έγραψες Τη 2η γραμμή μπορείς να την "κοντρολάρεις" οπτικά με explicit cast... > c = (char) i; αλλά το πρόβλημα παραμένει, γιατί έτσι κι αλλιώς το cast γίνεται implicitly ακόμα κι όταν δεν το βάζεις εσύ. EDIT: Διόρθωση των αναθέσεων του 1ου αποσπάσματος από = 12 σε = 'c' + μερικά διευκρινιστικά σχόλια
capoelo Δημοσ. 14 Ιουλίου 2012 Μέλος Δημοσ. 14 Ιουλίου 2012 Έχω το εξής πρόγραμμα: > #include<stdio.h> #include<stdlib.h> int main(void) { char *p="one"; char *w="two"; char *t="three"; printf("%p %p %p\n",p,w,t); system("Pause"); return 0; } το οποίο απλά αρχικοποιεί τρεις δείκτες με τα αλφαριθμητικά one,two,three και εν συνεχεία τα εμφανίζει στην οθόνη(κανονικά στην printf πρέπει να γράψω %s αλλά το άλλαξα λόγω της ερώτησης).Αν το γράψω υπο αυτή τη μορφή,μου βγάζει το αποτέλεσμα που φαίνεται στο πρώτο screenshot που παραθέτω.Αν προσθέσω τον τελεστή * πριν από κάθε δείκτη εμφανίζει το δεύτερο screenshot.Ποια η διαφορά τους(στη πρώτη περίπτωση αν δεν κάνω λάθος εμφανίζει τις διευθύνσεις);
imitheos Δημοσ. 14 Ιουλίου 2012 Δημοσ. 14 Ιουλίου 2012 Έχω το εξής πρόγραμμα: > #include<stdio.h> #include<stdlib.h> int main(void) { char *p="one"; char *w="two"; char *t="three"; printf("%p %p %p\n",p,w,t); system("Pause"); return 0; } το οποίο απλά αρχικοποιεί τρεις δείκτες με τα αλφαριθμητικά one,two,three και εν συνεχεία τα εμφανίζει στην οθόνη(κανονικά στην printf πρέπει να γράψω %s αλλά το άλλαξα λόγω της ερώτησης).Αν το γράψω υπο αυτή τη μορφή,μου βγάζει το αποτέλεσμα που φαίνεται στο πρώτο screenshot που παραθέτω.Αν προσθέσω τον τελεστή * πριν από κάθε δείκτη εμφανίζει το δεύτερο screenshot.Ποια η διαφορά τους(στη πρώτη περίπτωση αν δεν κάνω λάθος εμφανίζει τις διευθύνσεις); Ό,τι δίνεις στο printf αυτό τυπώνει. Στην πρώτη περίπτωση δίνεις ένα δείκτη δηλαδή μια μεταβλητή της οποίας η τιμή είναι μια διεύθυνση. Έτσι το printf σου τυπώνει ωραία και καλά τη διεύθυνση. Αν ενεργοποιήσεις τα διαγνωστικά μηνύματα του compiler σου, θα δεις για τη 2η περίπτωση κάτι παρόμοιο με το εξής > προειδοποίηση: format ‘%p’ expects argument of type ‘void *’, but argument 4 has type ‘int’ Το *p είναι ο πρώτος χαρακτήρας δηλαδή το 'o' το οποίο έχει τύπο int στην C. Έτσι η μεταβλητή που έδωσες αυτή τη φορά στο printf έχει τιμή 0x6F (111) γιατί αν δεις εδώ έτσι δηλώνεται στο ASCII το μικρό αγγλικό o. Για αυτό το λόγο, το printf σου εμφανίζει την ascii τιμή των 'o', 't', 't'. Πάντα να έχεις ενεργοποιημένα τα διαγνωστικά του compiler. Επίσης διάβασε καλύτερα τι κάνει το & και το *
capoelo Δημοσ. 14 Ιουλίου 2012 Μέλος Δημοσ. 14 Ιουλίου 2012 > #include<stdio.h> #include<stdlib.h> int main(void) { char *p="one"; char *w="two"; char *t="three"; printf("%s %s %s\n",*p,*w,*t); system("Pause"); return 0; } Ερώτηση:σε αυτή την εκδοχή,μου πετάει σφάλμα επειδή του "λέω" να εκτυπώσει αλφαριθμητικό,ενώ ο δείκτης δείχνει σε χαρακτήρα(στην αρχή του string).Σωστά;
παπι Δημοσ. 14 Ιουλίου 2012 Δημοσ. 14 Ιουλίου 2012 > #include<stdio.h> #include<stdlib.h> int main(void) { char *p="one"; char *w="two"; char *t="three"; printf("%s %s %s\n",*p,*w,*t); system("Pause"); return 0; } Ερώτηση:σε αυτή την εκδοχή,μου πετάει σφάλμα επειδή του "λέω" να εκτυπώσει αλφαριθμητικό,ενώ ο δείκτης δείχνει σε χαρακτήρα(στην αρχή του string).Σωστά; Οχι. Οταν βαζεις * μπροστα απο δεικτη κανεις dereference δηλαδη παιρνεις την τιμη που δειχνει ο δεικτης. Πχ στο παραπανω η printf ειναι (... , *p ('o'), *w ('t'), *t ('t')), αλλα εσυ λες στην printf οτι ειναι δεικτες ετσι αυτη μετατρεπει το χαρακτιρα 'o' σε δεικτη που λογικοτατα δειχνει στον γαμο του καραγκιοζη και ετσι το ος σου λεει οτι αυτη η διευθυνση δεν υπαρχει.
imitheos Δημοσ. 14 Ιουλίου 2012 Δημοσ. 14 Ιουλίου 2012 > #include<stdio.h> #include<stdlib.h> int main(void) { char *p="one"; char *w="two"; char *t="three"; printf("%s %s %s\n",*p,*w,*t); system("Pause"); return 0; } Ερώτηση:σε αυτή την εκδοχή,μου πετάει σφάλμα επειδή του "λέω" να εκτυπώσει αλφαριθμητικό,ενώ ο δείκτης δείχνει σε χαρακτήρα(στην αρχή του string).Σωστά; Όπως είπαμε πριν, το *p δεν είναι δείκτης αλλά ένα int (ή ένας χαρακτήρας αν το θες). Πάρε ένα καλό βιβλίο και διάβασε ή ψάξε στο internet για "dereference operator". Θα το εξηγήσω μπακάλικα μια και υπάρχουν άπειρα tutorials και βαριέμαι κιόλας (βασικά και γιατί μου είναι δύσκολο να βρω τις κατάλληλες λέξεις για να το εξηγήσω μη-μπακάλικα ). > int x, y; y = 5; x = y; Όταν δηλώνεις μια μεταβλητή, δεσμεύεται μνήμη για αυτήν. Στην παραπάνω περίπτωση δεσμεύεται μνήμη για 2 ints. Έστω ότι έχουν δεσμευτεί οι διευθύνσεις 50 και 60. Τα x και y είναι εύκολα συμβολικά ονόματα με τα οποία μπορούμε να χρησιμοποιούμε αυτές τις διευθύνσεις μνήμης που εκχωρήθηκαν. Η έκφραση y = 5 λέει "Γράψε την τιμή 5 στην διεύθυνση μνήμης που έχουμε ονομάσει y (δηλαδή την 60)". Η έκφραση x = y κάνει ουσιαστικά την ίδια δουλειά αλλά είναι λίγο πιο πολύπλοκη. Λέει "Διάβασε την τιμή που υπάρχει στην διεύθυνση μνήμης 60 (y) και μετά γράψε αυτή την τιμή στην διεύθυνση 50 (x)". Ας δούμε το ίδιο με δείκτες. > int x, *p; x = 5; p = &x; *p = 6; Τα ίδια και εδώ. Ένας δείκτης είναι και αυτός μια απλή μεταβλητή και έχει μια διεύθυνση και μια τιμή. Ας δεχτούμε και πάλι ότι οι διευθύνσεις είναι 50 για το x και 60 για το p. Στην έκφραση p = &x λέμε "Γράψε την διεύθυνση που έχεις δεσμεύει για το x ως τιμή στην διεύθυνση την οποία έχουμε ονομάσει συμβολικά p" οπότε θα γράψει την τιμή 50 στην διεύθυνση 60. Έτσι ο δείκτης p βρίσκεται στην διεύθυνση 60 και έχει τιμή 50. Τώρα ερχόμαστε σε αυτό που θες δηλαδή τον dereference τελεστή. Η έκφραση *p λέει "Διάβασε την τιμή του p (δηλαδή την τιμή της διεύθυνσης 60) άρα την τιμή 50 και χρησιμοποίησε αυτή την τιμή ως διεύθυνση". Έτσι το *p ισοδυναμεί με 50 και το *p = 6 πάει και γράφει στην διεύθυνση 50 την τιμή 6 οπότε και είναι ισοδύναμο του x = 6. Στη δική σου περίπτωση, το printf περιμένει ένα "string" δηλαδή ένα δείκτη στο 1ο στοιχείο μιας σειράς χαρακτήρων. Εσύ του περνάς το *p το οποίο είναι ο χαρακτήρας 'o' δηλαδή όπως είδαμε στο προηγούμενο μήνυμά σου ο αριθμός 111 (0x6F). Με το σκεπτικό που εξήγησα πριν, το printf θα χρησιμοποιήσει την τιμή 111 σαν διεύθυνση και θα πάει να διαβάσει τον χαρακτήρα που υπάρχει εκεί. Επειδή όμως η διεύθυνση 111 δεν ανήκει στο πρόγραμμά σου, δεν έχεις πρόσβαση σε αυτήν και κρασάρει. Πάνω-κάτω αυτό γίνεται αλλά περίμενε να το εξηγήσει κάποιος καλύτερα.
Star_Light Δημοσ. 14 Ιουλίου 2012 Δημοσ. 14 Ιουλίου 2012 > #include<stdio.h> #include<stdlib.h> int main(void) { char *p="one"; char *w="two"; char *t="three"; printf("%s %s %s\n",*p,*w,*t); system("Pause"); return 0; } Ερώτηση:σε αυτή την εκδοχή,μου πετάει σφάλμα επειδή του "λέω" να εκτυπώσει αλφαριθμητικό,ενώ ο δείκτης δείχνει σε χαρακτήρα(στην αρχή του string).Σωστά; Οχι. Κοιτα εσυ δινεις στην printf την οδηγια να εκτυπώσει κατι που ειναι string. Ένα string ειναι ένας δεικτης στο 1ο στοιχειο ενος πινακα χαρακτήρων . Το *p δεν ειναι δείκτης ειναι ακέραιος ή χαρακτήρας (αναλογα το περιεχομενο της τοποθεσιας μνημης που τον έχεις αρχικοποιησει). Εκεινο που εχεις να κάνεις ειναι λοιπον να δώσεις μεσα στην printf την οδηγια %s και μετα τον δεικτη σκετο χωρις τον αστερισκο. Μπορεις να χρησιμοποιησεις την puts αντι της printf > char *ptr = "Hello" ; puts(ptr); Ουσιαστικα της περνάς εναν δεικτη που δειχνει στο 1ο χαρακτηρα και μετα την δουλεια την κανει η συναρτηση και επιστρεφει το αποτελεσμα. EDIT ο ημιθεος στα λεει πολυ καλα.... ετσι αλλαζεις το περιεχομενο μιας θέσης μνημης μεσω του δεικτη της... επισης μπορεις να κάνεις και το εξης : > #include <stdio.h> int main(void) { int x=1 , y=2 , *p1 , *p2; p1= &x; p2=p1; printf(" %d ", *p2); // εκτυπωση 1 p2= &y; *p2=2; printf("%d" , y); // εκτύπωση 2 return 0; } ΑΡχικα ο p1 δειχνει στην x και φέρει την τιμή της ενω μετα βαζεις τον δεικτη p2 να δειχνει στο ιδιο σημειο που δειχνει και ο p1 . Μετα μεσω του p2 δινεις τιμη στην y Προσεξε μονο οταν χρησιμοποιεις δεικτες να μην αφησεις καποιον χωρις να τον αρχικοποιησεις να δειχνει καπου γιατι αν πας να κάνεις το κόλπο *p = ακέραιη τιμή ... ο δεικτης αυτος μπορεις να δειχνει οπουδηποτε και να κανεις overwrite αλλα αυτο το λεω χωρις να το εχω ψαξει γιατι λογικα οι πονηροι των λειτουργικων δεν θα έχουν σκεφτει τέτοια γκάφα εκ μερους του χρηστη??? Και αρα θα εχουν φροντισει ωστε ο compiler να δεσμευει θεσεις οχι και τοσο ζωτικης σημασιας για την γενικοτερη λειτουργια του υπολογιστη??? θελει ψαξιμο. Θες να συνεχισουμε με διπλούς?
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα