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

C Προβλημα με δυναμικη διχειριση μνημης.


antonis1245

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

@gon1332

Είδες την νέα παραλλαγή...που δεν έχει UB...

Δεν τη διάβασα. Τώρα όμως που είπες έριξα μία ματιά. Είσαι σίγουρος πως δεν έχει; :) I'll give you a chance.

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

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

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

Ναι! Αφού έκατσα και διάβασα! (και παράλληλα έφτιαξα και το φιδάκι στη Μ2000)

 

έχω χρησιμοποιήσει uint ...σε περίπτωση που κάπου δεν έχει οριστεί όπως στον gcc...έτσι ορίζεται:

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

Η αλήθεια είναι πως δε ξέρω αν θεωρείται UB, αλλά δεν ελέγχεις την επιστρεφόμενη τιμή των malloc. Οπότε αν αποτύχουν οι δεσμεύσεις θα τα buf και name θα είναι NULL.

 

Επίσης η scanf σου δεν έχει όριο στο μέγεθος εισαγώμενου αλφαριθμιτικού. Αν κάποιος πληκτρολογήσει ένα όνομα πάνω από s_size  χαρακτήρες, τότε δε θα μπει στο τέλος του buf NULL συν το γεγονός ότι μπορεί να αλλάξουν τα περιεχόμενα της μνήμης σου παραπέρα. Έτσι οι συναρτήσεις που μεταχειρίζονται nul-terminated strings, θα εμφανίζουν απροσδιόριστη συμπεριφορά. Αυτό γιατί έχουν σα σύμβαση τον τερματισμό σε nul, που ποιος ξέρει που θα το βρουν και αν θα το βρουν ποτέ.

 

Μια χαρά δουλεύει το unsigned. Υπάρχει και η βιβλιοθήκη inttypes.h που κάνει include την stdint.h που ορίζει ακέραιους τύπους με το μέγεθος να δηλώνεται explicitly. Πχ. uint8_t, int16_t.

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

Αυτό για την Scanf θα πρέπει να το δω...έχω βάλει στο buf ένα μέγεθος 255.

Τα malloc τα αρχικά ΔΕΝ τα ελέγχω πράγματι! (υποτίθεται ότι ξεκινάμε με μνήμη...εικονική τουλάχιστον κάτι γίγα..)

Αλλά για να είναι ο κώδικας καθώς πρέπει όντως θέλει και αυτόν τον έλεγχο!

θα βάλω μια if να τερματίζει αν δεν είναι οκ τα malloc.

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

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

 

Εκτός ότι είναι δυσανάγνωστος και δεν μπορεί κανείς αρχάριος να καταλάβει τι κάνει, δεν υπάρχει και νόημα να γίνει έτσι. Ακόμη και να μπουν σωστά ονόματα μεταβλητών και να γραφεί διαφορετικά ώστε να φαίνεται η λειτουργία του, και πάλι δεν θα έχει νόημα.

 

Στο συγκεκριμένο πλαίσιο της άσκησης δεν μιλάμε για ένα μικροελεγκτή με 64K μνήμη αλλά για μια "desktop" πλατφόρμα (το πιθανότερο x86) με άπλετη μνήμη οπότε τα άπειρα strlen και realloc δεν προσφέρουν τίποτα. Πολύ πιθανώς μάλιστα ο compiler να εντοπίσει το pattern και να αντικαταστήσει τα realloc με μεγάλες εκχωρήσεις μνήμης και να καταφύγει σε realloc πολύ λιγότερες φορές.

 

Ειδικά σε πλατφόρμες με "φτηνό" mmap, θα έχουμε κατευθείαν mmap ενός αριθμού από 4K pages και δεν θα ξανασχοληθεί με το θέμα :)

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

Πρόσεξε! Έχω δώσει μέγεθος σελίδας επίτηδες 20 χαρακτήρες για να μπορεί να δει κανείς ότι αν θέλουμε κάτι παραπάνω σε μια εισαγωγή υπολογίζουμε αυτόματα πόσες σελίδες θα προσθέσουμε. Αν ορίσουμε σελίδα π.χ. 1000000 χαρακτήρες τότε κάνουμε μηδενικό realloc μέχρι να γεμίσει αυτό! 

 

δες στην γραμμή 9

m_page=20

 

(δεν νομίζω ότι είναι κώδικας trolling...απλά δεν είναι όπως θα το έγραφε ο imitheos...)

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

Πρόσεξε! Έχω δώσει μέγεθος σελίδας επίτηδες 20 χαρακτήρες για να μπορεί να δει κανείς ότι αν θέλουμε κάτι παραπάνω σε μια εισαγωγή υπολογίζουμε αυτόματα πόσες σελίδες θα προσθέσουμε. Αν ορίσουμε σελίδα π.χ. 1000000 χαρακτήρες τότε κάνουμε μηδενικό realloc μέχρι να γεμίσει αυτό!

