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

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

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

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

Δημοσίευσηαπό migf1 » 17 Μαρ 2012, 12:27

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

Π.χ....
Μορφοποιημένος Κώδικας: Επιλογή όλων
enum ErrId {
STARTERR = -1, /* not an id, just a limit*/
ERR_NOERROR = 0,
ERR_MEM,
ERR_RFILE,
ERR_WFILE,
ERR_PARAM,
ENDERR /* not an id, just a limit */
};

/* table of strings (or 2d array of char) */
char *errmsg[] = {
"No error",
"Out of memory",
"Read file error",
"Write file error"
"Invalid parameter"
};

/* -------------------------------------------- */
enum ErrID file_read( const char *fname )
{
FILE *fp = NULL;
if ( !fname )
return ERR_PARAM;

if ( NULL == (fp = fopen(fname, "r")) )
return ERR_RFILE;

...
fclose(fp);
return ERR_NOERROR;
}

/* -------------------------------------------- */
int main( void )
{
enum ErrId errid = NO_ERROR;

if ( (errid=file_read(...)) != NO_ERROR )
puts( errmsg[ errid ] );
...
}

Μπορείς το errid να το περνάς by-reference στην file_read() και να το ενημερώνεις μέσα της (ώστε να απελευθερώσεις την τιμή επιστροφής της συνάρτησης, σε κάτι άλλο... π.χ. τον αριθμό των bytes που διάβασε).

Ή μπορείς το errid να το κάνεις καθολική μεταβλητή, ώστε να μην το περνάς καν σαν όρισμα και να το ενημερώνεις απευθείας. Προφανώς θα πρέπει να το ελέγχεις κατόπιν, στην main(), μετά από την κλήση της κάθε σου συνάρτησης.

Έτσι δουλεύει η στάνταρ βιβλιοθήκη <errno.h> που ορίζει καθολικά την μεταβλητή errno, που την χρησιμοποιούν συναρτήσεις άλλων βιβλιοθηκών (π.χ. η strtol() ) για να δηλώσουν περισσότερες πληροφορίες από ότι η τιμή επιστροφής τους σε περίπτωση αποτυχίας... Οπότε όταν π.χ. καλείς την strtol() και σου επιστρέψει 0, για να δεις αν αυτό το 0 είναι ένδειξη αποτυχίας ή όχι, κάνεις το εξής:

α) πριν καλέσεις την strtol() μηδενίζεις την errno
β) μετά την κλήση της strtol() ελέγχεις αν το errno είναι διάφορο του μηδενός (το μηδέν σημαίνει "no error").

Μορφοποιημένος Κώδικας: Επιλογή όλων
#include <stdio.h>
#include <stdlib.h> /* for strtol() */
#include <errno.h>

#define MAXINPUT (256+1)
int main( void )
{
char input[ MAXINPUT ] = {'\0'};
long int n, try;

printf( "Enter a long integer: " );
fgets( input, MAXINPUT, stdin );

errno = 0;
try = strtol(input, NULL, 10);
if ( 0L == try && errno != 0 )
perror( NULL ); /* μετατρέπει το τρέχον errno σε μήνυμα και το τυπώνει */
else
n = try;
...
}

* perror()
** η strtol() μπορεί να δώσει ακόμα περισσότερες πληροφορίες, μέσω του 2ου ορίσματός της, αν ΔΕΝ της το περάσεις ως NULL.

Ξέφυγα τελείως όμως. Αυτό που ξεκίνησα να πως είναι πως το errmsg[] είναι ένας πίνακας από strings, δηλαδή το κάθε στοιχείο του είναι ένας πίνακας από χαρακτήρες (και μάλιστα διαφορετικού μήκους, πολύ βολικό γιατί εξοικονομεί μνήμη).

Απλά στο παράδειγμά μου, χρειάζεται ΜΕΓΑΛΗ προσοχή ώστε οι enumerators να βρίσκονται σε ΑΠΟΛΥΤΗ αντιστοιχία με τα indices του errmsg[].
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

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

Δημοσίευσηαπό Ilias95 » 17 Μαρ 2012, 15:28

Λοιπόν τον παραπάνω κώδικα τον βρήκα απ' το βιβλίο του King. (σελ. 269)

migf1 έγραψε:Το p σε αυτή την περίπτωση είναι πίνακας από δείκτες ακεραίων (array of integer pointers). Δηλαδή το κάθε στοιχείο του p είναι: (int *). Επί της ουσίας το κάθε στοιχείο του p έχει τη δυναμική να δείχνει σε έναν πίνακα από int, που στο συγκεκριμένο παράδειγμα (με το loop) είναι μια σειρά (γραμμή) του a.

Τώρα με μπέρδεψες εντελώς. :D

