C语言之函数式宏

目录

函数和数据类型

函数式宏

函数和函数式宏

函数式宏和对象式宏

不带参数的函数式宏

函数式宏和逗号运算符


函数式宏和函数类似并且比函数更加灵活,下面我们就来学习函数式宏的相关内容。

函数和数据类型

我们来编写一个程序,它能计算出所读取数值的平方,并将结果显示出来,我们先来编写适用于int类型和double类型的函数。

/*整数和浮点数的平方*/
#include<stdio.h>

/*计算int型数的平方值*/
int sqr_int(int x)
{
	return x * x;
}
/*计算double型数的平方值*/
double sqr_double(double x)
{
	return x * x;
}
int main()
{
	int x;
	double n;
	
	printf("请输入一个整数:");
	scanf("%d", &x);
	printf("该整数的平方是%d\n", sqr_int(x));
	
	printf("请输入一个整数:");
	scanf("%lf", &n);
	printf("该浮点数的平方是%f\n", sqr_double(n));
	
	return 0;
}

如果我们又想计算其他数据类型的平方呢?比如计算long型数的平方,就得创建出一个sqr_long的函数,如果接二连三的写出这种功能相近,名称相似的函数,程序就会充斥着这种似是而非的函数

下面我们来学习解决办法:函数式宏


函数式宏

函数式宏(function—like macro)较之对象式宏可以进行更为复杂的代换。

#include<stdio.h>

#define sqr(x) ((x) * (x))//计算x平方的函数式宏

int main()
{
	int x;
	double n;
	
	printf("请输入一个整数:");
	scanf("%d", &x);
	printf("该整数的平方是%d\n", sqr(x));
	
	printf("请输入一个整数:");
	scanf("%lf", &n);
	printf("该整数的平方是%f\n", sqr(n));
	
	return 0;
}
//#define 给出的命令如下:

下文中若出现sqr(☺)形式的表达式就将其展开为:

((☺)* (☺))

因此,在调用printf函数时就可以像下面一样展开并执行:

printf("该数的平方是%d\n", ((x) * (x)));

函数和函数式宏

函数和函数式宏的调用看上去相同,但也有以下几个区别:

■函数式宏sqr是在编译时展开并填入程序的,因此只要能使用双目运算符 * 进行乘法运算的数据类型,都能使用函数式宏。

■而函数定义则需要每个形参都定义各自的数据类型,返回值类型也都只有一种,就这点而言,函数较为严格。


 ■函数为我们默默无闻的进行一系列的复杂处理:

☞参数传递(将实参复制给形参)

☞函数调用和函数的返回操作(函数流程的控制)

☞返回值的传递

而函数式宏所做的工作只是宏展开和填入程序,并不执行上述步骤。


 ■根据以上特征,函数式宏或许能使程序的运行速度稍微提高,但是程序自身可能会变得臃肿(如果宏展开式极为复杂,那么在使用到它的所有地方都会填入这些复杂的表达式)。


 ■函数式宏在使用时必须小心,比如sqr((a++)* (a++)),每次展开a的值都会递增两次。在不经意间表达式被执行了两次,导致程序出现了意料之外的结果,我们称这种情况为宏的副作用

注意:在定义和使用函数式宏的时候,要仔细考虑是否会使用产生副作用。

☞将函数版的sqr_int作为sqr_int(a++)调用时,a的值不会调用两次,如果是宏版,则要将sqr(a)和a++分开。


函数式宏和对象式宏

