Τι εκτυπώνει το πρόγραμμα;

#include <stdio.h>

int calculate(void);

int calculate(void)
{
int i;
int p[9];

for ( i = 0; i <= 3; i++ ) if (!(sizeof(int)%4))p[i*6+2]+=15; else p[i*3+2]+=16;
}

int main(int argc, char** argv)
{
calculate();
printf(“Hello, World!\n”);

return 0;
}

Τι εκτυπώνει το παραπάνω πρόγραμμα;
Δουλεύει τουλάχιστον σε Linux/i386.

Ενημέρωση: Λειτουργεί και για x86_64.

Permanent link to this article: https://blog.simos.info/%cf%84%ce%b9-%ce%b5%ce%ba%cf%84%cf%85%cf%80%cf%8e%ce%bd%ce%b5%ce%b9-%cf%84%ce%bf-%cf%80%cf%81%cf%8c%ce%b3%cf%81%ce%b1%ce%bc%ce%bc%ce%b1/

11 comments

Skip to comment form

    • Δημήτρης Καμενόπουλος on October 2, 2005 at 18:49
    • Reply

    Εντάξει δε χάθηκε και ο κόσμος για 2-3 θέσεις εκτός ορίου 🙂

    Το πιο πιθανό είναι ότι ο compiler δεσμεύει χώρο στη μνήμη σε τίποτα πολλαπλάσια του 2, ή κάτι ανάλογο. Για να κάνει segfault πρέπει να διαμαρτυρηθεί το λειτουργικό, δηλαδή να πειραχθεί χώρος της μνήμης που δεν ανήκει στο πρόγραμμα.

  1. Αν έχεις Linux/i386, δοκίμασε να το εκτελέσεις και ανέφερε το αποτέλεσμα.

    Για να βοηθήσω, τυπικές απαντήσεις είναι α) δίνει σφάλμα κατάτμησης, β) εκτυπώνει “Hello, World!” [αλλαγή γραμμής] ή γ) κάνει κάτι άλλο, ίσως παράξενο, οπότε προσπαθούμε να εξηγήσουμε γιατί το κάνει.

    Προσωπικά με ενδιαφέρει η περίπτωση (γ). 😉

    • Δημήτρης Καμενόπουλος on October 2, 2005 at 19:14
    • Reply

    Μα το τέσταρα, σε SuSE 9.3 (gcc με default options). Τυπώνει “Hello world”. Γι’ αυτό κατέληξα ότι δε χάθηκε ο κόσμος. 🙂

    • Simos on October 2, 2005 at 23:25
    • Reply

    Σίγουρα εμφανίζει Hello world;

    Μόλις δοκίμασα σε Ubuntu (gcc 4.0.2, i686) και εμφανίζει αυτό που θέλω. Αυτό που κάνει είναι να τερματίζει η εφαρμογή χωρίς να εμφανίζει κανένα μήνυμα.

  2. Ω, δεν έπρεπε να το δω αυτό 3:30 το βράδυ…

    Το τελευταίο iteration του for loop προφανώς πηγαίνει και αυξάνει κάποια θέση της στοίβας κατά 16. Αυτή η θέση είναι 3 words μετά το τέλος του p[]. Τι είναι τώρα εκεί, είναι ένα καλό ερώτημα.

    Απ’όσο θυμάμαι σε αυτήν την θέση πρέπει να είναι η return address της calculate(). Αν την αυξάνεις κατά 16 bytes == 4 words υπάρχει μία καλή περίπτωση να επιστρέψεις αμέσως μετά την κλήση της printf, οπότε δεν εκτυπώνεις τίποτα.

    Βέβαια το πόσες εντολές παρεμβάλλονται ανάμεσα στην αρχική return address και την κλήση της printf πρέπει να είναι τελειώς implementation depended, οπότε δεν μπορείς να βασίζεσαι σε αυτό…

    Ίσως με μεγαλύτερη αύξηση (πχ 128 αντί για 16) και με αρκετή σαβούρα κώδικα ανάμεσα στην printf και το return της main, θα μπορούσε να δουλέψει σίγουρα.

  3. Το πως και τι θα γίνει εξαρτάται πολύ από την οργάνωση της runtime στοίβας. Μπορεί να μη γίνει τίποτα… μπορεί να γίνει overwrite η διεύθυνση επιστροφής της calculate() και να επιστρέψει μετά την printf()… μπορεί να πετάξουν πορτοκαλί ρινικοί δαίμονες ντυμένοι με ασπρόμαυρες στολές πιερότου από τη μύτη σας και να δείτε σε όραμα τον Νοστράδαμο να πίνει φραπέ με τον Μακιαβέλι, τον Μάρξ και τον Πλάτωνα.

    Σύμφωνα με το C standard, η προσπέλαση θέσεων μνήμης μετά το τέλος ενός πίνακα προκαλεί “undefined behavior”. Τι θα είναι αυτό, είναι… uhm, “undefined”.

  4. μπορεί να πετάξουν πορτοκαλί ρινικοί δαίμονες ντυμένοι με ασπρόμαυρες στολές πιερότου από τη μύτη σας και να δείτε σε όραμα τον Νοστράδαμο να πίνει φραπέ με τον Μακιαβέλι, τον Μάρξ και τον Πλάτωνα

    Κάποιος κλέβει ναρκωτικά από τον Ρικούδη μου φαίνεται 😛

    • Δημήτρης Καμενόπουλος on October 3, 2005 at 09:57
    • Reply

    > Τι θα είναι αυτό, είναι… uhm, “undefined”

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

    @simos ναι σίγουρα, αλλά εγώ έχω gcc 3.3.

  5. Ευχαριστώ όλους για τα σχόλια.

    Fade, πράγματι έτσι είναι, βρήκες την απάντηση!

    Γιώργο, έχεις επηρεαστεί από την lgu και παρέσυρες και τον mperedim :).

    Πράγματι, το τι υπάρχει εκτός των συνόρων ενός τύπου δεδομένου είναι πληροφορία μη ορισμένη (undefined), σε τυπικές υλοποιήσεις μπορείς να χρησιμοποιήσεις τα σχετικά εργαλεία για να δεις.

    Σε επόμενη εγγραφή στο ιστολόγιό μου θα περιγράψω τις λεπτομέρειες.

  6. Σε amd64 που έτρεξα εγώ το πρόγραμμα, τα σχετικά μεγέθη των stack pointers είναι λίιιιιιγο διαφορετικά, οπότε πρέπει να παίξεις λίγο παραπάνω για να πετύχεις το σωστό stack byte.

    Δεν έχω επηρεαστεί από την lgu όμως. Από το comp.lang.c έχω επηρεαστεί, που οι “nasal demons” είναι κλασικό αστείο πλέον 🙂

  7. @Γιώργος: Σε AMD64 είναι sizeof(int)==8; Με objdump -d μπορείς να δεις τί διαφορά υπάρχει. Για παράδειγμα, είναι p[9]==i; μπορείς να δοκιμάσεις άλλες τιμές για την συνάρτηση επιστροφής, όπως p[11] ή μεγαλύτερη τιμή. Έπρεπε πάντως να δουλέψει διότι χρησιμοποιώ int…

    Για το σχόλιο για την lgu, αναφερόμουν στην συνήθεια που υπάρχει για κάθε θέμα να γίνονται άσχετα σχόλια και η προσοχή να πάει αλλού. Έτσι δεν επιλύεται κανένα θέμα και χάνουν όλοι πια το ενδιαφέρον. Είναι κάτι σαν attention deficit disorder… 🙁

    Έκανα νέα εγγραφή στο ιστολόγιό μου για το θέμα με την C. Δείτε
    http://simos.info/blog/?p=422

Leave a Reply to SimosCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.