Ο King λέει:
King έγραψε:I' ve declared p o be a pointer to an array of length NUM_COLS whose elements are integers. The parenthesis around *p in (*p)[NUM_COLS] are required; without them, the compiler ould treat p as an array of pointers instead of a pointer an array.

Άλλο πράγμα δεν λες εσύ και άλλο δεν λέει ο King; Η ξέχασα τα Αγγλικά που ήξερα; :problem:

migf1 έγραψε: Πρέπει όμως νομίζω να το ορίσεις με NUM_ROWS αντί για NUM_COLS...

Αν κάνω τον πίνακα 4x3, έτσι όπως είναι πάλι θα δουλέψει:
Spoiler: show
Μορφοποιημένος Κώδικας: Επιλογή όλων
#include <stdio.h>

#define NUM_ROWS 4
#define NUM_COLS 3

int main( void )
{
int a[NUM_ROWS][NUM_COLS] = {{1, 2, 3}, {4, 5, 6}, {7, 12, 14}, {23, 43, 54}};
int (*p)[NUM_COLS], i = 1, *x;

for (p = &a[0]; p < &a[NUM_ROWS]; p++)
(*p)[i] = 0;

// print array
for (x = a[0]; x <= &a[NUM_ROWS-1][NUM_COLS-1]; x++)
printf("%d ", *x);

return 0;
}

Αν όμως αλλάξω το (*p)[NUM_COLS] σε (*p)[NUM_ROWS]:
Κώδικας: Επιλογή όλων
gcc -W -Wextra -Wall -std=c99 -o "pun" "pun.c"
pun.c: In function ‘main’:
pun.c:11:12: προειδοποίηση: assignment from incompatible pointer type [enabled by default]
pun.c:11:23: προειδοποίηση: comparison of distinct pointer types lacks a cast [enabled by default]

Και αυτό που θα εκτυπωθεί τελικά δεν θα είναι σωστό.

