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

pointers c++


ALLisCHAOS

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

Γεία σας , το τελευταίο καιρό είπα να κάτσω να ασχοληθώ λίγο με c++ και προσπαθώ να καταλάβω τι παίζει με τους pointers. Όλα καλά μέχρι που έφτασα εδώ:

#include <cstdlib>
#include <iostream>

using namespace std;
void fun(char *s);
int main(int argc, char *argv[])
{
    char s[20]="Hello word";
    cout << s << endl;
    fun(&s);
    cout << s << endl;
    system("PAUSE");
    return EXIT_SUCCESS;
}


void fun(char *s){
     int i=0;
     
     *s="asd";
      
}

αν ήταν αντί για char[] ένας Integer θα άλλαζε τιμή και θα δούλευε κανονικά , τώρα όμως πέρνω ένα error όταν παω να καλέσω την fun()

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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

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

Δημοφιλείς Ημέρες

  • Moderators

Κατ' αρχάς αφού χρησιμοποιείς C++ δε θα έπρεπε να σε απασχολούν οι pointers εκτός αν γράφεις κάτι το οποίο είναι τόσο low level που δε σε εξυπηρετεί κάποιος container. Άμα έχεις χρόνο μπορείς να δεις κάποιες διαλέξεις εδώ που μιλάνε για το STL (Standard Template Library).

Τώρα, αυτό που πας να κάνεις εσύ είναι να βάλεις έναν πίνακα από χαρακτήρες στη θέση ενός χαρακτήρα, γι' αυτό σου χτυπάει. Άμα πας να κάνεις dereference το s (δηλαδή αν γράψεις *s) του λες "πήγαινε στη διεύθυνση που δείχνει το s και πες μου τι τιμή έχει". Αν δούμε λίγο τη μνήμη θα είναι κάπως έτσι πριν την κλήση της fun:

Θέση μνήμης: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Κείμενο:     H e l l o   w o r l d  \0 σ  κ  ο  υ  π  ι  δ  ι
Αυτό που λες στη fun είναι:
Πήγαινε στη θέση που δείχνει το s (δηλαδή στο πρώτο στοιχείο του - το όνομα ενός πίνακα στη C δείχνει στο πρώτο στοιχείο του) και εκεί βάλε το "asd". Όμως, το πρώτο στοιχείο του s είναι ο χαρακτήρας 'H'. Επειδή, λοιπόν, δε μπορεί να βάλει έναν πίνακα από χαρακτήρες στη θέση ενός χαρακτήρα παίρνεις αυτό το λάθος.
 
Θα επανέρθω αύριο με μια πιο αναλυτική εξήγηση για τους pointers και γιατί αυτό που έκανες είναι λάθος, αλλά τώρα είναι αργά και θα μπερδευτώ κι εγώ άμα δοκιμάσω να εξηγήσω τίποτα :P
  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ευχαριστώ !! νομίζω πως το εξήγησες όσο καλύτερα γινόταν !! :D , απλά θα μείνω στο : αφού χρησιμοποιείς c++ δεν θα έπρεπε να σε ενδιαφέρουν οι pointers , δλδ δεν χρησιμοποιούνται τόσο οι pointers? (χωρίς να θελω να παω low level)

ps: περιμένω κ την αναλυτικότερη εξήγηση αυριο ;)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

 

:P μη βαράτε, γίνεστε πολύ κακοί.

Γεία σας , το τελευταίο καιρό είπα να κάτσω να ασχοληθώ λίγο με c++ και προσπαθώ να καταλάβω τι παίζει με τους pointers. Όλα καλά μέχρι που έφτασα εδώ:

#include <cstdlib>
#include <iostream>

using namespace std;
void fun(char *s);
int main(int argc, char *argv[])
{
    char s[20]="Hello word";
    cout << s << endl;
    fun(s);
    cout << s << endl;
    system("PAUSE");
    return EXIT_SUCCESS;
}


void fun(char *s){
     int i=0;
     
     *s="asd";
      
}

