freestuff.gr αρχική σελίδα
 FAQFAQ    ΑναζήτησηΑναζήτηση   Λίστα ΜελώνΛίστα Μελών   Ομάδες ΜελώνΟμάδες Μελών   <b>Εγγραφή Μέλους</b>Εγγραφή Μέλους 
 ΠροφίλΠροφίλ   Επιλογές μέλους Επιλογές   Τα bookmarks μου Τα bookmarks μου   Προσωπικά μηνύματαΠροσωπικά μηνύματα 
  διαφήμιση  

Καλώς ήρθατε στο forum μας! Για να συμμετάσχετε στις συζητήσεις θα πρέπει να είσαστε μέλος. Γίνετε μέλος τώρα!.

Δείκτες **


 Forum index » Δημιουργία Web Sites, Γραφικών & Προγραμματισμός » Γλώσσες Προγραμματισμού » C, C++
Moderators:  Super-Moderators, WebDev Moderators
Εισαγωγή νέου Θέματος   Απάντηση στο Θέμα Σελίδα 1 από 1 [6 Μηνύματα]       Mark the topic unread :: Προηγούμενο θέμα :: Επόμενο θέμα
ΑποστολέαςΜήνυμα
Programmer


Μέλος από: 22 Σεπ 2007
Μηνύματα: 67

View users profile
ΜήνυμαΣτις: 02 Δεκ 2007 23:38    Θέμα: Δείκτες **
Περιγραφή θέματος: Ερώτηση εκμάθησης
Απάντηση με παράθεση  Mark this post and the followings unread

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

Ερώτηση:
α) που είναι τα λάθη στον παρακάτω κώδικα (δικαιολογήστε γιατί είναι λάθος αν είναι λάθος κάτι).
int matrix[5][5], **pp;
...
pp = matrix;
pp[4][3] = 100;

β)
Που βρίσκεται το λάθος στην παρακάτω συνάρτηση (αν υπάρχει λάθος) και πως μπορούμε να το αποφύγουμε;
void function(int **pp)
{
int i, j;
for(i=0; i<10; i++)
for(j=0; j<5; j++)
pp[i][j] = i*5 + j + 1;
}

Απαντήστε αν ξέρετε, ή έστω προβληματιστείτε...
MannyCalavera


Μέλος από: 11 Δεκ 2007
Μηνύματα: 13

View users profile Visit posters website
ΜήνυμαΣτις: 12 Δεκ 2007 01:26    Θέμα: Απάντηση με παράθεση  Mark this post and the followings unread

Ενδιαφέροντα ερωτήματα.

Α) Στο πρώτο ερώτημα κάνεις την ανάθεση pp=matrix, που είναι σφάλμα καθώς το pp είναι δείκτης-σε-δείκτη-σε-int, ενώ το matrix είναι δείκτης-σε-int, άρα έχουμε διαφορετικούς τύπους και η ανάθεση δεν είναι επιτρεπτή.

Β) Το δεύτερο ερώτημα είναι πιο tricky.
Υποθέτωντας πως αυτό που θες να κάνεις (το λογικό) είναι:

κώδικας:
int array[10][5];
function(&array);


τότε το αποτέλεσμα δε θα είναι το αναμενόμενο αφού η γραμμή:
pp[i][j]=100;
δεν κάνει το ίδιο με το επιθυμητό array[i][j]=100;

Για να έχουμε το σωστό αποτέλεσμα θα πρέπει να γράψουμε:
κώδικας:
(*pp)[i][j] = 100;


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


Βέβαια δεν έκανα compile τον κώδικα και μπορεί να κάνω και λάθος. Έχω σκουριάσει λίγο στη C

_________________
The living still give me the creeps
Programmer


Μέλος από: 22 Σεπ 2007
Μηνύματα: 67

View users profile
ΜήνυμαΣτις: 12 Δεκ 2007 17:19    Θέμα: Απάντηση με παράθεση  Mark this post and the followings unread

MannyCalavera ανέφερε:
Ενδιαφέροντα ερωτήματα.

