Τα πάντα για την C

...του ubuntu και έργων ΕΛ/ΛΑΚ (Έργα-Οδηγοί-Προτάσεις)

Συντονιστής: konnn

Re: Τα πάντα για την C

Δημοσίευσηαπό migf1 » 08 Απρ 2012, 09:55

alkismavridis έγραψε:... Οπότε πρέπει να είμαστε διαρκώς σε.. Επιφυλακή! :-)

Ναι! Δεν γίνεται διαφορετικά. Αλλά και στη Java δεν είναι ότι καλύτερο να αφήνεις τα σφάλματα στην τύχη τους (δλδ στην τύχη του VM). Κι εκεί πρέπει να τα προβλέψεις στο πρόγραμμά σου, γιατί π.χ. τι νόημα έχει να συνεχίσει το πρόγραμμά σου όταν σκάσει division by 0 αν π.χ. στο αποτέλεσμα της διαίρεσης βασίζεται ο επόμενος υπολογισμός;

Στη C ένας τρόπος είναι στην αρχή όλων σου των συναρτήσεων να βάζεις sanity checks πριν τον κύριο κώδικα, π.χ. αν μια συνάρτηση αλλάζει το i-οστό στοιχείο ενός πίνακα ακεραίων, μπορεί να γραφτεί κάπως έτσι

Μορφοποιημένος Κώδικας: Επιλογή όλων
bool arr_iupdate( int arr[], int i, int val, int arrlen )
{
/* sanity checks */
if ( !arr || i < 0 || i > arrlen-1 )
return false;

arr[i] = val;
return true;
}

Ο caller της συνάρτησης πρέπει κανονικά να ελέγξει την τιμή επιστροφή της, για να εξασφαλίσει πως όλα κύλισαν ομαλά. Αλλά και να μην την ελέγξει, έχουμε τουλάχιστον εξασφαλίσει πως αν προκύψει σφάλμα το πρόγραμμά μας δεν θα κρασάρει. Αντί για bool, μπορείς να βάλεις τις συναρτήσεις να επιστρέφουν error-codes, ξεχωριστά για κάθε σφάλμα (άρα σε ξεχωριστά if).

Το παράδοξο είναι πως σε πολλές περιπτώσεις μας "εξυπηρετεί" καλύτερα να σκάσει το πρόγραμμά μας, παρά να κάνει σιωπηλό recover και να βγάλει λάθος αποτελέσματα. Διότι αν συνεχίσει σιωπηλά, είναι πάρα πολύ δύσκολο να εντοπίσουμε το σημείο στο οποίο προέκυψε το σφάλμα, ώστε να το διορθώσουμε (ως προγραμματιστές εννοώ).

Υπό αυτό το πρίσμα, γλώσσες όπως η Java και η Python που κοντρολάρουν το run-time τους εξωγενώς από τα ίδια τα προγράμματα, είναι πολύ βολικό που μας ειδοποιούν... π.χ. αν ξεφύγεις από τα ορισμένα όρια ενός πίνακα... στη C απλά θα σου βγάλει ένα αόριστο "segmentation fault", το οποίο συχνά δεν αντιστοιχεί καν στο ακριβές σημείο του κώδικα.

Στη C βασίζεσαι εν πολλοίς στην ποιότητα του compiler και στην ικανότητά του να βγάζει προειδοποιήσεις για σημεία του κώδικα που έχουν τη δυναμική να προκαλέσουν σφάλμα στο run-time, Για αυτό και είναι σημαντικό να κάνει κανείς compile με ενεργοποιημένες όλες τις προειδοποιήσεις ( -Wall -Wextra στον gcc). Βέβαια ο compiler δεν μπορεί να πιάσει όλες τις περιπτώσεις.

Υπάρχουν λοιπόν tools που μπορούν να ελέγχουν το run-time, με την προϋπόθεση πως τα έχεις τρέξει πριν από το πρόγραμμά σου ή πως καλείς το πρόγραμμά σου μέσα από το tool. Ένα από τα πιο δημοφιλή στο linux είναι το Valgrind. Όλα τα σοβαρά C/C++ developer packages παρέχουν τέτοιου είδους εργαλεία, σε όλες τις πλατφόρμες.

