C语言-预处理

预编译-预处理

预处理不占运行时间、代码不能直接换行,需要用 / 换行

1. #include< > 和 #include" " : 

        `#include< >` 和 `#include" "` 的区别主要在于它们查找头文件的目录不同

        `#include< >` 用于包含系统头文件,编译器直接从系统类库目录里查找头文件,例如         `#include<stdio.h>`。

        而 `#include" "` 用于包含自定义头文件,它默认从项目当前目录查找头文件,如果在项目当          前目录下查找失败,再从项目配置的头文件引用目录查找头文件,如果项目配置的头文件引          用目录中仍然查找失败,再从系统类库目录里查找头文件。

因此,对于系统库头文件,`#include< >` 和 `#include" "` 的作用一样,都能查找成功。但是,虽然 `#include" "` 的查找范围更广,但是这并不意味着不论是系统头文件还是自定义头文件一律用 `#include" "` 包含。因为 `#include" "` 的查找顺序存在先后关系,如果项目当前目录或者引用目录下存在和系统目录下重名的头文件,那么编译器在当前目录或者引用目录查找成功后将不会继续查找,所以存在头文件覆盖的问题。另外,对于系统头文件,用 `#include< >` 包含,查找时一步到位,程序编译时的效率也会相对更高。

 

2. 宏定义#define:核心是替换,所以加括号很关键

一般宏定义用大写字母,便于区分,(用小写也不会出错)

宏定义是通过 #define 指令来实现的。它可以用来定义常量、表达式或者函数。可在本文件里面使用也可以在其他文件里面使用(注意要include)。

 #define和typedef、const的区别

/*
`#define` 和 `typedef` 都可以用来定义常量或者为代码创建别名,但它们之间存在
一些不同之处。

相同点:
- 它们都可以用来定义常量或者为代码创建别名,从而提高程序的可读性和可维护性。

不同点:
- `#define` 是一个预处理器指令,用于定义宏。它可以用来定义常量、函数、条件编译等。
在编译时,宏定义会被替换为其定义的内容,从而实现代码的重用和简化。
- `typedef` 是一个关键字,用于为一个已有的类型定义一个新的名称。它可以用来为结构体、
枚举、联合等类型定义别名。与 `#define` 不同,`typedef` 是在编译时处理的,它不会被
替换为其他内容。

在这个例子中,我们使用 `#define` 指令定义了一个宏 `PI`,并将其值指定为 `3.14159`。
我们还使用 `typedef` 关键字为类型 `int` 定义了一个新的名称 `Length`。在程序中,
我们使用这些宏和类型别名来输出结果。
 
*/


#include <stdio.h>
#define PI 3.14159
typedef int Length;
int main()
{
    printf("PI: %f\n", PI);
    Length x = 10;
    printf("Length: %d\n", x);
    return 0;
}
/* 
`#define` 和 `const` 都可以用来定义常量,但它们之间存在一些不同之处。

相同点:
- 它们都可以用来定义常量(不可更改),从而提高程序的可读性和可维护性。

不同点:
- `#define` 是一个预处理器指令,用于定义宏。它可以用来定义常量、函数、条件编译等。
在编译时,宏定义会被替换为其定义的内容,从而实现代码的重用和简化。
- `const` 是一个关键字,用于定义常量。它可以用来限制变量的值不能被修改。与 `#define` 不同,`const` 定义的常量具有类型信息,可以进行类型检查。

下面是一个简单的例子,演示了如何使用 `#define` 和 `const`:

在这个例子中,我们使用 `#define` 指令定义了一个宏 `PI`,并将其值指定为 `3.14159`。
我们还使用 `const` 关键字定义了一个常量 `x`,并将其值指定为 `10`。在程序中,我们
使用这些宏和常量来输出结果。
*/

#include <stdio.h>
#define PI 3.14159
int main()
{
    printf("PI: %f\n", PI);
    const int x = 10;
    printf("x: %d\n", x);
    return 0;
}
 