αν ήταν αντί για char[] ένας Integer θα άλλαζε τιμή και θα δούλευε κανονικά , τώρα όμως πέρνω ένα error invalid conversion from const char* to char, μπορεί να με διαφωτίσει κάποιος τι παίζει? ευχαριστώ

 

 

Υπάρχουν αρκετά πράγμα που θα μπορούσε να πει κανείς ανάλογα βέβαια και με το τί σκέφτεσαι να κάνεις.

 

Θέλεις να προγραμματίσεις σε C με συνακτικό C++

Αυτό που κάνεις τώρα δεν είναι C++, αλλά C. Εφόσον θέλεις να μάθεις C πρέπει να δεις τα strings σαν πίνακες από χαρακτήρες. Όπως το βλέπω εγώ τώρα, εσύ θέλεις να αναθέσεις μία τιμή "string" (string literal) σε μία μεταβλητή "string".

Κάποια πράγματα για τους pointers:

int *num_ptr;   // num_ptr is pointer-to integer
int num = 1332; // num is an integer

num_ptr = &num; // num_ptr contains the address of num

cout << *num_ptr << endl;  // 1332 is printed

Όσον αφορά τα strings στη C (εννοείται και στη C++, αλλά θα καταλάβεις αργότερα):

#include <string.h>

char static_str[20] = "I'm a static string";  // An array of 20 characters

for (int i = 0; i < 20; ++i)
        cout << static_str[i];

cout << endl;

static_str = "Badly done bro...";  // Not the correct way to copy a string
strcpy(static_str, "New & Refreshed!");   // I just copied a new string to static_str


// Don't go further if you're not familiarized with pointers. If you want to, ask for it
char *dynamic_str;

dynamic_str = ...

Αν δοκιμάσεις τα παραπάνω παραδείγματα θα δεις ότι δε μπορείς να αντιγράψεις ένα string σε ένα άλλο με ανάθεση. Ό,τι βλέπεις σε " ", είναι string literals (const char *). Αυτά είναι αποθηκευμένα στη μνήμη και όταν προσπαθείς να τα αναθέσεις, στην ουσία προσπαθείς να αναθέσεις τη διεύθυνση μνήμης στην οποία βρίσκονται. Κοίτα τώρα το παρακάτω:

char s[20] = "Hi there!";

cout << *s;     // s holds the address in memory. It points to the first character. It's the base address. s contains the *(s+0) character
cout << *(s+1); // the next address contains the *(s+1) character
cout << *(s+2); // you feel the groove
cout << *(s+3);
cout << *(s+4);
cout << *(s+5);

cout << endl;

// So the output will be "Hi the"

Αυτό που κάνεις στο παράδειγμά σου είναι ανάθεση στην πρώτη θέση του πίνακα από χαρακτήρες (char) τη διεύθυνση ενός string literal (const char *). Θα μπορούσες να υλοποιήσεις μία αντιγραφή string με συνεχόμενες αναθέσεις σε θέσεις του πίνακα χαρακτήρων. Κάτι τέτοιο δηλαδή:

void fun(char *s)
{
     *(s+0) = 'Q';	// or s[0] = 'Q'
     *(s+1) = 'u';	// or s[1] = 'u'
     *(s+2) = 'i';
     *(s+3) = 't';
     *(s+4) = '\0'; // Strings in C are nul terminated.
      
}

Σημείωση: Για το NUL σου είπε ο/η Kercyn. Αν γενικά έχεις απορίες, κάτι δεν καταλάβεις καλά ξαναρώτα. Είναι λίγο αργά και δεν είμαι σε θέση να δω τι έχω καλύψει και τί όχι.

 

Θέλεις να προγραμματίσεις σε C++

Ρίξε μία ματιά στο string library της C++. Θα σου φανούν όλα πιο εύκολα. Για δες ένα παράδειγμα:

#include <iostream>
#include <string>
using namespace std;

int main (void)
{
	string s1("Hi there!");
	cout << s1 << endl;
	
	string s2;
	s2 = s1;
	cout << s2 << endl;

	return 0;
}

