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

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

Δημοσ.

Θα με ενδιεφερε καποιο καλο tutorial στους pointers στην C.Οποιος εχει κατι στο μυαλο ας το μοιραστει !!!

 

 

Ευχαριστω εκ των προτερων!!

Δημοσ.

αδερφε βουτα το οπως εισαι,σου μιλαω κι εγω σαν αρχαριος που εχω απειρα pdf για C αλλα μολις διαβασα αυτο ξεκαθαρισα μεσα σε 10 λεπτα παρα πολλα πραγματα,ο τροπος που τα εξηγει μπορει να γινει κατανοητος ακομα κι απο μικρο παιδι,ηταν ακριβως αυτο που εψαχνα,δεν ειχα ποτε την ευκαιρια να του το πω αλλα πολλα συγχαρητιρια γι αυτο το αρθρο,απλα ΚΑΤΑΠΛΗΚΤΙΚΟ

Δημοσ.

αδερφε βουτα το οπως εισαι,σου μιλαω κι εγω σαν αρχαριος που εχω απειρα pdf για C αλλα μολις διαβασα αυτο ξεκαθαρισα μεσα σε 10 λεπτα παρα πολλα πραγματα,ο τροπος που τα εξηγει μπορει να γινει κατανοητος ακομα κι απο μικρο παιδι,ηταν ακριβως αυτο που εψαχνα,δεν ειχα ποτε την ευκαιρια να του το πω αλλα πολλα συγχαρητιρια γι αυτο το αρθρο,απλα ΚΑΤΑΠΛΗΚΤΙΚΟ

 

Αν εννοείς τις σημειώσεις μου, σε ευχαριστώ πολύ για τα καλά σου λόγια!

  • Like 1
Δημοσ.

 

Αν εννοείς τις σημειώσεις μου, σε ευχαριστώ πολύ για τα καλά σου λόγια!

 

Να σαι καλα αδερφε,ελπιζω μελλοντικα να ξαναφτιαξεις τετοια αρθρα για αλλο θεμα της C

Δημοσ.

Να σαι καλα αδερφε,ελπιζω μελλοντικα να ξαναφτιαξεις τετοια αρθρα για αλλο θεμα της C

Πιστεύω ένα θέμα που δυσκολεύει τους νέους είναι το linking. Πού θα μπει extern και πού static. Τι σημαίνει extern/static σε μεταβλητές και τι σε συναρτήσεις.

  • Like 1
Δημοσ.

Πιστεύω ένα θέμα που δυσκολεύει τους νέους είναι το linking. Πού θα μπει extern και πού static. Τι σημαίνει extern/static σε μεταβλητές και τι σε συναρτήσεις.

Καταρχήν σας ευχαριστώ όλους για τα καλά σας λόγια!

 

Βασικά τα extern και static δεν νομίζω πως είναι κάτι το δύσκολο στη C (με εξαίρεση ίσως τα inline στην C99). Έχουν κάποιες πτυχές που μπερδεύουν, αλλά ο γενικότερος μπούσουλας είναι πως κάνουμε τις συναρτήσεις και τα globals static όταν ΔΕΝ πρόκειται να χρησιμοποιηθούν σε άλλο source-module από αυτό στο οποία τα ορίζουμε (οπότε π.χ. αν το πρόγραμμά μας έχει μόνο ένα πηγαίο αρχείο, τις συναρτήσεις και τις καθολικές μεταβλητές καλό είναι να τα ορίζουμε πάντα static).

 

Από την άλλη μεριά, ότι δεν ορίζουμε ως static, σημαίνει πως προτιθέμεθα να το καλέσουμε/χρησιμοποιήσουμε κι από άλλα source modules (πηγαία αρχεία). Σε αυτή την περίπτωση, στα άλλα source modules δηλώνουμε τα πρότυπα των συναρτήσεων (και τις καθολικές μεταβλητές) του "ξένου source module" ως extern.

 

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

 

Π.χ. αν αρχικοποιήσουμε μια static μεταβλητή μέσα σε μια συνάρτηση, ας πούμε σε 0 και κατόπιν πάντα μέσα στη συνάρτηση της αλλάξουμε τιμή σε 5, τότε την επόμενη φορά που θα καλέσουμε τη συνάρτηση, αυτή η μεταβλητή θα ξεκινήσει με 5 (και όχι με το 0 της αρχικοποίησης).

 

 