宏定义是在预处理阶段进行的,它会被预处理器替换为其定义的内容。因此,宏定义本身并不占用
内存空间,只有它被替换后的内容才会占用内存空间。
const 是一个关键字,用于定义常量。与普通变量一样,const 定义的常量也会占用内存空间。
它们通常存储在程序的数据段(data segment)中。

 

下面是其他的一些例子:

定义100年是多少秒

#include <stdio.h>
#define SECONDS_PER_MINUTE 60
#define MINUTES_PER_HOUR 60
#define HOURS_PER_DAY 24
#define DAYS_PER_YEAR 365
#define YEARS 100

int main()
{
    long long seconds = (long long)SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * DAYS_PER_YEAR * YEARS;
    printf("There are %lld seconds in %d years.\n", seconds, YEARS);
    return 0;
}


#include <stdio.h>
#define SECONDS_IN_100_YEARS (60 * 60 * 24 * 365 * 100U)
#define SECONDS_IN_100_YEARS (60 * 60 * 24 * 365 * 100L) 
//U或者L表示无符号数和长整数,防止数据溢出
int main()
{
    printf("There are %lld seconds in 100 years.\n", (long long)SECONDS_IN_100_YEARS);
    return 0;
}

定义一个数

#include <stdio.h>
#define PI 3.14 // 定义一个宏,表示圆周率

int main() {
    double radius = 2.0;
    double area = PI * radius * radius; // 使用宏计算圆的面积
    printf("The area of the circle is: %f\n", area);
    return 0;
}

 用来计算两个数中的最大值。在程序中,使用printf函数输出了两个数x和y中的最大值。

// 在这个例子中,定义了一个带参数的宏MAX(a, b),
//用来计算两个数中的最大值。
//在程序中,使用printf函数输出了两个数x和y中的最大值。

#include <stdio.h>

#define MAX(a, b) ((a) > (b) ? (a) : (b))

int main()
{
    int x = 3, y = 5;
    printf("max = %d\n", MAX(x, y));
    return 0;
}

 宏定义实现交换数据

//宏定义实现交换数据

#include <stdio.h>
#define SWAP(a, b) do {typeof(a) temp = a; a = b; b = temp;} while(0)
int main()
{
    int x = 3, y = 5;
    printf("Before swap: x = %d, y = %d\n", x, y);
    SWAP(x, y);
    printf("After swap: x = %d, y = %d\n", x, y);
    return 0;
}

#include <stdio.h>
#define SWAP(a, b,type) do { temp = a; a = b; b = temp;} while(0)
int main()
{
    int x = 3, y = 5;
    printf("Before swap: x = %d, y = %d\n", x, y);
    SWAP(x, y, int);
    printf("After swap: x = %d, y = %d\n", x, y);
    return 0;
}

宏定义找数组的数据个数

/*
定义了一个宏ARRAY_SIZE(arr),用来计算数组的大小。使用printf函数输出了数组arr的大小。
这个宏定义实际上是通过计算数组的总字节数除以单个元素的字节数来实现的。
由于sizeof(arr)返回的是数组arr的总字节数,而sizeof(arr[0])返回的是单个元素的字节数,
因此sizeof(arr) / sizeof(arr[0])就能够计算出数组的大小。
*/


#include <stdio.h>
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
int main()
{
    int arr[] = {1, 2, 3, 4, 5};
    printf("Array size: %lu\n", ARRAY_SIZE(arr));
    return 0;
}

可以使用宏定义和函数结合的方式来实现字符串转数字,字符串变成全大写,字符串变成全小写,字符串大小写反转等功能。

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

#define TOUPPER(str) do {for (int i = 0; str[i]; i++) str[i] = toupper(str[i]);} while(0)
#define TOLOWER(str) do {for (int i = 0; str[i]; i++) str[i] = tolower(str[i]);} while(0)

