C/C++ compiler ερώτηση
Giorgos Keramidas
keramida at ceid.upatras.gr
Sun Jun 24 13:47:05 EEST 2012
On Sat, 23 Jun 2012 09:50:41 +0300, Theodore Lytras <thlytras at gmail.com> wrote:
> Καλησπέρα στη λίστα,
>
> θα ήθελα να κάνω μια ερώτηση, δε ξέρω αν είναι αυτονόητη, απλά ήθελα
> να είμαι σίγουρος.
>
> Σε ένα λογικό έλεγχο της μορφής:
>
> if (A && B) { blabla(); }
>
> μπορώ να είμαι σίγουρος οτι το A θα ελεγχθεί πάντα πριν από το B, και
> οτι αν το A είναι ψευδές το B δε θα ελεγχθεί καθόλου???
Ναι μπορείς να είσαι 100% σίγουρος.
Αυτό που περιγράφεις λέγεται «short-circuit evaluation» και είναι πολύ
χρήσιμο εργαλείο. Μάλιστα το παράδειγμα που έγραψες ακριβώς μετά:
> Το background της ερώτησης: μπορώ να είμαι σίγουρος οτι το παρακάτω δε
> θα κάνει ποτέ segfault?
> [...]
> Foo* f = 0;
> .... // various commands processing f
> if (f && f->isSomething()) { blabla(); }
είναι από τις πιο _συχνές_ περιπτώσεις που έχει νόημα το short-circuit
evaluation. Αν δεν έπαιζε έτσι όπως κατάλαβες ότι παίζει, θα έπρεπε να
γράφουμε πάντα κάτι σαν:
if (f) {
if (f->isSomething) {
blabla();
}
}
Ενώ τώρα μπορείς απλά να γράψεις αυτό που σου ήρθε φυσικά:
if (f && f->isSomething())
blabla();
--
Πάρα πολλά παραδείγματα από short-circuit evaluation έχει o networking
κώδικας π.χ. του FreeBSD. Ορισμένα παραδείγματα από τον κώδικα του
αρχείου ip_input.c είναι παρακάτω:
http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/netinet/ip_input.c?annotate=1.396
.. lines 406-410
406: if (m->m_len < sizeof (struct ip) &&
407: (m = m_pullup(m, sizeof (struct ip))) == NULL) {
408: IPSTAT_INC(ips_toosmall);
409: return;
410: }
Εδώ το m είναι 'struct mbuf', το οποίο αποθηκεύει τα δεδομένα ενός
πακέτου. Η συνάρτηση m_pullup() κάνει defragmentation από τα αρχικά
sizeof(struct ip) bytes, αλλά αν την καλέσεις με mbuf που δεν έχει
αρκετά δεδομένα θα 'χει πρόβλημα. Το short-circuit evaluation εγγυάται
ότι όταν η τιμή του m->m_len είναι μικρότερη από το σωστό μέγεθος η
m_pullup() δε θα κληθεί *ποτέ*.
.. lines 455-459
455: #ifdef ALTQ
456: if (altq_input != NULL && (*altq_input)(m, AF_INET) == 0)
457: /* packet is dropped by traffic conditioner */
458: return;
459: #endif
Αυτό είναι _ακριβώς_ το παράδειγμα που έδωσες κι εσύ. Αν το pointer
altq_input είναι non-NULL, δείχνει σε μια συνάρτηση που πρέπει να τρέξει
με input το πακέτο το ίδιο. Αν είναι NULL όμως το δεύτερο κομμάτι του
expression, καθώς και όλο το if-body, δεν τρέχει καν.
--
Και αντίστοιχα παραδείγματα από το ip_output.c με παρόμοια λογική:
http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/netinet/ip_output.c?annotate=1.331
.. lines 255-264
255: if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) &&
256: imo != NULL && imo->imo_multicast_ifp != NULL) {
257: /*
258: * Bypass the normal routing lookup for multicast
259: * packets if the interface is specified.
260: */
261: ifp = imo->imo_multicast_ifp;
262: IFP_TO_IA(ifp, ia);
263: isbroadcast = 0; /* fool gcc */
264: }
στη γραμμή 256 πάλι το ίδιο στυλ από έλεγχο: αν το 'imo' είναι NULL το
imo->imo_multicast_ifp είναι invalid memory access. Όμως ακριβώς λόγω
short-circuit evaluation δεν υπάρχει κανένα πρόβλημα.
.. lines 308-322
308: /*
309: * Calculate MTU. If we have a route that is up, use that,
310: * otherwise use the interface's MTU.
311: */
312: if (rte != NULL && (rte->rt_flags & (RTF_UP|RTF_HOST))) {
313: /*
314: * This case can happen if the user changed the MTU
315: * of an interface after enabling IP on it. Because
316: * most netifs don't keep track of routes pointing to
317: * them, there is no way for one to update all its
318: * routes when the MTU is changed.
319: */
320: if (rte->rt_rmx.rmx_mtu > ifp->if_mtu)
321: rte->rt_rmx.rmx_mtu = ifp->if_mtu;
322: mtu = rte->rt_rmx.rmx_mtu;
323: }
Το 'rte' (routing table entry) είναι pointer σε πληροφορίες routing για
το network interface που χρησιμοποιείται. Κι εδώ υπάρχει ο ίδιος
ακριβώς έλεγχος: αν το 'rte' είναι no-NULL, τότε μόνο είναι ασφαλές να
προσπελάσει κανείς τα routing entry flags 'rte->rt_flags'.
--
Τα παραδείγματα σε όλο τον υπόλοιπο κώδικα είναι _πάρα_ πολλά. Εγώ
προσωπικά το θεωρώ δείγμα «καλής γραφής» να βλέπω ότι ο κώδικας
χρησιμοποιεί με έξυπνο τρόπο το short-circuit evaluation. Αν δω σε code
review κάτι σαν:
if (condition1) {
if (condition2) {
if (condition3 || condition4) {
kwdikas_edw();
}
}
}
ζητάω πάντα να αλλάξει σε:
if (condition1 && condition2 && (condition3 || condition4))
kwdikas_edw();
Χωρίς _καμία_ εξαίρεση.
More information about the Linux-greek-users
mailing list