Α) Στο πρώτο ερώτημα κάνεις την ανάθεση pp=matrix, που είναι σφάλμα καθώς το pp είναι δείκτης-σε-δείκτη-σε-int, ενώ το matrix είναι δείκτης-σε-int, άρα έχουμε διαφορετικούς τύπους και η ανάθεση δεν είναι επιτρεπτή.

Β) Το δεύτερο ερώτημα είναι πιο tricky.
Υποθέτωντας πως αυτό που θες να κάνεις (το λογικό) είναι:

κώδικας:
int array[10][5];
function(&array);


τότε το αποτέλεσμα δε θα είναι το αναμενόμενο αφού η γραμμή:
pp[i][j]=100;
δεν κάνει το ίδιο με το επιθυμητό array[i][j]=100;

Για να έχουμε το σωστό αποτέλεσμα θα πρέπει να γράψουμε:
κώδικας:
(*pp)[i][j] = 100;


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


Βέβαια δεν έκανα compile τον κώδικα και μπορεί να κάνω και λάθος. Έχω σκουριάσει λίγο στη C


Μπράβο! πολύ καλή απάντηση!

Σε γενικές γραμμές όλοι έχουν ή είχαν (ή έχουν) πρόβλημα με τους δέικτες, τόσο με την έννοια τους και κυρίως με το συντακτικό τους...

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

Το int **pp; διαβάζεται σαν δείκτης... Δείκτης σε τί? δείκτης σε δείκτη int * (*pp)... Ο τύπος του δέιτη χρειάζεται για την αποαναφορά.... Αυτή η σύνταξη είναι ένα από τα προβλήματα της C σε σχέση με τους δέικτες... Μια και πιο διαφωτιστική σύνταξη θα μπορούσε να είναι το παρακάτω

pointer name to type;
όπου pointer θα ήταν ξεχωριστός τύπος δείκτη, το name το όνομα του δέικτη, και το to type ο τύπος που θα περιείχε η διεύθυνση που θα περιείχε με την σειρά του ο δέικτης, για να ξέρει ο μεταγλωττιστής τι να κάνει στην αποαναφορά για να παρουσιάσει την τιμή (γιατί δεν έχουν όλοι οι τύποι το ίδιο μέγεθος και κωδικοποίηση)...

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