Πάω για νάνι, γιατί κουτουλάω :P

 

  • Like 1
Δημοσ.

Παλαιότερα είχα γράψει μια μικρή εξήγηση για το static στο adslgr.

 

Το κακό με το static (και το const) είναι ότι έχουν 10 σημασίες. Ας τις δούμε μία - μία.

 

1) Τοπικές μεταβλητές συναρτήσεων:

#include <stdio.h>

void myfunc(void)
{
	int i = 5;

	i++;

	printf("i has address %p and value %d\n", (void *)&i, i);
}

void mysfunc(void)
{
	static int i = 5;

	i++;

	printf("static i has address %p and value %d\n", (void *)&i, i);
}

void again(void)
{
	myfunc();
	mysfunc();
}

int main(void)
{
	myfunc();
	mysfunc();
	again();   /* *** */

	return 0;
}
Έξοδος:
i has address 0xacc and value 6
static i has address 0xb98 and value 6
i has address 0xabc and value 6
static i has address 0xb98 and value 7
Η κλασική λειτουργία όταν καλούμε μια συνάρτηση είναι να "δεσμεύεται" μέσω χειρισμού της στοίβας μνήμη για τα ορίσματα και τις τοπικές μεταβλητές της συνάρτησης. Όταν βγαίνουμε από τη συνάρτηση αυτή η μνήμη "αποδεσμεύεται".

 

Αυτή τη λειτουργία βλέπουμε καλώντας τη συνάρτηση myfunc. Κάθε φορά που την τρέχουμε, η μεταβλητή i παίρνει άλλη διεύθυνση, αρχικοποιείται στη τιμή 5 και αυξάνεται η τιμή της κατά 1 οπότε πάντα θα τυπώνεται η τιμή 6.

 

Το παρόν πρόγραμμα είναι πολύ απλό οπότε αν έτρεχα απλά 2 φορές τη myfunc, το πιο πιθανό θα ήταν να έχει την ίδια διεύθυνση η μεταβλητή i. Για αυτό το λόγο τη δεύτερη φορά καλώ τις συναρτήσεις μέσω μιας άλλης συνάρτησης της again ώστε να αλλαχθεί η στοίβα και να φανεί ότι το μη-static i αλλάζει κάθε φορά διεύθυνση.

 

Στην περίπτωση που έχουμε δηλώσει την τοπική μεταβλητή ως static αυτό σημαίνει ότι θέλουμε να δεσμευτεί μόνο μία φορά μνήμη και έτσι τα περιεχόμενα της να υφίστανται και μετά το τέλος της συνάρτησης. Για αυτό το λόγο η δεύτερη κλήση της mysfunc εμφανίζει 7 γιατί η προηγούμενη τιμή 6 δεν έχει χαθεί. Αν την ξανατρέξουμε θα εμφανιστεί 8.

 

Για να λειτουργήσει αυτό πρέπει η αρχικοποίηση της μεταβλητής να γίνει στην ίδια δήλωση με τον ορισμό της όπως φαίνεται παραπάνω "static int i = 5".

 

Το "scope" δηλαδή η έκταση της μεταβλητής συνεχίζει να είναι η συνάρτηση αλλά η ζωή της δεν χάνεται όταν τελειώνει η συνάρτηση.

 

Θα μου πεις και τι χρειάζεται αυτό ? Υπάρχουν πολλοί λόγοι που μπορεί να θέλεις να παραμείνει ζωντανή μια μεταβλητή. Μια χρήση θα μπορούσε να είναι για να τρέξεις ένα κώδικα μόνο κάποια φορά (πχ στο δικό μου κώδικα θα μπορούσα να έχω ένα if (i == 6) που θα έτρεχε μόνο κατά τη δεύτερη κλήση της συνάρτησης). Άλλη χρήση θα μπορούσε να είναι για να ελέγχεις πότε θα σταματήσει μια αναδρομική συνάρτηση και χίλιες δυο άλλες περιπτώσεις.

 

 

