Πόση μνήμη καταλαμβάνει μια string μεταβλητή;

Συζητήσεις για την γλώσσα C και C++

Συντονιστές: Super-Moderators, WebDev Moderators

Απάντηση
lakritidis
Δημοσιεύσεις: 401
Εγγραφή: 04 Αύγ 2005 14:35
Τοποθεσία: Katerini
Επικοινωνία:

Πόση μνήμη καταλαμβάνει μια string μεταβλητή;

Δημοσίευση από lakritidis » 24 Φεβ 2008 13:13

Η ερώτηση μοιάζει απλή.

Κώδικας: Επιλογή όλων

string test1 = "0123456789";
string test2 = "0123456789012345678901234567890123456789";

cout << sizeof test1 << endl;
cout << sizeof test2 << endl;

// Τυπώνει
// 28
// 28
Η sizeof τυπώνει 28 και για τις δύο περιπτώσεις, ανεξαρτήτως του τι περιέχει μέσα το string. Ψάχνοντας στο Internet βρήκα ότι h sizeof επιστρέφει το μέγεθος του αντικειμένου std::string.

Αυτό το 28 δεν είναι standard και εξαρτάται από την υλοποίηση και τα optimizations που υπάρχουν για τα strings σε κάθε compiler. Πχ κάποιος αναφέρει ότι ο ίδιος κώδικας σε g++ σε ubuntu τυπώνει 4.

Αντίθετα, το ακόλουθο τυπώνει αυτό που περιμέναμε:

Κώδικας: Επιλογή όλων

cout << test1.size&#40;&#41; << endl;
cout << test2.size&#40;&#41; << endl;

// Τυπώνει
// 10
// 40
Ερωτήσεις
1. Ή τα strings δεν έχουν termination character όπως οι char, ή έχουν και η size τον αγνοεί. Τι από τα δύο συμβαίνει;
2. Τελικά πόση μνήμη καταλαμβάνει πχ ή test1? 28bytes; 10bytes; ή μήπως 11bytes (συν termination character);

Άβαταρ μέλους
MannyCalavera
Δημοσιεύσεις: 13
Εγγραφή: 11 Δεκ 2007 23:00
Επικοινωνία:

Πόση μνήμη καταλαμβάνει μια string μεταβλητή;

Δημοσίευση από MannyCalavera » 26 Φεβ 2008 17:13

1) Δε συμβαίνει τίποτα από τα δύο. Έχουν termination character, απλώς το είπες και μόνος σου: "η sizeof επιστρέφει το μέγεθος του αντικειμένου std::string" και όχι τον αριθμό των χαρακτήρων που έχει αποθηκεύσει.

Πρέπει να ξεκαθαρίσεις το εξής: Άλλο το αντικείμενο std::string και άλλο το string "0123456789". Το std::string είναι ένα αντικείμενο που ο ρόλος του στην ουσία είναι να διαχειρίζεται το string που του δίνεις και να σε διευκολύνει στις συνήθεις λειτουργίες που κάνει κάποιος σε ένα string (αντιγραφή, πρόσθαφαίρεση χαρακτήρων, συνένωση strings, κ.α.), κάτι που σε διαφορετική περίπτωση έπρεπε να κάνεις με functions της C (strcpy, strlen, strcat, κλπ.).

Το std::string, ως καλός διαχειριστής, δε σ'αφήνει να επέμβεις απ'ευθείας στο string (δηλ. εκεί που πραγματικά αποθηκεύονται οι χαρακτήρες "0123456789" στη μνήμη) για λόγους ασφάλειας. Όλες οι λειτουργίες πάνω στο string γίνονται μέσω των member functions και operators του std::string.
Θες να δεις πόσους χαρακτήρες έχει? Γράφεις test1.length()
Θες να μάθεις πόση μνήμη πιάνει? Γράφεις test1.size()

Και έρχομαι στο δεύτερο ερώτημά σου.

2) Τι γίνεται λοιπόν όταν δίνεις sizeof(test1) ?
Στην ουσία ζητάς να μάθεις το χώρο που καταλαμβάνει στη μνήμη το αντικείμενο std::string.
Η τιμή που θα σου δώσει, δηλαδή, είναι ο χώρος που καταλαμβάνει ο διαχειριστής, και όχι το πραγματικό string.

Τώρα γιατί βγάζει αυτό το 28, είναι άλλο θέμα, όχι και τόσο εύκολο να απαντηθεί:
Το μέγεθος ενός αντικειμένου (class, struct) καθορίζεται από πολλούς παράγοντες που χοντρικά είναι οι εξής

1. Από το σύνολικό μέγεθος των data members που περιέχει (εξαιρούνται οι static μεταβλητές γιατί δε θεωρούνται μέλη του αντικειμένου)

2. Από τη σειρά με την οποία δηλώνονται αυτά τα data members, όσο περίεργο κι αν ακούγεται αυτό. Έχει να κάνει με την έννοια του data structure alignment

3. Από το inheritance. Αν το αντικείμενό σου "κληρονομεί" από άλλο αντικείμενο, τότε στο μέγεθός του πρέπει να προστεθεί και το μέγεθος του "πατρικού" αντικειμένου (base class)

4. Από το virtual inheritance. Για κάθε virtual inheritance προσθέτουμε +4byte (νομίζω).

5. Από τον compiler. Διαφορετικοί compilers βγάζουν διαφορετικά sizeof για το ίδιο αντικείμενο(ειδικά αν έχει inheritance). Βέβαια τα 4byte που έγραψες για τον g++ δεν ακούγονται ρεαλιστικά(μήπως αυτός που το έγραψε μέτρησε το μέγεθος του δείκτη σε αντικείμενο std::string??). Οι διαφορές τους έχουν συνήθως να κάνουν με το padding (δες data structure alignment) και το virtual table (βλ. 6).

