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

Εργασία στην C


rafail1994

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

Γνωρίζω πως ο defacer σε ψήνει ενώ εγώ σου την σπάω, κανένα πρόβλημα από μένα, είπαμε that's life.

Έλα ρε συ τώρα.

 

Τα παιδιά στο δημοτικό λένε λόγια τύπου "ε άντε δείρτε με να δω τι θα κερδίσετε". Εκτός από τη συζήτηση, πιο πολύ μειώνει εσένα να το παίζεις θύμα και κατατρεγμένος. Μπορείς να επιχειρηματολογήσεις πολύ καλύτερα από αυτό.

 

Για τα πρακτικά, ούτε έχω τη φάτσα σου στο τοίχο και πετάω βελάκια βρίζοντας σε, ούτε με τον defacer βρισκόμαστε κάθε μέρα και πίνουμε vodka. Συμφωνώ πάντα με αυτόν που πιστεύω ότι έχει δίκιο (ή αν όχι δίκιο τουλάχιστον πιο πειστικά επιχειρήματα) άσχετα αν στατιστικά έχει τύχει από τις 100 αντιδικίες σας να συμφωνήσω στις 90 με τον defacer.

 

Τεσπα. Πάω για ύπνο γιατί είμαι πτώμα. Κακώς μίλησα εξαρχής.

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

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

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

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

Δημοσιευμένες Εικόνες

 

 

Έλα ρε συ τώρα.

 

Τα παιδιά στο δημοτικό λένε λόγια τύπου "ε άντε δείρτε με να δω τι θα κερδίσετε". Εκτός από τη συζήτηση, πιο πολύ μειώνει εσένα να το παίζεις θύμα και κατατρεγμένος. Μπορείς να επιχειρηματολογήσεις πολύ καλύτερα από αυτό.

 

 

Για τα πρακτικά, ούτε έχω τη φάτσα σου στο τοίχο και πετάω βελάκια βρίζοντας σε, ούτε με τον defacer βρισκόμαστε κάθε μέρα και πίνουμε vodka. Συμφωνώ πάντα με αυτόν που πιστεύω ότι έχει δίκιο (ή αν όχι δίκιο τουλάχιστον πιο πειστικά επιχειρήματα) άσχετα αν στατιστικά έχει τύχει από τις 100 αντιδικίες σας να συμφωνήσω στις 90 με τον defacer.

Δεν με θυμάμαι να το έχω παίξει ποτέ θύμα και κατατρεγμένος, διότι ούτε αισθάνομαι ούτε είμαι, και απορώ από που βγάζεις αυτό το συμπέρασμα. Π.χ. στο παρόν νήμα πως το "κανένα πρόβλημα από μένα, that's life" αναγάγεται σε "λόγια δημοτικού", "είμαι θύμα και κατατρεγμένος", δεν το καταλαβαίνω.

 

Δεν με θυμάμαι να συνηθίζω να ζητάω π.χ. την συνδρομή του φόρουμ να με υποστηρίξει όταν διαπληκτίζομαι με τον defacer ή οποιονδήποτε άλλον (σε αντίθεση με τον defacer). Η μια και μοναδική φορά που ζήτησα προστασία ήταν από τον moderator μετά από το ban στο οποίο με παρέσυρε μαζί του ο defacer, και αυτό μόνο και μόνο επειδή είχα ήδη εξαντλήσει προ πολλού κάθε άλλο μέσο και το είχα "υποσχεθεί" μετά το ban (ακόμα κι αυτό όμως το μετάνιωσα, γιατί στην τελική δεν έχει κανένα νόημα).

 

Δεν με ενδιαφέρει ούτε να κάνω φίλους (ούτε εχθρούς) στο φόρουμ του insomnia (και σε κανένα άλλο), δεν με ενδιαφέρει να αυξήσω τα likes μου ούτε να αυξήσω τα likes όσων συμφωνούν μαζί μου, δεν με ενδιαφέρει ούτε να δημιουργήσω ούτε να συμμετάσχω σε φορουμιστικές κλίκες, δεν με ενδιαφέρει ούτε να θεοποιήσω ούτε να ... απο-θεοποιήσω άγνωστους αξιολογώντας τους από το πλήθος των post τους και των likes που έχουν στο τάδε ή στο δείνα φόρουμ, δεν με ενδιαφέρει αν ο άλλος περιμένει από μένα να του αναλύω με σεντόνια την κάθε μου τοποθέτηση επειδή διαφορετικά δεν θα με... like ή θα με θεωρήσει ανήθικο, άσχετο ή οτισήποτε άλλο, και πολύ περισσότερο δεν με ενδιαφέρει να κερδίσω το χρυσό λουκούμι των top posters και most liked participants κόβοντας μεροκάματο σε κάθε πιθανό κι απίθανο νήμα και φόρουμ είτε έχω να προσφέρω είτε όχι.

 