Είμαι λίγο δυστακτικός στο να σου γράψω πολλά πράγματα για τη C++ γιατί είμαι καινούργιος κι εγώ, οπότε φοβάμαι μήπως πω καμιά πατάτα. Πάντως C++ δύσκολα θα χρειαστείς pointer για τέτοιες περιπτώσεις. Σου προσφέρει πράγματα έτοιμα. Οι pointers είναι must μόνο στη C και αυτή είναι και η γοητεία της.

 

 

:P Μη βαράτε.. Γίνεστε πολύ κακοί.

 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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

 

Αυτό συμβαίνει διότι το s[20] ισοδυναμεί εξ΄ορισμού με μονό δείκτη χαρακτήρων, οπότε όταν εσύ περνάς στην fun() το &s, ουσιαστικά της περνάς έναν δείκτη σε δείκτη χαρακτήρων (διπλό δείκτη δηλαδή). Οπότε, από εκεί και πέρα αρχίζει το... πανηγύρι :P

 

Αυτό που προσπαθείς να κάνεις σε αυτόν τον κώδικα επιτυγχάνεται με σχετική ασφάλεια με τη συνάρτηση strncpy() από το <cstring>...

#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace std;

void fun( char *s, size_t len );

int main( void )
{
	char s[20] = "Hello word";
	cout << s << endl;

	fun( s, 20 );
	cout << s << endl;

	system("PAUSE");
	return EXIT_SUCCESS;
}

void fun( char *s, size_t len )
{  
	strncpy( s, "asd", len );
	s[len-1] = '\0';
}
Επειδή το "asd" είναι μικρότερο του "Hello word" μπορείς να το κάνεις και με σκέτη strcpy() αλλά σε πιο γενικές συναρτήσεις πρέπει να προσέχεις ώστε το μήκος του προς αντιγραφή να μην ξεπερνάει το μέγιστο ορισμένο μέγεθος του του s (δηλαδή του 19+1 σε αυτό το παράδειγμα).

 

Στο link της υπογραφής μου θα βρεις μερικές σημειώσεις μου στα ελληνικά για δείκτες, strings και linked-lists... ίσως σου φανούν χρήσιμες.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

απλά θα μείνω στο : αφού χρησιμοποιείς c++ δεν θα έπρεπε να σε ενδιαφέρουν οι pointers , δλδ δεν χρησιμοποιούνται τόσο οι pointers?

 

Χρησιμοποιούνται πολύ. Αλλά.

 

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

 

Εν προκειμένω, η βασική διαφορά ανάμεσα σε C και C++ είναι πως στη C γενικά δεν υπάρχουν υψηλότερου επιπέδου έννοιες που μπορείς να χρησιμοποιήσεις όταν γράφεις το πρόγραμμά σου, οπότε καταλήγεις με pointers παντού. Στη C++ υπάρχουν τέτοιες έννοιες, οπότε "the C++ way" είναι να τις χρησιμοποιείς. Αυτό καταλήγει στο να χρησιμοποιείς pointers μόνο εκεί που πραγματικά χρειάζονται (που είναι και πάλι σε πολλές περιπτώσεις λόγω του ότι η γλώσσα έχει χειροκίνητη διαχείριση μνήμης) και όχι παντού.

 

Παραδείγματα εννοιών στη C++ τα οποία "αντικαθιστούν" τους pointers σε συγκεκριμένες περιπτώσεις είναι:

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Θέλεις να προγραμματίσεις σε C++

Ρίξε μία ματιά στο string library της C++. Θα σου φανούν όλα πιο εύκολα. Για δες ένα παράδειγμα:

#include <iostream>
#include <string>
using namespace std;

int main (void)
{
	string s1("Hi there!");
	cout << s1 << endl;
	
	string s2;
	s2 = s1;
	cout << s2 << endl;

	return 0;
}

 

 

Αυτό που προσπαθείς να κάνεις σε αυτόν τον κώδικα επιτυγχάνεται με σχετική ασφάλεια με τη συνάρτηση strncpy() από το <cstring>...

Disclaimer: Πέρα από πολύ απλά πράγματα, δεν έχω εμπειρία από C++ οπότε η γνώμη μου δεν έχει ιδιαίτερη βαρύτητα.

 

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

 