migf1 έγραψε:ΥΓ. Επίσης, στο print-loop, πρέπει να είναι: for ( x = &a[0]; ...

Έτσι όπως είναι τώρα δουλεύει κανονικά.
Αν το κάνω "for ( x = &a[0]; ..." πάλι θα εκτυπωθεί το σωστό αποτέλεσμα όμως θα πάρω κατά το compile:
Κώδικας: Επιλογή όλων
pun.c:15:12: προειδοποίηση: assignment from incompatible pointer type [enabled by default]

Γιατί αυτό;

Ευχαριστώ.
ΥΓ. Δεν είδα ακόμα το τελευταίο σου post.
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

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

Δημοσίευσηαπό migf1 » 17 Μαρ 2012, 15:41

Ilias95 έγραψε:
...
Άλλο πράγμα δεν λες εσύ και άλλο δεν λέει ο King; Η ξέχασα τα Αγγλικά που ήξερα; :problem:
...

Έχετε απόλυτο δίκιο (και ο King και εσύ)!

Δικό μου είναι το λάθος, γιατί παρερμήνευσα το...
Μορφοποιημένος Κώδικας: Επιλογή όλων
int (*p)[NUM_COLS];    /* pointer to an array of ints */

ως...
Μορφοποιημένος Κώδικας: Επιλογή όλων
int *p[NUM_COLS];     /* array of int-pointers */

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

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

Δημοσίευσηαπό Ilias95 » 17 Μαρ 2012, 16:33

Ωραία.
Καμιά ιδέα για το πως δουλεύει τότε τελικά; :D
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

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

Δημοσίευσηαπό stamatiou » 17 Μαρ 2012, 16:41

@migf1
Μήπως ξέρεις κανένα βιβλίο αποκλειστικά για αλγοριθμους;
1Γνώσεις→Linux: Αρχάριος┃Προγραμματισμός:Αρχάριος┃Αγγλικά:Μέτριος
2Λειτουργικό→Arch Linxu 32bit
3Προδιαγραφές→2x AMD AthlonX2 DualCore QL-66 ‖ RAM 1751 MiB ‖ Hewlett-Packard 308C - Hewlett-Packard Compaq 615
4Κάρτες γραφικών:ATI RS780M/RS780MN [Radeon HD 3200 Graphics][1002:9612]
5Δίκτυα:eth0:Marvell 88E8042 PCI-E Fast Ethernet Controller [11ab:4357] (rev 10)⋮eth1: Broadcom BCM4312 802.11b/g LP-PHY [14e4:4315](rev 01)
Πρωσοπική Ιστοσελίδα: http://giwrg98.co.cc
Άβαταρ μέλους
stamatiou
daemonTUX
daemonTUX
 
Δημοσιεύσεις: 947
Εγγραφή: 25 Ιουν 2010, 20:23
Εκτύπωση

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

Δημοσίευσηαπό migf1 » 17 Μαρ 2012, 16:44

Νοηματικά, δουλεύει όπως σου εξήγησα και πριν. O *p δείχνει σε πίνακα από NUM_COL ακέραιους (ουσιαστικά γραμμές του a[][]).

Οπότε με το...
Μορφοποιημένος Κώδικας: Επιλογή όλων
for ( p = &a[0];
τον βάζει αρχικά να δείχνει στην 1η γραμμή (row) του a[][].

Κατόπιν με τα...
Μορφοποιημένος Κώδικας: Επιλογή όλων
p < &a[NUM_ROWS]; p++ )
τον μετακινεί στην κάθε επόμενη γραμμή του a[][], μηδενίζοντας το 2ο (i=2) στοιχείο της κάθε γραμμής.

Ένας εναλλακτικός τρόπος (όχι και μοναδικός όμως), χωρίς δείκτη θα μπορούσε να είναι κάπως έτσι...
Μορφοποιημένος Κώδικας: Επιλογή όλων
for ( irow=0; irow < NUM_ROWS; i++ )
a[irow][2] = 0;

Τσέκαρέ το γιατί το έγραψα απευθείας από μνήμης και μπορεί να είναι λάθος.
Τελευταία επεξεργασία από migf1 και 17 Μαρ 2012, 17:31, έχει επεξεργασθεί 2 φορά/ες συνολικά
Go under the hood with C: Pointers, Strings, Linked Lists
Άβαταρ μέλους
migf1
powerTUX
powerTUX
 
Δημοσιεύσεις: 2082
Εγγραφή: 03 Ιουν 2011, 16:32
Εκτύπωση

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

Δημοσίευσηαπό migf1 » 17 Μαρ 2012, 16:49

stamatiou έγραψε:@migf1
Μήπως ξέρεις κανένα βιβλίο αποκλειστικά για αλγοριθμους;

Αυτά που ξέρω είναι παλιά... αλλά με λίγο googling θα βρεις σίγουρα (υπάρχουν άπειρα)... δοκίμασε φράσεις όπως: "most popular books data structures and algorithms".

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

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

Δημοσίευσηαπό migf1 » 17 Μαρ 2012, 17:08

Ηλία, προσπάθησε να μην αναλωθείς πολύ στο (*p)[] ... το *p[] είναι μακράν πιο συνηθισμένο. Ασχολήσου μονάχα μέχρι να ξεκαθαρίσεις ποια είναι η διαφορά τους, κάνε και 3-4 ασκήσεις και προχώρα παρακάτω.

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

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

Δημοσίευσηαπό stamatiou » 17 Μαρ 2012, 18:12

migf1 έγραψε:Ηλία, προσπάθησε να μην αναλωθείς πολύ στο (*p)[] ... το *p[] είναι μακράν πιο συνηθισμένο. Ασχολήσου μονάχα μέχρι να ξεκαθαρίσεις ποια είναι η διαφορά τους, κάνε και 3-4 ασκήσεις και προχώρα παρακάτω.

Σημείωσε όμως στο μυαλό σου πως είναι εύκολο να μπερδευτεί κανείς με αυτά τα 2 (καλή ώρα όπως εγώ) οπότε αν το βρεις μπροστά σου και δεν το θυμάσαι ακριβώς, μπορείς να το ξανακοιτάξεις στο βιβλίο ;)
τωρα που το λέμε, ποια είναι η διαφορα των (*p)[] και *p[];
1Γνώσεις→Linux: Αρχάριος┃Προγραμματισμός:Αρχάριος┃Αγγλικά:Μέτριος
2Λειτουργικό→Arch Linxu 32bit
3Προδιαγραφές→2x AMD AthlonX2 DualCore QL-66 ‖ RAM 1751 MiB ‖ Hewlett-Packard 308C - Hewlett-Packard Compaq 615
4Κάρτες γραφικών:ATI RS780M/RS780MN [Radeon HD 3200 Graphics][1002:9612]
5Δίκτυα:eth0:Marvell 88E8042 PCI-E Fast Ethernet Controller [11ab:4357] (rev 10)⋮eth1: Broadcom BCM4312 802.11b/g LP-PHY [14e4:4315](rev 01)
Πρωσοπική Ιστοσελίδα: http://giwrg98.co.cc
Άβαταρ μέλους
stamatiou
daemonTUX
daemonTUX
 
Δημοσιεύσεις: 947
Εγγραφή: 25 Ιουν 2010, 20:23
Εκτύπωση

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

Δημοσίευσηαπό Ilias95 » 17 Μαρ 2012, 18:22

@migf1
Μου πήρε αρκετή ώρα αλλά τελικά νομίζω ότι επιτέλους κατάλαβα πως δουλεύει.

@stamatiou
Το int *p[] δηλώνει μια array με pointers, ενώ το (*p)[] έναn pointer που δείχνει σε array.
Ilias95
saintTUX
saintTUX
 
Δημοσιεύσεις: 1548
Εγγραφή: 29 Απρ 2011, 23:26
Εκτύπωση

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

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