Κυρίως δεν με ενδιαφέρει να μου επιβάλλει ο άλλος το πως και πότε θα απαντήσω, σε ποιο νήμα και με ποιον τρόπο. Btw, για αυτό και μου τη σπάει το SO (το οποίο btw κι εγώ διαβάζω αραιά και που, αν και προτιμώ το Programmer's branch του).

 

Τεσπα. Πάω για ύπνο γιατί είμαι πτώμα. Κακώς μίλησα εξαρχής.

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

 

 

Άσχετα από το off-topic spoiler, όποιος ενδιαφέρεται μπορεί πολύ εύκολα να βρει ακόμα και στο google τι εστί VxWorks και Nucleus RTOS, ποιο ρόλο διαδραματίζουν αυτά τα λειτουργικά στα παγκόσμια δρώμενα των ενσωματωμένων, και κυρίως πόσοι και ποιοι τα έχουν χρησιμοποιήσει και ακόμα τα χρησιμοποιούν.

 

ΥΓ. Btw, με τον albNik μια χαρά συνεννοηθήκαμε σε 3 μόλις ποστς, με το καθένα τους να αποτελείται από μόλις 1-2 γραμμές.

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

Δύσκολο θέμα..

 

Καθώς ξεκίνησα τον προγραμματισμό σε συστήματα που δεν είχαν αυτόματη αποδέσμευση μνήμης συνήθισα να αποδεσμεύω ότι δεσμεύω (ήταν βασική πρακτική της εποχής - διαφορετικά τιμωρία δια ροπάλου!), από την άλλη πλευρά στα μοντέρνα mainstream συστήματα αυτό δεν είναι πλέον απαραίτητο (εκτός και αν δεν εμπιστευτούμε τον Memory Manager τους κλπ), τελικά στο σύνολο των λογισμικών μου προτιμώ "καθαρή έξοδο" με αποδέσμευση της μνήμης (συνήθεια κύριοι - άσε ότι από πίσω τρέχουν και εργαλεία παρακολούθησης μνήμης [κατά το debug] οπότε βρίσκω άνετα mem. leaks) - με μια εξαίρεση, αν έχω δεσμεύσει τεράστια ποσά RAM εκεί (επειδή έχω ένα πολύ ενοχλητικό παράδειγμα με έναν γνωστό Browser που στο κλείσιμο δοκιμάζει να αποδεσμεύσει ότι έχει δεσμεύσει και τρώει πολύ χρόνο, οπότε βαριέμαι και τον τερματίζω από Task Manager -το έχω δει το έργο πριν το δημοσίευμα του Chen :-P) φλερτάρω με την ιδέα (δίχως ενοχές) να κλείσω κατευθείαν αδιαφορώντας για την αποδέσμευση της μνήμης ώστε να μην με βλαστημούν οι χρήστες κατά την έξοδο :-D

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

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

Βασικά εδώ μπλέκονται δύο διαφορετικά θέματα:

  • η διαχείριση μνήμης κάποιου process κατά τη διάρκεια της εκτέλεσής του
  • η διαχείριση μνήμης σε επίπεδο λειτουργικού καθώς παλιές processes τερματίζουν και νέες ξεκινούν

Για το πρώτο είναι τελείως εμφανές ότι αν έχεις leaks θα μείνεις eventually από μνήμη οπότε δεν έχουμε τίποτα ιδιαίτερο να πούμε, πάντα θα πρέπει κανείς να αποδεσμεύει τη μνήμη εκτός κι αν είναι α) πολύ βιαστικός και β) πολύ σίγουρος ότι το συγκεκριμένο πρόγραμμα ποτέ δε θα κινδυνέψει να μείνει (π.χ. αν έφτιαχνα ένα πρόγραμμα για να μου τυπώσει ένα ιστόγραμμα συχνοτήτων που λέει και ένα άλλο επίκαιρο θέμα). Ακόμα και τότε αυτά είναι πράγματα που δικαιολογείται κανείς να τα κάνει μόνο αφότου έχει φάει τη "σωστή" διαχείριση μνήμης με το κουτάλι.

Για το δεύτερο τώρα, ένα σεντονάκι χρειάζεται...

Ανάλυση

Κρατάω στην άκρη (και θα επανέλθω παρακάτω) το θέμα ότι αν δεν κάνεις free στο τέλος δε μπορείς γενικά να ξέρεις αν το πρόγραμμά σου είναι leak-free ή όχι. Απο κει και πέρα:

1. Αν μιλάμε για λειτουργικό με virtual memory (π.χ. Windows, Linux και οποιοδήποτε άλλο σύγχρονο mainstream λειτουργικό): δεν υπάρχει τίποτα να συζητήσουμε, μόλις κάποιο process τερματίσει ο memory manager απλά παύει να θεωρεί πως η virtual μνήμη του υπάρχει και το "deallocation" γίνεται αυτόματα. Directx, θεωρητικά ναι πρέπει να εμπιστεύεσαι τον VMM του λειτουργικού αλλά πρακτικά αν βάλουμε στην κουβέντα το ενδεχόμενο bugs του λειτουργικού σε τόσο βασικά σημεία πολύ σύντομα δε θα μπορούμε να κάνουμε κουβέντα γιατί "τα πάντα μπορεί να έχουν bug".

2α. Αν μιλάμε για non-multitasking λειτουργικό χωρίς virtual memory (π.χ. DOS, CP/M): κι εδώ δεν υπάρχει τίποτα να συζητήσουμε. Εφόσον η μοναδική process που τρέχει έχει τον έλεγχο του address space, εξ ορισμού δεν είναι δυνατόν να προκληθεί "leak" σε επίπεδο λειτουργικού: όταν μια νέα process ξεκινήσει έχει στη διάθεσή της πρακτικά το σύνολο της μνήμης του συστήματος. Το αν αυτή η μνήμη περιέχει ακόμα τιμές από την προηγούμενη process είναι αδιάφορο.


Βέβαια είναι γεγονός πως γενικά υπάρχουν τρόποι μια εφαρμογή να διατηρήσει μέρος της μνήμης που χρησιμοποιούσε αφότου "τερματίσει". Για παράδειγμα σε DOS υπάρχουν τα προγράμματα TSR τα οποία δεν τερματίζουν με το κλασικό int 21h/4Ch και εξακολουθούν να καταλαμβάνουν μνήμη (στην ουσία από τη σκοπιά του λειτουργικού αυτά δεν είναι πλέον "προγράμματα" αλλά δυναμικά φορτωμένοι device drivers). Αυτές οι δυνατότητες όμως δε σχετίζονται με την παρούσα συζήτηση καθώς απαιτούν από την υπο συζήτηση εφαρμογή να ζητήσει ρητά από το λειτουργικό τη μη-αποδέσμευση της μνήμης της.



2β. Αν μιλάμε για multitasking λειτουργικό χωρίς virtual memory: αυτή είναι η μοναδική περίπτωση στην οποία το να μη κάνεις free μπορεί να σε πονέσει αργότερα. Αλλά ας εξετάσουμε λίγο καλύτερα για τι πράγμα μιλάμε ακριβώς...