Με chars και strncpy δεν είναι C++ αλλά C. Η "C" υποστήριξη της C++ ήταν λάθος και ειδικά οι cτάδε headers δεν θα έπρεπε να υπάρχουν καν ή τουλάχιστον να βγαίνει ένα τεράστιο warning που να σου δίνει να καταλάβεις ότι προσπαθείς να κάνεις κάτι με λάθος τρόπο και να πρέπει να κάνεις include το <cshutupiknowwhatiamdoing> για να σωπάσει.

 

Αν ο OP θέλει να ασχοληθεί με C++, τότε πιστεύω πως πρέπει να τον καθοδηγήσουμε πως να ασχοληθεί με C++.

 

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

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

αν ήταν αντί για char[] ένας Integer θα άλλαζε τιμή και θα δούλευε κανονικά , τώρα όμως πέρνω ένα error όταν παω να καλέσω την fun()

Τι ερρορ; Μηπως σου λεει οτι δεν μπορεις να "εκχωρησεις" pointer to const char σε char; Γιατι να το λεει αυτο;
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

...

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

 

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

 

Απλά τη συγκεκριμένη συμβουλή και των τριών σας τη θεωρώ off-topic (όχι κακή δηλαδή, απλά offtopic), διότι ο φίλος έκανε συγκεκριμένη ερώτηση για να καταλάβει πως λειτουργούν οι δείκτες (αυτό είπε στο αρχικό του ποστ) κι εσείς επί της ουσίας του λέτε ΜΗΝ χρησιμοποιείς δείκτες.

 

Εγώ προτίμησα να απαντήσω απευθείας σε αυτό που ρώτησε.

 

ΥΓ. Και με την C++ μπορεί να κάνει κανείς υπέροχο low-level programming, οπότε γιατί να μη μάθει κάτι και για αυτό τώρα που έχει χρόνο και διάθεση εφόσον τον ενδιαφέρει;

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Moderators

Βλέπω ότι έχει απαντήσει πολύς κόσμος, αλλά δεν πειράζει. Τι είναι ένας pointer λοιπόν. Ένας pointer είναι μια μεταβλητή της οποίας η τιμή είναι μια διεύθυνση μνήμης. Έστω ότι έχουμε τον παρακάτω κώδικα:

int a = 5;
int b = 8;

Τότε η μνήμη μας θα μοιάζει κάπως έτσι (πάντα στην πρώτη σειρά θα βάζω το όνομα της μεταβλητής, στη δεύτερη τη διεύθυνση της μνήμης και στην τρίτη την τιμή αυτής της διεύθυνσης):

(όπου '%' θεώρησέ το σκουπίδι, κάτι που δεν ξέρουμε τι είναι και δε μας νοιάζει)

???? a    b    ????
1c65 1c66 1c67 1c68
%    5    8    %

(το ότι τα a και b είναι σε διαδοχικές θέσεις μνήμης είναι τυχαίο και δε μας λέει κανείς ότι θα ισχύει πάντα)

 

Έστω ότι τώρα προσθέτουμε αυτό:

int *ap = &a;
int *bp = &b;

Τότε η μνήμη θα είναι κάπως έτσι:

      a    b                   ap   bp
1c65 1c66 1c67 1c68 .... .... f437 f438
%     5    8    %             1c66 1c67

Όταν βάζουμε το & μπροστά από μία μεταβλητή, αυτό μας δίνει τη διεύθυνση αυτής της μεταβλητής. Οπότε στον κώδικα δίνουμε στη μεταβλητή ap τη διεύθυνση που είναι η μεταβλητή a και στο bp τη διεύθυνση που είναι η μεταβλητή b.

 

Ωραία όλα αυτά ως εδώ. Πάμε τώρα σε κάτι πιο πολύπλοκο.

int a[5] = { 1, 2, 3, 4, 5 };

Τώρα η μνήμη μας θα μοιάζει κάπως έτσι:

a+0  a+1  a+2  a+3  a+4
1c65 1c66 1c67 1c68 1c69
1    2    3    4    5

