我是靠谱客的博主 专注黑猫,最近开发中收集的这篇文章主要介绍关于C之预处理指令#define宏及#include,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

#define:

对于预处理指令#define,是程序已经准备好进入预处理阶段,预处理器查找一行中以#号开始的预处理指令。

预处理器不做计算,不对表达式求值,它只进行替换。一般而言,预处理器发现程序中的宏后,会用宏等价的替换文本进行替换。如果替换的字符串中还包含宏,则继续替换这些宏。

如果这样定义一条预处理指令:

#define FOUR 2+2

然后这样调用:

int hex = FOUR * FOUR;

打印hex的值为8而不是想要的16。因为在预处理阶段只进行替换操作,所以hex=2+2*2+2=8。所以在声明预处理指令时要多加括号,比如下面的定义就能解决问题:

#define FOUR (2+2)

只进行替换,所以下面两条定义是不同的:

#define SIX 2 * 3
#define SIX 2*3

因为第一条定义里面包含两个空格。

类函数宏定义:可以在#define中使用参数,当作函数来用。其实,在stdio.h标准库中的getc()函数就是类函数宏。

#define SQUARE(X) X*X
可以这样使用:z = SQUARE(2);

就像之前所述,要多加括号,特别是对表达式或每个变量。比如有如下的运用宏:

int x = 5;
int y = SQUARE(x+2);

y=17而不是49,替换一下试试便知:y = 5+2*5+2=17。所以要这样定义宏(多加括号):

#define SQUARE(X) ((X)*(X))

同样对于定义一个函数宏处理z=(x+y)/2,就要像下面这样定义,否则也会在特定条件下出现计算错误:

#define AVERAGE(X,Y) (((X)+(Y))/2)

看到上面对每个变量都加了(),(X)+(Y)加了(),同时最外层也加了(),保证不会在替换时出现任何的计算错误。

#define PSQR(X) printf("The square of X is %d.n", ((X)*(X)));

PSQR(8);
输出为:
The square of X is 64.

①记号转字符串:#运算符

可以在字符串中包含宏参数,这时就要用到预处理运算符#:把记号转换成字符串。如果x是宏形参,#x就转换为字符串"x"的形参名。这个过程称为字符串化(stringizing)。

#include <stdio.h>
#define PSQR(x) printf("The square of " #x " is %d.n",((x)*(x)))
int main(void) {
    int y = 5;
    PSQR(y);
    PSQR(2 + 4);
    return 0;
}
The square of y is 25.
The square of 2 + 4 is 36.

②预处理器黏合剂:##运算符

与#运算符类似,##运算符可用于类函数宏的替换部分。而且,##还可用于对象宏的替换部分。##运算符把两个记号组合成一个记号。

#define XNAME(n) x ## n

宏XNAME(4)将展开为:x4
是不是黏合起来了:x ## n -> xn
#include <stdio.h>
#define XNAME(n) x ## n
#define PRINT_XN(n) printf("x" #n " = %dn", x ## n);
int main(void) {
    int XNAME(1) = 14;  // 变成 int x1 = 14;
    int XNAME(2) = 20;  // 变成 int x2 = 20;
    int x3 = 30;
    PRINT_XN(1);      // 变成 printf("x1 = %dn", x1);
    PRINT_XN(2);      // 变成 printf("x2 = %dn", x2);
    PRINT_XN(3);      // 变成 printf("x3 = %dn", x3);
    return 0;
}
x1 = 14
x2 = 20
x3 = 30

变参宏:..._ _VA_ARGS_ _

#define PR(...) printf(_ _VA_ARGS_ _)
PR("Howdy");
PR("weight = %d, shipping = $%.2fn", wt, sp);

展开后的代码是:

printf("Howdy");
printf("weight = %d, shipping = $%.2fn", wt, sp);

宏可以完成函数的功能,那么宏和函数如何选择?

宏和函数的选择实际上是时间和空间的权衡宏生成内联代码,即在程序中生成语句。如果调用20次宏,即在程序中插入20行代码。如果调用函数20次,程序中只有一份函数语句的副本,所以节省了空间。然而另一方面,程序的控制必须跳转至函数内,随后再返回主调程序,这显然比内联代码花费更多的时间。

宏的一个优点是,不用担心变量类型(这是因为宏处理的是字符串,而不是实际的值)。因此,只要能用int或float类型都可以使用SQUARE(x)宏。

对于简单的函数,程序员通常使用宏,如下所示:

#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
#define ABS(X) ((X) < 0 ? -(X) : (X))
#define ISSIGN(X) ((X) == '+' || (X) == '-' ? 1 : 0)

如果打算使用宏来加快程序的运行速度,那么首先要确定使用宏和使用函数是否会导致较大差异。在程序中只使用一次的宏无法明显减少程序的运行时间。在嵌套循环中使用宏更有助于提高效率。许多系统提供程序分析器以帮助程序员压缩程序中最耗时的部分。

#include:

当预处理器发现#include 指令时,会查看后面的文件名并把文件的内容包含到当前文件中,即替换源文件中的#include指令。这相当于把被包含文件的全部内容输入到源文件#include指令所在的位置。

#include <stdio.h>      ←查找系统目录
#include "hot.h"       ←查找当前工作目录
#include "/usr/biff/p.h"  ←查找/usr/biff目录

头文件.h可以去用作类似JAVA的接口的函数原型,然后在另一个.c文件中实现这些“接口”。

“接口”——头文件:

// 头文件:names_st.h
#include <string.h>
#define SLEN 32 // 常量
struct names_st { // 结构声明
    char first[SLEN];
    char last[SLEN];
};
typedef struct names_st names; // 类型定义
// 函数原型
void get_names(names *);
void show_names(const names *);
char * s_gets(char * st, int n);

“实现”——.c文件:

// names_st.c -- 定义 names_st.h中的函数
#include <stdio.h>
#include "names_st.h"  // 包含头文件
// 函数定义
void get_names(names * pn) {
    printf("Please enter your first name: ");
    s_gets(pn->first, SLEN);
    printf("Please enter your last name: ");
    s_gets(pn->last, SLEN);
}
void show_names(const names * pn) {
    printf("%s %s", pn->first, pn->last);
}
char * s_gets(char * st, int n) {
    char * ret_val;
    char * find;
    ret_val = fgets(st, n, stdin);
    if (ret_val) {
        find = strchr(st, 'n');  // 查找换行符
        if (find)          // 如果地址不是NULL,
            *find = '';     // 在此处放置一个空字符
        else
            while (getchar() != 'n')
                continue;   // 处理输入行中的剩余字符
    }
    return ret_val;
}

有main()入口的执行程序:

// useheader.c
#include <stdio.h>
#include "names_st.h"
int main(void) {
    names candidate;
    get_names(&candidate);
    printf("Let's welcome ");
    show_names(&candidate);
    printf(" to this program!n");
    return 0;
}
Please enter your first name: |Ian
Please enter your last name: |Smersh
Let's welcome Ian Smersh to this program!

必须编译和链接names_st.c和useheader.c源代码文件。声明和指令放在nems_st.h头文件中,函数定义放在names_st.c源代码文件中。

最后

以上就是专注黑猫为你收集整理的关于C之预处理指令#define宏及#include的全部内容,希望文章能够帮你解决关于C之预处理指令#define宏及#include所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(70)

评论列表共有 0 条评论

立即
投稿
返回
顶部