6. Από το αν έχει virtual methods. Τα πράγματα δεν είναι τόσο απλά εδώ. Εξαρτάται από τον compiler και πως υλοποιεί το vtable.


Το ζουμί της υπόθεσης είναι το εξής: Άλλο πράμα ένα string χαρακτήρων και άλλο ένα αντικέιμενο std::string. Επίσης το μέγεθος στη μνήμη ενός αντικειμένου δεν υπολογίζεται εύκολα με το χέρι, ιδίως για μεγάλα και περίπλοκα αντικείμενα όπως το std::string.
The living still give me the creeps

lakritidis
Δημοσιεύσεις: 401
Εγγραφή: 04 Αύγ 2005 14:35
Τοποθεσία: Katerini
Επικοινωνία:

Πόση μνήμη καταλαμβάνει μια string μεταβλητή;

Δημοσίευση από lakritidis » 26 Φεβ 2008 18:20

Ευχαριστώ για την αναλυτική σου απάντηση.

Απλά στην εφαρμογή που φτιάχνω με ενδιαφέρει να γνωρίζω που πηγαίνει και το τελευταίο byte. Βλέπω ότι αυτό είναι ψιλοδύσκολο με χρήση strings, επομένως επιστρέφω σα βρεγμένη γάτα στα απλά c-strings (char), όπου σίγουρα το μέγεθος στη μνήμη είναι 1byte για κάθε χαρακτήρα (ή μήπως όχι;).

Μιας και είσαι γνώστης του αντικειμένου, έχω υλοποιήσει ένα binary tree με τη βοήθεια της παρακάτω class.

Κώδικας: Επιλογή όλων

class Lexicon &#123;
	public&#58;
		// Public Functions for Lexicon Manipulation
		Lexicon&#40;&#41;;
		~Lexicon&#40;&#41;;
		// Count Lexicon Members
		int countNodes&#40;&#41;;
		// Insert/update a term to/in Lexicon
		class Lexicon* InsertNode&#40;wchar_t*& term, int* termid, int address&#41;;
		// Calculate memory occupied by Lexicon
		void OccupiedMemory&#40;int* Memory&#41;;
		// Print the Lexicon &#40;Screen&#41;
		void display&#40;&#41;;
		// Write the Lexicon to disk &#40;LexiconFile&#41;
		void flush&#40;wofstream& LexiconFile&#41;;
	private&#58;
		// Private Lexicon Members
		wchar_t* term;		// Term
		int termId;			// Term Id
		int docfreq;		// Term frequency
		int il_address;		// Inverted List Address on disk
		// Branch Pointers
		class Lexicon* leftChild;
		class Lexicon* rightChild;
&#125; *p;
Με βάση τα όσα είπες υποθέτω ότι κάθε κόμβος του δέντρου καταλαμβάνει στη μνήμη

4 bytes για τον pointer σε wchat_t +
4 bytes για τον integer termid +
4 bytes για τον integer docfreq +
4 bytes για τον integer il_address +
4 bytes για τον pointer στο δεξί child του κόμβου +
4 bytes για τον pointer στο αριστερό child του κόμβου
= 24 bytes

Και συνολικά το δεντρο πιάνει 24 * (πλήθος κόμβων) bytes στη μνήμη.

Σωστά;

Edit: Εκπληκτικό αυτό με το data structure alignment. Αυτή η δομή

Κώδικας: Επιλογή όλων

struct MixedData
&#123;
    char Data1;
    short Data2;
    int Data3;
    char Data4;
&#125;;

θα περιμέναμε να καταλαμβάνει 8 bytes και όμως καταλαμβάνει 12! Με μια απλή αναδιάταξη των members της, ρίχνουμε το μέγεθος στα 8 bytes

Κώδικας: Επιλογή όλων

struct MixedData
&#123;
    short Data2;
	char Data1;
	char Data4;
    int Data3;
&#125;;


Άβαταρ μέλους
MannyCalavera
Δημοσιεύσεις: 13
Εγγραφή: 11 Δεκ 2007 23:00
Επικοινωνία:

Πόση μνήμη καταλαμβάνει μια string μεταβλητή;

Δημοσίευση από MannyCalavera » 27 Φεβ 2008 15:55

Λογικά κάθε Lexicon έχει μέγεθος 24 και σε vc++ compiler και σε gcc και όλα το tree θα έχει μέγεθος 24*κόμβους .

Βέβαια αν δεν το δοκιμάσεις ποτέ μην είσαι βέβαιος, όπως είπα και πριν. Σε MingW (gcc 3.4.5) που το δοκίμασα έχει όντως μέγεθος 24.

Το data alignment είναι ένας από τους λόγους που ένας καλός προγραμματιστής πρέπει να έχει και καλή γνώση του hardware. Στην πλειοψηφία των εφαρμογών δεν πρόκειται ποτέ να απασχολήσει, αλλά υπάρχουν ειδικές περιπτώσεις όπου γίνεται σημαντικό (όταν προγραμματίζεις μια συσκευή με πολύ περιορισμένη μνήμη, π.χ. ένα κινητό).
Πρόσεχε μόνο, διότι ένα misaligned structure μπορεί να καταλαμβάνει λιγότερο χώρο, ότι κερδίζεις όμως σε μνήμη το χάνεις σε ταχύτητα.
The living still give me the creeps

Απάντηση

Επιστροφή στο “C, C++”

Μέλη σε σύνδεση

Μέλη σε αυτήν τη Δ. Συζήτηση: Δεν υπάρχουν εγγεγραμμένα μέλη και 0 επισκέπτες