Λειτουργίες όπως virtual memory και memory protection μπορούν να υλοποιηθούν μόνο σε hardware¹, δηλαδή απαιτούν την ύπαρξη μιας MMU. Αυτό σημαίνει πως έχουμε τώρα τις εξής περιπτώσεις:

2β(i). Multitasking λειτουργικό σε αρχιτεκτονική χωρίς MMU.

Εδώ λοιπόν μιλάμε για ένα περιβάλλον όπου ισχύουν τα παρακάτω:

  • οποιαδήποτε εφαρμογή τρέχει μπορεί να κρασάρει άνετα όλο το σύστημα (αν πειράξει δεδομένα του λειτουργικού, πράγμα που μπορεί να κάνει αφού δεν υπάρχει memory protection)
  • οποιαδήποτε εφαρμογή τρέχει μπορεί να "προκαλέσει αναταράξεις" σε οποιαδήποτε άλλη εφαρμογή (αν πειράξει τη μνήμη της) και αυτή με τη σειρά της να καταλήξει να φερθεί σα μεθυσμένη πειράζοντας άλλα πράγματα που δεν της ανήκουν -- στην ουσία δηλαδή ένα domino effect

Αυτό ήδη θα πρέπει να έχει ξεκινήσει να ακούγεται σαν bad idea αφού έτσι κι αλλιώς μιλάμε για καταστάσεις όπου μια buggy εφαρμογή μπορεί να κατεβάσει όλο το σύστημα, υπάρχει σαφής αντένδειξη να μην σχεδιάσει κανείς το λειτουργικό έτσι ώστε να υποστηρίζεται η εκτέλεση πολλών εφαρμογών ταυτόχρονα -- φανερά κάτι τέτοιο είναι asking for trouble. Και όντως, π.χ. σε x86 η δυνατότητα multitasking και το memory protection ήρθαν πακέτο.

Επιπλέον, η απουσία MMU τα τελευταία 30 ας πούμε χρόνια δικαιολογείται με το σκεπτικό της διατήρησης του κόστους της πλατφόρμας σε εξαιρετικά χαμηλά επίπεδα. Όταν λοιπόν συναντάμε πλατφόρμες χωρίς MMU αυτό πρακτικά σημαίνει πως μιλάμε για microcontrollers των οποίων το κόστος είναι εξαιρετικά χαμηλό (για παράδειγμα, με googling βρήκα πως ένας ATmeta328 σαν αυτόν που έχει ο Arduino Uno κοστίζει ακόμα και λιγότερο από $1 σε μαζικές παραγγελίες).
 
Εδώ λοιπόν έρχεται η δεύτερη αντένδειξη: για ποιό λόγο σκοπεύει κανείς να τρέχει πολλαπλές εφαρμογές ταυτόχρονα -- και μη ξεχνάμε, αυτό σημαίνει εφαρμογές ανεπτυγμένες ξεχωριστά εφόσον σε διαφορετική περίπτωση απλά θα μπορούσε ο κώδικας να ενοποιηθεί -- σε ένα τόσο χαμηλών δυνατοτήτων και αντίστοιχου προσανατολισμού σύστημα; Δε νομίζω πως υπάρχει ικανοποιητική απάντηση εδώ. Αν σκοπεύεις να κυκλοφορήσεις ένα προϊόν (πλατφόρμα) με στόχο να επιτρέπεται το development και η εκτέλεση ανεξάρτητων εφαρμογών από τρίτους, δεν έχει καμία λογική η πλατφόρμα αυτή να είναι τόσο χαμηλών δυνατοτήτων γιατί απλά δε θα καλύψει τις απαιτήσεις των developers και των τελικών χρηστών σε πρακτικό επίπεδο.

Για τους παραπάνω λόγους συμπεραίνω (και αν έχει κανείς σχετικά χειροπιαστά δεδομένα θα χαρώ να ενημερωθώ σχετικά) πώς το σενάριο 2β(i) είναι καθαρά θεωρητικό. Και σαν επιπλέον επιχείρημα να αναφέρω ότι ανάλογο hardware σε όλες τις περιπτώσεις που τυχαίνει να γνωρίζω δεν έχει καν operating system: αφού έτσι κι αλλιώς το μόνο πράγμα που θα τρέχει θα είναι μία συγκεκριμένη εφαρμογή, ποιός ο λόγος να υπάρχει ξεχωριστό "layer παροχών υπηρεσίας στις εφαρμογές"; Έτσι κι αλλιώς η μία και μοναδική εφαρμογή μπορεί να τα υλοποιήσει μόνη της, και η απουσία του επιπλέον abstraction layer είναι κέρδος σε αποτελεσματικότητα.

2β(ii). Multitasking λειτουργικό σε αρχιτεκτονική με MMU η οποία παρέχει μόνο memory protection και όχι VM.

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

Ξεκινάω λέγοντας πως εφόσον έχουμε πολλαπλά processes αλλά όχι virtual memory, το λειτουργικό προφανώς δε μπορεί να δίνει "όλο το address space" σε κάθε εφαρμογή όπως π.χ. κάνει το MS-DOS (το οποίο υπενθυμίζω τρέχει αποκλειστικά σε x86 real mode όπου η MMU "δεν υπάρχει"). Επομένως αναγκαστικά το όποιο memory allocation από τη σκοπιά της εφαρμογής θα πρέπει να γίνεται ζητώντας αντίστοιχα μνήμη από το λειτουργικό.

Αυτό σημαίνει πως το λειτουργικό απαραίτητα ξέρει ποιά εφαρμογή έχει δεσμεύσει ποιά μνήμη. Το οποίο με τη σειρά του σημαίνει πως είναι καθαρά στην ευχέρεια του λειτουργικού να αποδεσμεύσει όλη αυτή τη μνήμη όταν η process τερματίσει.

