Μεταγλώττιση Προγραμμάτων C σε περιβάλλον Unix/Linux


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

Δημιουργία, Μεταγλώττιση και Εκτέλεση Προγράμματος

Τα στάδια ανάπτυξης ενός προγράμματος C είναι τα ακόλουθα.

Δημιουργία

Δημιουργείτε ένα αρχείο που περιέχει ένα πλήρες πρόγραμμα. Μπορείτε να χρησιμοποιήσετε όποιδήποτε συντάκτη κειμένου γνωρίζετε. Παραδείγματα τέτοιων συντακτών σε Linux είναι οι vi, gvim, gedit, kedit.

Το όνομα αρχείου πρέπει να τελειώνει σε ``.c'' (τελεία, πεζό c), π.χ. myprog.c ή progtest.c. Τα περιεχόμενα πρέπει να υπακούν στο συντακτικό της C. 

Μεταγλώττιση

Υπάρχουν αρκετοί μεταγλωττιστές της C/C++. Ο GNU μεταγλωττιστής C gcc είναι δημοφιλής και διαθέσιμος σε πολλές πλατφόρμες. Στο Linux συνήθως υπάρχουν διαθέσιμα διάφορα συνώνυμα όπως cc, c++, g++ καθώς και τα c89, c99 (που ακολουθούν το αντίστοιχα ANSI πρότυπα) και o προεπεξεργαστής  cpp

Η καλύτερη πηγή πληροφόρησης είναι οι σελίδες εγχειριδίου του μεταγλωττιστή, δηλαδή εισάγουμε την εντολή man gcc είτε  info gcc ή χρησιμοποιούμε την γραφική διεπαφή λήψης βοήθειας του συστήματος. Επίσης, υπάρχει δυνατότητα πρόσβασης στο εγχειρίδο μεσω διαδικτύου στην διεύθυνση  

  http://www.gnu.org/software/gcc/

Για λόγους απλότητας από εδώ και στο εξής η κλήση του μεταγλωττιστή θα αναφέρεται ως gcc.

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

    gcc program.c

όπου program.c είναι το όνομα του αρχείου κειμένου του προγράμματος.

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

Αν η μεταγλώττιση είναι επιτυχής, το εκτελέσιμο αποθηκεύεται στον τρέχοντα κατάλογο με όνομα a.out εκτός αν χρησιμοποιηθεί η επιλογή -o : τότε χρησιμοποιείται το όνομα που ακολουθεί τη επιλογή -o. Είναι πιό βολικό να χρησιμοποιούμε την επιλογή -o όπως

    gcc -o program program.c

που θέτει το εκτελέσιμο στο αρχείο program (ή οτι όνομα βάλετε μετά το όρισμα "-o") αντί για το αρχείο a.out .

Εκτός από τη λύση της γραμμής εντολών, υπάρχουν αρκετά IDE's (Integrated Development Environments), είτε αποκλειστικά για Linux ή και cross-platform, τα οποία διευκολύνουν σημαντικά την ανάπτυξη μεγάλων εφαρμογών, ιδίως για προγραμματιστές που προέρχονται από πλατφόρμες προγραμματισμού MS-Windows. Τα πιο γνωστά περιβάλλοντα είναι τα:

Τα περιβάλλοντα αυτά παρέχουν εκτενή εγχειρίδια και online help, των οποίων η ανάλυση ξεφεύγει από τους σκοπούς αυτού του κειμένου. Πολλά από αυτά τα περιβάλλοντα αναγνωρίζουν την ύπαρξη της gcc και τη χρησιμοποιούν ως back-end. Έτσι τα περισσότερα που αναφέρονται στο κείμενο μπορούν να επιτευχθούν και μέσω των IDE's. Στο υπόλοιπο μέρος του κειμένου εννοείται η χρήση μόνο της απλής γραμμής εντολών του UNIX/Linux.

Εκτέλεση