2) Συναρτήσεις και global μεταβλητές

 

Όταν δηλώνουμε static σε μία συνάρτηση ή μια global μεταβλητή τότε αυτό σημαίνει ότι μπορούμε να τα προσπελάσουμε μόνο από το παρόν αρχείο.

% cat funcs.c
#include <stdio.h>

int global_int = 3;
static int static_global_int = 5;

void myefunc(void)
{
	printf("Hello\n");
	printf("gi = %d, si = %d\n", global_int, static_global_int);
}

static void mysfunc(void)
{
	printf("World\n");
	printf("gi = %d, si = %d\n", global_int, static_global_int);
}

% cat main.c
#include <stdio.h>

extern int global_int;
extern int static_global_int;
extern void myefunc(void);
extern void mysfunc(void);

int main(void)
{
	printf("gi = %d, si = %d\n", global_int, static_global_int);
	myefunc();
	mysfunc();

	return 0;
}
Εδώ βλέπουμε ένα κώδικα που αποτελείται από δύο αρχεία. Στο funcs.c ορίζω δύο συναρτήσεις και δύο global μεταβλητές. Στο αρχείο main.c έχω τη κλασική main συνάρτηση και κάποιες δηλώσεις extern. Κανονικά θα έπρεπε πιο σωστά να έχω κάποιο αρχείο κεφαλίδας .h και να έχω εκεί τις απαραίτητες δηλώσεις αλλά για πιο εύκολα το έχω κάνει έτσι.

 

Εδώ βλέπουμε για πρώτη φορά το extern. Αν πήγαινα να κάνω compile το αρχείο main.c ο compiler θα μου έλεγε ότι δεν βρίσκει ούτε τις μεταβλητές ούτε τις συναρτήσεις. Οι δηλώσεις extern λένε στον compiler ότι την μεταβλητή global_int που με βλέπεις να χρησιμοποιώ την ορίζω αλλού και δεσμεύεται εκεί μνήμη για να ξέρεις όμως τι κώδικα θα παράξεις σου λέω ότι έχει τύπο int. Ομοίως η συνάρτηση myefunc δεν δέχεται κανένα όρισμα και δεν επιστρέφει τίποτα.

 

Ας κάνουμε τώρα compile να δούμε τι θα δούμε:

% gcc -c main.c funcs.c
% gcc -o teliko main.o funcs.o 
main.o: In function `main':
main.c:(.text+0x6): undefined reference to `static_global_int'
main.c:(.text+0x27): undefined reference to `mysfunc'
collect2: σφάλμα: η ld επέστρεψε κατάσταση εξόδου 1
Έχω επίτηδες σπάσει τη διαδικασία σε δύο τμήματα, αυτό του compilation και αυτό του linking. Όπως βλέπουμε η πρώτη εντολή επιτυγχάνει χωρίς κανένα μήνυμα λάθους.

 

Στη δεύτερη εντολή ο gcc καλεί τον linker ld για να "ενώσει" τα δύο object αρχεία και να παράξει το τελικό εκτελέσιμο αρχείο. Ο linker μας εμφανίζει δύο μηνύματα λάθους ότι δεν μπορεί να βρει τη μεταβλητή static_global_int και τη συνάρτηση mysfunc.

 

Ενώ δηλαδή με τις extern δηλώσεις καταφέραμε το compile να παίξει, ο linker δεν μπορεί να δουλέψει. Αυτό έγινε ακριβώς επειδή δηλώσαμε το static το οποίο σημαίνει ότι τα αντικείμενα αυτά θα έχουν "εσωτερική linkage" και θα είναι ορατά μόνο μέσα στο αρχείο funcs.c ανεξάρτητα αν έχουμε ενημερώσει με extern τον compiler.

 

Σε αυτό το σημείο να πούμε ότι η δόκιμη έκφραση που χρησιμοποιείται σε βιβλία είναι "μονάδα μεταγλώττισης (translation unit)" και όχι αρχείο γιατί υπάρχει διαφορά. Χάριν ευκολίας όμως για την ώρα ας θεωρήσουμε ότι το static αντικείμενο μπορεί να προσπελαστεί μόνο μέσα από το ίδιο αρχείο στο οποίο ορίζεται.

 

Κάτι που ξέχασα να αναφέρω είναι ότι θα μπορούσαμε να είχαμε γράψει "extern void myefunc(void)" και θα ήταν ακριβώς το ίδιο με τη σκέτη δήλωση. Όταν δεν έχουμε δηλώσει static τότε το αντικείμενο έχει "εξωτερική linkage" και είτε παραλείψουμε ή δηλώσουμε extern είναι το ίδιο.

 

Και εδώ τώρα ισχύει η ίδια απορία. Κάποιος θα έρθει και θα πει καλά ρε ψηλέ και τι χρησιμεύει αυτό.

 

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

 

Έτσι μπορούμε στα αρχεία κεφαλίδων μας να δηλώσουμε όλες τις "public" συναρτήσεις που αποτελούν το API που παρέχουμε στους χρήστες και οι βοηθητικές συναρτήσεις να δηλωθούν static ώστε να μην μπορεί να τις δει κανείς.

 

3) Ορίσματα συναρτήσεων

#include <stdio.h>

void myfunc1(int a[5][3])
{
	return;
}

void myfunc2(int a[static 5][3])
{
	return;
}

int main(void)
{
	int a[9][3];
	int b[2][3];

	myfunc1(a);
	myfunc1(;
	myfunc1(NULL);

	myfunc2(a);
	myfunc2(;
	myfunc2(NULL);

	return 0;
}
% gcc -std=c11 -Wall -Wextra tmp.c
Τίποτα
% clang tmp.c 
tmp.c:23:2: warning: array argument is too small; contains 2 elements, callee
      requires at least 5 [-Warray-bounds]
        myfunc2(;
        ^       ~
tmp.c:8:18: note: callee declares array parameter as static here
void myfunc2(int a[static 5][3])
                 ^~~~~~~~~~~

tmp.c:24:2: warning: null passed to a callee which requires a non-null argument
      [-Wnonnull]
        myfunc2(NULL);
        ^       ~~~~
tmp.c:8:18: note: callee declares array parameter as static here
void myfunc2(int a[static 5][3])
                 ^~~~~~~~~~~
2 warnings generated.
Όπως ξέρουμε, μια δήλωση πίνακα ως όρισμα συνάρτησης μετατρέπεται σε δείκτη στο τύπο των στοιχείου του. Έτσι ο αριθμός της πρώτης διάστασης συχνά παραλείπεται γιατί δεν παίζει ρόλο. Η δήλωση "void f(int a[5][3])" ισοδυναμεί με τη δήλωση "void f(int a[4735][3]) και με τη δήλωση "void f(int a[][3])" επειδή το a θα είναι δείκτης σε πίνακα τριών στοιχείων.

 

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

 

Όταν δηλώνουμε [static 5] λέμε στον compiler ότι ο πίνακας μου θα έχει πάντα τουλάχιστον 5 στοιχεία και κατά συνέπεια εφόσον θα έχει στοιχεία δεν θα είναι ποτέ NULL. Αυτό μπορεί να το χρησιμοποιήσει ο compiler για κάποιο branch prediction ή για να αφαιρέσει ένα έλεγχο για NULL ή ποιος ξέρει τι.

 

Ο gcc στην 4.8.3 έκδοση που έχω δεν το χρησιμοποιεί αλλά ο clang όπως βλέπουμε εμφανίζει προειδοποιητικά μηνύματα ότι δώσαμε μη-επιτρεπτό όρισμα στη συνάρτηση.

  • Like 4
Δημοσ.

Να σαι καλα αδερφε,ελπιζω μελλοντικα να ξαναφτιαξεις τετοια αρθρα για αλλο θεμα της C

 

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

  • Like 1
Δημοσ.

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

 

Κάτι είπες για project. Έχεις σκεφτεί κάτι; Πάνω σε τί; System Programming; Αν είναι έτσι θα ήμουν μέσα. Εκτός κι αν με έκανες να τρέφω φρούδες ελπίδες. :P

 

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

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

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

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

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

Σύνδεση

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

Συνδεθείτε τώρα
  • Δημιουργία νέου...