Επομένως, το να μην αποδεσμευτεί τελικά αυτή η μνήμη θα μπορούσε να συμβεί:

  • είτε γιατί το λειτουργικό δεν θέλει
  • είτε γιατί το λειτουργικό θέλει αλλά δεν μπορεί (δηλαδή bug)

Αφήνω κατα μέρος το δεύτερο σενάριο μιας και θεωρώ ότι δεν έχει νόημα να γενικολογούμε με επιχείρημα πως "το τάδε συγκεκριμένο λειτουργικό στην τάδε συγκεκριμένη πλατφόρμα, και τα δύο εκ των οποίων πιθανότατα θα αγνοείς την ύπαρξη για όλη σου τη ζωή, έχει bug και πρέπει να προφυλαχτείς μόνος σου απ' αυτό", κρατάω το πρώτο.

Εδώ που τα λέμε τώρα: γιατί να μη θέλει το λειτουργικό να κάνει κάτι τέτοιο, εφόσον επιλέγοντας να μη το κάνει τα memory leaks των εφαρμογών αυτόματα παίρνουν προαγωγή σε memory leaks του συστήματος τα οποία αργά η γρήγορα θα οδηγήσουν στην κατάρρευσή του; Δε νομίζω πως υπάρχει ικανοποιητική απάντηση σ' αυτό το ερώτημα.

Συνοψίζοντας (TL;DR)

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

Επίλογος

Θα χαρώ πολύ να δεχτώ σχόλια, παρατηρήσεις και κριτική πάνω στην παραπάνω ανάλυση. Επίσης, χρωστάω (σε επόμενο post όμως) μια εξέταση του τι γίνεται όσον αφορά το πρακτικό και πολύ σημαντικό θέμα του εντοπισμού των memory leaks μέσα σε μια εφαρμογή (κατά τη διάρκεια της εκτέλεσής της).

Κυρίες, κύριοι και μικρό παιδί καλό σας απόγευμα.

 

PS: Δεν πρέπει να υπάρχει χειρότερος και πιο buggy editor από αυτόν εδώ. Epic fail.

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

Όπως έγραψα στην προηγούμενη ανάρτηση μου, την εποχή που μάθαινα προγραμματισμό η φιλοσοφία των λειτουργικών συστημάτων (μιλάμε για υπολογιστές 16-bit, όπως Amiga, Atari ST κλπ) βασιζόταν στο multitask μεν αλλά δίχως προστασία μνήμης και δίχως επίσης σοφιστικέ διαχείριση της όποιας δεσμευμένης μνήμης, συνεπώς η πρώτη οδηγία ανάπτυξης σε αυτά τα περιβάλλοντα ήταν, "καθαρή έξοδος", διαφορετικά μπορούσες να πάρεις στο λαιμό σου το Σύστημα συνολικά ή να συμβάλεις στην αποσταθεροποίηση του. Ορμώμενοι (εγώ και πολλοί άλλοι προφανώς) προγραμματιστές εκείνης της εποχής αναπτύξαμε το "θέσφατο" (τότε - και μια συνήθεια ύστερα), ότι δεσμεύσεις (και δεν το εκχωρείς κάπου άλλου -σε κάποια λειτουργία του συστήματος |πχ. Clipboard) πρέπει να το αποδεσμεύεις όταν παύεις να το χρησιμοποιείς ή κατά την έξοδο σου από το πρόγραμμα - για τους λόγους που προανέφερα. Άλλες εποχές - άλλα μηχανήματα, άλλες προδιαγραφές.

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

Όπως έγραψα στην προηγούμενη ανάρτηση μου, την εποχή που μάθαινα προγραμματισμό η φιλοσοφία των λειτουργικών συστημάτων (μιλάμε για υπολογιστές 16-bit, όπως Amiga, Atari ST κλπ) βασιζόταν στο multitask μεν αλλά δίχως προστασία μνήμης και δίχως επίσης σοφιστικέ διαχείριση της όποιας δεσμευμένης μνήμης, συνεπώς η πρώτη οδηγία ανάπτυξης σε αυτά τα περιβάλλοντα ήταν, "καθαρή έξοδος", διαφορετικά μπορούσες να πάρεις στο λαιμό σου το Σύστημα συνολικά ή να συμβάλεις στην αποσταθεροποίηση του. Ορμώμενοι (εγώ και πολλοί άλλοι προφανώς) προγραμματιστές εκείνης της εποχής αναπτύξαμε το "θέσφατο" (τότε - και μια συνήθεια ύστερα), ότι δεσμεύσεις (και δεν το εκχωρείς κάπου άλλου -σε κάποια λειτουργία του συστήματος |πχ. Clipboard) πρέπει να το αποδεσμεύεις όταν παύεις να το χρησιμοποιείς ή κατά την έξοδο σου από το πρόγραμμα - για τους λόγους που προανέφερα. Άλλες εποχές - άλλα μηχανήματα, άλλες προδιαγραφές.

 

Εξακολουθεί να θεωρείται καλή πρακτική, τουλάχιστον στους επαγγελματικούς κύκλους. Η καλή πρακτική ως ιδέα είναι άμεσα συνδεδεμένη με τη συνήθεια. Το link που έχω δώσει εξηγεί συνήθεις λόγους.

 

Το scalability είναι ένας βασικός ανάμεσά τους, ανεξαρτήτως ΟS, ειδικά όταν μιλάμε για server apps, resident apps, libraries κλπ. Το debugging είναι ένας ακόμα, στον οποίον αναφέρθηκες ήδη κι εσύ. Από τη συγκεκριμένη καλή πρακτική επωφελούμαστε όχι μόνο για την μνήμη αλλά και για άλλου είδους resources (handles, files, connections, etc).

 

Παρεμπιπτόντως, ο χρόνος δέσμευσης & αποδέσμευσης μνήμης μπορεί να επισπευσθεί δραματικά με χρήση  heap pools. Περισσότερα για την υλοποίησή τους υπάρχουν στο άρθρο του Murray που έχω δώσει σε προηγούμενο ποστ (ξέρεις, εκείνο για το οποίο το μεγάλο παιδί αποφάνθηκε πως είναι άσχετο με το νήμα).

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