Τέλος, υπάρχουν και οι C Interpreters, όπου πλέον τα προγράμματά σου τρέχουν μέσα σε απόλυτα ελεγχόμενο περιβάλλον, όπως γίνεται με τη Java και την Python (με τα ίδια πλεονεκτήματα και μειονεκτήματα έναντι του compiled κώδικα). Από τους πιο δημοφιλείς, ειδικά σε embedded πλατφόρμες είναι ο Ch ενώ για ANSI C είναι και ο CINT.

ΥΓ. Γνωρίζεις Java κι έχεις ξεκινήσει να μαθαίνεις C;
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

Re: Τα πάντα για την C

Δημοσίευσηαπό alkismavridis » 08 Απρ 2012, 10:31

Σε ευχαριστώ πολύ! Η απάντησή σου ήταν πληρέστατη!

Ναι ξέρω java, έχω ξεκινήσει να χτίζω και ένα προγραμματάκι στο launchpad ;)

Τελευταία με κέντρισε η C, και τώρα κάνω τα πρώτα μου βήματα σε Gtk+..
Παρ όλο που το να "ζωγραφίζω" gui μου είναι πολύ γνώριμο από την java και σε αυτό το θέμα το Gtk+ δε με δυσκολεύει, σε θέματα του "πυρήνα" της γλώσσας είμαι σχετικά αρχάριος..
Γνώσεις ⇛ Linux: Μέτριο┃ Προγραμματισμός: Java, Assembly, Fortran, μαθαίνω C/X11┃ Αγγλικά: Μέτρια
Λειτουργικό σε Η/Υ ϰ μοντέλο: Ubuntu 14.04 64-bit ┃ Τρόπος εγκατάστασης: Live USB
Προδιαγραφές ⇛ Desktop: Intel i5 2320 3.00GHz.┃ MotherBoard: Asus p8h61 -m pro
Προδιαγραφές ⇛ RAM: 4GB ┃ Τροφοδοτικό Corsair CX430

GPU: Intel 2nd Generation Core Processor Family Integrated Graphics Controller [8086:0102] {i915}
5 eth0: Realtek RTL8111/8168B PCI Express Gigabit Ethernet controller [10ec:8168] (rev 06) ⋮ wlan0: 0b05:1723 ASUS WL-167G v2 802.11g Adapter [Ralink RT2571W]
Οθόνη Schaub Lorenz (Tv)
alkismavridis
punkTUX
punkTUX
 
Δημοσιεύσεις: 273
Εγγραφή: 18 Μαρ 2009, 18:46
Εκτύπωση

Re: Τα πάντα για την C

Δημοσίευσηαπό migf1 » 08 Απρ 2012, 12:40

Καλή αρχή εύχομαι :)

ΥΓ. Στο νήμα έχει προταθεί ένα πολύ καλό εισαγωγικό βιβλίο για C: "C Programming, A Modern Approach, 2nd Edition", by King.
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

Re: Τα πάντα για την C

Δημοσίευσηαπό Ilias95 » 08 Απρ 2012, 14:26

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

Μορφοποιημένος Κώδικας: Επιλογή όλων
#include <stdio.h>

int main(void)
{
char *p = "Ena string.";

p[1] = 'o';
puts(p);

return 0;
}

Το παραπάνω παράδειγμα δίνει segmentation fault και είχαμε εξηγήσει στο παρελθόν ότι αυτό συμβαίνει λόγο του ότι ο pointer p δείχνει σε read-only περιοχή μνήμης.

Όμως το παρακάτω παράδειγμα:
Μορφοποιημένος Κώδικας: Επιλογή όλων
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// concatenates two strings
char *concat (const char *s1, const char *s2)
{
char *result;

result = malloc(strlen(s1) + strlen(s2) + 1);
if (result == NULL) {
printf("Error: malloc failed in concat\n");
exit(EXIT_FAILURE);
}
strcpy(result, s1);
strcat(result, s2);
return result;
}

