#define MACRO
定义宏
。
宏
有2
个作用:
符号
(常量名、函数名等)的别名
,以简化编码,在编译的时候进行替换。示例:
#ifndef DEBUG
#define DEBUG
#endif
int main(){
#ifdef DEBUG
printf("debug\n");
#endif
}
为了避免重复定义宏
,#define MACRO
通常会与#ifndef MACRO结合使用。
示例:
#define ONE 1
#define TWO 2
#define THREE (ONE + TWO)
注意:上面的宏定义使用了括号。尽管它们并不是必须的。但出于谨慎考虑,还是应该加上括号的。
示例:
#include<stdio.h>
#define MAX(a, b) a > b ? a : b
int main() {
int max = MAX(3, 4);
printf("max = %d\n", max);
return 0;
}
注意:有人也把这个称为带参数的宏
。总之,它就像函数一样,可以使用参数。只是它与函数的定义不同,它没有返回值。 类似于内联函数
,在编译的第一个阶段,也就是预处理阶段
,被完整的替换。
使用cc命令预处理:
gcc -E test.c
我们看到:
int max = MAX(3, 4);
被替换为如下:
int max = 3 > 4 ? 3 : 4;
而且:
#define MAX(a, b) a > b ? a : b
也被删除了,因为一旦被替换,就没有必要在存在了。
示例:
#include<android/log.h>
#define TAG "com_fpliu_common_httpsign_HttpSign.c"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__);
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, TAG, __VA_ARGS__);
int x = 20;
LOGV("x = %s", x)
...
表示可变的参数
,它只能放在参数列表的最后。
__VA_ARGS__
就代表...
匹配到的实际参数列表,这里是"x = %s", x
#运算符
用于把实际参数
字符串化。
示例:
#define PASTE(n) "adhfkj"#n
int main(){
printf("%s\n", PASTE(15));
return 0;
}
##运算符
用于把实际参数
连接起来。
示例:
#define NUM(a,b,c) a##b##c
#define STR(a,b,c) a##b##c
int main() {
printf("%d\n", NUM(1, 2, 3));
printf("%s\n", STR("aa", "bb", "cc"));
return 0;
}
Standard C
规定:在stdlib.h
头文件中必须定义下面的宏:
宏 | Since | 类型 | 说明 |
---|---|---|---|
__DATE__ | C89 | char* | 对源文件进行预处理的日期,格式:Mmm dd yyyy |
__TIME__ | C89 | char* | 对源文件进行预处理的时间,格式:hh:mm:ss |
__LINE__ | C89 | int | 当前源代码中的行号 |
__FILE__ | C89 | char* | 当前源代码的文件名 |
__func__ | C99 | char* | 代表当前函数名称,只能在函数中使用。 |
这几个宏在调试程序时是很有用的,因为你可以很容易的知道程序运行到了哪个文件的哪一行。
示例:
#include<stdio.h>
#include<stdlib.h>
#define DATE_TIME __DATE__" "__TIME__
void func() {
printf("release date_time=%s, file=%s, line=%-2d, fuction=%s\n", DATE_TIME, __FILE__, __LINE__, __func__);
}
int main() {
func();
printf("release date_time=%s, file=%s, line=%-2d, fuction=%s\n", DATE_TIME, __FILE__, __LINE__, __func__);
return 0;
}
使用cc命令编译 ⤵︎
gcc -o test test.c
运行结果如下 ⤵︎
release date_time=Mar 13 2020 12:20:52, file=esc.c, line=7 , fuction=func
release date_time=Mar 13 2020 12:20:52, file=esc.c, line=12, fuction=main
Standard C
定义的宏
很少,在实际使用中显然是不够的,所以不同的编译器扩展了一些宏
。
GCC
扩充了的宏
可以通过如下命令获得:
cpp -dM < /dev/null
GCC
扩充了非常多的预定义宏,下面只介绍几个常用的:
宏 | Since | 类型 | 说明 |
---|---|---|---|
__GNUC__ | int | GCC的主版本号 | |
__GNUC_MINOR__ | int | GCC的次版本号 | |
__GNUC_PATCHLEVEL__ | int | GCC的补丁号 | |
__unix__ | char* | 代表UNIX系统 | |
__linux__ | char* | 代表Linux系统 | |
__FreeBSD__ | char* | 代表FreeBSD系统 | |
__NetBSD__ | char* | 代表NetBSD系统 | |
_WIN32 | char* | 代表32-bit Windows系统 | |
_WIN64 | char* | 代表64-bit Windows | |
_WINDOWS | char* | 代表GUI程序 | |
_CONSOLE | char* | 代表控制台程序 |
使用示例:
#include <stdio.h>
#if defined (__GNUC__) && defined (__GNUC_MINOR__) && defined (__GNUC_PATCHLEVEL__)
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#endif
int main() {
/* Test for GCC > 3.2.0 */
#if defined(GCC_VERSION) && GCC_VERSION > 30200
printf("GCC_VERSION = %d\n", GCC_VERSION);
#endif
return 0;
}
这些特定于编译器的宏,使用的时候需要结合条件编译
,以判断他们是否被定义了。
宏 | Since | 类型 | 说明 |
---|---|---|---|
_MSC_VER | int | 如果Visual C++ 的版本号为15.00.20706.01 ,_MSC_VER 宏计算结果为1500 。
|
宏 | 说明 |
---|---|
NDEBUG | 如果定义了该宏, assert(CONDITION) 宏将不起作用;如果没有定义该宏, assert(CONDITION) 宏才会起作用常用的 ./configure --disable-debug 就是给编译器传递-DNDEBUG 参数。 |
_FILE_OFFSET_BITS | 大多数软件使用这个宏来控制该软件是否提供大文件(大于2G)支持。 -D_FILE_OFFSET_BITS=64 表示支持大文件 |
__cplusplus
是C++标准定义的宏
。 当编译器
以C++方式编译代码的时候,会定义此宏
。
C++的编译器
为了支持函数重载
, 在汇编
的时候,要对函数的名字进行一些处理,加入比如函数的返回类型等等。 而C
的编译器
只是记录了简单的函数名字而已,不会加入其他的信息。 也就是说:编译器
对C++和C
的函数的处理是不一样的。
extern "C"{}
是C++的语法,表示extern "C"{}
中的代码按照C
语法编译, 这样是为了让C
编写的代码尽可能的兼容于C++。
所以,我们写C
代码的时候,通常会将函数的声明用extern "C"{}
包裹起来,以便兼容于C++,好让C++能够调用这些函数。
示例:
#ifndef URL_CODING_H
#define URL_CODING_H
#ifdef __cplusplus
extern "C" {
#endif
char* urlEncode(unsigned char *bytes);
unsigned char* urlDecode(char *url);
#ifdef __cplusplus
}
#endif
#endif
使用cc命令进行预处理:
gcc -E test.c
得到如下:
char* urlEncode(unsigned char *bytes);
unsigned char* urlDecode(char *url);
使用g++命令进行预处理:
g++ -E test.c
得到如下:
extern "C" {
char* urlEncode(unsigned char *bytes);
unsigned char* urlDecode(char *url);
}