לדלג לתוכן

9.1 שלבי הקומפילציה תרגול

תרגול - שלבי הקומפילציה

תרגיל 1 - מעקב אחרי שלבי הקומפילציה

צרו את הקובץ הבא:

// stages.c
#include <stdio.h>

#define NAME "Student"
#define YEAR 2026

int main() {
    printf("Hello, %s! Year: %d\n", NAME, YEAR);
    return 0;
}

בצעו את הפעולות הבאות:

  1. הריצו gcc -E stages.c -o stages.i ופתחו את stages.i. כמה שורות יש בקובץ? (רמז: השתמשו ב-wc -l)
  2. חפשו את הפונקציה main בתוך stages.i. מה קרה ל-NAME ול-YEAR?
  3. הריצו gcc -S stages.c -o stages.s ובדקו את קובץ האסמבלי. מצאו את המחרוזות "Hello" ו-"Student" - באיזה סקשן הן נמצאות?
  4. הריצו gcc -c stages.c -o stages.o ואז readelf -S stages.o. רשמו את שמות כל הסקשנים שנוצרו.
  5. הריצו readelf -r stages.o - מהם הסמלים שצריכים relocation?

תרגיל 2 - הpreprocessor בפעולה

צרו את הקובץ הבא:

// preproc.c
#include <stdio.h>

#define DEBUG
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x) * (x))
#define LOG(msg) printf("[%s:%d] %s\n", __FILE__, __LINE__, msg)

int main() {
    int x = 5, y = 10;

#ifdef DEBUG
    LOG("Debug mode is ON");
    printf("MAX(%d, %d) = %d\n", x, y, MAX(x, y));
#endif

    printf("SQUARE(%d) = %d\n", x, SQUARE(x));
    printf("Compiled on: %s at %s\n", __DATE__, __TIME__);

    return 0;
}
  1. הריצו gcc -E preproc.c ובדקו מה קרה לכל מקרו. מצאו את השורה שהיתה LOG("Debug mode is ON") - למה היא הורחבה?
  2. מחקו את השורה #define DEBUG והריצו שוב gcc -E preproc.c. מה קרה לקוד שהיה בתוך #ifdef DEBUG?
  3. קמפלו והריצו את התוכנית בשני המצבים (עם ובלי #define DEBUG). מה ההבדל בפלט?
  4. במקום לשנות את הקוד, השתמשו בדגל -D של gcc כדי להגדיר מקרו מבחוץ: gcc -DDEBUG preproc.c -o preproc. מה היתרון של גישה זו?

תרגיל 3 - בדיקת סמלים עם nm

צרו שני קבצים:

// utils.c
int global_counter = 42;
static int local_counter = 0;

int increment(int x) {
    local_counter++;
    return x + 1;
}

static int internal_helper(int x) {
    return x * 2;
}
// app.c
#include <stdio.h>

extern int global_counter;
int increment(int x);

int app_function(void) {
    return increment(global_counter);
}

int main() {
    printf("Result: %d\n", app_function());
    return 0;
}
  1. קמפלו כל קובץ לקובץ אובייקט: gcc -c utils.c ו-gcc -c app.c
  2. הריצו nm utils.o - מהם הסמלים שמופיעים? מה ההבדל בין סמלים מסוג T לסמלים מסוג t? מה ההבדל בין D ל-d?
  3. הריצו nm app.o - מהם הסמלים מסוג U? למה הם undefined?
  4. חברו את שני הקבצים: gcc utils.o app.o -o app. האם הלינקוג' הצליח?
  5. מה יקרה אם ננסה לגשת ל-local_counter מתוך app.c? נסו והסבירו את השגיאה.

תרגיל 4 - אזהרות ודגלי קומפילציה

צרו את הקובץ הבא (הקובץ מכיל באגים מכוונים):

// warnings.c
#include <stdio.h>

int calculate(int a, int b) {
    int result;
    if (a > 0)
        result = a + b;
    return result;
}

int main() {
    int x = 3.14;
    int arr[5];
    arr[10] = 42;

    printf("Result: %d\n", calculate(x, 2));
    printf("Value: %d\n");

    return 0;
}
  1. קמפלו ללא אזהרות: gcc warnings.c -o warnings. מה קורה?
  2. קמפלו עם -Wall: gcc -Wall warnings.c -o warnings. אילו אזהרות מופיעות?
  3. קמפלו עם -Wall -Wextra: מה אזהרות נוספות מופיעות?
  4. קמפלו עם -Wall -Wextra -Werror: מה קורה עכשיו?
  5. תקנו את כל האזהרות בקוד עד שהקימפול עם -Wall -Wextra -Werror יצליח.

תרגיל 5 - השוואת רמות אופטימיזציה

צרו את הקובץ הבא:

// optimize.c
#include <stdio.h>

int compute(int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += i * 2 + 1;
    }
    return sum;
}

int dead_code(void) {
    int x = 3 + 4;
    int y = x * 2;
    return 42;  // x ו-y לא משמשים לכלום
}

int main() {
    int a = 10 + 20;
    int b = a * 2;
    printf("compute(100) = %d\n", compute(100));
    printf("dead_code() = %d\n", dead_code());
    return 0;
}
  1. קמפלו לאסמבלי ב-4 רמות אופטימיזציה:
    gcc -O0 -S optimize.c -o optimize_O0.s
    gcc -O1 -S optimize.c -o optimize_O1.s
    gcc -O2 -S optimize.c -o optimize_O2.s
    gcc -O3 -S optimize.c -o optimize_O3.s
    
  2. השוו את הפונקציה dead_code בין -O0 ל--O2. מה הקומפיילר עשה עם x ו-y?
  3. בדקו את main ב--O2 - האם 10 + 20 מחושב בזמן ריצה או בזמן קומפילציה?
  4. השוו את הפונקציה compute בין -O0 ל--O3. האם הלולאה עדיין קיימת ב--O3?
  5. השוו את גודל קבצי האסמבלי (שורות) בין הרמות: wc -l optimize_O*.s