11.02 Препроцессор

Форма для сдачи

Классическая схема компиляции

  1. Трансляция: текст на ЯП → объектный файл (результат трансляции — машинный код — и ссылки на требуемые внешние функции/переменные)
  2. Компоновка: объектные файлы (в т. ч. взятые из библиотек) → исполняемый файл

Си:

  1. Препроцессор
  2. Трансляция в язык Ассемблера
  3. Трансляция в объектный код
  4. Компоновка

Вычисления периода компиляции

int main(void) {
        int two = 13 + 14;
}

Атомарные макроопределения в роли констант

   1 #include <stdio.h>
   2 #define SIZE 12
   3 
   4 int code[SIZE];
   5 char message[SIZE+1];
   6 
   7 int main(void) {
   8         fgets(message, SIZE+1, stdin);
   9         for (int i=0; i<SIZE; i++) {
  10                 code[i] = message[i];
  11                 printf("%02d ", code[i]);
  12         }
  13         puts("");
  14 }

Условная компиляция

Встроенные макросы

Используются для кроссплатформенной условной компиляции

$ touch пустой_файл.h
$ gcc -E -dM пустой_файл.h

Параметрические макросы

Есть два способа source code reuse:

:)) На что из начала семестра похожи макросы?

Если успеем

Макрос — не обязательно одно выражение, это может быть любая строка (несколько операторов, например)

Д/З (можно делать)

TODO Предполагается, что в некоторых задачах надо будет писать только макроопределения. Во время тестирования к файлу с решением будет приписан (сначала или в конце) текст функции main() с макроподстановками, и полученный генерат уже пойдёт на компиляцию и тестирование. Такие задачи будут начинаться словами «Написать макроопределение …». В этих задачах разрешается определять функции, а в макрос класть вызов этих функций, если так удобнее.

  1. Написать макроопределения SCREEN(имя, ширина, высота), FILL(имя, символ), DOT(имя, x, y, символ) и DISPLAY(имя). SCREEN(имя, ширина, высота) задаёт «текстовый экран» — двумерный массив имя типа char; FILL(имя, символ) заполняет весь этот экран точками; DOT(x, y, символ) помещает символ на «экран» имя по координатам x, y (координаты начинаются с 0 в левом нижнем углу); DISPLAY(имя) выводит экран построчно.

    • Подсказка 1: в моём решении экран — это двумерный символьный массив шириной на 1 больше и заполненный нулями. Его удобно выводить с помощью puts() в цикле

    • Подсказка 2. В FILL(screen, символ) как бы не хватает параметров «ширина» и «высота». Откуда их взять? В моём решении «Ширина» — это sizeof нулевого элемента экрана (только -1), а высота получается делением на неё sizeof всего экрана. То же и для DISPLAY.

    • Подсказка 3. В тестах будет FILL(screen, getchar()) — при этом getchar() должен срабатывать однократно

    • Необязательная подсказка. Я написал вспомогательный макрос BYLINE(экран, оператор), который применяет оператор к каждой строке экрана. И с его помощью уже реализовал FILL и DISPLAY

    #include <stdio.h>
    int main(void) {
            SCREEN(scr, 24, 12);
            FILL(scr, getchar());
            for(int i=0; i<24; i++)
                    DOT(scr, i, i/2, '*');
            DISPLAY(scr);  
            return 0;
    }
    +
    .
    →
    ......................**
    ....................**..
    ..................**....
    ................**......
    ..............**........
    ............**..........
    ..........**............
    ........**..............
    ......**................
    ....**..................
    ..**....................
    **......................
  2. Написать макроопределения INIT(имя, количество), INPUT(имя) и FREE(имя). INIT(имя, количество) заводит локальный массив строк имя на количество элементов (изначально содержащий NULL-ы); INPUT(имя) вводит не более количества строк, а FREE(имя) — освобождает память, занятую этими строками. Тестирующая программа при этом можем выглядеть так:

    •    1 int main(void) {
         2         INIT(arr, 12);
         3         INPUT(arr);
         4         for(int i=0; i<12; i++)
         5                 fputs(arr[i]?arr[i]:"EMPTY\n", stdout);
         6         FREE(arr);
         7         return 0;
         8 }
      
    +
         Ноябрь 2026    
    Пн Вт Ср Чт Пт Сб Вс
                       1
     2  3  4  5  6  7  8
     9 10 11 12 13 14 15
    16 17 18 19 20 21 22
    23 24 25 26 27 28 29
    30
    →
         Ноябрь 2026    
    Пн Вт Ср Чт Пт Сб Вс
                       1
     2  3  4  5  6  7  8
     9 10 11 12 13 14 15
    16 17 18 19 20 21 22
    23 24 25 26 27 28 29
    30                  
    EMPTY
    EMPTY
    EMPTY
    • Подсказка. В INPUT(имя) как бы не хватает параметра «количество»? Откуда его взять? Поскольку мы заводим ''локальный массив'' строк (а не char **), от него можно брать sizeof (не забываем, что это количество байтов, а не количество элементов типа char *`)

    • Подсказка 2. В free() можно передавать NULL

  3. Написать программу, при компиляции которой будет задан макрос TYPENUM — чиcло от 1 до 4. 1 соответствует типу int, 2 — char, 3 — double и 4 — float. Программа должна будет ввести два значения соответствующего типа, сложить их и вывести результат. char вводится/выводится с помощью "%c"

    • В примере — запуск программы, скомпилированной с -DTYPENUM=3

      2.3 4.5
    →
    • 6.800000
  4. Написать программу, при компиляции которой будет задан макрос FORMULA(x) — некоторое выражение над переменной x типа double — и числовые константы MINIMUM и MAXIMUM. Написать программу, которая в цикле вводит число y и выводит double x такое, что y == FORMULA(x). Гарантируется, что FORMULA(x) — монотонно возрастающая на отрезке [MINIMUM, MAXIMUM] функция от x (например, x² на отрезке [1, 100]), и -DBL_MAX < FORMULA(MINIMUM) ⩽ FORMULA(MAXIMUM) < DBL_MAX . Если x не принадлежит отрезку, вывести Out of interval. Конец ввода — EOF.

    • Указание 1. Если взять a<x и b>x, то для монотонной функции f() годится метод половинного деления: если f((a+b)/2) меньше y, то сделать a равным (a+b)/2, иначе b, и так до равенства

    • Указание 2. Для вещественных чисел не работает a==b, но функция nextafter() покажет, что между a и b никаких других чисел уже нет. DBL_MAX описан в float.h

    Вот пример для -D"FORMULA(x)"="((x)*(x))" -D MINIMUM=10 -DMAXIMUM=100:

    0
    3333
    6666
    9999
    13332
    →
    Out of interval
    57.732140
    81.645576
    99.995000
    Out of interval

LecturesCMC/AL/Prac/18_Preprocessor (последним исправлял пользователь FrBrGeorge 2026-11-08 16:58:51)