int main(void)
{
char *p;

p = concat("Ena st", "ring.");
p[1] = 'o';
puts(p);

return 0;
}

δουλεύει κανονικά...

Τι συμβαίνει στην προκειμένη; Ο p αρχικά δείχνει σε read-only περιοχή και μετά σε μη read-only;
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

Re: Τα πάντα για την C

Δημοσίευσηαπό migf1 » 08 Απρ 2012, 15:26

Στην 1η περίπτωση, με την αρχικοποίηση που κάνεις βάζεις τον p να δείχνει σε read-only μνήμη (δηλαδή στο string-literal με το οποίο τον αρχικοποιείς).

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

ΥΓ. Πρέπει πάντα να ελέγχεις αν τα malloc() πέτυχαν ( να μην επιστρέφουν NULL δηλαδή) πριν κάνεις οτιδήποτε άλλο στη μνήμη που σου επιστρέφουν. Επίσης, για κάθε πετυχημένο malloc() πρέπει να κάνεις ένα αντίστοιχο free() πριν τερματίσεις το πρόγραμμά σου. Στην προκειμένη περίπτωση πρέπει να κάνεις free(p).
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

Re: Τα πάντα για την C

Δημοσίευσηαπό Ilias95 » 08 Απρ 2012, 15:30

Άρα η μνήμη που δεσμεύει η malloc() (και οι όμοιες της) είναι πάντα read-write;
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

Re: Τα πάντα για την C

Δημοσίευσηαπό migf1 » 08 Απρ 2012, 15:43

Ναι!
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

Re: Τα πάντα για την C

Δημοσίευσηαπό migf1 » 08 Απρ 2012, 15:50

Παρεμπιπτόντως, ο συνηθισμένος τρόπος υλοποίησης του 2ου παραδείγματος που παρέθεσες παραπάνω είναι έτσι...

Μορφοποιημένος Κώδικας: Επιλογή όλων
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// concatenates two strings
char *concat (const char *s1, const char *s2)
{
char *result = NULL;

result = malloc(strlen(s1) + strlen(s2) + 1);
if (result == NULL)
return NULL;

strcpy(result, s1);
strcat(result, s2);

return result;
}

int main(void)
{
char *p = NULL;

p = concat("Ena st", "ring.");
if ( p == NULL )
{
printf("Error: malloc failed in concat\n");
exit(EXIT_FAILURE);
}

p[1] = 'o';
puts(p);

free(p);
return 0;
}
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

Re: Τα πάντα για την C

Δημοσίευσηαπό migf1 » 08 Απρ 2012, 15:56

Και με sanity checks, η concat() γίνεται έτσι... (ένα-ένα τα βλέπω :lol:) ...

Μορφοποιημένος Κώδικας: Επιλογή όλων
char *concat( const char *s1, const char *s2 )
{
char *result= NULL;

/* sanity checks */
if ( !s1 || !s2 )
return NULL;

/* δυναμική δέσμευση (εναλλακτική, πιο συμπτυγμένη σύνταξη... απλώς εγκυκλοπαιδικά) */
if ( NULL == (result = malloc(strlen(s1) + strlen(s2) + 1) )
return NULL;

strcpy(result, s1);
strcat(result, s2);

return result;
}
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

Re: Τα πάντα για την C

Δημοσίευσηαπό Ilias95 » 08 Απρ 2012, 16:00

Κώδικας: Επιλογή όλων
if ( !s1 || !s2 )
return NULL;

Εδώ ελέγχουμε αν κάποιο εκ των s1, s2 είναι NULL;
Αν ναι, δεν είναι καλύτερα να το κάνουμε πιο explicit (s1 == NULL);
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

ΠροηγούμενηΕπόμενο

Επιστροφή στο Ανάπτυξη Λογισμικού / Αλγόριθμοι

cron