моя контактная информация
Почтамезофия@protonmail.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
в последнем выпуске[Язык C] — Подробное объяснение предварительной обработки (часть 2) В ходе исследования мы подробно представили соответствующие знания о макросах в предварительной обработке. Я думаю, каждый получит много пользы. Не волнуйтесь, давайте продолжим изучать другие знания о предварительной обработке в этом выпуске.
# 运算符
Преобразует аргумент макроса в строковый литерал.Допускается появление в списке подстановок макросов с параметрами.# 运算符
Выполняемую операцию можно понимать как «стрингификацию».
Что это значит?
Давайте сначала сделаем прогноз:
int mian()
{
printf("hello" "worldn");
printf("helloworldn");
return 0;
}
В чем разница между двумя приведенными выше строками кода? Давайте посмотрим вместе:
Как видите, эффект двух струн и одной струны одинаковый.C语言会把两个字符串天然连成一个字符串
, добавление пробела посередине бесполезно.
Сейчас есть такая сцена:
int main()
{
int a = 1;
printf("The value of a is %dn", a);
int b = 20;
printf("The value of b is %dn", b);
float f = 8.5f;
printf("The value of f is %fn", f);
return 0;
}
Мы обнаружили, что логика трех строк кода очень相像
Да, но есть些许不同
。
Поэтому мы подумали, раз уж они так похожи, можем ли мы поместить их封装成一个函数
, для удобства использования?
Но функции не могут выполнять эту функцию.
тогда что нам делать?
Мы можем попытаться использовать макросы, чтобы решить эту проблему.
#define Print(n, format) printf("The value of n is " format "n", n)
int main()
{
int a = 1;
Print(a, "%d");
//printf("The value of a is %dn", a);
int b = 20;
Print(b, "%d");
//printf("The value of b is %dn", b);
float f = 8.5f;
Print(f, "%f");
//printf("The value of f is %fn", f);
return 0;
}
результат операции:
мы обнаруживаем ннн Он не изменился, так как мне его изменить?
Здесь следует использовать наш оператор #:# Преобразует аргумент макроса встроковый литерал,Прямо сейчас ннн становиться «н» «н»“н”
На данный момент мы можем использовать метод сращивания, чтобы стать
#define Print(n, format) printf("The value of " #n " is " format "n", n)
не знаешь?Вы поймете, прочитав объяснение ниже.
##
может находиться в немСимволы с обеих сторон объединены в один символ., что позволяет определять макросы从分离的文本片段创建标识符
。##
называетсянаклеивание меток .Такое соединение должно создавать合法
индикатор, в противном случае результат не определен.
Ранее мы говорили, что для написания функции, которая находит большее значение двух чисел, нам нужно написать разные функции для разных типов данных.
int int_max(int x, int y)
{
return x > y ? x : y;
}
float float_max(float x, float y)
{
return x > y ? x : y;
}
Это неизбежно слишком громоздко, и между этими двумя функциями есть много общего.
Есть ли способ быстро создать такую функцию?как функцияФормаТо же самое, просто примените функцию, и она выйдет
Мы можем написать что-то вроде этого宏
:
#define GENERIC_MAX(type)
type type##_max(type x, type y)
{
return x > y ? x : y;
}
#define GENERIC_MAX(type)
type type##_max(type x, type y)
{
return x > y ? x : y;
}
GENERIC_MAX(int); //相当于定义了一个函数int_max
GENERIC_MAX(float); //相当于定义了一个函数float_max
int main()
{
int r1 = int_max(3, 5);
printf("%dn", r1);
float r2 = float_max(2.3f, 7.6f);
printf("%fn", r2);
return 0;
}
результат операции:
Мы также можем gcc gccгкопий Наблюдайте за предварительно обработанными результатами в среде.i
файл, иметь более интуитивное понимание
Конечно, сгенерированную таким образом функцию тоже неудобно отлаживать.
Тогда здесь ##
Какую роль это играет?
добавлять ##
, компилятор подумает, что это символ
Давайте посмотрим на Кангу##
Эффект:
Вообще говоря, синтаксис использования функций и макросов очень похож, поэтому语言本身没法帮我们区分二者
Одна из наших обычных привычек:
- Назовите макросвсе заглавные буквы
- Имя функцииНе используйте все заглавные буквы
Конечно, эти правила именования не являются абсолютными.
например смещение смещениеоффсет Этот макрос написан строчными буквами.
Примечание: смещение смещениеоффсет Он используется для расчета смещения элементов конструкции относительно начальной позиции конструкции.
# неопределенный неопределенныйтынгеф Инструкции используются дляУдаление определения макроса
Приведенный выше код используется в строке 169.# неопределенный неопределенныйтынгеф Удален макрос MAX.При вызове 168-й линии перед удалением проблем не возникает, но при вызове 170-й линии после удаления будет сообщено об ошибке.
Многие компиляторы C (кроме VS) предоставляют возможность определять символы в командной строке.Используется для запуска процесса компиляции
Например : Эта функция полезна, когда мы хотим скомпилировать разные версии программы на основе одного и того же исходного файла. (Предположим, в программе объявлен массив определенной длины. Если память машины ограничена, нам нужен очень маленький массив, но если память другой машины больше, нужный нам массив может быть больше)
Определение командной строки находится вэтап предварительной обработкиОбработано на этапе предварительной обработки в приведенном выше коде сз сзсз Стоимость уже определена
При компиляции программы очень сложно скомпилировать или отказаться от одного (группы операторов).方便
из.потому что мы можем использоватьдиректива условной компиляции
Инструкция условной компиляции — это этот код我想让你编译就编译,不想让你编译你就不要编译了
. Мы можем задать ему условие. Если условие истинно, этот код будет участвовать в компиляции. Если условие ложно, этот код не будет скомпилирован.
Например:
Было бы жаль удалять часть отладочного кода, но это помешало бы его сохранить, чтобы мы могли его выборочно скомпилировать.
Часто используемые инструкции условной компиляции:
#if 常量表达式
//···
#endif
#if 常量表达式
//···
#elif 常量表达式
//···
#else
//···
#endif
Какое утверждение верно, это утверждение выполняется
#define M 1
int main()
{
#if M == 0
printf("hellon");
#elif M == 1
printf("worldn");
#elif M == 2
printf("csdnn");
#endif
printf("886n");
return 0;
}
#if defined(symbol)
#ifdef symbol
//上面两个的反面
if !defined(symbol)
#ifndef symbol
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif
# include "filename"
Стратегия поиска: первый вКаталог проекта, в котором расположены исходные файлы.Если заголовочный файл не найден, компилятор будет искать заголовочный файл библиотечной функции таким же образом.Стандартный поиск местоположенияГоловной файл
Если вы не можете найти его сноваОшибка компиляции
Linux LinuxЛвux Стандартный путь к файлу заголовка среды (где находится файл заголовка):
/usr/include
Стандартный путь к заголовочному файлу для среды VS:
C:Program Files (x86)Microsoft Visual Studio 12.0VCinclude
//这是VS2013的默认路径
#include <filename.h>
Найдите заголовочный файл и перейдите непосредственно кстандартный путьИдите и ищите, и если вы не можете его найти, вы получите подсказку.Ошибка компиляции。
Означает ли это, что его также можно использовать для файлов библиотеки?“ ”
Форма содержит
ответподтверждатьДа, но поиск осуществляется такменее эффективныконечно, это тоже такнелегко отличитьЭто файл библиотеки или локальный файл?
Изучив предыдущие (компиляцию и компоновку), мы знаем, что включение заголовочных файлов на этапе предварительной обработки является直接将该文件的代码拷贝到包含头文件的地方
Если файл заголовка включен 10 раз, он фактически компилируется 10 раз. Если он включается повторно, нагрузка на компиляцию будет больше.
тест . c тест.cтэст.с
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
int main()
{
return 0;
}
тест . ч тест.чтэст.час
void test();
struct Stu
{
int id;
char name[20];
};
Но в проекте файл неизбежно будет включаться несколько раз, как же решить эту проблему?
Отвечать:условная компиляция
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif
Как понять это?
- Следует ли компилировать заголовочный файл при первом включении?судите первым
- Символ __TEST_H__ не являетсяне определен,хотетькомпилировать
- Сразу после этогоОпределите символ __TEST_H__
- Затем снова включите файл заголовка и найдите __TEST_H__.был определен,больше никогдаДля файла заголовка, включенного позже, выполнитекомпилировать
Однако описанный выше способ записи более затруднителен. Существует и другой способ записи:
#pragma once
Эффект тот же, что и вышеописанный метод.
Это позволяет избежать повторного внедрения заголовочных файлов.
#error
#pragma
#line
···
#pragma pack()//在结构体部分介绍
Заинтересованные друзья могут прочитать «Углубленная анатомия языка C»
Хорошо, это все знания о предварительной обработке в этом выпуске. Надеюсь, этот блог может быть вам полезен. В то же время, пожалуйста, поправьте меня, если есть какие-либо ошибки, и давайте вместе добиваться прогресса на пути изучения языка C!