Αυτή τη φορά ξέρεις ότι τα διαδοχικά στοιχεία του πίνακα θα είναι σε διαδοχικές θέσεις μνήμης. Αυτό σου επιτρέπει να κάνεις κάτι τέτοιο:

printf("%x\n", a);
printf("%d\n", *(a + 1));
printf("%d\n", a[2]);
printf("%d\n", 3[a]);

Ο τελεστής * λέει στο πρόγραμμα ότι δε θέλεις να πάρεις τη θέση μνήμης της μεταβλητής, αλλά την τιμή την οποία έχει αυτή η θέση μνήμης. Γι' αυτό και στο πρώτο printf θα σου εκτυπώσει το 1c65 (ντάξει, όχι αυτό ακριβώς, αλλά θα σου εκτυπώσει τη θέση μνήμης στην οποία είναι αποθηκευμένο το πρώτο στοιχείο του a).

Στη δεύτερη printf, του λες: δώσε μου την τιμή της μεταβλητής που υπάρχει στη διεύθυνση a, συν μία θέση παρακάτω. Δηλαδή την τιμή της διεύθυνσης 1c66, που είναι το δεύτερο στοιχείο του a.

Επειδή αυτός ο τρόπος είναι λίγο περίεργος και μπερδευτικός, υπάρχει η τρίτη printf, που λέει: δώσε μου το 3ο στοιχείο του πίνακα a.

Η τέταρτη printf μπορεί να φαίνεται περίεργη, αλλά δουλεύει, και κάνει το ίδιο πράγμα που κάνουν και οι παραπάνω: εκτυπώνει μια τιμή του a (στην περίπτωσή μας την τέταρτη).

 

Τι έκανες εσύ:

s+0  s+1  s+2  s+3  s+4  s+5  s+6  s+7  s+8  s+9  s+10 s+11 s+12 s+13
5f32 5f33 5f34 5f35 5f36 5f37 5f38 5f39 5f4a 5f4b 5f4c 5f4d 5f4e 5f4f
H    e    l    l    o         w    o    r    l    d    \0   %    %

Πήγαινε στην πρώτη θέση του s (δηλαδή στην διεύθυνση 5f32) και βάλε μου το "asd"

Πώς μπορείς να το κάνεις αυτό; Η θέση 5f32 χωράει μόνο έναν χαρακτήρα κι εσύ του δίνεις 3. Θα μπορούσε να βάλει το s στη θέση του e και το d στη θέση του l, καταλαμβάνοντας 3 θέσεις, αλλά η C κάτι τέτοιο δεν το κάνει. Αν χρησιμοποιούσες το string της C++ και του έλεγες κάτι τέτοιο, θα στο έκανε αλλά θα έσβηνε όλα τα υπόλοιπα γράμματα και θα σου έμενε μόνο το asd στο τέλος (αυτό που ήθελες δηλαδή). Επειδή όμως χρησιμοποιείς char*, κάτι τέτοιο δε γίνεται.

 

Μπορείς να δεις το τελευταίο κομμάτι κώδικα εδώ.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

@Kercyn:

Μετά το zero byte δεν είναι σκουπίδια αλλά initialized μνήμη (επίσης zero bytes). Δες την ερώτηση 1.30 από το C-faq.

%p και cast σε (void *) για να εκτυπώσεις το pointer.

  • Like 4
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Moderators

@Kercyn:

Μετά το zero byte δεν είναι σκουπίδια αλλά initialized μνήμη (επίσης zero bytes). Δες την ερώτηση 1.30 από το C-faq.

%p και cast σε (void *) για να εκτυπώσεις το pointer.

 

Μάλιστα, δεν το ήξερα αυτό.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

βασικα καλα τα λες. σκουπιδα θα εχει. init σε zero γινεται μονο σε global

 

 

 

When an automatic array or structure has a partial initializer, the remainder is initialized to 0

 

Απο το λινκ της αναρχιας

Επίσης το δοκιμασα.

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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

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

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

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

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

Σύνδεση

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

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