Обмен технологиями

[Язык C] — Подробное объяснение предварительной обработки (часть 2)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Предисловие

в последнем выпуске[Язык C] — Подробное объяснение предварительной обработки (часть 2) В ходе исследования мы подробно представили соответствующие знания о макросах в предварительной обработке. Я думаю, каждый получит много пользы. Не волнуйтесь, давайте продолжим изучать другие знания о предварительной обработке в этом выпуске.

7. # и ##

7.1 # Оператор

  • # 运算符 Преобразует аргумент макроса в строковый литерал.Допускается появление в списке подстановок макросов с параметрами.
  • # 运算符Выполняемую операцию можно понимать как «стрингификацию».

Что это значит?
Давайте сначала сделаем прогноз:

int mian()
{
	printf("hello"   "worldn");
	printf("helloworldn");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

В чем разница между двумя приведенными выше строками кода? Давайте посмотрим вместе:

Вставьте сюда описание изображения

Как видите, эффект двух струн и одной струны одинаковый.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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Мы обнаружили, что логика трех строк кода очень相像Да, но есть些许不同
  
Поэтому мы подумали, раз уж они так похожи, можем ли мы поместить их封装成一个函数, для удобства использования?
Но функции не могут выполнять эту функцию.
тогда что нам делать?
Мы можем попытаться использовать макросы, чтобы решить эту проблему.

#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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

  
результат операции:

Вставьте сюда описание изображения

мы обнаруживаем ннн Он не изменился, так как мне его изменить?
Здесь следует использовать наш оператор #:# Преобразует аргумент макроса встроковый литерал,Прямо сейчас ннн становиться «н» «н»н

На данный момент мы можем использовать метод сращивания, чтобы стать

#define Print(n, format) printf("The value of " #n " is " format "n", n)
  • 1

  
не знаешь?Вы поймете, прочитав объяснение ниже.
Вставьте сюда описание изображения

  

7.2 Оператор ##

  ## может находиться в немСимволы с обеих сторон объединены в один символ., что позволяет определять макросы从分离的文本片段创建标识符## называетсянаклеивание меток .Такое соединение должно создавать合法индикатор, в противном случае результат не определен.
  
Ранее мы говорили, что для написания функции, которая находит большее значение двух чисел, нам нужно написать разные функции для разных типов данных.

int int_max(int x, int y)
{
	return x > y ? x : y;
}
float float_max(float x, float y)
{
	return x > y ? x : y;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

  

Вставьте сюда описание изображения

Это неизбежно слишком громоздко, и между этими двумя функциями есть много общего.
Есть ли способ быстро создать такую ​​функцию?как функцияФормаТо же самое, просто примените функцию, и она выйдет
  
Мы можем написать что-то вроде этого

#define GENERIC_MAX(type) 
		type type##_max(type x, type y)
		{
			return x > y ? x : y;
		}
  • 1
  • 2
  • 3
  • 4
  • 5

  

#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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

  
результат операции:

Вставьте сюда описание изображения

  
Мы также можем gcc gccгкопий Наблюдайте за предварительно обработанными результатами в среде.i файл, иметь более интуитивное понимание

Вставьте сюда описание изображения

Конечно, сгенерированную таким образом функцию тоже неудобно отлаживать.

Тогда здесь ## Какую роль это играет?

добавлять ##, компилятор подумает, что это символ
Давайте посмотрим на Кангу## Эффект:

Вставьте сюда описание изображения

  

8. Соглашение об именах

Вообще говоря, синтаксис использования функций и макросов очень похож, поэтому语言本身没法帮我们区分二者
  
Одна из наших обычных привычек:

  • Назовите макросвсе заглавные буквы
  • Имя функцииНе используйте все заглавные буквы

Конечно, эти правила именования не являются абсолютными.
например смещение смещениеоффсет Этот макрос написан строчными буквами.
  
Примечание смещение смещениеоффсет Он используется для расчета смещения элементов конструкции относительно начальной позиции конструкции.
  

Девять,# неопределенный неопределенныйтынгеф

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

Вставьте сюда описание изображения
Приведенный выше код используется в строке 169.# неопределенный неопределенныйтынгеф Удален макрос MAX.При вызове 168-й линии перед удалением проблем не возникает, но при вызове 170-й линии после удаления будет сообщено об ошибке.

  

10. Определение командной строки

Многие компиляторы C (кроме VS) предоставляют возможность определять символы в командной строке.Используется для запуска процесса компиляции
  
  Например : Эта функция полезна, когда мы хотим скомпилировать разные версии программы на основе одного и того же исходного файла. (Предположим, в программе объявлен массив определенной длины. Если память машины ограничена, нам нужен очень маленький массив, но если память другой машины больше, нужный нам массив может быть больше)

Вставьте сюда описание изображения

Вставьте сюда описание изображения

Определение командной строки находится вэтап предварительной обработкиОбработано на этапе предварительной обработки в приведенном выше коде сз сзсз Стоимость уже определена

  

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

При компиляции программы очень сложно скомпилировать или отказаться от одного (группы операторов).方便 из.потому что мы можем использоватьдиректива условной компиляции
  
Инструкция условной компиляции — это этот код我想让你编译就编译,不想让你编译你就不要编译了 . Мы можем задать ему условие. Если условие истинно, этот код будет участвовать в компиляции. Если условие ложно, этот код не будет скомпилирован.
  
Например:
Было бы жаль удалять часть отладочного кода, но это помешало бы его сохранить, чтобы мы могли его выборочно скомпилировать.
  

Вставьте сюда описание изображения

  
Часто используемые инструкции условной компиляции:

11.1. Одноветвевая условная компиляция.

#if 常量表达式
    //···
#endif
  • 1
  • 2
  • 3

Вставьте сюда описание изображения

  

11.2. Многоветвевая условная компиляция.

#if  常量表达式
	//···
#elif  常量表达式
	//···
#else
	//···
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Какое утверждение верно, это утверждение выполняется

#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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

  

11.3. Определить, определено ли оно

#if defined(symbol)
#ifdef symbol

//上面两个的反面
if !defined(symbol)
#ifndef symbol
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Вставьте сюда описание изображения

  

11.4. Вложенные инструкции.

#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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

  

12. Включение заголовочных файлов

12.1 Как включаются заголовочные файлы

(1) Как включаются локальные файлы

# include "filename"
  • 1

Стратегия поиска: первый вКаталог проекта, в котором расположены исходные файлы.Если заголовочный файл не найден, компилятор будет искать заголовочный файл библиотечной функции таким же образом.Стандартный поиск местоположенияГоловной файл
Если вы не можете найти его сноваОшибка компиляции
  
Linux LinuxЛвux Стандартный путь к файлу заголовка среды (где находится файл заголовка):

 /usr/include
  • 1

Стандартный путь к заголовочному файлу для среды VS:

C:Program Files (x86)Microsoft Visual Studio 12.0VCinclude
//这是VS2013的默认路径
  • 1
  • 2

  

(2) Файл библиотеки содержит

#include <filename.h>
  • 1

Найдите заголовочный файл и перейдите непосредственно кстандартный путьИдите и ищите, и если вы не можете его найти, вы получите подсказку.Ошибка компиляции
  
Означает ли это, что его также можно использовать для файлов библиотеки?“ ” Форма содержит
ответподтверждатьДа, но поиск осуществляется такменее эффективныконечно, это тоже такнелегко отличитьЭто файл библиотеки или локальный файл?
  

12.2 Включение вложенных файлов

Изучив предыдущие (компиляцию и компоновку), мы знаем, что включение заголовочных файлов на этапе предварительной обработки является直接将该文件的代码拷贝到包含头文件的地方
  
Если файл заголовка включен 10 раз, он фактически компилируется 10 раз. Если он включается повторно, нагрузка на компиляцию будет больше.

тест . c тест.cтэст.с

#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
int main()
{
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

тест . ч тест.чтэст.час

void test();
struct Stu
{
int id;
char name[20];
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Но в проекте файл неизбежно будет включаться несколько раз, как же решить эту проблему?
Отвечать:условная компиляция

#ifndef __TEST_H__
#define __TEST_H__
		//头文件的内容
#endif
  • 1
  • 2
  • 3
  • 4

  
Как понять это?

  • Следует ли компилировать заголовочный файл при первом включении?судите первым
  • Символ __TEST_H__ не являетсяне определенхотетькомпилировать
  • Сразу после этогоОпределите символ __TEST_H__
  • Затем снова включите файл заголовка и найдите __TEST_H__.был определенбольше никогдаДля файла заголовка, включенного позже, выполнитекомпилировать

Однако описанный выше способ записи более затруднителен. Существует и другой способ записи:

#pragma once
  • 1

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

13. Другие инструкции по предварительной обработке

#error
#pragma
#line
···
#pragma pack()//在结构体部分介绍
  • 1
  • 2
  • 3
  • 4
  • 5

Заинтересованные друзья могут прочитать «Углубленная анатомия языка C»
  
  
  
  
  


   Хорошо, это все знания о предварительной обработке в этом выпуске. Надеюсь, этот блог может быть вам полезен. В то же время, пожалуйста, поправьте меня, если есть какие-либо ошибки, и давайте вместе добиваться прогресса на пути изучения языка C!