Директивы препроцессора в Си

Язык Си / Директивы препроцессора в Си

Препроцессор — это специальная программа, являющаяся частью компилятора языка Си. Она предназначена для предварительной обработки текста программы. Препроцессор позволяет включать в текст программы файлы и вводить макроопределения.
Работа препроцессора осуществляется с помощью специальных директив (указаний). Они отмечаются знаком решетка #. По окончании строк, обозначающих директивы в языке Си, точку с запятой можно не ставить.

Основные директивы препроцессора
#include — вставляет текст из указанного файла
#define — задаёт макроопределение (макрос) или символическую константу
#undef — отменяет предыдущее определение
#if — осуществляет условную компиляцию при истинности константного выражения
#ifdef — осуществляет условную компиляцию при определённости символической константы
#ifndef — осуществляет условную компиляцию при неопределённости символической константы
#else — ветка условной компиляции при ложности выражения
#elif — ветка условной компиляции, образуемая слиянием else и if
#endif — конец ветки условной компиляции
#line — препроцессор изменяет номер текущей строки и имя компилируемого файла
#error — выдача диагностического сообщения
#pragma — действие, зависящее от конкретной реализации компилятора.

Директива #include

Директива #include позволяет включать в текст программы указанный файл. Если файл является стандартной библиотекой и находится в папке компилятора, он заключается в угловые скобки. Если файл находится в текущем каталоге проекта, он указывается в кавычках. Для файла, находящегося в другом каталоге необходимо в кавычках указать полный путь.

#include <stdio.h>
#include "func.c"

Директива #define

Директива #define позволяет вводить в текст программы константы и макроопределения.
Общая форма записи

#define идентификатор замена

Поля идентификатор и замена разделяются одним или несколькими пробелами. Директива #define указывает компилятору, что нужно подставить строку, определенную аргументом замена, вместо каждого аргумента идентификатор в исходном файле. Идентификатор не заменяется, если он находится в комментарии, в строке или как часть более длинного идентификатора.

#include <stdio.h>
#define A 3
int main(){
  printf("%d + %d = %d", A, A, A+A); // 3 + 3 = 6
  getchar();
  return 0;
}

В зависимости от значения константы компилятор присваивает ей тот или иной тип. С помощью суффиксов можно переопределить тип константы:

  • U или u представляет целую константу в беззнаковой форме (unsigned);
  • F (или f) позволяет описать вещественную константу типа float;
  • L/span> (или l) позволяет выделить целой константе 8 байт (long int);
  • L/span> (или l) позволяет описать вещественную константу типа long double
#define A 280U   // unsigned int
#define B 280LU  // unsigned long int
#define C 280    // int (long int)
#define D 280L   // long int
#define K 28.0   // double
#define L 28.0F  // float
#define M 28.0L  // long double

Вторая форма синтаксиса определяет макрос, подобный функции, с параметрами. Эта форма допускает использование необязательного списка параметров, которые должны находиться в скобках. После определения макроса каждое последующее вхождение

идентификатор(аргумент1, ..., агрументn)

замещается версией аргумента замена, в которой вместо формальных аргументов подставлены фактические аргументы.

Пример Вычисление синуса угла

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PI 3.14159265
#define SIN(x) sin(PI*x/180)
int main() {
  int c;
  system("chcp 1251");
  system("cls");
  printf("Введите угол в градусах: ");
  scanf("%d", &c);
  printf("sin(%d)=%lf", c, SIN(c));
  getchar(); getchar();
  return 0;
}

Результат выполнения
Директива define
Отличием таких макроопределений от функций в языке Си является то, что на этапе компиляции каждое вхождение идентификатора замещается соответствующим кодом. Таким образом, программа может иметь несколько копий одного и того же кода, соответствующего идентификатору. В случае работы с функциями программа будет содержать 1 экземпляр кода, реализующий указанную функцию, и каждый раз при обращении к функции ей будет передано управление.
Отменить макроопределение можно с помощью директивы #undef.

Однако при использовании таких макроопределений следует соблюдать осторожность, например

#include <stdio.h>
#define sum(A,B) A+B
int main(){
  int a,b,c,d;
  a=3; b=5;
  c = (a + b)*2; // c = (a + b)*2
  d = sum(a, b) * 2; // d = a + b*2;
  printf(" a = %d\n b = %d\n", a, b);
  printf(" c = %d \n d = %d \n", c, d);
  getchar();
  return 0;
}

Результат выполнения:
Использование макроопределений define

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

Директивы #if или #ifdef/#ifndef вместе с директивами #elif, #else и #endif управляют компиляцией частей исходного файла.Если указанное выражение после #if имеет ненулевое значение, в записи преобразования сохраняется группа строк, следующая сразу за директивой #if. Синтаксис условной директивы следующий:

#if константное выражение
   группа операций
#elif константное выражение
   группа операций
#else
   группа операций
#endif

Отличие директив  #ifdef/#ifndef заключается в том, что константное выражение может быть задано только с помощью #define.

У каждой директивы #if в исходном файле должна быть соответствующая закрывающая директива #endif. Между директивами #if и #endif может располагаться любое количество директив #elif, однако допускается не более одной директивы #else. Директива #else, если присутствует, должна быть последней перед директивой #endif.

Пример

#include <stdio.h>
#include <stdlib.h>
#define P 2
int main() {
  system("chcp 1251");
  system("cls");
#if P==1
  printf("Выполняется ветка 1");
#elif P==2
  printf("Выполняется ветка 2, P=%d", P);
#else
  printf("Выполняется другая ветка, P=%d", P);
#endif
  getchar();
  return 0;
}

Результат выполнения
Условная компиляция
Назад

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *