C语言中的宏(整理)

文章转自:http://www.cnblogs.com/wb-DarkHorse/archive/2013/04/27/3046749.html

一:# 、##和__VA_ARGS__

1、#
假如希望在字符串中包含参数,ANSI C是允许这样做的,在类函数宏的替换部分,#的作用相当于一个预处理的运算符,他可以把语言字符串转化成字符串。例如,如果x是一个宏变量,那么#x可以把参数名转化成相应的字符串。该过程成为字符串化(stringizing)。

#include <stdio.h>

#define PSQR(x) printf("the square of "#x" is %d.\n",(x)*(x))


int main(int argc,char *argv[]){
    int y = 4;
    PSQR(y);
    PSQR(2+4);
    return 0;
}

输出的结果是:

the square of y is 16.
the square of 2+4 is 36.

第一次调用宏时使用“y”代替#x;第二次调用时用“2+4”代替#x。

2、##
##运算符可以用于类函数宏的替换部分。另外。##还可以用于类对象宏的替换部分。这个运算符号把两个语言符号组合成单个语言符号。例如:#define XNAME(n) x##n
然后这样宏调用:
XNAME(4)
展开之后:x4
例如程序:

#include <stdio.h>

#define XNAME(n) x##n
#define PXN(n) printf("x"#n" = %d\n",x##n)

int main(int argc,char *argv[]){
    int XNAME(1) = 12; //int x1 = 12
    PXN(1); //printf("x1 = %d\n",x1)
    return 0;
}

程序结果:x1 = 12

关于#和##再补充一点,来自《C语言高级编程》

当宏参数是另一个宏的时候
需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开.
1, 非'#'和'##'的情况
#define TOW(2)
#define MUL(a,b) (a*b)
printf("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW));
这行的宏会被展开为:
printf("%d*%d=%d\n", (2), (2), ((2)*(2)));
MUL里的参数TOW会被展开为(2).
2, 当有'#'或'##'的时候
#define A(2)
#define CONS(a,b)int(a##e##b)
这行会被展开为:
printf(“%s\n”, CONS(A, A));// compile error
这一行则是:printf("%s\n", int(AeA));
INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏. 加这层宏的用意是把所有宏的参数在这层里全部展开, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数.
#define A(2)
#define _CONS(a,b)int(a##e##b)
#define CONS(a,b)_CONS(a,b)// 转换宏
printf("%d\n", CONS(A, A));
输出为:200
CONS(A, A)-->_CONS((2), (2))--> int((2)e(2))

3、可变参数宏 … 和 VA_ARGS
__VA_ARGS__是一个可变参数的宏,很少有人知道这样一个宏,这个可变参数的宏时新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。
实现思路就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。这样预定义宏__VA_ARGS__就可以被用在替换部分中,替换省略号所代表的字符串。比如:

#include <stdio.h>

#define PR(...) printf(__VA_ARGS__)

int main(int argc,char *argv[]){
    int wt = 1,sp = 2;
    PR("hello\n");
    PR("weight = %d,shipping = %d\n",wt,sp);
    return 0;
}

输出的结果是:

hello
weight = 1,shipping = 2
  • 注意:省略号只能代表最后面的宏参数,#define W(x,…,y)错误!!

另外可变宏的另外一个在调试中很是给力,也可以使用来输出调试信息到日志:
(参见原作者文章:http://blog.youkuaiyun.com/hinyunsin/article/details/6546670 ,现在整理如下,感谢原作者!)

编译器内置宏:
先介绍几个编译器内置的宏定义,这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙帮助我们输出非常有用的调试信息!

ANSI C 标准中有几个标准预定义的宏(非常有用!):
__LINE__:在源代码中插入当前源代码的行号:
__FILE__:在源文件中插入当前源文件名;
__DATE__:在源文件中插入当前的编译日期;
__TIME__:在源文件中插入当前编译时间;
__STDC__:当要求程序严格遵循ANSI C 标准时该标识被赋值为1;
__cplusplus__:当编写C++程序时该标识符被定义。
编译器在进行源码编译的时候,会自动将这些宏替换成为相应的内容。

看到这里,你的眼睛应该一亮了吧,嗯,是的,__FILE__和__LINE__正是我们前面想要的输出的,于是,我们的每一条语句都变成了:
DEBUG("FILE:%s,LINE:%d...",__FILE__,__LINE__,...)
但是,我们还是不满足,依然发现,还是很讨厌,为什么每条语句都要写"FILE:"__FILE__", LINE: %d 以及,__LINE,这两个部分呢?这不是浪费我们时间么?

哈哈,是的,这就是本次大结局,把DEBUG写成这样:
DEBUG(format,...) printf("FILE:"__FILE__",LINE:%d:"format"/n",__LINE__,##__VA_ARGS__)

没错,就是这样!下面,所有的DEBUG信息都会按照这样的方式输出:
FILE: xxx, LINE: xxx, …….
最后,老规矩,coding测试。
//============================================================================
// Name : debug.cpp
// Author : toohoo
// Version : 1.0
// Copyright : pku
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <stdio.h>

#define __DEBUG__

#ifdef __DEBUG__
#define DEBUG(format,...) printf("File: "__FILE__", Line: %05d: "format"\n", __LINE__, ##__VA_ARGS__)
#else
#define DEBUG(format,...)
#endif

int main(int argc, char **argv) {
    char str[]="Hello World";
    DEBUG("A ha, check me: %s",str);
    return 0;
}

测试结果为:

toohoo@ubuntu:~/myblog/C+CPP$ gcc  hello.c -o hello
toohoo@ubuntu:~/myblog/C+CPP$ ./hello 
File: hello.c, Line: 00020: A ha, check me: Hello World

关于#和##,还可以看看这篇博客:http://www.cnblogs.com/morewindows/archive/2011/08/18/2144112.html,讲的比较详细)

4、 计算结构体中成员变量的偏移

#define offset(s,m) (size_t)&(((s*)0)->m)

二:C++中有用的一些宏定义

1、命名空间使用的宏定义
最近在看cocos2d-x的代码,有些地方写的很有意思(我自己以前代码写的很少,可能少见多怪了),所以记录下来。其实这也不算技巧了,对于做事讲究省布料的人,可以这么做:

#define NS_CC_BEGIN                     namespace cocos2d {
#define NS_CC_END                       }
#define USING_NS_CC                     using namespace cocos2d

用的时候,可以这样写:

/****custom head file***/
//**.h

NS_CC_BEGIN
//add your code here
NS_CC_END

//**.cpp
#include "**.h"
USING_NS_CC;
//add your code here

2,成员变量和函数的定义

#define CC_PROPERTY_READONLY(varType, varName, funName)\
protected: varType varName;\
public: virtual varType get##funName(void);

#define CC_PROPERTY_READONLY_PASS_BY_REF(varType, varName, funName)\
protected: varType varName;\
public: virtual const varType& get##funName(void);

再看一个:

#define CC_PROPERTY(varType, varName, funName)\
protected: varType varName;\
public: virtual varType get##funName(void);\
public: virtual void set##funName(varType var);

#define CC_PROPERTY_PASS_BY_REF(varType, varName, funName)\
protected: varType varName;\
public: virtual const varType& get##funName(void);\
public: virtual void set##funName(const varType& var);

这样的宏定义是不是很奇葩?但很好懂,而且比较节省体力,方便为类声明一些成员,同时提供外部读或写接口。写在这里借鉴下。

三:常用宏

这个也是来自cocos2d-x的代码,在头文件"CCPlatformMacros.h"中。

#define CC_SAFE_DELETE(p)            if(p) { delete (p); (p) = 0; }
#define CC_SAFE_DELETE_ARRAY(p)    if(p) { delete[] (p); (p) = 0; }
#define CC_SAFE_FREE(p)            if(p) { free(p); (p) = 0; }
#define CC_SAFE_RELEASE(p)            if(p) { (p)->release(); }
#define CC_SAFE_RELEASE_NULL(p)    if(p) { (p)->release(); (p) = 0; }
#define CC_SAFE_RETAIN(p)            if(p) { (p)->retain(); }
#define CC_BREAK_IF(cond)            if(cond) break;

总结:宏这个玩意是把双刃剑,因为编译器不会提供类型安全检查,只是进行替换,所以很容易将一些错误隐藏起来,总之,要在能把控的情况下玩弄,否则磨刀出鞘,会嗜血的!

<think>好的,我现在要帮用户了解C语言中的定义。首先,我需要整理自己关于的知识,确保回答准确。根据用户提供的引用内容,引用[1]提到了是预处理器指令,用于定义常量和函数,可以在多个文件中共享。引用[2]详细讲了带参数和不带参数的,以及替换时的注意事项。引用[3]则提到了用实现函数,比如swap的例子。 用户的问题是关于定义的使用教程,所以需要结构清晰,分步骤讲解。首先应该从基本概念开始,解释什么是,然后分点讲不带参数和带参数的,接着讲多文件共享的方法,再给出示例,最后注意事项和常见问题。 需要检查是否有遗漏的部分,比如的优缺点,或者常见的错误用法。比如,引用[2]提到不带参数的只是简单的替换,而带参数的需要注意括号,避免展开错误。例如,S(a,b)定义为a*b的话,当传入参数是表达式时可能出错,所以应该用括号包裹参数和整个表达式。 另外,用户提供的引用中有提到在多个文件中共享,这可以通过头文件来实现,所以在教程中需要说明如何将定义在头文件中,然后在其他.c文件中包含该头文件。 还要注意和函数的区别,比如在预处理阶段展开,没有类型检查,而函数在编译后执行,有类型安全。可能用户会关心什么时候该用,什么时候用函数,所以需要解释适用场景。 在示例部分,可以举定义常量、带参数的,以及多行的例子。比如用实现MAX函数,或者交换两个变量的SWAP,同时要指出可能的问题,比如参数中有自增运算符时的副作用。 最后生成相关问题的时候,要覆盖的定义、使用、优缺点、多文件共享、常见错误等方面的问题,帮助用户进一步学习。</think>### C语言定义使用教程 #### 一、定义基本概念 定义是C语言的**预处理器指令**,以`#define`开头,用于在编译前对代码进行文本替换。它分为两类: 1. **不带参数的** 用于定义常量或简化复杂表达式,例如: ```c #define PI 3.1415926 // 定义常量 #define BUFFER_SIZE 1024 ``` 预处理时,所有`PI`会被替换为`3.1415926`[^2]。 2. **带参数的** 类似函数,但无类型检查,例如: ```c #define MAX(a,b) ((a) > (b) ? (a) : (b)) // 求最大值 #define SQUARE(x) ((x) * (x)) // 平方计算 ``` 调用`MAX(3,5)`会被替换为`((3) > (5) ? (3) : (5))`。 --- #### 二、定义的使用方法 ##### 1. 定义常量 ```c #define MAX_ITERATION 1000 // 定义循环最大次数 #define MESSAGE "Hello" // 定义字符串常量 ``` ##### 2. 带参数的 - **基本语法** ```c #define (参数列表) 替换文本 ``` - **示例** ```c #define SWAP(T, a, b) { T temp = a; a = b; b = temp; } // 泛型交换 #define PRINT_INT(x) printf("%d\n", x) // 打印整数 ``` 使用`SWAP(int, x, y)`会展开为`{ int temp = x; x = y; y = temp; }`[^3]。 ##### 3. 多行定义 用`\`实现多行定义: ```c #define LOG(msg) \ printf("[INFO] %s\n", msg); \ write_log(msg) // 记录日志到文件 ``` --- #### 三、多文件共享定义 将集中定义在**头文件**(如`config.h`)中: ```c // config.h #ifndef CONFIG_H #define CONFIG_H #define DEBUG_MODE 1 // 调试开关 #define MAX_DATA_LEN 4096 // 数据长度限制 #endif ``` 其他文件通过`#include "config.h"`即可使用[^1]。 --- #### 四、注意事项 1. **括号必要性** 错误示例:`#define SQUARE(x) x*x` 调用`SQUARE(1+2)`会展开为`1+2*1+2=5`(正确值为9)。 修正:`#define SQUARE(x) ((x)*(x))`。 2. **副作用问题** 错误示例:`#define INCREMENT(x) x++` 调用`INCREMENT(a++)`会导致`a++++`(语法错误)。 建议:避免在参数中使用自增/自减操作。 3. **与函数的区别** | 特性 | | 函数 | |------------|---------------------|-------------------| | 执行阶段 | 预处理阶段替换文本 | 运行时调用 | | 类型检查 | 无 | 有 | | 调试难度 | 难以追踪 | 可单步调试 | --- #### 五、代码示例 ```c #include <stdio.h> #define CIRCLE_AREA(r) (PI * (r) * (r)) // 圆面积计算 #define PI 3.1415926 int main() { double radius = 5.0; printf("Area: %.2f\n", CIRCLE_AREA(radius)); // 输出Area: 78.54 return 0; } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值