Πέμπτη 7 Ιανουαρίου 2016

Από τη C στη Μ2000 - Αλφαριθμητικά!

Έτυχε χθες και έγραψα ένα πρόγραμμα σε C για ένα φίλο στο Insomnia.gr (φόρουμ). Το ζήτημα ήταν να μπορέσει να φτιάξει ένα λεξιλόγιο (δεν θα βάλω εδώ το πώς το σώνεις στο δίσκο). Απλά θέλουμε να χρησιμοποιήσουμε δυναμικό πίνακα. Δηλαδή να καταχωρούμε λέξεις/φράσεις σε πίνακα που θα αυξάνεται αν χρειάζεται!
Στα προγράμματα που έγραψα έχω βάλει και περιορισμό, για το παράδειγμα δέκα εισαγωγών (5 ζευγαριών). Καλό είναι ένα πρόγραμμα να ορίζει έναν αριθμό μέγιστων εισαγωγών. Το "δυναμικό" της αύξησης των στοιχείων δεν έχει να κάνει μόνο με τον αριθμό αλλά και το μήκος των αλφαριθμητικών που θα χρησιμοποιηθούν.  Άλλες λέξεις/εκφράσεις είναι μεγάλες και άλλες μικρές.

Στην Μ2000 τα αλφαριθμητικά αποδίδονται σε μνήμη ως BSTR. Αυτά τα αλφαριθμητικά έχουν και μια παράμετρο, εσωτερικά, που δηλώνει το μήκος τους. Στη C το μήκος δεν δηλώνεται χωριστά αλλά με "σκοπό". Σκοπός είναι αυτό που όταν το βρούμε κάνουμε κάποια ενέργεια. Και στα αλφαριθμητικά στη C σκοπός είναι το 0. Μάλιστα ακόμα και Μ2000 βάζει διπλά 0 (0 ανά byte, ή δυο 0 σε ένα word, διπλό byte), διότι και τα BSTR έχουν από κατασκευής αυτή την ιδιότητα. Όμως δέχονται να έχουν πολλά 0 μέσα στο "σώμα" του αλφαριθμητικού. Αν βάλουμε στη μέση ενός αλφαριθμητικού το 0 στη C τότε το κοντεύουμε. Το θέμα στη C είναι ότι ο μηχανισμός για τα αλφαριθμητικά και ο μηχανισμός για τη μνήμη είναι ανεξάρτητοι. Μπορούμε να έχουμε κάπου μνήμη που μπορεί να γραφεί και να διαβάζουμε απ΄οπουδήποτε αρκεί να βρούμε το 0, το τερματισμό μέσα στο χώρο που μπορεί να διαβαστεί! Επιπλέον μπορούμε να αλλάζουμε άμεσα τα "γράμματά" του, εφόσον πάλι η μνήμη είναι διαθέσιμη για αλλαγή (διότι υπάρχουν και αλφαριθμητικά στη C που δεν αλλάζουν, μόνο διαβάζονται). Οι εντολές char *A; Α="ΑΛΦΑ";  δεν περιλαμβάνουν απόδοση μνήμης. Απλά κάνουν το Α να δείχνει το "ΑΛΦΑ" που είναι τοποθετημένο σε τμήμα μνήμης μόνο για ανάγνωση.
Στη Μ2000 ένα Α$="ΑΛΦΑ" θα κάνει το εξής: το "Αλφα" θα γραφεί σε ένα BSTR το οποίο θα δείχνει η Α$. Αν κάνω αυτό Α$="ΑΛΦΑ1" τότε το Α$ θα δείχνει σε ένα άλλο BSTR και το προηγούμενο θα απελευθερωθεί.
Δείτε τώρα τι γίνεται στη C. Έχουμε φτιάξει μια char *A; και θέλουμε να βάζουμε ένα αλφαριθμητικό. Θα πρέπει να δώσουμε μνήμη σε αυτό και να την απελευθερώσουμε αργότερα. (αυτά η Μ2000 τα κάνει μόνη της).  Μια Α=malloc(1000); δίνει χώρο για 1000 χαρακτήρες. Ουσιαστικά στο Α γράφτηκε η διεύθυνση ενός μπλοκ μνήμης. Ο αριθμός χειρισμού του μπλοκ και η αρχική διεύθυνση είναι ίδια. Με Free(A); αποδεσμεύουμε τη μνήμη.
Αν θέλουμε να γράψουμε σε αυτό τότε με strcpy(A,"Γεια Χαρά"); γίνεται το εξής...το "Γεια Χαρά" μαζί με ένα χαρακτήρα 0 είναι σε μνήμη μόνο ανάγνωσης, και στην strcpy δίνεται η διεύθυνση του πρώτου γράμματος Γ. Ο προορισμός δίνεται από τη διεύθυνση στο Α, που δείχνει στο μπλοκ που φτιάξαμε. Έτσι θα έχουμε την αντιγραφή του αλφαριθμητικού στο Α. Μια printf("%s\n", A); θα τυπώσει το Γεια Χαρά (και μια αλλαγή γραμμής). Το πλεονέκτημα στη C είναι ότι αν θέλουμε να γράψουμε άλλο αλφαριθμητικό δεν χρειάζεται να βγει ένα καινούργιο BSTR. Το μειονέκτημα είναι ότι έχουμε περιορισμό στο μέγεθος. Αν θέλουμε περισσότερο πρέπει να κάνουμε τελικά αντιγραφή και αυτή μας τη κάνει η realloc() που μας δίνει ένα νέο χώρο (ή αν μπορεί αφήνει τον ίδιο, ανάλογα δηλαδή αν υπάρχει συνεχόμενη ελεύθερη μνήμη). Δηλαδή το μπλοκ μνήμης πρέπει να είναι "συνεχόμενης μνήμης". Στη Μ2000 δεν υπάρχει θέμα, διότι κάθε φορά δίνεται ένα νέο BSTR,
Από τα παραπάνω βλέπουμε ότι η δυσκολία της C δίνει πιο καλά αποτελέσματα σε ότι έχει να κάνει με την ταχύτητα και τη διαχείριση μνήμης. Απλά το πέναλτι είναι στη διαμόρφωση του κώδικα!