Όπως έγραψα στην προηγούμενη ανάρτηση μου, την εποχή που μάθαινα προγραμματισμό η φιλοσοφία των λειτουργικών συστημάτων (μιλάμε για υπολογιστές 16-bit, όπως Amiga, Atari ST κλπ) βασιζόταν στο multitask μεν αλλά δίχως προστασία μνήμης και δίχως επίσης σοφιστικέ διαχείριση της όποιας δεσμευμένης μνήμης, συνεπώς η πρώτη οδηγία ανάπτυξης σε αυτά τα περιβάλλοντα ήταν, "καθαρή έξοδος", διαφορετικά μπορούσες να πάρεις στο λαιμό σου το Σύστημα συνολικά ή να συμβάλεις στην αποσταθεροποίηση του. Ορμώμενοι (εγώ και πολλοί άλλοι προφανώς) προγραμματιστές εκείνης της εποχής αναπτύξαμε το "θέσφατο" (τότε - και μια συνήθεια ύστερα), ότι δεσμεύσεις (και δεν το εκχωρείς κάπου άλλου -σε κάποια λειτουργία του συστήματος |πχ. Clipboard) πρέπει να το αποδεσμεύεις όταν παύεις να το χρησιμοποιείς ή κατά την έξοδο σου από το πρόγραμμα - για τους λόγους που προανέφερα. Άλλες εποχές - άλλα μηχανήματα, άλλες προδιαγραφές.

 

Σ' ευχαριστώ για την ενημέρωση -- δεν είχα την τύχη (πραγματικά έτσι το αισθάνομαι) να έχω στα χέρια μου κάποιο από τα δημοφιλή 16μπιτα της εποχής οπότε δεν ασχολήθηκα ποτέ με τον προγραμματισμό τους και δεν ήξερα πως ο 68000 ήταν καθαρά υπόθεση χωρίς MMU.

 

Όπως λες και συ βέβαια -- άλλες εποχές, και σ' αυτό το θέμα και σε πολλά άλλα. Την εποχή που ξεκινούσα να ασχολούμαι "σοβαρά" με προγραμματισμό αν κάποιος μου έλεγε άσε μας με το θεωρητικό performance κουκλίτσα μου και κοίτα να γράψεις maintainable κώδικα θα του κατέβαζα τον εξάψαλμο, ενώ σήμερα εγώ ο ίδιος το λέω και το ξαναλέω.

 

Η αλήθεια είναι πάντως πως τα παραπάνω απεικονίζουν την κατάσταση με bias προς την μετά των home computers εποχή. Θα ήταν πολύ καλύτερα όμως, και το αποτέλεσμα πολύ πληρέστερο, αν υπήρχε και το στοιχείο της ιστορικής εξέλιξης (πώς βρεθήκαμε εδώ που είμαστε και γιατί). Αν θέλεις κι έχεις τη δυνατότητα να γράψεις μερικά πράγματα σχετικά please PM me, και τη χαμαλοδουλειά του editing την αναλαμβάνω εγώ.

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

defacer και migf1 σας ευχαριστώ πολύ για τις γνώσεις που προσφέρετε απλόχερα, ο καθένας με τον τρόπο του.

 

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

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

defacer και migf1 σας ευχαριστώ πολύ για τις γνώσεις που προσφέρετε απλόχερα, ο καθένας με τον τρόπο του.

 

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

 

Χεχε, κι εσύ δεν πας πίσω jsiskos από τα λίγα που έχω καταλάβει στην μικρή μέχρι στιγμής συμμετοχή σου στο φόρουμ ;)

 

Σχετικά με τα tsr, δεν είναι και τόσο σπάνια στα ενσωματωμένα. Για παράδειγμα...

 

http://msdn.microsoft.com/en-us/library/ms859408.aspx

 

What are Memory Leaks?

....

Memory leaks are bad on any system. For the automotive platform, however, they become much more of a significant problem. This is due to the following:

  • Applications stay in memory when not in use. Typically, the operating system can clean up after a program which leaks memory after it terminates. However, since we do not allow the users to easily shut down automotive applications, Windows CE cannot clean up after any ill–behaved applications.
  • System run–time is expected to be unlimited. If all else fails, a system reboot will clean up all memory leaks. However, although we do support system reboots, our goal is that the user will never need to do so. Since you do not need to “reboot” your car stereo every few weeks because it stops working, or because the performance becomes poor, it is unacceptable to need to do so on the automotive platform.
...

 

Τα Desk Accessories του GEM TOS στον Atari ST, και τα MacOS Extensions προϋπήρχαν των TSR του μεταγενέστερου DOS. Στα σημερινά mainstream OS τα *nix deamons και τα Windows services θα μπορούσαμε να πούμε πως είναι οι χρηστικοί απόγονοί τους (αλλά τα internals τους είναι προφανώς τελείως διαφορετικά).

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

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

Σ' ευχαριστώ για την ενημέρωση -- δεν είχα την τύχη (πραγματικά έτσι το αισθάνομαι) να έχω στα χέρια μου κάποιο από τα δημοφιλή 16μπιτα της εποχής οπότε δεν ασχολήθηκα ποτέ με τον προγραμματισμό τους και δεν ήξερα πως ο 68000 ήταν καθαρά υπόθεση χωρίς MMU.

 

Όπως λες και συ βέβαια -- άλλες εποχές, και σ' αυτό το θέμα και σε πολλά άλλα. Την εποχή που ξεκινούσα να ασχολούμαι "σοβαρά" με προγραμματισμό αν κάποιος μου έλεγε άσε μας με το θεωρητικό performance κουκλίτσα μου και κοίτα να γράψεις maintainable κώδικα θα του κατέβαζα τον εξάψαλμο, ενώ σήμερα εγώ ο ίδιος το λέω και το ξαναλέω.

 