Το επόμενο στάδιο είναι η εκτέλεση του εκτελέσιμου. Στο UNIX / Linux,  απλά εισάγουμε στη γραμμή εντολής το όνομα του εκτελεσίμου programa.out). Δύο παρατηρήσεις. Πρώτη, σιγουρευτείτε οτι έχετε δικαιώματα εκτέλεσης στο πρόγραμμα, αν όχι μεταβάλλετε τα σχετικά δικαιώμτα με την εντολή chmod. Δεύτερη, σε περίπτωση που ο τρέχον κατάλογος δεν είναι δηλωμένος στη μεταβλητή περιβάλλοντος PATH, ο φλοιός δεν θα βρεί το εκτελέσιμο. Έχετε δύο λύσεις, είτε θα εκτελέσετε το πρόγραμμα γράφοντας

./program ή ./a.out

είτε θα ενημερώσετε τη μεταβλητή περιβάλλοντος PATH.

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

Εργαλεία Μεταγλώττισης της C

Θα επισημάνουμε σύντομα τα βασικά χαρακτηριστικά του μοντέλου μεταγλώττισης της C (Σχ. 2.1).

 

Σχήμα 1 Μοντέλο Μεταγλώττισης της C

Προεπεξεργαστής (Preprocessor, cpp)

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

Ο Προεπεξεργαστής δέχεται ως είσοδο το κείμενο του προγράμματος και είναι υπέυθυνις για

Για παράδειγμα

Μεταγλωττιστής (Complier, gcc)

Ο μεταγλωττιστής μεταφράζει τον πηγαίο κώδικα σε συμβολικό κώδικα (assembly). Ο πηγαίος κώδικας παραλαμβάνεται από το προεπεξεργαστή. Ο μεταγλωττιστής μπορεί να παράγει κώδικα assembly, αν χρησιμοποιήσουμε την επιλογή -S.

Συμβολομεταφραστής (Assembler, gas)

Ο συμβολομεταφραστής παράγει τον αντικείμενo κώδικα (object code). Στα συστήματα UNIX / Linux μπορεί να δείτε τέτοιο κώδικα σε αρχεία με κατάληξη .o .

Συντάκτης Συνδέσεων (Link Editor, ld)

Αν ο πηγαίος κώδικας αναφέρεται σε συναρτήσεις βιβλιοθήκης ή σε συναρτήσεις που ορίζονται σε άλλα αρχεία πηγαίου κώδικα ο συντάκτης συνδέσεων (link editor) συνδυάζει αυτές τις συναρτήσεις (με τη συνάρτηση main()) ώστε να παραχθεί ένα μοναδικό εκτελέσιμο αρχείο. Επίσης επιλύονται αναφορές σε Εξωτερικές Μεταβλητές. Πιο πολλά αργότερα.

Βοηθητικά Προγράμματα (Binutils)

Το GNU Project παρέχει ένα σύνολο βοηθητικών προγραμμάτων (binutils) που είναι χρήσιμα για επεξεργασία και διαχείριση προγραμμάτων C σε χαμηλό επίπεδο. Πέρα από τον assembler και link editor, μπορούμε να βρούμε dissaembler, object dump viewer, binary code editor, profiler, κλπ. Μερικά από αυτά θα τα δούμε στο παράδειγμα που ακολουθεί. 

Η συλλογή GNU περιλαμβάνει:

Τα εγχειρίδια είναι διαθεσιμα στο σύνδεσμο

       http://sourceware.org/binutils/docs/

Χρήσιμες Επιλογές Μεταγλώττισης

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

-c
Ακυρώνει τη διαδικασία σύνδεσης και παράγει ένα αρχείο αντικείμενου κώδικα .o για κάθε αρχείο πηγαίου κώδικα που αναφέρεται. Στη συνέχεια, πολλά αρχεία αντικείμενου κώδικα μπορούν να συνδεθούν με την εντολή gcc, π.χ.:

    gcc file1.o file2.o ...... -o executable

