计算机c语言宏定义实例讲解

合集下载

详解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++语言宏定义使用实例详解

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语言的宏定义

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 函数内部宏定义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课件

C语言宏定义(高级篇)PPT课件
在软件开发过程中需要定制各种小工具在软件开发过程中需要定制各种小工具实现模块公共功能的复用和易用其中编码实现模块公共功能的复用和易用其中编码过程中的过程中的bugbug调试工具就是非常重要的一种调试工具就是非常重要的一种类型
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语言中是一种预处理指令,它可以在编译之前对代码进行替换。

宏定义可以用来定义常量、创建函数等。

下面是一个简单的宏定义函数的例子:
```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语言宏定义详解

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语言宏定义与使用分析

C语言宏定义与使用分析
#define MALLOC(type, x) (type*)malloc(sizeof(type)*x)
#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;
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 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,主要用于条件编译,用于实现跨平台。

这些宏将在后续的条件编中译详解。

相关文档
最新文档