Στο πρόγραμμα στη Μ2000 δεν ενδιαφερόμαστε για το μέγεθος των bstr, αφού αυτά γράφονται χωριστά το καθένα (σε μια heap, ένα σωρό ή στοίβα στη μνήμη). Ο πίνακας που είναι σε ένα μπλοκ (και περιέχει δείκτες στα BSTR) μπορεί να επεκταθεί. Εδώ βάζουμε ότι η σελίδα είναι των 4 στοιχείων (θα μπορούσε να ήταν των 4000, δηλαδή το έχουμε βάλει εδώ αυθαίρετα). Και έχουμε έναν τρόπο να αυξάνουμε τις σελίδες. (Δείτε όμως ότι εδώ αυξάνονται κατά ένα κάθε φορά)

Σελ=4 : Σελ_νο=1
Πίνακας Ον$(2), Λεξ$(Σελ*Σελ_νο)
Ον$(0)="Δώσε Ελληνική λέξη ή φράση","Δώσε Αγγλική λέξη ή φράση"
Όριο=Διάσταση(Λεξ$(),1)
Κ=10
κορυφή=0
Ενώ Κ>0 {
      Τύπωσε Ον$(κ υπολ 2);
      Αν κορυφή>=Όριο τότε {
            Σελ_νο++ : Πίνακας Λεξ$(Σελ*Σελ_νο) : Όριο=Διάσταση(Λεξ$(),1)
      }
      Δες οκ {
            Εισαγωγή ": ", Λεξ$(κορυφή)
      }
      Αν Λεξ$(κορυφή)="" ή όχι οκ τότε έξοδος
      κορυφή++
      Κ--
}
Τύπωσε "Να δούμε τι έχουμε"
Αν κορυφή >0 τότε {
      Για ι=0 έως κορυφή {
            Τύπωσε Λεξ$(ι)
      }
}



εδώ είναι το πρόγραμμα στη C
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main()
{
    char  *name, *buf,  *temp;
    uint word=0, top=0, limit=0;
    const int s_size=255, m_page=20;
    char  label[][100]= {"Enter a Greek phrase/word: ","Enter an English phrase/word: "};
    int k=10, p=1, new_p;
    buf=malloc(s_size);
    name=malloc(p*m_page+1);
    limit=m_page*p;
    top=0;
    word=0;
    do {
    printf("%s",label[k % 2]);
    scanf("%[^\n]%*c",buf);
    if(strlen(buf)>0) {
        top+=strlen(buf)+1;
        if(top>limit) {
                // problem because p is changed before check  for realloc.
                new_p=p+(top-limit)/m_page+1;
                temp=realloc(name,new_p*m_page+1);
                if (!temp) break;
                p=new_p;  // now is ok
                limit=m_page*p;
                name=temp;
        }
 
        strcpy(name+word, buf);
        strcpy(buf,"");  // if you forget it...you have a problem
        printf("Your phrase is: %s\n",word+name);
        word=top;
     } else { k=1;
        printf("Look this %d 2\n", k);
     }
    k--;
    printf("%d\n", k);
    }while(k>0);
    printf("Now we see what we have\n");
    word=0;
    strcpy(name+top,"");  // guard
    k=strlen(name+word);
    while(k>0) {
        printf("%s\n",name+word);
        word+=k+1;
        k=strlen(name+word);
    }
    free(name);
    free(buf);
    return 0;

Σε ένα πίνακα name βάζουμε το ένα αλφαριθμητικό μετά το άλλο! Οι σελίδες αυξάνονται τόσες όσες χρειάζονται για να καλύψουν την επόμενη εισαγωγή (αν η εισαγωγή έχει μέγεθος μεγαλύτερης αυτής της σελίδας). Εδώ η σελίδα είναι σε Bytes και όχι σε θέσεις πίνακα.

Σε Linux ανοίγουμε τη κονσόλα και πάμε σε ένα test φάκελο με cd test
γράφουμε nano lex.c
αντιγράφουμε το παραπάνω
πατάμε ctrl+X μετά Υ για να σώσουμε
γράφουμε Make lex
και μετά
./lex
και έτσι τρέχει το πρόγραμμα!
(θα δω για οδηγίες και σε windows)
Διορθώθηκε το πρόγραμμα στη C για να μην έχει UB δηλαδή κάποια απροσδιόριστη συμπεριφορά! (είχε πριν αφαίρεση δυο  δεικτών ενώ τώρα έχει αφαίρεση αριθμού από δείκτη, καθώς και εκεί που λέει problem  p, άλλαζα το p που είχε επίπτωση στο μέγεθος πριν σιγουρευτώ ότι θα πετύχει η αλλαγή μεγέθους του πίνακα)


Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου

You can feel free to write any suggestion, or idea on the subject.