-llibrary
Σύνδεση αντικειμένων βιβλιοθηκών. Αυτή η επιλογή πρέπει να ακολουθεί τα ορίσματα του πηγαίου κώδικα. Οι αντικείμενες βιβλιοθήκες είναι αρχειοθετημένες και μπορεί να είναι πρότυπες, τρίτων ή του χρήστη (Συζητούμε αυτό το θέμα σύντομα παρακάτω και αναλυτικά αργότερα). Πιθανότατα η πιο συνηθισμένη βιβλιοθήκη είναι των μαθηματικών συναρτήσεων (math.h). Πρέπει να τη συνδέσετε ρητά (σημείωση μη ξεχάσετε να περιλάβετε την οδηγία #include <math.h>), π.χ.:

    gcc calc.c -o calc -lm

Με παρόμοιο τρόπο συνδέονται πολλές άλλες βιβλιοθήκες (δείτε παρακάτω)

-Ldirectory
Πρόσθεση καταλόγου στη λίστα καταλόγων που περιέχουν αντικείμενες βιβλιοθήκες. Ο συντάκτης συνδέσεων πάντα κοιτάζει τις πρότυπες βιβλιοθήκες του συστήματος στο /lib και /usr/lib. Αν θέλετε να συνδέσετε βιβλιοθήκες που δημιουργήσατε ή εγκαταστήσατε μόνοι σας (εκτός αν έχετε δικαιώματα διαχειριστή και τις τοποθετήσατε στο /usr/libπρέπει να ορίσετε το κατάλογο, π.χ.:

    cc prog.c -L/home/myname/mylibs mylib.a

-Ipathname
Πρόσθεση καταλόγου στη λίστα των καταλόγων αναζήτησης αρχείων #include με σχετικά ονόματα (όχι απόλυτα, δηλαδή αυτά που ξεικνούν με /). Ως προεπιλογή, ο προεπεξεργαστής αναζητά αρχεία #include πρώτα στον κατάλογο του πηγαίου κώδικα, στη συνέχεια στος καταλόγους που ονομάζονται στην επιλογή -I (αν υπάρχουν), και τελικά στο /usr/include. Έτσι για να περιλάβουμε αρχεία κεφαλής αποθηκευμένα στο κατάλογο /home/myname/myheaders πρέπει να γράψουμε:

    gcc prog.c -I/home/myname/myheaders

Σημείωση: Τα αρχεία κεφαλής του συστήματος βρίσκονται στο /usr/include και δεν επηρρεάζονται από την επιλογή -I. Τα αρχεία κεφαλής του συστήματος ενσωματώνονται λίγο διαφορετικά από τα αρχεία κεφαλής του χρήστη ή τρίτων (θα αναλυθεί αργότερα).

-g
Επιλογή εκσφαλμάτωσης. Δίνει εντολή στον μεταγλωττιστή να παράξει πρόσθετη πληροφορία για τον πίνακα συμβόλων που είναι χρήσιμη στις λειτουργίες εκσφαλμάτωσης.
-pg
Επιλογή εκσφαλμάτωσης και profiling.
-D
Ορισμός συμβόλων ως ονόματα (-Didentifer) ή ως τιμές (-Dsymbol=value) παρόμοια όπως η οδηγία προεπεξεργαστή #define. Περισσότερες λεπτομέρειες σε επόμενα κεφάλαια.
-S
Παραγωγή κώδικα assembly.

Χρήση Βιβλιοθηκών

Η C είναι εξαιρετικά μικρή γλώσσα. Πολλές από τις λειτουργίες της δεν περιλαμβάνονται στη γλώσσα καθ' αυτή, π.χ. δεν ευπάρχουν λειτουργίες Ε/Ε, διαχείριση συμβολοσειρών ή μαθηματικές συναρτήσεις. Η C παρέχει αυτές τις λειτουργίες μέσω βιβλιοθηκών συναρτήσεων.

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

Ένας προγραμματιστής μπορεί να αναπτύξει τις δικές του βιβλιοθήκες και να τις διαθέσει ως βιβλιοθήκες τρίτων (σε πηγαίο ή αντικείμενο κώδικα).

Όλες οι βιβλιοθήκες (εκτός από τη πρότυπη Ε/Ε) πρέπει να συνδεθούν ρητά με τις επιλογές μεταγλωττιστή -l και, πιθανώς -L και με τις κατάλληλες οδηγίες προεπεξεργαστή #include.

Συναρτήσεις Βιβλιοθηκών UNIX/Linux

Τα συστήματα UNIX/Linux, αλλά και γενικότερα το Ελεύθερο/Ανοικτό Λογισμικό, παρέχει μια τεράστια ποσόστητα συνατήσεων βιβλιοθηκών της C. Ορσιμένες αναφέρονται σε συχνά χρησιμοποιούμενες διαδικασίες, άλλες σε πολύ εξειδικευμένα θέματα.

Μη ξανα-ανακαλύπτετε τον τροχό: Πριν ξεκινήσετε να γράφετε κώδικα ελέγξτε τις διαθέσιμες βιβλιοθήκες.Έτσι ο χρόνος ανάπτυξης λογισμικού θα μειωθεί σημαντικά. Οι συναρτήσεις βιβλιοθηκών έχουν ελεγθεί, άρα πιθανότατα θα είναι ορθότερες από τον κώδικα που θα γράψετε. 

Σε επόμενα κεφάλαια θα συζητήσουμε αρκετά θέματα σχετικά με βιβλιοθήκες και τις πιο συνηθισμένες βιβλιοθήκες συστημάτων UNIX/Linux.


Παράδειγμα

Έστω το πρόγραμμα hello.c

/* $begin hello */
#include <stdio.h>
int main()
{
    printf("hello, world\n");
}
/* $end hello */

Το ίδιο πρόγραμμα σε απλό ASCII, Χρήση του εργαλείου od  με έξοδο στο stdout σε printable ASCII..

$od -a hello.c > hello.asc

$more hello.asc


0000000  /   *  sp  $   b   e   g   i   n  sp  h   e   l   l   o  sp
0000020  *   /  nl  #   i   n   c   l   u  d   e  sp   <   s   t   d
0000040  i   o   . h   >  nl nl   i   n  t  sp   m   a   i   n   (
0000060  )  sp  nl  {  nl  sp  sp  sp  sp  p   r   i   n   t   f   (
0000100  "   h   e  l   l   o   ,  sp   w  o   r   l   d   \   n   "
0000120  )   ;  nl  } nl   /   * sp   $  e   n   d  sp   h   e   l
0000140 l   o  sp  *   /  nl

Το ίδιο πρόγραμμα σε δεκαεξαδική μορφή, Χρήση του εργαλείου od  με έξοδο στο stdout σε hex. Προσοχή στη θέση των bytes μέσα στη λέξη.

$od -x hello.c > hello.hex
$more hello.hex


0000000 2a2f 2420 6562 6967 206e 6568 6c6c 206f
0000020 2f2a 230a 6e69 6c63 6475 2065 733c 6474
0000040 6f69 682e 0a3e 690a 746e 6d20 6961 286e
0000060 2029 7b0a 200a 2020 7020 6972 746e 2866
0000100 6822 6c65 6f6c 202c 6f77 6c72 5c64 226e
0000120 3b29 7d0a 2f0a 202a 6524 646e 6820 6c65
0000140 6f6c 2a20 0a2f 000a

Αν στο ίδιο πρόγραμμα δοκιμάσουμε 

$cpp hello.c > hello.i
$more hello.i

μπορούμε να δούμε την έξοδο που παράγει ο preprocessor, τα pathnames για τις βιβλιοθήκες, τα include files και τα εκτελέσιμα του μεταγλωττιστή μας, καθώς εππίσης και όλους τους ορισμούς και δηλώσεις του <stdio.h>

Αν στο αρχικό πρόγραμμα hello.c δοκιμάσουμε 

$gcc -S hello.c > hello.s
$more hello.s
μπορούμε να δούμε την έξοδο που παράγει ο μεταγλωττιστής για τον assembler.
.file "hello.c"
.section .rodata
.LC0:
.string "hello, world"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)‏
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $4, %esp
movl $.LC0, (%esp)‏
call puts
addl $4, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.2.4 (Ubuntu 4.2.4-1ubuntu3)"
.section .note.GNU-stack,"",@progbits
Σε επόμενο μάθημα, σχετικό με τη εκσφαλμάτωση, θα δούμε οτι μπορούμε να παρακολουθήσουμε την εκτέλεση ενός προγράμματος ταυτόχρονα σε δύο επίπεδα, γλώσσας C και γλώσσας assembly (ή γλώσσας μηχανής).

Προχωρούμε να παράγουμε το object (αντικείμενο πρόγραμμα) με την εντολή

$gcc -c hello.c > hello.o
$od -x hello.o
αυτό που βλέπουμε στην οθόνη είναι κάπως έτσι
0000000 457f 464c 0101 0001 0000 0000 0000 0000
0000020 0001 0003 0001 0000 0000 0000 0000 0000
0000040 00e4 0000 0000 0000 0034 0000 0000 0028
0000060 000b 0008 4c8d 0424 e483 fff0 fc71 8955
0000100 51e5 ec83 c704 2404 0000 0000 fce8 ffff
0000120 83ff 04c4 5d59 618d c3fc 0000 6568 6c6c
0000140 2c6f 7720 726f 646c 0000 4347 3a43 2820
0000160 4e47 2955 3420 322e 342e 2820 6255 6e75
0000200 7574 3420 322e 342e 312d 6275 6e75 7574
0000220 2933 0000 732e 6d79 6174 0062 732e 7274
0000240 6174 0062 732e 7368 7274 6174 0062 722e
0000260 6c65 742e 7865 0074 642e 7461 0061 622e
0000300 7373 2e00 6f72 6164 6174 2e00 6f63 6d6d
0000320 6e65 0074 6e2e 746f 2e65 4e47 2d55 7473
0000340 6361 006b 0000 0000 0000 0000 0000 0000
0000360 0000 0000 0000 0000 0000 0000 0000 0000
0000400 0000 0000 0000 0000 0000 0000 001f 0000
0000420 0001 0000 0006 0000 0000 0000 0034 0000
....

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

$objdump -d hello.o > hello.ds
$more hello.ds

Ο κώδικας που προκύπτει είναι αρκετά κοντά στον κώδικα assembly που είδαμε προηγουμένως, εκτός φυσικά από τις πληροφορίες μεταγλώττισης που δεν έχει υπ' όψη του ο dis-assembler.
Disassembly of section .text:

00000000
:
0: 8d 4c 24 04 lea 0x4(%esp),%ecx
4: 83 e4 f0 and $0xfffffff0,%esp
7: ff 71 fc pushl -0x4(%ecx)‏
a: 55 push %ebp
b: 89 e5 mov %esp,%ebp
d: 51 push %ecx
e: 83 ec 04 sub $0x4,%esp
11: c7 04 24 00 00 00 00 movl $0x0,(%esp)‏
18: e8 fc ff ff ff call 19

1d: 83 c4 04 add $0x4,%esp
20: 59 pop %ecx
21: 5d pop %ebp
22: 8d 61 fc lea -0x4(%ecx),%esp
25: c3 ret

Επικύρωση Προγραμμάτων C

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

Ο στατικός επικυρωτής προγραμμάτων C splint μπορεί να σας βοηθήσει να αποφύγετε αρκετά προγραμματιστικά λάθη. Μπορείτε να τον εγκαταστήσετε και να ελέγξετε τη τεκμηρίωσή του (man splint ή info splint). Αξίζει τον κόπο. Η κλήση του γίνεται ως:

    splint myprog.c.

O επικυρωτής splint είναι ιδαίτερα καλός στον έλεγχο τύπων μεταβλητών και συναρτήσεων, σε θέματα απόδοσης, στην επισήμανση αχρησιμοποίητων μεταβλητών ή συναρτήσεων, μη-προσπελάσιμου κώδικα και πιθανών διαρροών μνήμης.

Ένα απλό παράδειγμα. Έστω ένα πρόγραμμα  ctest1.c 

main(){

int x[2], temp;

temp=x[1];
x[1]=x[2];
x[2]=temp;
}
Αν το επικυρώσουμε με το splint λαμβάνουμε τα παρακάτω μηνύματα. Το σημαντικότερ είναι η ειδοποίηση για τη μη-σωστή χρήση του x[2].
$ splint ctest1.c
Splint 3.1.1 --- 03 Nov 2006
ctest1.c: (in function main)
ctest1.c:6:6: Array element x[1] used before definition
An rvalue is used that may not be initialized to a value on some execution
path. (Use -usedef to inhibit warning)
ctest1.c:7:6: Array element x[2] used before definition
ctest1.c:10:2: Path with no return in function declared to return int
There is a path through a function declared to return a value on which there
is no return statement. This means the execution may fall through without
returning a meaningful result to the caller. (Use -noret to inhibit warning)
Finished checking --- 3 code warnings

Profiling Προγραμμάτων C

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

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

Το profiling έχει τα ακόλουθα βήματα:

Παράγονται κατά κύριο λόγο δύο ειδών αποτελέσματα, το flat graph που δείχνει το χρόνο εκτέλεσης κάθε συνάρτησης και το call graph που δείχνει το δένδρο των κλήσεων συναρτήσεων. Ακολουθούν δύο ενδεικτικά αποσπάσματα.

Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
33.34 0.02 0.02 7208 0.00 0.00 open
16.67 0.03 0.01 244 0.04 0.12 offtime
16.67 0.04 0.01 8 1.25 1.25 memccpy
16.67 0.05 0.01 7 1.43 1.43 write
16.67 0.06 0.01 mcount
...


 granularity: each sample hit covers 2 byte(s) for 20.00% of 0.05 seconds

index % time self children called name
<spontaneous>
[1] 100.0 0.00 0.05 start [1]
0.00 0.05 1/1 main [2]
0.00 0.00 1/2 on_exit [28]
0.00 0.00 1/1 exit [59]
-----------------------------------------------
.

Ασκήσεις

Άσκηση 1

Εισάγετε, μεταγλωττίστε και εκτελέστε το ακόλουθο πρόγραμμα:


#include <stdio.h>
main()
{
int i;

printf("\t Number \t\t Square of Number\n\n");
for (i=0; i<=25;++i)
printf("\t %d \t\t\t %d \n",i,i*i);
}

Άσκηση 2

Το παρακάτω πρόγραμμα χρησιμοποιεί βιβλιοθήκη μαθηματικών. Εκτελέστε το σωστά. 

#include <stdio.h>
#include <math.h>

main()
{
int i;

printf("\t Number \t\t Square Root of Number\n\n");
for (i=0; i<=360; ++i)
printf("\t %d \t\t\t %f \n",i, sqrt((double) i));
}

Άσκηση 3

Στα παραπάνω προγράμματα εφαρμόστε τις τεχνικές binutils που εξηγούνται στο παράδειγμα. Επίσης δοκιμάστε το splint και το gprof.


Dave Marshall
1/5/1999
μετάφραση και προσαρμογή στα Ελληνικά Κ.Γ. Μαργαρίτης
29/2/2008