//注意换行不能直接换行
#define REVCASE(str) do {for (int i = 0; str[i]; i++) \
         str[i] = islower(str[i]) ? toupper(str[i]) : olower(str[i]);} while(0)

int main()
{
    char str[] = "Hello, World!";
    int num = atoi("12345");

    printf("Original string: %s\n", str);
    printf("Number: %d\n", num);

    // Convert to uppercase
    TOUPPER(str);
    printf("Uppercase: %s\n", str);

    // Convert to lowercase
    TOLOWER(str);
    printf("Lowercase: %s\n", str);

    // Reverse case
    REVCASE(str);
    printf("Reverse case: %s\n", str);

    return 0;
}

其他常见预处理命令
#undef:取消已定义的宏。

#ifdef:如果宏已经定义,则返回真。

#endif:结束一个 #if……#else 条件编译块。

#include <stdio.h>
#define YEARS_OLD 12
#undef YEARS_OLD
int main()
{
#ifdef YEARS_OLD
    printf("TechOnTheNet is over %d years old.\n", YEARS_OLD);
#endif
    printf("TechOnTheNet is a great resource.\n");
    return 0;
}


#ifndef:如果宏没有定义,则返回真。

`#ifndef` 是一个预处理器指令,用于检查一个宏是否未被定义。如果宏未被定义,则编译器会编译 `#ifndef` 和 `#endif` 之间的代码。这个指令通常用于避免头文件被重复包含。

下面是一个简单的例子,演示了如何使用 `#ifndef` 指令:

```c
// myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H

// Header file content here

#endif
```

在这个例子中,我们使用了 `#ifndef` 指令来检查宏 `MYHEADER_H` 是否未被定义。
如果宏未被定义,则编译器会编译 `#ifndef` 和 `#endif` 之间的代码,并定义宏 `MYHEADER_H`。这样,当我们在其他文件中再次包含这个头文件时,由于宏 `MYHEADER_H` 已经被定义,因此 `#ifndef` 和 `#endif` 之间的代码不会再次被编译,从而避免了头文件被重复包含。

 #if:如果给定条件为真,则编译下面代码。
#else:#if 的替代方案。
#elif:如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码。
#endif:结束一个 #if……#else 条件编译块。

#include <stdio.h>
#define LEVEL 2
int main()
{
#if LEVEL == 0
    printf("Level is 0\n");
#elif LEVEL == 1
    printf("Level is 1\n");
#elif LEVEL == 2
    printf("Level is 2\n");
#else
    printf("Level is unknown\n");
#endif
    return 0;
}

#error:当遇到标准错误时,输出错误消息。
#pragma:使用标准化方法,向编译器发布特殊的命令到编译器中。

`#error` 指令用于在编译时生成错误消息。当编译器遇到 `#error` 指令时,它会停止编译并输出指定的错误消息。这个指令通常用于检查程序中的某些条件,如果条件不满足,则生成错误消息并停止编译。下面是一个简单的例子:

```c
#include <stdio.h>
#if __STDC_VERSION__ < 199901L
#error This program requires C99 or later
#endif
int main(void)
{
    printf("Hello, world!\n");
    return 0;
}
```

在这个例子中,我们使用 `#if` 和 `#error` 指令来检查当前编译器是否支持 C99 或更高版本的标准。如果不支持,则生成错误消息并停止编译。

`#pragma` 指令用于向编译器提供特定的信息或指令。这个指令通常用于实现与编译器或平台相关的功能。不同的编译器可能支持不同的 `#pragma` 指令,因此使用这个指令时需要注意兼容性问题。下面是一个简单的例子:

```c
#include <stdio.h>
#pragma GCC optimize("Ofast")
int main(void)
{
    printf("Hello, world!\n");
    return 0;
}
```

在这个例子中,我们使用 `#pragma GCC optimize("Ofast")` 指令来告诉 GCC 编译器使用最高级别的优化来编译程序。


 

猜你喜欢

转载自blog.csdn.net/qq_55906687/article/details/131114924
今日推荐