如果在宏名称sqr和紧邻其后的( 之间插入空格,进行如下宏定义:

#define sqr (x) ((x) * (x))

 则sqr会被编译器当做对象式宏,程序中的sqr都会被替换为(x) ((x) * (x))

因此我们在定义函数式宏时,不要在宏名称和(之间插入空格。

 以下是计算二值之和的函数式宏:

#define sum_of(x,y) x + y

我们使用以下语句来调用这个函数式宏

z = sum_of(a, b) * sum_of(c, d);

让我们来看看宏展开式是否符合我们的意愿呢?

z = a + b * c + d;

很显然结果不尽人意,保险起见我们在宏定义时将每个参数以及整个表达式都用括号括起来

#define sum_of(x,y) ((x) + (y))

这样表达式就能正确展开了:

z = (a + b) * (c + d);

不带参数的函数式宏

函数式宏也可以像函数那样进行不带参数的定义,例如下面这个响铃的宏alert()

#define alert() (putchar('\n'))

函数式宏和逗号运算符

下面我们来介绍函数式宏的一个重要使用方法,我们先来看下错误示范:

#include<stdio.h>

#define puts_alert(str) {putchar('\a');  puts(str)}

int main()
{
	int n;
	
	printf("请输入一个整数:");
	scanf("%d", &n);
	
	if(n)
		puts_alert("这个整数不是0");
	else
		puts_alert("这个整数是0");
	
	return 0;
}//本程序在编译时报错,因此不能运行

让我们来分析下原因:

函数式宏put_alert的定义是在puts函数显示字符串str时响铃,只不过这个程序在编译时出错,不能运行。

main函数的if语句展开后如下图所示,if语句会在第一个复合语句{ }处结束,这时因为末尾的 ;会被视为空语句,因此编译器会认为“没有if,为何出现了else”(即使这样,也不能去掉{}否则会出现别的错误)

下面就需要讲到逗号运算符了:

逗号运算符
a,b                                  按顺序判断a和b,整个表达式最终生成b的判断结果
#include<stdio.h>

#define puts_alert(str)  (putchar('\a'),  puts(str))

int main()
{
	int n;
	
	printf("请输入一个整数:");
	scanf("%d", &n);
	
	if(n)
		puts_alert("这个整数不是0");
	else
		puts_alert("这个整数是0");
	
	return 0;
}

一般由逗号运算符连接的两个表达式“a, b”在语法上可以视为一个表达式(其实不仅限于逗号运算符,只要是由运算符连接的多个表达式,例如“a + b”,都可以视为一个表达式),因此在本程序中if语句在语法上就是正确的。

 如果宏定义中要代换两个以上的表达式,则使用逗号运算符连接,使其在语法上构成一个表达式。

 我们对逗号运算符是怎么执行的来具体说明下:

对于逗号运算符“a, b”,会按顺序判断表达式a和b。对左侧的a仅进行判断,判断结果会被省略去,对于右侧的表达式b进行判断所得到的类型和值,就是逗号表达式“a, b”的类型和值。

例如:i=1,j=5

运行x = (++i, ++j),则i和j的值都会递增,但是递增后j的值会被赋值给x。


感觉基本数据类型中的整型和字符型、浮点型的知识点好复杂啊!!!

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在C语言中,程序员可以定义不同类型的函数,这些函数可以接受不同类型的参数。在函数定义中,程序员可以使用参数列表来声明函数接受哪些参数。参数列表指定参数的类型、顺序和名称。 在调用函数时,程序员必须向函数传递与其参数类型匹配的值。如果传递的值与参数类型不匹配,编译器将会发出警告或错误。 使用函数参数可以让程序员编写更通用、可重用的代码。例如,如果程序员需要编写一个函数来计算两个整数的和,可以定义一个接受两个int类型参数的函数。如果程序员需要计算两个浮点数的和,可以定义一个接受两个float类型参数的函数。 程序员也可以使用指针作为函数参数。指针是一个变量,它存储了另一个变量的地址。通过传递指向变量的指针作为函数参数,函数可以修改该变量的值。 因此,通过选择不同类型的参数,程序员可以根据需要定义不同的函数。这样可以使函数更通用、更灵活,并且可以减少代码的冗余。 ### 回答2: 在C语言中,可以使用定义来根据参数选择不同的定义。可以通过定义中的条件判断语句来实现这一功能。 首先,在定义中使用条件判断语句,可以使用预处理指令#ifdef和#endif来选择性地定义某个定义。例如: #define PARAMETER 1 #ifdef PARAMETER // 定义参数为1时的定义 #define MACRO_DEFINITION_1 #else // 定义参数为其他值时的定义 #define MACRO_DEFINITION_2 #endif 在上述代码中,如果参数PARAMETER的值为1,则会定义MACRO_DEFINITION_1定义;如果参数PARAMETER的值为其他值,则会定义MACRO_DEFINITION_2定义。 其次,要根据参数选择不同的定义,可以利用函数的特性,使用的参数值来进行选择。例如: #define SELECT_MACRO_DEFINITION(PARAMETER) \ (PARAMETER == 0 ? MACRO_DEFINITION_1 : MACRO_DEFINITION_2) 在上述代码中,通过函数SELECT_MACRO_DEFINITION来根据参数PARAMETER的值选择不同的定义。如果参数PARAMETER的值为0,则选择MACRO_DEFINITION_1定义;如果参数PARAMETER的值为其他值,则选择MACRO_DEFINITION_2定义。 综上所述,通过使用条件判断语句和函数,可以根据参数选择不同的定义。这种方法可以在编译时根据参数的不同选择实现不同的定义,避免了运行时的性能开销。 ### 回答3: 在C语言中,可以通过使用定义来根据参数选择不同的定义。借助预处理指令和的特性,我们可以在编译阶段根据参数的不同选择不同的定义。 首先,可以使用条件编译指令`#ifdef`和`#endif`来实现根据参数的选择。假设有两个定义`#define OPTION_A`和`#define OPTION_B`,我们可以通过如下定义实现根据参数选择不同的定义: ``` #define OPTION_A // 或者 // #define OPTION_B ``` 之后,在代码的其他部分,可以使用条件编译指令`#ifdef`和`#endif`来根据参数的选择执行相应的代码。例如: ``` #ifdef OPTION_A // 在此处编写针对OPTION_A的代码 // 例如 printf("使用了OPTION_A定义\n"); #endif #ifdef OPTION_B // 在此处编写针对OPTION_B的代码 // 例如 printf("使用了OPTION_B定义\n"); #endif ``` 当参数中定义了`OPTION_A`时,编译器将会编译对应的代码段,并执行其中的代码。而当参数中定义了`OPTION_B`时,编译器将会编译对应的代码段,并执行其中的代码。 需要注意的是,根据参数选择不同的定义时,只有定义了才会编译对应代码段,因此未定义的对应的代码段将会被忽略。 通过使用定义来根据参数选择不同的定义,可以在编译阶段根据需要灵活地选择不同的代码实现。这在代码的复用和优化方面具有很大的作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学海无涯.苦作舟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值