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

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

Δημοσ.

Καλησπέρα ,

 

έχω ένα θεματάκι που με κούρασε λίγο και είπα να ρωτήσω αν γνωρίζει κάποιος καλύτερα για το scoping των μεταβλητών στη C να με βοηθήσει και εμένα .

 

Λοιπόν . Έχουμε ένα αρχείο parser.y το οποίο γίνεται "compile" (δεν το λες και ακριβώς compile) από το εργαλείο συντακτικής ανάλυσης yacc/bison, αυτό το αρχείο περιέχει κάποιους γραμματικούς κανόνες οι οποίοι εκτελούν κάποια actions (καλούν δηλαδή κάποιες συναρτήσεις) και μία main συνάρτηση γραμμένη σε C .

 

Αυτή η main χρησιμοποιεί συναρτήσεις (όπως και οι γραμματικοί κανόνες) από το αρχείο header.h το οποίο πάει πακέτο με το functions.c (το header εχει δηλαδη κάποιες global μεταβλητές και τις υπογραφές των συναρτήσεων που χρησιμοποιούνται στο functions.c) . Το θέμα είναι πως υπάρχουν και τα ακόλουθα αρχεία πλέον: symboltable.h symboltable.c quad.h quad.c scope.h scope.c . Στην αρχή υπήρχε μόνο το symboltable και η λύση ήταν απλή . Απλώς τα έκανα include μέσα στο parser δηλαδή

 

#include "symboltable.c"

#include "functions.c"

 

**με αυτή τη σειρά επειδή ξέχασα να αναφέρω πως το functions.c καλεί συναρτήσεις από το symboltable και ήθελα να "φαίνονται". Λίγο μπακάλικο ξέρω , όμως δούλευε και αυτό μετρούσε . 

 

Πλέον όμως έχει αλλάξει η δομή μας .

 

Το header.h κάνει include το quad.h και το scope.h

Το quad.h    κάνει include το symboltable.h

 

Έτσι φαίνεται λογικό αφού οι συναρτήσεις του header χρησιμοποιούν συναρτήσεις από quad, symboltable και scope και το quad χρησιμοποιεί συναρτήσεις του symboltable .

 

Όμως , υπάρχουν global μεταβλητές στο header πχ int scope; που οι συναρτήσεις του symbotable χρησιμοποιούν αλλά πλέον δεν τις βλέπουν .

Τι πρέπει να γίνει για να έχουμε access σε αυτές τις μεταβλητές ;;

 

 

Δημοσ.

Ολα global ειναι. Δεν ειναι κατι δυσκολο.

πχ εχεις στο αρχειο algorith.c μια συναρτηση void run_algorithm(){mpa mpa} και μια global μεταβλητη int algLastError = 0;

 

Πως θα καλεσεις την run_algorithm στο ξερωγω στη main.c ; Πολυ απλα θα βαλεις ενα declaration της run_algortihm (δηλαδη πανω απο την main θα βαλεις void run_algorithm() ; )

 

Πως θα παρεις την algLastError; Πολυ απλα, θα βαλεις πανω απο την main το extern int algLastError;

 

Τελος εφοσον καταλαβες πως δεν ειναι πυρηνικη φυσικη. Βγαζεις τα παραπανω απο την main.c και τα βαζεις στο algorithm.h με ενα header gaurd

#ifndef __INC_ALGORITHM_H__
#define __INC_ALGORITHM_H__

extern int algLastError;
void run_algrotihm();

#endif

το οποιο header το κανεις include εκει που θες.

  • Like 1
Δημοσ.

Γενικά καλό είναι να αποφεύγεις να βάζεις variable definitions σε header files. Μία φορά σε ένα source και στα υπόλοιπα declare extern.

 

Στα include δε θα πρεπε να μετράει η σειρά αν το κάνεις για το λόγο που υποπεύομαι είναι λάθος, θα κάνεις include σε κάθε file ( source ή header δεν έχει να κάνει ) τι χρειάζεται ατομικά και για να αποφύγεις τυχόν multiple declarations θα βάλεις σε όλα τα header, guards.

  • Like 1
Δημοσ.

Δείτε εδώ για παράδειγμα το πρώτο error που πετάει .

 

Το quad που κάνει include το symboltable δεν βρίσκει το struct που ειναι δηλωμένο εκεί .

 

Ελπίζω να φαίνονται οι εικόνες .

 

***Συγκεκριμένα βλέπετε πως το struct expr έχει μέσα μία μεταβλητή τύπου SymbolTableEntry*

Δημοσ.

Απ' ό,τι καταλαβαίνω μάλλον έχεις μπουρδουκλώσει λίγο τη δομή των includes

 

έχεις το symboltable.h που κάνει include το header.h που κάνει include το quad.h που κάνει include πάλι το symboltable.h

τα λέω σωστά;

 

αν ναι αυτό δε δουλεύει θα σου χτυπήσει στο header guard και καλώς θα χτυπήσει. Σχεδιαστικά θα έπρεπε να το κάνεις κάπως αλλιώς. τώρα αν θες απλά να σου δουλέψει το πιο απλό είναι να χώσεις κάπου ένα forward declaration του struct σου στην αρχή του quad.h ας πούμε δηλαδή μόνο όνομα και τύπος.

 

Αυτό δε θα σου δουλέψει φυσικά για περιπτώσεις όπου θα πρέπει ο compiler να ξέρει το definition την ώρα που το χρησιμοποιείς αλλά αν το μόνο που κάνεις με αυτό είναι να δηλώνεις pointers πάνω του δε θα έχεις πρόβλημα.

Δημοσ.

Σημείωση για include guards: δεν είναι standard βέβαια, αλλά στην πράξη δεν ξέρω κανένα compiler που να μην υποστηρίζει #pragma once

  • Like 2
Δημοσ. (επεξεργασμένο)

Σημείωση για include guards: δεν είναι standard βέβαια, αλλά στην πράξη δεν ξέρω κανένα compiler που να μην υποστηρίζει #pragma once

 

Το θέμα είναι όμως να το υποστιρίζει και στα μηχανήματα που θα τρέξει το πρόγραμμα , διότι θα γίνει compile και εκεί .

 

Επίσης δεν το έχω ξαναχρησιμοποιήσει , είναι αποδοτικότερο;

 

 

EDIT: βάζω guards κανονικά πως γίνεται να χτυπάει ξανά ο compiler;;;

Επεξ/σία από Επισκέπτης
Δημοσ.

Επίσης δεν το έχω ξαναχρησιμοποιήσει , είναι αποδοτικότερο;

Είναι αποδοτικότερο στην πληκτρολόγηση δεν έχει άλλη διαφορά.(από όσο ξέρω τουλάχιστον)

 

Εδώ μπορείς να δεις ποιοι compilers το υποστηρίζουν

https://en.wikipedia.org/wiki/Pragma_once#Portability

Δημοσ.

update: νομίζω λύθηκε το θέμα . Αλλά για να είμαι σίγουρος πρέπει να ολοκληρωθούν όλα τα αρχεία για να αρχίσει το testing και εκεί θα δείξει .

 

 

Guards χρησιμοποιούσα από την αρχή , απλά υπήρχαν μεταβλήτες που είχαν μπει στο header τις οποίες χρησιμοποιούσε το symboltable αλλα το header καλουσε συναρτησεις απο το symboltable . Εκεί ναι παίζει ρόλο ποιο θα δηλωθεί πρώτο για να το βλέπει το άλλο οπότε αν γίνονταν πρώτα include το symboltable δεν θα έβλεπε τις μεταβλητές του header που θα έμπαιναν από κάτω . Οπότε έφτιαξα ένα νέο header με αυτές τις μεταβλητές και ο θεός βοηθός .

Δημοσ.

Δεν βαζεις μεταβλητες μεσα σε header!!! Ειτε θα το κανεις σωστα, δηλαδη θα τις βαλεις στη TU και στους headers θα βαλεις externs ειτε θα το γραψεις ΟΛΟ σε ΕΝΑ αρχειο c

Δημοσ.

Το θέμα είναι όμως να το υποστιρίζει και στα μηχανήματα που θα τρέξει το πρόγραμμα , διότι θα γίνει compile και εκεί .

 

Επίσης δεν το έχω ξαναχρησιμοποιήσει , είναι αποδοτικότερο;

Είναι αποδοτικότερο στην πληκτρολόγηση δεν έχει άλλη διαφορά.(από όσο ξέρω τουλάχιστον)

Υπάρχουν φυσικά πολλοί compilers που δεν το υποστηρίζουν αλλά όλοι οι "mainstream" compilers που πιθανώς να χρησιμοποιήσεις το υποστηρίζουν οπότε μην σε απασχολεί αυτό. Θα μπορούσαμε δηλαδή καταχρηστικά να πούμε "Όλοι οι compilers το υποστηρίζουν". Η απόδοση είναι ακριβώς ίδια οπότε μην σε απασχολεί ούτε αυτό.

 

Αν θέλεις να το δούμε λίγο πιο λεπτομερώς, όλα εξαρτώνται αν μιλάμε θεωρητικά ή πρακτικά. Θεωρητικά, η λειτουργία των δύο είναι τελείως διαφορετική. Το pragma once εγγυάται ότι το αρχείο θα ανοιχτεί και θα διαβαστεί μόνο μία φορά. Ο compiler σημειώνει το i-node, fat entry, γενικά το που είναι αποθηκευμένο το αρχείο και δεν το ξανα-ανοίγει ακόμη και αν υπάρχει με διαφορετικό όνομα σαν συντόμευση. Με τα include guards, τα αρχεία θα πρέπει να διαβαστούν κάθε φορά από τον pre-processor. Με αυτό το σκεπτικό, το pragma once είναι πιο αποδοτικό.

 

Πρακτικά όμως, όλοι οι compilers έχουν optimizations για τα include guards ώστε να _μην_ διαβάζεται ξανά το αρχείο. Όταν ανοίγουν ένα αρχείο το οποίο έχει σαν πρώτη γραμμή "#ifndef τάδε" ή παρόμοιο και τίποτα άλλο μετά το #endif, τότε σημειώνουν το όνομα και δεν το ξανα-ανοίγουν οπότε δεν έχουμε μειωμένες επιδόσεις. Επίσης ο GCC έχει ένα πρόβλημα υλοποίησης στο pragma once το οποίο το καθιστά πάρα πολύ πιο αργό από τα include guards.

 

Άσχετα όμως με την υλοποίηση του κάθε compiler, διαφορά σε επιδόσεις θα δεις μόνο σε κώδικες που έχουν 20000-30000 αρχεία για αυτό είπα πριν μην σε απασχολεί η απόδοση.

  • Like 2

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

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

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

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

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

Σύνδεση

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

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