计算机c语言宏定义实例讲解
详解C语言的宏定义
详解C语⾔的宏定义宏定义介绍假设我们有⼀个 C 源⽂件 main.c,那么只需要通过 gcc main.c -o main.exe 即可编译成可执⾏⽂件(如果只写 gcc main.c,那么 Windows 上会默认⽣成 a.exe、Linux 上会默认⽣成 a.out ),但是这⼀步可以拆解成如下步骤:预处理:gcc -E main.c -o main.i,根据 C 源⽂件得到预处理之后的⽂件,这⼀步只是对 main.c 进⾏了预处理:⽐如宏定义展开、头⽂件展开、条件编译等等,同时将代码中的注释删除,注意:这⾥并不会检查语法;编译:gcc -S main.i -o main.s,将预处理后的⽂件进⾏编译、⽣成汇编⽂件,这⼀步会进⾏语法检测、变量的内存分配等等;汇编:gcc -c main.s -o main.o,根据汇编⽂件⽣成⽬标⽂件,当然我们也可以通过 gcc -c main.c -o main.o 直接通过 C 源⽂件得到⽬标⽂件;链接:gcc main.o -o main.exe,程序是需要依赖各种库的,可以是静态库也可以是动态库,因此需要将⽬标⽂件和其引⽤的库链接在⼀起,最终才能构成可执⾏的⼆进制⽂件。
⽽这⾥我们主要来介绍⼀下预处理中的宏定义,相信很多⼈都觉得宏定义⾮常简单,但其实宏定义有很多⾼级⽤法。
我们先来看看简单的宏定义:#include <stdio.h>// 宏定义的⽅式为:#define 标识符常量// 然后会将所有的 PI 替换成 3.14#define PI 3.14int main() {printf("%f\n", PI);}我们⽣成预处理之后的⽂件:gcc -E main.c -o main.i我们看到 PI 被替换成了 3.14,当然除了浮点型之外,也可以是其它的类型:#include <stdio.h>#define NAME "satori"#define AGE 17#define GENDER 'f'int main() {printf("%s %d %c\n", NAME, AGE, GENDER); // satori 17 f}我们再来查看⽣成的预处理⽂件:我们看到确实只是简单替换,除此之外,没有做任何的处理。
CC++语言宏定义使用实例详解
CC++语⾔宏定义使⽤实例详解C/C++语⾔宏定义使⽤实例详解1. #ifndef 防⽌头⽂件重定义在⼀个⼤的软件⼯程⾥⾯,可能会有多个⽂件同时包含⼀个头⽂件,当这些⽂件编译链接成⼀个可执⾏⽂件时,就会出现⼤量“重定义”的错误。
在头⽂件中实⽤#ifndef #define #endif能避免头⽂件的重定义。
⽅法:例如要编写头⽂件test.h在头⽂件开头写上两⾏:#ifndef TEST_H#define TEST_H //⼀般是⽂件名的⼤写头⽂件结尾写上⼀⾏:#endif这样⼀个⼯程⽂件⾥同时包含两个test.h时,就不会出现重定义的错误了。
注:Visual C++中有⼀种简化的⽅法,那就是使⽤ #pragma once2. 编写跨平台的C/C++程序2.1 操作系统相关宏定义Windows: WIN32Linux: linuxSolaris: __sun2.2 编译器相关宏定义VC: _MSC_VERGCC/G++: __GNUC__SunCC: __SUNPRO_C 和 __SUNPRO_CC3. 完整的代码实例//Avoid redefine anything in this header#ifndef UUID_H#define UUID_H// Check platform is Windows or Linux#ifdef _MSC_VER#ifndef DLL_API#define DLL_API __declspec(dllexport)#endif#else#ifndef DLL_API#define DLL_API#endif#endif#include <string>#include <random>#include <time.h>#include <stdlib.h>using namespace std;class DLL_API UUID {public:static string getUuidString();};#endif感谢阅读,希望能帮助到⼤家,谢谢⼤家对本站的⽀持!。
C语言的宏定义
C语言中的宏定义SUNNY.MAN1.宏定义格式:#define 标识符字符串其中的标识符就是所谓的符号常量,也称为“宏名”。
预处理(预编译)工作也叫做宏展开,就是把宏名替换为字符串。
2.为什么要使用宏定义我认为主要有两点:1.增强程序的可读性2.增强程序的可移植性int a=16;int *a=(int *)0;如果我们都是把int当32位来处理的,有一天我们的程序需要把int当16位来处理,你怎么办呢?#define int int16//请注意这个int16才是真正的类型(int *)0;也不好看啊。
我们喜欢用NULL,那也定义一个宏好了。
#define NULL (myint *)03.宏定义的作用域这是我写这文的目的。
请看下面的小例子Int sum(int a);Int main(void){Int a=sum(1);Int b=MM;Return 0;}Int sum(int a){#define MM 123Return a+MM;}这个程序在编译的时假会产生一个类似于下面的错误:MM undeclared (first use in this function)。
所以切记宏展开的位置是在文件中的位置,而不是调用它的位置。
运行和编译是两个阶段。
如果1.c文件中#undef MM#define MM 102.c文件中#undef MM#define MM 20那么到底是MM被定义为10呢还是20呢。
请记住宏展开的时机是预处理阶段而不是编译阶段,预处理阶段不检查语法错误。
也就是文件1中的还是10,文件2中的还是20。
4.宏的基本使用1.宏定义末尾不需要像语句一样加分号,但你可以根据需要自己进行添加;2.宏定义不管写在函数的花括号里外边,作用域为其后的程序行,通常在文件的最开头。
3.可以用#undef命令终止宏定义的作用域4.宏定义允许嵌套5.字符串( " " )中不会进行宏替换例如:#define TEST "this is a test"程序行中有"TEST"时不会替换为”this is a test”.6.宏定义的第二个“标识符”必须是合法的C语言的标识符。
c 函数内部宏定义
c 函数内部宏定义C函数内部宏定义一、参数检查宏定义在函数内部,我们经常需要对参数进行检查,以确保参数的合法性。
为了简化参数检查的代码,我们可以使用宏定义来实现。
下面是一个示例:```#define CHECK_PARAM(param) \if (!(param)) { \printf("参数错误:%s\n", #param); \return; \}```在上面的示例中,我们定义了一个宏CHECK_PARAM,该宏接受一个参数param,并在参数为假时输出错误信息并返回。
通过使用这个宏,我们可以简化参数检查的代码,提高代码的可读性和可维护性。
二、条件编译宏定义在函数内部,有时我们需要根据不同的条件编译不同的代码。
为了简化条件编译的代码,我们可以使用宏定义来实现。
下面是一个示例:```#define DEBUG_MODE#ifdef DEBUG_MODE#define DEBUG_LOG(...) printf(__VA_ARGS__)#else#define DEBUG_LOG(...)#endif```在上面的示例中,我们定义了一个宏DEBUG_MODE,该宏用于开启或关闭调试模式。
当DEBUG_MODE被定义时,宏DEBUG_LOG会输出调试信息;当DEBUG_MODE未被定义时,宏DEBUG_LOG为空宏。
通过使用这个宏,我们可以方便地在调试模式和发布模式之间切换,提高代码的可维护性。
三、局部变量宏定义在函数内部,有时我们需要定义一些局部变量来辅助实现某些功能。
为了简化局部变量的定义,我们可以使用宏定义来实现。
下面是一个示例:```#define LOCAL_VAR(type, name, value) \type name = value;```在上面的示例中,我们定义了一个宏LOCAL_VAR,该宏接受三个参数:变量的类型type、变量的名称name和变量的初始值value。
C语言宏定义(高级篇)PPT课件
Special Issue for HITMath-07
补充(三) 宏 编程 —— #define
& 常用调试技巧
Macro Programming Using #define &
General Debug Technology
1
内容简介
本部分将主要讲解C++中宏定义的 应用——如何利用宏来简化程序设计, 实现代码级的简化、封装、重用等。
2
简单常量的定义
#define N 1000
定义简单的常量,便于修改。 切不可在后面加上分号! 等效于 const int N = 1000; 仅是简单替换,而不是作为一个量来使用。
则,IW3DScene* pScene = MAKE_COMPONENT( Scene ); 将被理解为:
pScene = ((IW3DScene *)MakeComponent("Scene");
8
宏定义举例(二)
#define IMPLEMENT_COMPONENT( I, C )
\
static IComponent* Component_Factory_##C() \
// …
void Draw() {
// … } // Draw
private: DWORD m_dwFogColor; ///< Fog color
// … }; //class CW3DCamera
c语言宏定义函数实例
宏定义在C语言中是一种预处理指令,它可以在编译之前对代码进行替换。
宏定义可以用来定义常量、创建函数等。
下面是一个简单的宏定义函数的例子:
```c
#include <stdio.h>
// 宏定义函数,将输入的两个整数相加
#define ADD(x, y) ((x) + (y))
int main() {
int a = 5;
int b = 10;
int sum = ADD(a, b);
printf("The sum of %d and %d is %d\n", a, b, sum);
return 0;
}
```
在这个例子中,我们定义了一个名为ADD的宏,它接受两个参数x 和y,并返回它们的和。
在main函数中,我们使用这个宏来计算两
个整数的和,并打印结果。
注意,为了在宏中使用加法运算符,我们需要使用括号将其包围起来,否则编译器可能会将其解析为乘法运算符。
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 */分析:#includetypedef struct person {int num;int age;char name[20];}person;#define FPOS(type,field) sizeof(((type *)0)->field)main(){printf("%d\n",FPOS(person,name));}^_^[sunny@localhost ~]52$ ./a.out20#define OFFSETOF(type, field) ((size_t)&(((type *)0)->field))(type *)0:把0地址当成type类型的指针。
C语言宏定义与使用分析
#define FREE(p) (free(p), p=NULL)
#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__,
__FILE__, __LINE__, s)
return 0; }
预编译: # 1 "test.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 1 "<command-line>" 2 # 1 "test.c"
int main() {
#define FOREACH(i, m) for(i=0; i<m; i++) #define BEGIN { #define END }
int main(void) {
int x = 0; int* p = MALLOC(int, 5);
LOG("Begin to run main code...");
例程: //#include <stdio.h>
#define _SUM_(a, b) (a) + (b) #define _MIN_(a, b) ((a) < (b) ? (a) : (b)) #define _DIM_(a) sizeof(a)/sizeof(*a)
int main() {
int a = 1;
c语言之带参数的宏定义
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语言宏定义详解我们可能要定义很多常量( 不管是放在源文件还是头文件 ),那么我们有时考虑定义某个常量时,我们就必须返回检查原来此常量是否定义,但这样做很麻烦.if defined宏正是为这种情况提供了解决方案.举个例子,如下:#define ....#define ....................#define a 100.......此时,我们要检查a是否定义(假设我们已经记不着这点了),或者我们要给a一个不同的值,就加入如下句子#if defined a#undef a#define a 200#endif上述语句检验a是否被定义,如果被定义,则用#undef语句解除定义,并重新定义a为200 同样,检验a是否定义:#ifndef a //如果a没有被定义#define a 100#endif以上所用的宏中:#undef为解除定义,#ifndef是if not defined的缩写,即如果没有定义。
这就是#if defined 的唯一作用常用宏定义总结*pclint////////////////////////////////// #include <stdio.h>////////////////////////////////#define VPLS_EXT_DEBUG 1#if VPLS_EXT_DEBUG#define _VE_DEBUG(msg...) printf("[VPLS_DEBUG] %s,%d => ",__FILE__, __LINE__);printf(msg);printf("\r\n")#else#define_VE_DEBUG(x1,x2) (void)(x1),(void)(x2)#endif调用直接写为_VE_DEBUG("XXXXX %d",adb)开关关闭后这句直接失效//////////////////////////////main(void){ int a,b,c,d; a = b = c = d = 9;_BUG("a = %d,b = %d,c = %d,d = %d\n",a,b,c,d);}#define assert(e)\ ((void)((e) || fprintf(stderr,"line %d: 'asserterror!'\n",__LINE__)))////////////////////////////////////__attribute__ 作用- -__attribute__ 是GCC的关键字,描述变量的属性。
C语言预处理命令--宏定义
C语⾔预处理命令--宏定义⼀、宏讲解1、宏定义宏(Macro),是⼀种的称谓。
⾥的宏是⼀种(Abstraction),它根据⼀系列预定义的规则替换⼀定的⽂本模式。
或在遇到宏时会⾃动进⾏这⼀模式替换。
2、C语⾔宏定义的常规⽤法1) 定义符号常量#define PI 3.1415926#define MAX_N 100002) 定义傻⽠表达式(注意,定义的这种表达式⼀不⼩⼼很容易出现bug,下⽂会讲)#define S(a, b) a * b#define MAX(a, b) (a) > (b) ? (a) : (b)3) 定义代码段#define P(a) {\ printf("%d\n", a);\} ps:编译器对于宏的解析是很严谨的,只能⽀持⼀⾏解析,\是起连接作⽤,表⽰当⾏的宏代码与下⼀⾏宏连接在⼀起,使得编译器当成⼀⾏看待。
3、编译器预定义的宏在C语⾔中,我们有很多预定义的宏,就是C语⾔帮程序员预先定义好的宏,可以让我们使⽤。
宏说明__DATE__ ⽇期:Mmm dd yyyy__TIME__ 时间:hh:mm:ss__LINE__ 当前源⽂件的代码⾏号__FILE__ ⽂件名__func__ 函数名/⾮标准__FUNC__ 函数名/⾮标准__PRETTY_FUNCTION__ 更详细的函数信息/⾮标准4、预定义命令-条件式编译函数说明#ifdef DEBUG 是否定义了DEBUG宏#ifndef DEBUG 是否没有定义DEBUG宏#if MAX_N == 5 宏MAX_N是否等于5#elif MAX_N == 4 否则宏MAX_N是否等于4#else#endif5、预定义命令从上图可以看到: 预编译 将.c ⽂件转化成 .i⽂件 使⽤的gcc命令是:gcc –E 对应于预处理命令cpp 编译 将.c/.h⽂件转换成.s⽂件 使⽤的gcc命令是:gcc –S 对应于编译命令 cc –S 汇编 将.s ⽂件转化成 .o⽂件 使⽤的gcc 命令是:gcc –c 对应于汇编命令是 as 链接 将.o⽂件转化成可执⾏程序 使⽤的gcc 命令是: gcc 对应于链接命令是 ld 总结起来编译过程就上⾯的四个过程:预编译、编译、汇编、链接。
宏定义解析
指令用途# 空指令,无任何效果#include 包含一个源代码文件#define 定义宏#undef 取消已定义的宏#if 如果给定条件为真,则编译下面代码#ifdef 如果宏已经定义,则编译下面代码#ifndef 如果宏没有定义,则编译下面代码#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码#endif 结束一个#if……#else条件编译块#error 停止编译并显示错误信息一、文件包含#include预处理指令的作用是在指令处展开被包含的文件。
包含可以是多重的,也就是说一个被包含的文件中还可以包含其他文件。
标准C编译器至少支持八重嵌套包含。
预处理过程不检查在转换单元中是否已经包含了某个文件并阻止对它的多次包含。
这样就可以在多次包含同一个头文件时,通过给定编译时的条件来达到不同的效果。
例如:#define AAA #include t.c #undef AAA #include t.c为了避免那些只能包含一次的头文件被多次包含,可以在头文件中用编译时条件来进行控制。
例如:/*my.h*/#ifndef MY_H#define MY_H……#endif在程序中包含头文件有两种格式:#include#include my.h第一种方法是用尖括号把头文件括起来。
这种格式告诉预处理程序在编译器自带的或外部库的头文件中搜索被包含的头文件。
第二种方法是用双引号把头文件括起来。
这种格式告诉预处理程序在当前被编译的应用程序的源代码文件中搜索被包含的头文件,如果找不到,再搜索编译器自带的头文件。
采用两种不同包含格式的理由在于,编译器是安装在公共子目录下的,而被编译的应用程序是在它们自己的私有子目录下的。
一个应用程序既包含编译器提供的公共头文件,也包含自定义的私有头文件。
采用两种不同的包含格式使得编译器能够在很多头文件中区别出一组公共的头文件。
二、宏宏定义了一个代表特定内容的标识符。
预处理过程会把源代码中出现的宏标识符替换成宏定义时的值。
C语言宏定义
C语言宏定义C语言宏定义C语言既具有高级语言的功能,又具有低级语言的许多功能。
那么大家知道C语言宏定义是怎样的呢?下面一起来看看!宏定义是预处理命令的一种,它允许用一个标识符来表示一个字符串。
先看一个例子:#include#define N 100int main(){ int sum = 20 + N; printf("%d ", sum); return 0;}运行结果:120该示例中的语句int sum = 20 + N;,N被100代替了。
#define N 100就是宏定义,N为宏名,100是宏的内容。
在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。
宏定义是由源程序中的宏定义命令#define完成的,宏代换是由预处理程序完成的。
宏定义的一般形式为:#define 宏名字符串#表示这是一条预处理命令,所有的预处理命令都以#开头。
define是预处理命令。
宏名是标识符的一种,命名规则和标识符相同。
字符串可以是常数、表达式等。
这里所说的字符串是一般意义上的字符序列,不要和C语言中的字符串等同,它不需要双引号。
程序中反复使用的表达式就可以使用宏定义,例如:#define M (n*n+3*n)它的作用是指定标识符M来代替表达式(y*y+3*y)。
在编写源程序时,所有的(y*y+3*y)都可由M代替,而对源程序编译时,将先由预处理程序进行宏代换,即用(y*y+3*y)表达式去替换所有的宏名M,然后再进行编译。
将上面的例子补充完整:#include#define M (n*n+3*n)int main(){ int sum, n; printf("Input a number: "); scanf("%d", &n); sum = 3*M+4*M+5*M; printf("sum=%d ", n); return 0;}运行结果:Input a number: 10↙sum=1560上面的程序中首先进行宏定义,定义M来替代表达式(n*n+3*n),在sum=3*M+4*M+5*M中作了宏调用。
C语言程序设计第十七讲 宏定义
二、文件包含处理
所谓“文件包含” 所谓“文件包含”处理是指一个源文件 可以将另外一个源文件的全部内容包含进来, 可以将另外一个源文件的全部内容包含进来, 即将另外的文件包含到本文件之中。 即将另外的文件包含到本文件之中。 C语言提供了 include 命令来实现文 语言提供了# 语言提供了 件包含的操作
说明: 说明: 1)、宏名一般都用大写表示,以便与变量区别; )、宏名一般都用大写表示 )、宏名一般都用大写表示,以便与变量区别; 2)、宏定义使用宏名代替一个字符串,只是简单 )、宏定义使用宏名代替一个字符串 )、宏定义使用宏名代替一个字符串, 的置换,不做正确性判断; 的置换,不做正确性判断; 如:#define PI 3.141115 3)、宏定义不是 语句,不必在行末加分号; )、宏定义不是 语句, )、宏定义不是C语句 不必在行末加分号; 4)、可以用 )、可以用 命令终止宏定义的作用域; )、可以用#undef命令终止宏定义的作用域; 命令终止宏定义的作用域
功能: 功能:一个源文件可将另一个源文件的内容全部 包含进来 一般形式: #include “文件名” 一般形式: 文件名” 文件名 文件名> 或 #include <文件名 文件名 处理过程:预编译时 用被包含文件的内容取代 处理过程:预编译时,用被包含文件的内容取代 该预处理命令,再对“包含”后的文件作一个源 该预处理命令,再对“包含” 文件编译
如:# define G 9.8
main(){ ... } # undef G f1(){ ... }
/*作用域终止 作用域终止*/ 作用域终止
5)、在进行宏定义时,可以引用已经定义好的宏 )、在进行宏定义时, )、在进行宏定义时 可以层层置换; 名,可以层层置换; 如:# define R 3.0
c语言宏定义用法规则
c语言宏定义用法规则C语言宏定义是一种C语言中最常使用的技术,它可以将经常使用的长句子等缩短,可以将复杂的语句也变得更加简单。
使用宏定义,可以提高程序的可读性,使程序更加便于维护和修改,并且可以更加高效地使用程序。
一、宏定义的语法形式C语言的宏定义的语法有以下几种格式:1. #define:#define宏定义用于定义字符串宏或符号宏,本质上它就是把特定的字符串或符号,映射到一个相应的宏名称。
例如:#define PI 3.14159293表示宏定义一个PI,其值为3.141592932. #undef:#undef用于取消宏定义,例如:#undef PI表示取消之前定义流程中的PI宏定义;3. #ifdef:#ifdef可以根据宏定义的存在与否,在程序编译时有选择性的执行一段代码,例如:#ifdef PIprintf("PI is define\n");#endif上述代码表示:如果PI的宏定义存在的话,则编译执行printf("PI is define\n"),否则不执行。
C语言宏定义可以使用参数,这些参数可以是函数、符号、字符串或者表达式,它们都可以在宏定义中扮演角色,使用参数可以提高宏的可扩展性,从而提高程序的复用性,简化程序的结构。
1. 宏定义参数的表示参数的格式使用形式参数名称来表示,一般使用字母a~z表示参数,形式参数可以使用多次,但参数名必须是唯一的。
例如:#define MIN(x, y) ((x) < (y))? (x): (y)这是一个使用参数的宏定义示例,其中参数x,y是表示形式参数的名称,宏定义的意思是返回x和y的较小值。
使用宏定义参数需要在宏定义时明确参数的含义,每个参数都必须有明确的含义,有利于后续的维护和修改。
例如:三、C语言宏定义书写规范1. #define是注释符号,使用时要在一行的开头,以#开头,表示此行的内容是宏定义,且宏定义的关键词必须全大写。
C语言宏定义详解
C语言宏定义详解转自:[url]/fengyu ruhui/archiv e/2007/08/16/1747090.aspx[/u rl]原作者不详1,防止一个头文件被重复包含#ifndef COMDEF_H#define COMDEF_H//头文件内容#endif2,重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。
typede f unsign ed char boolea n; /* Boolea n valuetype. */typede f unsign ed long int uint32; /* Unsign ed 32 bit value*/typede f unsign ed short uint16; /* Unsign ed 16 bit value*/typede f unsign ed char uint8; /* Unsign ed 8 bit value*/typede f signed long int int32; /* Signed 32 bit value*/typede f signed short int16; /* Signed 16 bit value*/typede f signed char int8; /* Signed 8 bit value*///下面的不建议使用typede f unsign ed char byte; /* Unsign ed 8 bit valuetype. */typede f unsign ed short word; /* Unsing ed 16 bit valuetype. */typede f unsign ed long dword; /* Unsign ed 32 bit valuetype. */typede f unsign ed char uint1; /* Unsign ed 8 bit valuetype. */typede f unsign ed short uint2; /* Unsign ed 16 bit valuetype. */typede f unsign ed long uint4; /* Unsign ed 32 bit valuetype. */typede f signed char int1; /* Signed 8 bit valuetype. */typede f signed short int2; /* Signed 16 bit valuetype. */typede f long int int4; /* Signed 32 bit valuetype. */typede f signed long sint31; /* Signed 32 bit value*/typede f signed short sint15; /* Signed 16 bit value*/typede f 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,得到一个fi eld在结构体(struct)中的偏移量#define FPOS( type, field) \/*lint -e545 */ ( (dword) &(( type *) 0)-> field) /*lint +e545 */6,得到一个结构体中fie ld所占用的字节数#define FSIZ( type, field) sizeof( ((type *) 0)->field)7,按照LSB格式把两个字节转化为一个Word#define FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )8,按照LSB格式把一个W ord转化为两个字节#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_L O(xxx) ((byte) ((word)(xxx) & 255))#define WORD_H I(xxx) ((byte) ((word)(xxx) >> 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_SA T( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))16,返回数组元素的个数#define ARR_SI ZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )17,返回一个无符号数n尾的值MOD_BY_PO WER_O F_TWO(X,n)=X%(2^n) #define MOD_BY_POWE R_OF_TWO( val, mod_by ) \( (dword)(val) & (dword)((mod_by)-1) )18,对于IO空间映射在存储空间的结构,输入输出处理#define inp(port) (*((volati le byte *) (port)))#define inpw(port) (*((volati le word *) (port)))#define inpdw(port) (*((volati le dword*)(port)))#define outp(port, val) (*((volati le byte *) (port)) = ((byte) (val)))#define outpw(port, val) (*((volati le word *) (port)) = ((word) (val)))#define outpdw(port, val) (*((volati le dword*) (port)) = ((dword) (val)))19,使用一些宏跟踪调试A N S I标准说明了五个预定义的宏名。
C语言中宏定义技巧讲解
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 */3,求最大值和最小值#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )4,得到指定地址上的一个字节或字#define MEM_B( x ) ( *( (byte *) (x) ) )#define MEM_W( x ) ( *( (word *) (x) ) )5,得到一个结构体中field所占用的字节数#define FSIZ( type, field ) sizeof( ((type *) 0)->field )6,得到一个field在结构体(struct)中的偏移量#define FPOS( type, field ) /*lint -e545 */ ( (dword) &(( type *) 0)-> field ) /*lint +e545 */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(xxx) ((byte) ((word)(xxx) & 255))#define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8))11,返回一个比X大的最接近的8的倍数#define RND8( x ) ((((x) + 7) / 8 ) * 8 )12,将一个字母转换为大写#define UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) )13,判断字符是不是16进值的数字#define HEXCHK( c ) ( ((c) >= '0' && (c) <= '9') ||\((c) >= 'A' && (c) <= 'F') ||\((c) >= 'a' && (c) <= 'f') )14,判断字符是不是10进值的数字#define DECCHK( c ) ((c) >= '0' && (c) <= '9')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)))19,使用一些宏跟踪调试A N S I标准说明了五个预定义的宏名。
c语言的宏的用法
c语言的宏的用法
在C语言中,宏(Macro)是一种预处理指令,它可以在编译之前替换代码中的特定标记。
宏的主要用途是简化代码、减少重复的代码片段,以及创建可重用的代码块。
宏的用法如下:
1. 定义宏:使用`define`指令定义宏。
宏的名称后面通常跟着一对括号,用于表示参数。
例如:
```c
define SQUARE(x) ((x) (x))
```
这个宏定义了一个名为`SQUARE`的宏,它接受一个参数`x`,并返回`x`的平方。
2. 使用宏:在代码中使用宏时,需要使用宏名称加上参数。
预处理器会用宏定义中的替换文本替换这些宏调用。
例如:
```c
int y = SQUARE(5); // 预处理器将替换为 int y = ((5) (5));
```
3. 展开宏:在定义宏时,可以使用``或``运算符来控制宏的展开方式。
例如:
```c
define CONCAT(x, y) x y
```
这个宏使用``运算符将两个参数连接起来。
例如:
```c
int z = CONCAT(a, b); // 预处理器将替换为 int z = ab;
```
需要注意的是,使用宏时需要谨慎处理括号和运算优先级,以确保正确的展开和计算顺序。
同时,还要注意宏可能引入的一些问题,例如名称冲突和调试困难。
计算机c语言宏定义实例讲解
计算机c语言宏定义实例讲解c语言宏定义使用之最全讲解1宏定义(1)宏定义的意义宏定义简化了重复性的劳动,便于程序修改。
我们常常会遇到需要修改一个特定的数字,然而这个数字在程序中很多地方都出现,譬如圆周率π,在之前的程序中我们取得精度是3.14,现在我们需要将精度改为3.1415926,这样在所有含有3.14的地方全部都去修改,太繁琐了,容易疏漏和出错。
可以使用宏定义将字符串3.14给它定义一个标识符来代替,只需要修改标识符后的字符串,就达到全部修改的目的。
这样既增加了程序的可读性,另外也避免了重复劳动,一般将宏定义放在头文件中,便于查找和提高了编程效率。
但是同时要注意,标识符后的字符串在预处理中仅仅只是文本上的替换。
相信读者这一点在之前的编译链接的章节中已经非常的熟悉了。
带参宏也可以减小函数调用的开销,能够简化复杂表达式。
但是只是在文本上进行替换也会蒙蔽人们的双眼,下面内容详细地为大家解开迷雾。
(2)宏定义的规则和使用解析(1)宏定义在预处理阶段由预处理器进行替换,这个替换是原封不动的替换。
(2)宏定义替换会递归进行,直到替换出来的值本身不再是一个宏为止。
(3)一个正确的宏定义本身分为三个部分:第一部分是#define,第二部分是宏名,剩下的所有为第三部分。
(4)宏可以带参数,称为带参宏。
带参宏的使用和带参函数非常像,但是使用上有一些差异。
在定义带参宏时,每一个参数在宏体中引用时都必须加括号,最后整体再加括号,括号缺一不可。
(3)无参数宏定义无参宏的宏名后不带参数,定义的一般形式是:#define标识符宏体标识符为符号常量,宏体为常数、表达式、格式串等带参数宏定义举例:SEC_PER_YEAR,用宏定义表示一年中有多少秒#define SEC_PER_YEAR(365*24*60*60UL)365*24*60*60这个常整数的缺省类型为int。
低位系统中(如16位系统),365*24*60*60这个数会整数溢出。
c中各类宏定义的用法
假设a.h内容如下:#ifndef AH#define AHtypedef int INT;#endifb.h内容如下:#include "a.h"a.c内容如下:#include "a.h"#include "b.h"main(){INT a;}(1)如果a.h中不写#ifndef #define ... #endif的话a.c展开结果会是下面这样:typedef int INT;typedef int INT;main(){INT a;}编译结果会报标识符INT重复错误。
(2)如果a.h中加上#ifndef #define ... #endif的话a.c展开结果会是下面这样:#ifndef AH#define AHtypedef int INT;#endif#ifndef AH#define AHtypedef int INT;#endifmain(){INT a;}这样的话,因为程序中已经定义了一次AH,所以不会走到第二次声明INT的分支。
所以不会出错。
#ifndef #define #endif的用法整理:shichenghua/blog/?56085/action_viewspace_itemid_1145.htm l(前段时间要到这个,感觉shichenghua整理得不错,所以收藏到此处,若不同意,随时可以撤下谢谢shichenghua)文件中的#ifndef头件的中的#ifndef,这是一个很关键的东西。
比如你有两个C文件,这两个C文件都include了同一个头文件。
而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
还是把头文件的内容都放在#ifndef和#endif中吧。
不管你的头文件会不会被多个文件引用,你都要加上这个。
一般格式是这样的:#ifndef <标识>#define <标识>............#endif<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
c语言宏定义使用之最全讲解
1宏定义
(1)宏定义的意义
宏定义简化了重复性的劳动,便于程序修改。
我们常常会遇到需要修改一个特定的数字,然而这个数字在程序中很多地方都出现,譬如圆周率π,在之前的程序中我们取得精度是3.14,现在我们需要将精度改为3.1415926,这样在所有含有3.14的地方全部都去修改,太繁琐了,容易疏漏和出错。
可以使用宏定义将字符串3.14给它定义一个标识符来代替,只需要修改标识符后的字符串,就达到全部修改的目的。
这样既增加了程序的可读性,另外也避免了重复劳动,一般将宏定义放在头文件中,便于查找和提高了编程效率。
但是同时要注意,标识符后的字符串在预处理中仅仅只是文本上的替换。
相信读者这一点在之前的编译链接的章节中已经非常的熟悉了。
带参宏也可以减小函数调用的开销,能够简化复杂表达式。
但是只是在文本上进行替换也会蒙蔽人们的双眼,下面内容详细地为大家解开迷雾。
(2)宏定义的规则和使用解析
(1)宏定义在预处理阶段由预处理器进行替换,这个替换是原封不动的替换。
(2)宏定义替换会递归进行,直到替换出来的值本身不再是一个宏为止。
(3)一个正确的宏定义本身分为三个部分:第一部分是#define,第二部分是宏名,剩下的所有
为第三部分。
(4)宏可以带参数,称为带参宏。
带参宏的使用和带参函数非常像,但是使用上有一些差异。
在
定义带参宏时,每一个参数在宏体中引用时都必须加括号,最后整体再加括号,括号缺一不可。
(3)无参数宏定义
无参宏的宏名后不带参数,定义的一般形式是:
#define标识符宏体
标识符为符号常量,宏体为常数、表达式、格式串等
带参数宏定义举例:SEC_PER_YEAR,用宏定义表示一年中有多少秒
#define SEC_PER_YEAR(365*24*60*60UL)
365*24*60*60这个常整数的缺省类型为int。
低位系统中(如16位系统),365*24*60*60这个数会整数溢出。
所以将其转为无符号长整形,在其后加上UL(即unsigned long)。
虽然在高位操作系统中,这个数字不会超出int的范围,可以不加UL;但是这样存在安全隐患。
(4)带参数宏定义
带参数宏定义举例:MAX宏,求2个数中较大的一个
#define MAX(a,b)(((a)>(b))?(a):(b))
关键:
第一点:要想到使用三目运算符来完成;
第二点:注意括号的使用;
这些括号的使用是为了防止优先级有关的问题。
若参数未加括号,很容易出错。
(2)带参数宏定义需要注意括号的使用
宏定义中关于括号有很多陷阱,所以宏定义一定要注意括号的使用,我们来看一个例子;
macro_test.c:
1#define X(a,b)a+b
2
3int main(void)
4{
5int x=1;y=2;
6int c=3*X(x,y);
7return0;
8}
gcc-E macro_test.c-o macro_test.i得到如下macro_test.i文件:
macro_test.i:
1#1"macro_test.c"
2#1"<built-in>"
3#1"<command-line>"
4#1"/usr/include/stdc-predef.h"134
5#1"<command-line>"2
6#1"macro_test.c"
7
8
9int main(void)
10{
11int x=1;y=2;
12int c=3*x+y;
13return0;
14}
由于在定义X(a,b)时未给字符串加对应的括号,所以3*(x+y),变成了不是我们想象的3*x+y;所以宏定义应该写成#define X(a,b)((a)+(b)),保证万无一失。
练习:设想如果宏定义:#define abs(x)((x)>=0?(x):-(x)),如果丢失了括号#deffine abs(x)x>=0?x:-x求abs(a-b)求值是什么样的结果?所以注意带参宏并不是函数,只是相似而已。
(3)带参数宏定义与普通函数的区别(宏定义的缺陷)
(1)宏定义是在预处理期间处理的,而函数是在编译期间处理的。
这个区别带来的实质差异是:宏定义最终是在调用宏的地方把宏体原地展开,而函数是在调用函数处跳转到函数中去执行,执行完后再跳转回来。
宏定义和函数的最大差别就是,宏定义是原地展开,因此没有调用开销;而函数是跳转执行再返回,因此函数有比较大的调用开销。
所以宏定义和函数相比,优势就是没有调用开销,没有传参开销,所以当函数体很短(尤其是只有一句代码时)可以用宏定义来替代,这样效率较高。
(2)带参宏和带参函数的一个重要差别就是:宏定义不会检查参数的类型,返回值也不会附带类型;而函数有明确的参数类型和返回值类型。
当我们调用函数时编译器会帮我们做参数的静态类型检查,如果编译器发现我们实际传参和参数声明不同时会报警告或错误。
注意:用函数的时候程序员不需要太操心类型不匹配的问题,因为编译器会检查,如果不匹配编译器会报警告;用宏的时候程序员必须注意实际传参和宏所希望的参数类型一致,否则可能编译不报错但是运行有误。
通过上表我们发现,宏和函数各有千秋,各有优劣。
总的来说,如果代码比较多用函数适合而且不影响效率;但是对于那些只有一两句代码的函数开销就太大了,适合用带参宏。
但是用带参宏又有缺点:不会检查参数类型。
(4)宏定义和函数综合产物之内联函数和inline 关键字
(1)内联函数通过在函数定义前加inline 关键字实现,所以仅把inline 放在函数声明处是不起作用的。
(2)内联函数本质上是函数,所以有函数的优点,内联函数是编译器负责处理的,编译器可以帮我们做参数的静态类型检查;但是它同时也有带参宏的优点,没有调用开销,而是原地展开。
(3)当我们的函数内函数体很短(譬如只有一两句代码)的时候,我们又希望利用编译器的参数类型检查来排错,我还希望没有调用开销时,最适合使用内联函数。
如需要获取更多学习视频资源欢迎加入397164505朱老师物技术交带参函数宏内联函数优点编译器会做参数的静态类型检查。
原地展开,没有调用开销;并且在预处理阶段完成,不占编译的时间。
函数代码被放入符号表中,
在使用时进行替换(像宏一样展开),没有调用开销,
效率很高;并且会进行参数
类型检查
缺点
函数需要参数、返回地址等的压栈和出栈,栈变量的开辟和销毁;运行效率没有带参宏高。
不进行类型检查,多次宏替换会导致代码体积变大,而且由于宏本质上是原封不动的替换,可能会由于一些参数的
副作用导致得出错误的
结果。
尤其是在宏体内
对参数进行++和--操作
时。
如果函数的代码较长,使用内联将消耗过多内存;如果函数体内有循环,那么执行函数代码时间比较长。
流群学习讨论。
(4)普通带参函数、宏和内联函数三者之间的优劣对比
(5)只有宏名没有宏体的宏
比如:#define DEBUG,主要用于条件编译,用于实现跨平台。
这些宏将在后续的条件编中译详解。