Το ξέρω ότι επέλεξες realloc κάθε 20 χαρακτήρες και θα μπορούσες να επιλέξεις μεγαλύτερο. Αυτό που τόνισα πριν και δεν θέλεις να το καταλάβεις είναι ότι και με "20 χαρακτήρες σελίδα" πάλι μηδενικό realloc γίνεται. Δεν είμαστε σε embedded σύστημα χωρίς MMU.

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

struct buf {
	size_t len;
	size_t max;
	char *buf;
};

int main(void)
{
	struct buf buf;
	char tmp[256];
	size_t tmplen;

	/* no sanity checks on malloc, realloc, etc */
	buf.len = 0;
	buf.max = 20;
	buf.buf = malloc(20);
	printf("After initial malloc\n");
	malloc_info(0, stdout);
	
	do {
		fgets(tmp, 255, stdin);
		tmplen = strlen(tmp);
		while (buf.len + tmplen > buf.max) {
			buf.max += 20;
			buf.buf = realloc(buf.buf, buf.max);
			printf("After realloc buf.max=%zu\n", buf.max);
			malloc_info(0, stdout);
		}
		strcpy(buf.buf + buf.len, tmp);
		buf.len += tmplen;
	} while (strcmp(tmp, "EOT\n"));

	free(buf.buf);
	printf("In the end\n");
	malloc_info(0, stdout);

	return 0;
}
Τυχαίος κώδικας που κάνει realloc κάθε 20 bytes. Η malloc_info είναι μια από τις πολλές μη-πρότυπες συναρτήσεις που παρέχει η glibc στο linux και σου δίνει πληροφορίες για την εκχώρηση. Σε άλλα λειτουργικά υπάρχουν αντίστοιχες μη-πρότυπες συναρτήσεις.

 

Έβαλα μετά από κάθε realloc να εμφανίζεται τι ποσό μνήμης έχει εκχωρηθεί και του πέταξα ένα μεγάλο αρχείο σαν είσοδο. Θα μπορούσα να χρησιμοποιήσω και την strace για να δείξω τι system calls γίνονται αλλά έτσι θα είναι πιο ευκολονόητο στον αναγνώστη.

 

Αποτέλεσμα:

After initial malloc
<system type="current" size="135168"/>
<system type="max" size="135168"/>
<aspace type="total" size="135168"/>
<aspace type="mprotect" size="135168"/>
Αμέσως εκχωρείται ένα μεγάλο ποσό που χρησιμεύει για όλες τις πιθανές ανάγκες του προγράμματος και είναι συνήθως είναι τόσο για όλα τα προγράμματα.

 

After realloc buf.max=131600
<total type="mmap" count="1" size="135168"/>
After realloc buf.max=135160
<total type="mmap" count="1" size="139264"/>
After realloc buf.max=139260
<total type="mmap" count="1" size="143360"/>
After realloc buf.max=143340
<total type="mmap" count="1" size="147456"/>
After realloc buf.max=147440
<total type="mmap" count="1" size="151552"/>
After realloc buf.max=151540
<total type="mmap" count="1" size="155648"/>
After realloc buf.max=155640
<total type="mmap" count="1" size="159744"/>
.
.
.
After realloc buf.max=706400
<total type="mmap" count="1" size="708608"/>

In the end
Τίποτα. Όλα μηδενικά.
Παρέλειψα τα ενδιάμεσα στάδια και εμφανίζω μόνο τα σημεία που εκχωρείται μνήμη. Όπως βλέπεις, μέχρι τη στιγμή που έφτασε να χρησιμοποιεί 135160 bytes δεν έγινε ούτε μία φορά εκχώρηση μνήμης. 135000 / 20 = 6750 realloc χωρίς λόγο ύπαρξης.

 

Από εκεί και πέρα γίνεται εκχώρηση μνήμης κατά μία 4K page την φορά και φυσικά μόνο αφού χρησιμοποιήσεις το προηγούμενο ποσό δηλαδή στο περίπου έχουμε 200 realloc την φορά χωρίς λόγο ύπαρξης.

 

Αυτό σε linux (και υποθέτω και στα υπόλοιπα *nix που το mmap είναι φτηνό).

 

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

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

Δημοσ. (επεξεργασμένο)

Μια φορά λέμε ότι ο κώδικας δεν πρέπει να ταυτίζεται με την υλοποίηση. Τώρα έκανες το ανάποδο..

Να αποφασίσουμε τι θέλουμε!

 

(όταν έγραφα το κώδικα δεν ήξερα ότι απλά χρησιμοποιεί το ίδιο κομμάτι...αφού το κατάλαβα έκανα μια "μαγκιά" στο τεστάρισμα...έβαλα μετά το πρώτο malloc ένα άσχετο malloc για να δω αν σπάσει την "συνέχεια", δηλαδή την δυνατότητα να σου δίνει το realloc συνεχόμενη μνήμη, άρα να μην αντιγράφει)

 

Είμαι σίγουρος ότι η αντιπάθεια που δείχνεις ιmitheos είναι "επαγγελματικού τύπου"..

Ευχαριστώ!

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

Επισκέπτης
Αυτό το θέμα είναι πλέον κλειστό για περαιτέρω απαντήσεις.

  • Δημιουργία νέου...