Η αλήθεια είναι πάντως πως τα παραπάνω απεικονίζουν την κατάσταση με bias προς την μετά των home computers εποχή. Θα ήταν πολύ καλύτερα όμως, και το αποτέλεσμα πολύ πληρέστερο, αν υπήρχε και το στοιχείο της ιστορικής εξέλιξης (πώς βρεθήκαμε εδώ που είμαστε και γιατί). Αν θέλεις κι έχεις τη δυνατότητα να γράψεις μερικά πράγματα σχετικά please PM me, και τη χαμαλοδουλειά του editing την αναλαμβάνω εγώ.

Σε γενικές γραμμές το Amiga OS ήταν ένα από τα πρώτα ή ίσως το πρώτο OS με preemptive multitasking σε προσιτή τιμή, δίχως όμως προστασία μνήμης. Ο MC68000 δεν προσέφερε τέτοια καλούδια και το κόστος ενός MMU θα καθιστούσε την τιμή του υπολογιστή πολύ ακριβή. Ήταν ένα tradeoff τυπικό για τα μηχανήματα παρόμοιας αρχιτεκτονικής εκείνου του καιρού (πλην της Apple LISA -κόστος 9500$+/- το 1983).

 

Η δέσμευση μνήμης γινόταν από τον Memory Manager μέσο μιας διπλής συνδεδεμένης λίστας (Exec-List στην ορολογία του Συστήματος) όπου κάθε κόμβος υποδείκνυε και ένα διαθέσιμο μπλοκ (Long-Word aligned σύμφωνα με την τεκμηρίωση). Αν η εφαρμογή έκλεινε δίχως αποδέσμευση μνήμης, η λίστα αυτή δεν ενημερωνότανε οδηγώντας σε απώλεια πολύτιμων πόρων (η τυπική Amiga [500] ερχότανε με 512KB μνήμης και ήταν δεδομένο ότι οι χρήστες θα πρόσθεταν +1MB αν θέλανε να μπορούν να τρέχουν τα περισσότερα παιχνίδι της εποχής).

 

Το σύστημα πέρα από την εκχώρηση και την αποδέσμευση μνήμης κατ' εντολή των εφαρμογών (με τις εντολές AllocMem & FreeMem) δεν λάμβανε άλλες πρωτοβουλίες σε αυτό το ζήτημα. Μάλιστα η αποδέσμευση μνήμης στις πρώτες εκδόσεις (Kickstart 1.x) του OS προϋπέθετε γνώση του μεγέθους του αρχικά δεσμευμένου μπλοκ (η FreeMem δηλαδή γραφότανε έτσι “FreeMem(myBuffer, 100)” αν το myBuffer είχε μέγεθος 100 bytes). Σε μεταγενέστερες εκδόσεις (Kickstart 2.x-3.x κλπ) η κατάσταση βελτιώθηκε με την προσθήκη της συνάρτησης AllocVec που θυμόταν το μέγεθος του δεσμευμένου μπλοκ αυτόματα (αντίστοιχα υπήρχε και FreeVec). Βέβαια μέρος της πολυπλοκότητας μπορούσε να αποκρυφτεί με την βοήθεια των standard συναρτίσεων δέσμευσης μνήμης της C.

 

Οι συναρτήσεις δημιουργίας και διαχείρισης memory lists (ουσιαστικά επρόκειτο για Exec-Lists) ήταν προσβάσιμες και στις εφαρμογές ώστε να δημιουργούν εύκολα δικές τους memory lists (ή κάποιες εξειδικευμένες «λίστες» όπως Stacks/Queues) με ότι δέσμευαν εντός προγράμματος με την ελπίδα ότι θα διευκόλυνε τους προγραμματιστές στην διαχείριση μνήμης. Η φιλοσοφία τους ήταν πως κάθε προγραμματιστής κατά την δημιουργία της εφαρμογής του (στην ορολογία του Συστήματος “Task”) θα όριζε σε μια συγκεκριμένη δομή (struct Task) την κεφαλή της memory list (Task.tc_MemEntry) του προγράμματος του. Κατά την έξοδο θα αρκούσε λοιπόν μια κλήση σε μια ειδική συνάρτηση (RemTask) που θα αναλάμβανε την αποδέσμευση κάθε δεσμευμένου κόμβου (η κλήση θα μπορούσε να γίνει και από το Σύστημα). Η πρακτική αυτή ήταν ότι πιο κοντινό στην «αυτόματη αποδέσμευση μνήμης» μπορούσε να προσφέρει το Σύστημα αλλά δεν γνώρισε μεγάλη απήχηση διότι α)δεν ήταν συνηθισμένο να χρησιμοποιείται από τους προγραμματιστές και β)ούτε σίγουρα τα αποτελέσματα της αν η εφαρμογή κράσαρε ή γ)αν κάποια άλλη εφαρμογή από λάθος έγραφε στην tc_MemEntry την δικιά της ή κάποιας άλλης Task κλπ -Στην πραγματικότητα ο Task/Memory Manager [EXEC] δεν κρατούσε ο ίδιος αναφορά για την δεσμευμένη μνήμη (παρά μόνο για την ελεύθερη) σε δικιά του ανεξάρτητη λίστα οπότε όλα τα υπόλοιπα ήταν "γιατροσόφια" :-/.

 

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

 

Βέβαια όταν έχεις 7MHz ταχύτητα, ανυπαρξία MMU κλπ δεν μπορείς να κανείς και πολλά μέσο software οπότε πετάς την διαχείριση μνήμης στον προγραμματιστή με βάση την θεμελιώδης αρχή εκείνο τον καιρό - "ο προγραμματιστής ξέρει πάντα τι κάνει ή τι πρέπει να κάνει".

 

 

Υ.Γ.

