使用C语言函数案例6-参数化宏和函数
- 格式:doc
- 大小:52.00 KB
- 文档页数:3
C语⾔:代码宏详解⽬录1、定义宏2、宏函数3、多⾏宏4、宏变长参数5、原样输出变量名6、例⼦7、宏与函数的差异总结1、定义宏#define ARRAY_SIZE 100double data[ARRAY_SIZE];如下图,上⽅代码在编译器进⾏宏替换时会将代码中的ARRAY_SIZE替换成1002、宏函数宏函数的参数是没有任何类型的概念的,因此宏函数使⽤如下,代码中的MAX(3,4)会替换成宏定义的表达式#define MAX(a,b) a > b ? a : bint n1 = MAX(3,4);注意上⽅替换出错,是因为给宏函数的参数传递的是⼀个表达式,可以使⽤下图⽅法宏函数的参数不要传表达式,如下图,表达式进⾏了2次运算3、多⾏宏使⽤斜杠连接下⼀⾏代码,适⽤于代码很长的宏#define IS_HEX_CHARACTOR(ch) \( (ch) >= '0' && (ch) <= '9') || \( (ch) >= 'A' && (ch) <= 'F') || \( (ch) >= 'a' && (ch) <= 'f')int main(){printf("is hex charactor:%d", IS_HEX_CHARACTOR('a'));}4、宏变长参数#define PRINTLNF(format, ...) printf(format, __VA_ARGS__)5、原样输出变量名6、例⼦#include <stdio.h>#define PRINTF(format, ...) printf("("__FILE__":%d) %s: "format,__LINE__,__FUNCTION__, ##__VA_ARGS__) #define PRINT_INT(value) PRINTF(#value":%d \n", value)int main(){int no = 1;PRINT_INT(no);return 0;}7、宏与函数的差异总结本篇⽂章就到这⾥了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!。
c语言带参数的宏定义例题一、引言在C语言中,宏定义是一种常用的预处理指令,它允许程序员创建可重用的代码片段。
通过宏定义,我们可以简化代码,提高可读性和维护性。
在本文中,我们将讨论如何使用带参数的宏定义在C语言中创建自定义函数。
二、带参数的宏定义基础在C语言中,宏定义类似于函数定义,但它允许我们在编译时直接替换代码中的宏名称。
带参数的宏允许我们在调用宏时提供具体的数据,这样宏可以在不改变代码结构的情况下执行更复杂的操作。
以下是一个简单的带参数的宏定义的例子:```c#defineSQUARE(x)((x)*(x))```这个宏定义将把任何传入的参数`x`的平方作为结果。
例如:```cinta=5;intresult=SQUARE(a);//result的值现在是25```三、带参数的宏定义的应用带参数的宏定义在很多情况下都非常有用,例如:*简化复杂的表达式:使用带参数的宏可以避免编写冗长的表达式,同时保持代码的可读性和简洁性。
*条件编译:通过传递不同的参数,我们可以根据条件选择性地使用不同的代码片段。
*生成代码:宏可以用于生成特定的代码片段,例如循环或条件语句。
下面是一个使用带参数的宏定义的例子,用于生成一个简单的for循环:```c#defineLOOP_STARTdo{\intx=0;\while(1){\if(x<MAX){\x++;\}else{\break;\}\}\}while(0)```这个宏定义将生成一个无限循环,直到`x`的值达到`MAX`为止。
你可以通过传递不同的参数来生成不同条件的循环。
四、总结通过使用带参数的宏定义,我们可以创建自定义函数,简化代码,提高可读性和维护性。
在许多情况下,使用带参数的宏定义可以更有效地处理复杂的逻辑和生成特定的代码片段。
在C语言编程中,灵活使用宏定义可以提高效率和代码质量。
C语言中如何使用宏C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念)。
下面对常遇到的宏的使用问题做了简单总结。
宏使用中的常见的基础问题#符号和##符号的使用...符号的使用宏的解释方法我们能碰到的宏的使用宏使用中的陷阱常见的基础性问题关于#和##在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。
比如下面代码中的宏:#define WARN_IF(EXP) \do{ if (EXP) \fprintf(stderr, "Warning: " #EXP "\n"); } \while(0)那么实际使用中会出现下面所示的替换过程:WARN_IF (divider == 0);被替换为do {if (divider == 0)fprintf(stderr, "Warning" "divider == 0" "\n");} while(0);这样每次divider(除数)为0的时候便会在标准错误流上输出一个提示信息。
而##被称为连接符(concatenator),用来将两个Token连接为一个Token。
注意这里连接的对象是Token就行,而不一定是宏的变量。
比如你要做一个菜单项命令名和函数指针组成的结构体的数组,并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。
那么下面的代码就非常实用:struct command{char * name;void (*function) (void);};#define COMMAND(NAME) { NAME, NAME ## _command }// 然后你就用一些预先定义好的命令来方便的初始化一个command结构的数组了:struct command commands[] = {COMMAND(quit),COMMAND(help),...}COMMAND宏在这里充当一个代码生成器的作用,这样可以在一定程度上减少代码密度,间接地也可以减少不留心所造成的错误。
C语言中宏的使用C 语言中宏的使用宏的主要作用是在编译预处理时,对程序中所有出现的“宏名”都用宏定义中的字符串去代换。
宏定义是由源程序中的宏定义命令完成的,宏代换是由预处理程序自动完成的。
在C 语言中,“宏”分为有参数和无参数两种,即分别简称为有参宏和无参宏。
无参宏无参宏的宏名后不带参数,其定义的一般形式为:#define 标识符字符串其中的. “#” 表示这是一条预处理命令,凡是以“#” 开头的均为预处理命令。
“define” 为宏定义命令,“标识符” 为所定义的宏名称,“字符串” 可以是常数、表达式、格式串等。
#include#define NUM 4int main(int argc, const char * argv[]) { int a = 4;a *= NUM; printf("%d ", a); return 0;}运行结果:16 。
这里可以看出我们定义了一个宏名称为 NUM 的宏,当 main 中代码出现 NUM 的地方,就会自动用数字 4 进行替换,这样做的好处是当代码中多处存在同一变量时,只需要修改宏NUM 的值即可,而无需在代码中一处处的进行修改。
有参宏C 语言允许宏带有参数,在宏定义中的参数称为形式参数,宏调用中的参数称为实际参数。
对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。
带参宏定义的一般形式为:#define 宏名(形参表) 字符串示例代码:#include#define SUM(a) a+aint main(int argc, const char * argv[]) { int a = SUM(4); printf("%d ", a); return 0;}运行结果:8 。
这里可以发现 SUM 将 4 传入,通过 a + a ,即 4 + 4 = 8 。
【C 语言中宏的使用】。
C语⾔深⼊分析函数与宏的使⽤⽬录⼀、函数与宏⼆、宏的妙⽤三、⼩结⼀、函数与宏宏是由预处理器直接替换展开的,编译器不知道宏的存在函数是由编译器直接编译的实体,调⽤⾏为由编译器决定多次使⽤宏会导致最终可执⾏程序的体积增⼤函数是跳转执⾏的,内存中只有⼀份函数体存在宏的效率⽐函数要⾼,因为是直接展开,⽆调⽤开销函数调⽤时会创建活动记录,效率不如宏下⾯看⼀个函数与宏的⽰例,先看这个程序:#include <stdio.h>#define RESET(p, len) \while( len > 0 ) \((char*)p)[--len] = 0void reset(void* p, int len){while( len > 0 )((char*)p)[--len] = 0;}int main(){int array[] = {1, 2, 3, 4, 5};int len = sizeof(array);int i = 0;RESET(array, len);for(i=0; i<5; i++){printf("array[%d] = %d\n", i, array[i]);}return 0;}输出结果如下:但是如果我们这么写,RESET(6, len); 程序直接出现段错误,都没有给出警告:⽽我们使⽤函数 reset(6, len); 时,则会出现警告:所以说能⽤函数实现的功能就尽可能的不使⽤宏。
宏的效率⽐函数稍⾼,但是其副作⽤巨⼤宏是⽂本替换,参数⽆法进⾏类型检查可以⽤函数完成的功能绝对不⽤宏宏的定义中不能出现递归定义下⾯看⼀个宏的副作⽤的代码:#include <stdio.h>#define _ADD_(a, b) a + b#define _MUL_(a, b) a * b#define _MIN_(a, b) ((a) < (b) ? (a) : (b))int main(){int i = 1;int j = 10;printf("%d\n", _MUL_(_ADD_(1, 2), _ADD_(3, 4)));printf("%d\n", _MIN_(i++, j));return 0;}输出结果如下:按理说输出结果应该是 21 和 1 ,为什么是 11 和 2 呢?下⾯进⾏单步调试,输⼊ gcc -E test.c -o test.i ,得到 test.i ⽂件,部分结果如下:这样就能解释了。
c语⾔之带参数的宏定义1.带参数的宏定义中,宏名和新参表之间不能有空格,2.在带参数的宏定义中,形参参数不分配内存单元,因此不必作类型定义。
⽽宏调⽤中的实参有具体值,要⽤它去代换形参,因此必须作类型说明。
#include<stdio.h>#include<iostream>#define MAX(a,b) (a>b)?a:bint main() {int x, y, max;x = 2;y = 3;max = MAX(x,y);printf("%d\n", max);system("pause");return0;}3.在宏定义中的形参是标识符,⽽宏调⽤中实参可以是表达式。
4.在宏定义中,字符串内的形参通常要⽤括号括起来以避免出错。
5.带参的宏和代餐函数类似,但本质不同,除此之外,把同⼀表达式⽤函数处理和⽤宏处理两者的结果有可能不同。
普通函数:#include<stdio.h>#include<iostream>int SQ(int y) {return ((y) * (y));}int main() {int i = 1;int SQ(int y);while (i <= 5) {printf("%d ", SQ(i++));}printf("\n");system("pause");return0;}输出:宏定义:#include<stdio.h>#include<iostream>#define SQ(y) (y)*(y)int main() {int i = 1;while (i <= 5) {printf("%d ", SQ(i++));}printf("\n");system("pause");return0;}输出:为什么结果不同呢?这是因为普通函数调⽤时,实参传给形参的是值,⽽在宏定义时,要⽤表达式进⾏替换,即(i++)*(i++),所以I++会被执⾏两次。
c语言带参数的宏定义用法
C 语言带参数的宏定义用法如下:
```c
#define macro_name(parameter) macro_body
```
在宏定义中,`macro_name` 是宏的名称,`parameter` 是宏的参数,在宏的主体部分
`macro_body` 中可以使用参数进行替换和操作。
例如,下面是一个带参数的宏定义示例:
```c
#include <stdio.h>
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
int main() {
int x = 10;
int y = 20;
int max_num = MAX(x, y);
printf("The maximum number is %d\n", max_num);
return 0;
}
```
在上面的示例中,宏定义 `MAX(a, b)` 带有两个参数 `a` 和 `b`,并返回其中较大的数。
在
`main` 函数中,通过调用宏 `MAX` 传入不同的参数 `x` 和 `y`,得到最大的数并进行输出。
注意,在带有参数的宏定义中,参数的使用需要用括号括起来,以防止出现意外的优先级问题。
在宏的主体部分,可以对参数进行计算、替换等操作,使用括号可以确保操作正确执行。
C语⾔:宏参数的字符串化和宏参数的连接在宏定义中,有时还会⽤到#和##两个符号,它们能够对宏参数进⾏操作。
# 的⽤法#⽤来将宏参数转换为字符串,也就是在宏参数的开头和末尾添加引号。
例如有如下宏定义:#define STR(s) #s那么:printf("%s", STR());printf("%s", STR(""));分别被展开为:printf("%s", "");printf("%s", "\"\"");可以发现,即使给宏参数“传递”的数据中包含引号,使⽤#仍然会在两头添加新的引号,⽽原来的引号会被转义。
将上⾯的例⼦补充完整:1. #include <stdio.h>2. #define STR(s) #s3. int main() {4. printf("%s\n", STR());5. printf("%s\n", STR(""));6. return 0;7. }运⾏结果:""##的⽤法##称为连接符,⽤来将宏参数或其他的串连接起来。
例如有如下的宏定义:#define CON1(a, b) a##e##b#define CON2(a, b) a##b##00那么:printf("%f\n", CON1(8.5, 2));printf("%d\n", CON2(12, 34));将被展开为:printf("%f\n", 8.5e2);printf("%d\n", 123400);将上⾯的例⼦补充完整:1. #include <stdio.h>2. #define CON1(a, b) a##e##b3. #define CON2(a, b) a##b##004. int main() {5. printf("%f\n", CON1(8.5, 2));6. printf("%d\n", CON2(12, 34));7. return 0;8. }运⾏结果:850.000000123400。
c语言带参数的宏定义调用函数
在C语言中,你可以在宏定义中使用参数,并且可以在宏调用时传递参数。
这使得宏在处理简单的代码片段重用时非常有用。
下面是一个简单的示例,演示了如何定义和使用带参数的宏。
```c
include <>
// 定义一个带参数的宏
define MAX(a, b) ((a) > (b) ? (a) : (b))
int main() {
int x = 5;
int y = 10;
int max_val = MAX(x, y); // 宏调用,将宏展开为 ((x) > (y) ? (x) : (y)) printf("The maximum value is %d\n", max_val);
return 0;
}
```
在这个例子中,`MAX` 是一个带两个参数 `a` 和 `b` 的宏。
在宏调用
`MAX(x, y)` 时,预处理器会将 `x` 和 `y` 替换到宏定义中的相应位置。
注意我们在比较操作符两侧使用了括号,这是为了确保运算符的优先级正确。
如果不加括号,像 `a > b ? a : b` 这样没有括号的表达式可能会产生不预期的结果,因为 `?:` 的优先级低于 `>`。
然而,需要注意的是,尽管宏在某些情况下可以简化代码,但它们也有一些潜在的问题。
例如,宏不会进行类型检查,也不会进行作用域检查,而且由于预处理器直接替换代码,所以可能导致代码可读性降低。
因此,尽管在某些情况下使用宏可能是有用的,但在其他情况下,使用内联函数或标准函数可能会更好。
C语言宏的使用(转载)2010-03-12 16:19:52| 分类:C语言| 标签:|字号大中小订阅写好C语言,漂亮的宏定义很重要,使用宏定义可以防止出错,提高可移植性,可读性,方便性等等。
下面列举一些成熟软件中常用得宏定义。
1,防止一个头文件被重复包含#ifndef COMDEF_H#define COMDEF_H//头文件内容#endif2,重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。
typedef unsigned char boolean; /* Boolean value type. */typedef unsigned long int uint32; /* Unsigned 32 bit value */typedef unsigned short uint16; /* Unsigned 16 bit value */typedef unsigned char uint8; /* Unsigned 8 bit value */typedef signed long int int32; /* Signed 32 bit value */typedef signed short int16; /* Signed 16 bit value */typedef signed char int8; /* Signed 8 bit value *///下面的不建议使用typedef unsigned char byte; /* Unsigned 8 bit value type. */typedef unsigned short word; /* Unsinged 16 bit value type. */typedef unsigned long dword; /* Unsigned 32 bit value type. */typedef unsigned char uint1; /* Unsigned 8 bit value type. */typedef unsigned short uint2; /* Unsigned 16 bit value type. */typedef unsigned long uint4; /* Unsigned 32 bit value type. */typedef signed char int1; /* Signed 8 bit value type. */typedef signed short int2; /* Signed 16 bit value type. */typedef long int int4; /* Signed 32 bit value type. */typedef signed long sint31; /* Signed 32 bit value */typedef signed short sint15; /* Signed 16 bit value */typedef signed char sint7; /* Signed 8 bit value */3,得到指定地址上的一个字节或字#define MEM_B( x ) ( *( (byte *) (x) ) )#define MEM_W( x ) ( *( (word *) (x) ) )4,求最大值和最小值#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )5,得到一个field在结构体(struct)中的偏移量#define FPOS( type, field ) \/*lint -e545 */ ( (dword) &(( type *) 0)-> field ) /*lint +e545 */6,得到一个结构体中field所占用的字节数#define FSIZ( type, field ) sizeof( ((type *) 0)->field )7,按照LSB格式把两个字节转化为一个Word#define FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )8,按照LSB格式把一个Word转化为两个字节#define FLOPW( ray, val ) \(ray)[0] = ((val) / 256); \(ray)[1] = ((val) & 0xFF)9,得到一个变量的地址(word宽度)#define B_PTR( var ) ( (byte *) (void *) &(var) )#define W_PTR( var ) ( (word *) (void *) &(var) )10,得到一个字的高位和低位字节#define WORD_LO(***) ((byte) ((word)(***) & 255))#define WORD_HI(***) ((byte) ((word)(***) >> 8))11,返回一个比X大的最接近的8的倍数#define RND8( x ) ((((x) + 7) / 8 ) * 8 )12,将一个字母转换为大写#define UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) ) 13,判断字符是不是10进值的数字#define DECCHK( c ) ((c) >= '0' && (c) <= '9')14,判断字符是不是16进值的数字#define HEXCHK( c ) ( ((c) >= '0' && (c) <= '9') ||\((c) >= 'A' && (c) <= 'F') ||\((c) >= 'a' && (c) <= 'f') )15,防止溢出的一个方法#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))16,返回数组元素的个数#define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )17,返回一个无符号数n尾的值MOD_BY_POWER_OF_TWO(X,n)=X%(2^n)#define MOD_BY_POWER_OF_TWO( val, mod_by ) \( (dword)(val) & (dword)((mod_by)-1) )18,对于IO空间映射在存储空间的结构,输入输出处理#define inp(port) (*((volatile byte *) (port)))#define inpw(port) (*((volatile word *) (port)))#define inpdw(port) (*((volatile dword *)(port)))#define outp(port, val) (*((volatile byte *) (port)) = ((byte) (val)))#define outpw(port, val) (*((volatile word *) (port)) = ((word) (val)))#define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val)))[2005-9-9添加]19,使用一些宏跟踪调试A N S I标准说明了五个预定义的宏名。
C函数和宏中的可变参数⼀:调⽤惯例函数的调⽤⽅和被调⽤⽅对函数如何调⽤应该有统⼀的理解,否则函数就⽆法正确调⽤。
⽐如foo(int n, int m),调⽤⽅如果认为压栈顺序是m,n,⽽foo认为压栈顺序是n, m,那么这个函数就不会调⽤成功。
因此,函数的调⽤⽅和被调⽤⽅对于函数如何调⽤需要有个明确的约定,双⽅都遵守同样的约定,函数才能调⽤成功,这种约定称为调⽤惯例,⼀个调⽤惯例⼀般会规定如下⼏个⽅⾯的内容:1:函数参数的传递顺序和⽅式函数参数的传递有多种⽅式,最常见的是通过栈传递。
函数的调⽤⽅将参数压⼊栈中,函数⾃⼰在从栈中将参数取出。
如果有多个参数,调⽤惯例要规定函数调⽤⽅参数压栈的顺序:从左⾄右,还是从右⾄左。
有些调⽤惯例还允许使⽤寄存器传递参数,以提⾼性能。
2:栈的维护⽅式在函数将参数压栈之后,函数体会被调⽤,此后需要将被压⼊栈中的参数全部弹出,使得栈在函数调⽤前后保持⼀致。
这个弹出的⼯作可以由函数调⽤⽅完成,也可以由函数本⾝完成。
3:名字修饰策略不同的调⽤惯例对函数名有不同的修饰策略在C中,存在多个调⽤惯例,默认的调⽤惯例是cdecl,任何⼀个没有显⽰执⾏调⽤惯例的函数默认都是cdecl惯例。
另外,_cdecl是⾮标准关键字,在不同的编译器中可以有不同的写法,⽐如在GCC中,使⽤:__attribute__((cdecl))。
cdecl的调⽤惯例:参数从右⾄左的顺序⼊栈,参数出栈由调⽤⽅完成。
除了cdecl调⽤惯例之外,还存在许多别的调⽤惯例,⽐如stdcall,fastcall等,不再赘述。
⼆:函数中的可变参数printf函数的原型如下:int printf(const char *format, ...);printf函数就是可变参数的典范。
除了第⼀个参数类型为const char *之外,可以追加任意数量,任意类型的参数。
可变参数的实现,得益于C语⾔默认的cdecl调⽤惯例,它从右向左进⾏参数的⼊栈,⽐如函数:int sum(unsignednum, ...);num表⽰后⾯会传递num个整数,当调⽤sum时:int n = sum(3, 16, 38, 53);参数在栈上的布局如下图:函数内部,可以使⽤num得到数字3,⽽且其他参数在栈上的排列就是在num的⾼地址⽅向,从⽽可以通过num的地址计算出其他参数的地址,所以,sum函数的实现如下:int sum(unsigned num, ...){int *p = &num + 1;int ret = 0;while(num--)ret += *p++;return ret;}所以,cdecl调⽤惯例保证了参数的正确处理,但是在调⽤sum函数的时候,必须要知道有多少个不定参数,每个不定参数的类型是什么。
《C概念C语言能力教程》实
验报告
使用C语言函数案例
参数化宏和函数
一、实验目的
对参数化宏和函数进行理解和区别
二、实验环境
VC++
实验内容与实验过程及分析(写出详细的实验步骤,并分析实验结果)实验步骤:
#include <stdio.h>
#define A(x) ((x)>0?(x):(-x))
double my(double x)
{
return x>0?x:-x;
}
void main ()
{
printf("A(-3)=%d\n",A((-3)));
printf("my(-3)=%lf\n",my(-3));
printf("A(-3-1)=%d\n",A((-3-1)));
printf("my(-3-1)=%lf\n",my(-3-1));
printf("A(-3.5)=%.1f\n",A((-3.5)));
printf("my(-3.5)=%lf\n",my(-3.5));
}
实验结果图为:
三、实验总结(每项不少于20字)
存在问题:对文件包含不熟悉,在同一个文件中的分布程序连接时不能分开的完整或者连接时左差右差。
解决方法:既然不能将程序很好的分开,就不能完成以后的合作任务,我对程序的每一步都进行尝试分离,最终得到了很好的效果。
收获:学会了参数化宏与函数可以达到相同的效果,却也有些区别。
还巩固了对文件包含的理解和应用以及条件编译。
四、教师批语
宋体,小四,行间距20磅,首行缩进2字符,段前段后0行,两端对齐。