void *ptr; /* Δείκτης που θα αποθηκέυει διευθύνσεις για τις οποίες ο μεταγλωττιστής δεν μπορεί να ξέρει τον τύπο δεδομένων που περιέχουν /*

int a = 10;
float b = 10.1F;

ptr = (void*) &a;
printf( "%d \n", *((int*) ptr) );

ptr = (void*) &b;
printf( "%f \n", *((float*) ptr) );

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

(οι δείκτες συνατακτικά είναι το ατύχημα της C...)

Για την συνάρτηση λίγα τώρα
το
int array[10][5];
function(&array);

είναι λάθος για την C++, και σωστά. (στην c που έχει χαλαρότερο έλεγχο τύπων κάποια που τα βγάζει λάθος η C++, μπορέι να περνάνε)
Το
int **pp και το int array[10][5] καμία λογική σχέση δεν έχουν σαν τύποι! Το ένα είναι δέικτης σε δέικτη ακεραίων και το άλλο πίνακας ακεραίων 10Χ5. Λογική σχεση δέν εχουν τελικά ούτε το int arrayΑ[10][5] με το int arrayΒ[2][5] που είναι λογικά διαφορετικοί παράγωγοι τύποι δεδομένων με την ομοιότητα στο ότι αποθηκέυουν μια λίστα ακεραίων αλλά κα την ουσιαστική διαφορά ότι αυτή η λίστα δεν έχει το ίδιο μέγεθος...

όταν γράφουμε
void func(int **pp)
{
...
pp[4][6] = 100;
...
}

τι λέμε στον μεταγλωτιστή? πάρε τον δέικτη σε δείκτη και μετακινήσου από την διεύθυνση αυτή 4*άγνωστο_μέγεθος μήκη σε bytes ακεραίων και μετατόπισε την διεύθυνση επιπλέον 6 μήκη ακεραίων και αποθήκευσε στην διεύθυνση την τιμή 100... Και πως θα βρεί ο μεταγλωττιστής που θα πάει αφού δεν ξέρει το μέγεθος της τελευταίας διάστασης του πίνακα? Δεν θα το βρει...

ενώ στην περίπτωση του

int array[5][10];

array[4][6] = 100;

ξέρει τι θα κάνει... έχει το μέγεθος των διαστάσεων κατα την φάση της μεταγλώττισης.

θα πάρει την διέυθυνση αρχής του πίνακα το array θα πολλαπλάσιάσει τις γραμμές με τον αριθμό στηλών που ξέρει από την δήλωση του πίνακα 4*6 και θα προσθέσει τον αριθμό στηλών για να βρει πόσα στοιχέια απο την αρχή του πίνακα απέχει το σημειο που δείχνει ο προγραμματιστής στον κώδικα.

μετά θα προσθέσει στην διεύθυση αρχής πίνακα την μετατόπιση σε bytes που είναι ίση με αριθμός_στοιχέιων επί μέγεθος_στοιχέιου, και θα καταχωρήσει σε αυτήν την διέυθυνση την τιμή...
δηλαδή θα κάνει το
*((int*)array + 4*10+6) = 100; σε στοιχέια πίνακα,
ή αν θέλετε το
*(int*)( ((char*)array) + (4*10+6)* sizeof(int)) = 100; σε bytes

και τα δύο ισοδύναμα με το array[4][6] = 100;

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

Σημ. το συντακτικά για την C++ σωστό αλλά όχι και λογικά ορθό θα ήταν να γράψεις τα

void func(int **pp) ...
main...
...
int *p, array[1][5];

p = & array[0][0];

func( &p );
...

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

void other_func(int *p, int rows, int columns)
{
int i,j;

for (i = 0; i<rows; i++)
for (j = 0; j<columns; j++)
p[i*columns + j] = τιμή
....
}


main...

int array[10][5];
...
other_func(&array[0][0], 10, 5);
...

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

Υ.Γ πλάκα - πλάκα μου έφαγε τρία τέτατρα να γράψω αυτό το ποστ... Α!! δεν το ξανακάνω. Ας καθήσουν οι αρχάριοι να διαβάσουν... εμείς πως μάθαμε δηλαδή? μας τα εξήγησε μήπως κανείς?
MannyCalavera


Μέλος από: 11 Δεκ 2007
Μηνύματα: 13

View users profile Visit posters website
ΜήνυμαΣτις: 12 Δεκ 2007 18:36    Θέμα: Απάντηση με παράθεση  Mark this post and the followings unread

Γενικά η σύνταξη των δεικτών είναι ένα από τα μεγαλύτερα (κατ' εμέ) προβλήματα στη C.

Από τη μία σου δίνουν τη δυνατότητα για low level πρoγραμματισμό και τη δυνατότητα να κάνεις "κόλπα" που βελτιώνουν την απόδοση (ταχύτητα συνήθως) ενός προγράμματος.

Από την άλλη υπονομεύουν τον λεγόμενο strongly-typed προγραμματισμό, μια έννοια πολύ διαδεμομένη στις σύγχρονες γλώσσες προγραμματισμού (όπως η Java και η C++) και πολύ χρήσιμη, αφού πρακτικά εξαλείφει προβλήματα όπως τα προηγούμενα (το κουλουβάχατο που προκύπτει από τη μετατροπή ενός τύπου σε άλλο).

Εγώ είμαι υπέρ του strongly-typed προγραματισμού και θεωρώ κακή πρακτική τα προηγούμενα παραδείγματα. Ένας πίνακας είναι ένας πίνακας, και δεν πρέπει να μετατρέπεται σε pointer, όπως και ένας ακέραιος (int) είναι ένας ακέραιος, και δεν πρέπει να μετατρέπετααι σε δεκαδικό (float).

Τώρα θα μου πεις "ωραία όλα αυτά, αλλά γιατί η δημοφιλέστερη γλώσσα προγραμματισμού, η C++, αφήνει τέτοιες 'ταρζανιές' να περάσουν;".
Αυτό συμβάινει διότι υπάρχουν περιπτώσεις (λίγες, αλλά πολύ χρήσιμες) όπου τέτοιου είδους τρελές μετατροπές φτιάχνουν πολύ γρήγορα προγράμματα, λόγω της φύσης του hardware (της λειτουργίας του).

Ένα παράδειγμα είναι ο ακόλουθος κώδικας:

κώδικας:

float InvSqrt(float x)
{
float xhalf = 0.5f*x;
int i = *(int*)&x; // get bits for floating value
i = 0x5f3759df - (i>>1); // gives initial guess y0
x = *(float*)&i; // convert bits back to float
x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy
return x;
}


ο οποίος εμφανίζεται στον κώδικα του Quake III και υπολογίζει το αντίστροφο της τετραγωνικής ΄ριζας ενός αριθμού.
Είναι από τους καλύτερους κώδικες που έχω δει στη ζωή μου, προϊόν ιδιοφυίας, αλλά κάνει κόλπα μετατροπής από float σε int, που ναι μεν δε θα πρεπε να είναι επιτρεπτοί, αλλά από την άλλη είναι πολλές φορές ταχύτερος από το να χρησιμοποιήσεις το απλό 1/sqrt(x).

Δεσμεύομαι κάποια στιγμή να γράψω ένα άρθρο για τον παρόντα κώδικα στα ελληνικά.
Προσπάθησε να καταλάβεις τι κάνει, πίστεψέ με δεν είναι εύκολο. Επίσης ψάξε στο google τον αριθμό 0x5f3759df για να μάθεις περισσότερες λεπτομέρειες.

_________________
The living still give me the creeps
vspartan


Μέλος από: 03 Δεκ 2007
Μηνύματα: 57

View users profile
ΜήνυμαΣτις: 05 Φεβ 2008 20:03    Θέμα: Απάντηση με παράθεση  Mark this post and the followings unread

Παιδιά υπάρχει κανένα βοήθημα για c και για λίγο δείκτες?Στα ελληνικά?
Ευχαριστω.

_________________
Οι Θερμοπύλες απέδειξαν ότι υπάρχουν πολλοί άνθρωποι,αλλά ολίγοι άνδρες.
Programmer


Μέλος από: 22 Σεπ 2007
Μηνύματα: 67

View users profile
ΜήνυμαΣτις: 10 Φεβ 2008 08:49    Θέμα: Απάντηση με παράθεση  Mark this post and the followings unread

vspartan ανέφερε:
Παιδιά υπάρχει κανένα βοήθημα για c και για λίγο δείκτες?Στα ελληνικά?
Ευχαριστω.


βιβλία μα κυρίως εξάσκηση. Βιβλίο μόνο για δέικτες δεν νομίζω να υπάρχει. Άρθρα πολλά στα αγγλικά. Στα ελληνικά δεν ξέρω. Αν ασχολείσαι με τον προγραμματισμό επαρκης γνώση των αγγλικών είνια απαραίτητη...
(είχα ξεκινήσει να γράφω εγώ ένα άρθρο, αλλα έιναι ημιτελές για δημοσίευση. Αν το τελέιώσω θα το δημοσιεύσω...)
Εμφάνιση Μηνυμάτων:   
Εισαγωγή νέου Θέματος   Απάντηση στο Θέμα Σελίδα 1 από 1 [6 Μηνύματα] Mark the topic unread :: Προηγούμενο θέμα :: Επόμενο θέμα
 Forum index » Δημιουργία Web Sites, Γραφικών & Προγραμματισμός » Γλώσσες Προγραμματισμού » C, C++
Τώρα είναι 21 Ιαν 2017 21:44 | All times are UTC + 2


Email This Page to Someone! add to Favorites

     Powered by p h p B B © 2001,2005 p h p B B Group
Για άμεση επικοινωνία με τον διαχειριστή του freestuff.gr στο email: freestuff.gr(παπάκι)gmail.com


Copyright © 1999-2013 Freestuff.gr All Rights Reserved  
Version Aegean, designed by N. Tsaganos