- Αργά πάντως, στο λυκόφως της ύπαρξης του, το Amiga OS από την έκδοση 2+ προσέφερε ένα FLAG στην AllocMem το οποίο ήταν reserved για προστασία μνήμης με την υποσημείωση (βέβαια) στην τεκμηρίωση πως αυτό το FLAG θα είχε μελλοντική χρησιμότητα όταν το Σύστημα θα υλοποιούσε κάποια μορφή Memory Protection.

 

- Στην Amiga δεν υπήρχε από όσο θυμάμαι η έννοια του TSR (δεν το είχε ανάγκη) καθώς όλες οι εφαρμογές της έτρεχαν παράλληλα.

 

- .. Πολλοί ιστορικοί της Apple τείνουν να θάβουν το Amiga OS με διάφορους ιταμούς χαρακτηρισμούς (τρομάρα τους! Χα.. στην πραγματικότητα ήταν πολύ ανώτερο του "Machintosh SYSTEM" και λέγεται ότι υπήρξε διάχυτη η αγωνία στην ηγεσία της Apple τότε.. μήπως η Commodore μπει "στα χωράφια της" - πράγμα που δεν έγινε ποτέ καθώς η τελευταία πλάσαρε πολύ περισσότερο την Amiga ως μια εξελιγμένη παιχνιδομηχανή παρά ως ένα επαγγελματικό Η/Υ |οι A2000/A3000 δεν βοήθησαν την κατάσταση) ..

 

- Οπωσδήποτε τις παραπάνω λεπτομέρειες δεν θα τις θυμόμουν πλέον αν δεν διέθετα το AMIGA ROM Kernel Reference Manual: Libraries (3rd Ed.) το οποίο παρέχει μια συνοπτική περιγραφή του Amiga BIOS (Kickstart) v2 ;-)

 

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

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

 

 

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

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

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

Μια μέθοδος είναι αν θέλεις διαστάσεις Μ χ Ν να δημιουργήσεις ένα μονοδιάστατο με διάσταση το γινόμενο M*N και μετά να εξομοιώνεις την προσπέλαση του σαν να ήταν δισδιάστατος. Ο migf1 έχει πολλά μηνύματα που περιγράφει αυτή τη τακτική και μάλιστα έγραψε ένα πριν λίγες ώρες αν θυμάμαι καλά.

 

Αλλιώς μπορείς κάλλιστα να τον δημιουργήσεις ως δισδιάστατο. Τι είναι ένας δισδιάστατος πίνακας ? Στην ουσία είναι ένας μονοδιάστατος που τα στοιχεία του είναι και αυτά πίνακες. Με αυτό στο μυαλό σου, σε ένα διπλό δείκτη δεσμεύεις μνήμη για Μ δείκτες (δηλαδή για Μ γραμμές) και μετά στον καθένα από αυτούς τους δείκτες δεσμεύεις μνήμη για Ν στοιχεία (πχ int). Έτσι έχεις τον δυναμικά εκχωρημένο δισδιάστατο πίνακά σου με μόνη διαφορά ότι τα στοιχεία δεν θα είναι όλα συνεχόμενα (όπως ισχύει πχ σε ένα πίνακα a[3][5])

 

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

 

 

RETURN VALUE

The malloc() ... functions return a pointer to the allocated memory ... On error, these functions return NULL.

Αν υπάρξει πρόβλημα, η malloc θα σου επιστρέψει NULL. Το τι κάνει από εκεί και πέρα το κάθε πρόγραμμα είναι στην προαίρεση του προγραμματιστή. Συνήθως ελέγχεται αν έχει επιστραφεί NULL και το πρόγραμμα σταματάει αφού εμφανίσει κάποιο περιγραφικό μήνυμα λάθους (και ίσως αφού έχει αποδεσμεύσει τυχόν άλλη μνήμη που δέσμευσε προηγουμένως).

 

Για να είμαι πιο ακριβής στην ερώτησή σου η λειτουργία της malloc δεν πάρα πολύ απλή και υπάρχουν διάφορες συμπεριφορές. Για παράδειγμα NULL επιστρέφεται σε οποιοδήποτε error χωρίς απαραίτητα να σημαίνει ότι δεν υπάρχει αρκετή μνήμη. Σε unix μπορεί να γίνει έλεγχος της μεταβλητής errno για τιμή ENOMEM για να είμαστε σίγουροι αλλά συνήθως δεν ελέγχεται το errno γιατί στο 99% των περιπτώσεων επιστροφή NULL ισοδυναμεί με την ανυπαρξία διαθέσιμης μνήμης.

 

Επίσης, κάποια λειτουργικά (όπως το linux) χρησιμοποιούν από τη μάνα τους μια μαγκιά που λέγεται "overcommit" δηλαδή ακόμη και να έχουμε 1GB μνήμη και να ζητήσουμε από την malloc 4GB, αυτή δεν θα επιστρέψει NULL αλλά θα δουλέψει κανονικά. Η εκχώρηση της μνήμης γίνεται εικονικά χωρίς να δεσμευτεί καθόλου μνήμη. Μόνο αφού το πρόγραμμα πάει να γράψει δεδομένα γίνεται η πραγματική δέσμευση της μνήμης. Αυτό γίνεται γιατί σου λέει πολλές φορές ένα πρόγραμμα ζητάει παραπάνω μνήμη χωρίς όμως να την χρησιμοποιεί οπότε γιατί να του χαλάσουμε το χατήρι ? Έτσι ένα πρόγραμμα που ζητάει 1GB αλλά χρησιμοποιεί μόνο 5MB θα παίξει κανονικά χωρίς προβλήματα. Αυτό όμως έχει ως συνέπεια να χρειάζονται στα προγράμματα πρόσθετοι έλεγχοι εκτός από τον έλεγχο της malloc.

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

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

Αναγνωρίζει πίνακες με πολλές διαστάσεις, αλλά όταν τους ορίζεις στατικά τότε δεσμεύονται στην stack σε μονοκόμματη μνήμη. Όταν τους ορίζεις δυναμικά στην heap τότε κάνεις ότι θέλεις εσύ.
 
Στατικά ορισμένος 2Δ:

int arr[rows][cols];

Με το παραπάνω δεσμεύονται μονοκόμματα στην μνήμη (stack) rows * cols * sizeof(int) bytes, δηλαδή rows * cols integer στοιχεία. Όταν εσύ γράφεις π.χ....

arr[1][3] = ...

ο compiler το μετατρέπει σε κάτι σαν κι αυτό...

*(arr + 1 * cols + 3) = ...

σε γενικές γραμμές ισχύει πως αν i και j είναι οι 2Δ συντεταγμένες ενός στοιχείου και n η σειριακή θέση του στοιχείου, τότε ....

n = i * cols + j;

καθώς και το ανάποδο...

i = n / cols;
j = n % cols;

 
Δυναμικά δεσμευμένος 2Δ πίνακας:
 
Εδώ μπορείς να κάνεις ότι θέλεις. Οι πιο συνηθισμένοι τρόποι είναι 2 όμως:
 
1ος τρόπος:

int **arr = malloc( rows * sizeof(int *) );   // δεσμεύουμε συνεχόμενη μνήμη για rows int pointers
if (NULL == arr ) {
    // malloc() failed, handle error here
}
 
// κατόπιν για κάθε char pointer δεσμεύουμε cols συνεχόμενους integers
for (int r=0; r < rows; r++)
{
    arr[r] = malloc( cols * sizeof(int) );  // για καθέναν από τους rows int pointers δεσμεύουμε μνήμη για cols integers
    if ( NULL == arr[r] ) {       // malloc() failed
        /* release mem of any previous successfully allocated rows */
        for (int i=r-1; i > -1; i--) {
            free( arr[i] )
        }
        /* release also the mem reserved for the rows int pointers */
        free( arr );
        exit(1);
    }
}
...
// όταν τελειώσουμε με τον πίνακά μας, απελευθερώνουμε την μνήμη που έχουμε δεσμεύσει δυναμικά για αυτόν
for (int r = 0; r < rows; r++)
    if ( arr[r] )
        free( arr[r] );
free( arr );

 
Στο παραπάνω όπως ίσως έχεις ήδη διαπιστώσει, ΔΕΝ δεσμεύουμε τον πίνακα σε συνεχόμενη μνήμη. Πρώτα δεσμεύουμε συνεχόμενη μνήμη για rows γραμμές (int *), και κατόπιν δεσμεύουμε για την κάθε μια ξεχωριστά cols integers.
 
Κάτι τέτοιο δηλαδή:

0 -> cols integers
1 -> cols integers
2 -> cols integers
...
(rows-1) -> cols integers

2ος τρόπος:

 

Μπορούμε να προσομοιώσουμε τον τρόπο που χρησιμοποιεί ο compiler για τους στατικούς πίνακες, δεσμεύοντας δηλαδή δυναμικά έναν 1Δ πίνακα, που θα μπορούμε να τον διαχειριζόμαστε είτε ως 1Δ είτε ως 2Δ.

#define ROWS ...
#define COLS ...
// δεσμεύουμε απευθείας και μονοκόμματα μνήμη για rows * cols integers
int *arr = malloc( ROWS * COLS * sizeof(int));
if ( NULL == arr ) {
    // malloc failed, handle error here
}
...
// όταν τελειώσουμε με τον πίνακά μας, απελευθερώνουμε την μνήμη που έχουμε δεσμεύσει
if ( arr )
    free( arr );

Με αυτόν τον τρόπο έχουμε σαφώς απλοποιημένη την δέσμευση/αποδέσμευση μνήμης, αλλά πλέον δεν μπορούμε να χρησιμοποιήσουμε την σύνταξη arr[j] μιας και ο πίνακας ΔΕΝ είναι 2Δ. Για το indexing πρέπει να χρησιμοποιήσουμε 1Δ σύνταξη, σύμφωνα με τους τύπους που έγραψα στην αρχή, για τους στατικούς πίνακες.

Π.χ.

for (n=0; n < ROWS* COLS; n++)
{
    i = n / COLS;
    j = n % COLS;
    printf( "arr[%d] == arr[%d][%d]\n", n, i, j );
}

Μπορούμε όμως να φτιάξουμε 3 macros στον preprocessor...

#define _I(n)   ( (n) / cols )
#define _J(n)   ( (n) % cols )
#define _N(i,j) ( (i * COLS + j)

και να κάνουμε διάφορα κόλπα, πχ...

for (i=0; i < ROWS; i++)
  for (j=0; j < COLS; j++)
    printf( "arr[%d][%d] == arr[%d]\n", i, j, _N(i,j));

ή...

for (n=0; n < ROWS * COLS; n++)
    printf( "arr[%d] == arr[%d][%d]\n", n, _I(n), _J[n] );

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

 

EDIT:

 

Αντί για "integers" έγραφα "χαρακτήρες"  :P

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

...

#define _I(n)   ( (n) / cols )
#define _J(n)   ( (n) % cols )
#define _N(i,j) ( (i * COLS + j)
και να κάνουμε διάφορα κόλπα, πχ...
for (i=0; i < ROWS; i++)
  for (j=0; j < COLS; j++)
    printf( "arr[%d][%d] = arr[%d]\n", _N(i,j), i, j );
ή...
for (n=0; n < ROWS * COLS; n++)
    printf( "arr[%d] == arr[%d][%d]\n", n, _I(n), _J[n] );

Η πρώτη printf δεν πρέπει να είναι όπως και η δέυτερη;

printf( "arr[%d] == arr[%d][%d]\n", _N(i,j), i, j );
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Labels είναι βρε συ, δεν είναι πράξεις που εκτελεούνται... εκτός αν εννοείς κάτι άλλο.



Α, ναι ναι... τώρα το είδα... έχω ανάποδα τη σειρά των arguments! Σωστός, μισό να το διορθώσω.

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

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

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

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

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

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

Σύνδεση

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

Συνδεθείτε τώρα

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