C语言的预处理详析

预定义符号

C语言中含有一些预定义符号,我们可以直接使用。
•__FILE__ 当前文件路径
•__LINE__ 文件当前行号
•__DATE__ 文件编译时的日期
•__TIME__ 文件编译时的时间
•__STDC__ 如果编译器遵循ANSI C则为1,否则未定义

#include<stdio.h>
int main()
{
	printf("%s\n", __FILE__);
	printf("%d\n", __LINE__);
	printf("%s\n", __TIME__);
	printf("%s\n", __DATE__);
	return 0;
}

运行结果为:
在这里插入图片描述

#define定义常量

基本语法:#define name stuff
源代码预编译后会将name全部替换成stuff。
常见用法

#define MAX 1000
#define reg register //为 register这个关键字,创建⼀个简短的名字
#define do_forever for(;;) //⽤更形象的符号来替换⼀种实现
#define CASE break;case //在写case语句的时候⾃动把 break写上。
// 如果定义的 stuff过⻓,可以分成⼏⾏写,除了最后⼀⾏外,每⾏的后⾯都加⼀个反斜杠(续⾏符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
 date:%s\ttime:%s\n" ,\
 __FILE__,__LINE__ , \
 __DATE__,__TIME__)

#define定义宏

#define允许将参数替换到文本中,这种实现方式称为宏(macro)或定义宏(define macro)
基本语法:#define name( parament-list ) stuff
其中 parament-list为一个符号表,它的内容可能出现在stuff中,注意参数列表的“(”必须与name紧邻,不能有空格。
常见使用方式:

#define SQUARE(x) ((x)*(x))
#define DOUBLE(x) ((x)+(x))
#define ADD(x,y) ((x)+(y))

•值得注意的是,当宏里面参数出现了一次以上,那么宏参数可能带有副作用。

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);

此时x、y、z的值难以确定。
•宏替换规则

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上
    述处理过程。
    •注意:定义宏中可以出现其他#define定义的参数,但是宏不能实现递归;字符串内容不被翻译成宏参数。
    •offsetof的模拟实现:
#include<stdio.h>
#define OFFSETOF(type,men) (size_t)&(((type*)0)->men)

宏和函数的比较

属性#define定义宏函数
代码长度一般较长较短
执行速度更快较慢
操作符优先级可能出现错误准确、可预测
带有副作用的参数难以观察,易出现易控制
参数类型参数无关,可以是任意类型类型固定
调试不能调试可以调试
递归不能递归可以递归

#和##

#运算符

#运算符可以将一个宏参数转换为字符串字面量,比如#a就会变成“a”。
示例:

#include<stdio.h>
#define PUTS(x,format) printf("the value of "#x" is "format"\n",x)
int main()
{
	int a = 95;
	PUTS(a, "%d");
	double b = 3.1415;
	PUTS(b, "%lf");
	return 0;
}

运行结果为:
在这里插入图片描述

##运算符

##又称为记号粘合符,## 可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。
例如实现多个相似函数的定义:

#include<stdio.h>
#define  GENERIC_MAX(type) \
type type##max(type x,type y)\
{\
	return x>y?x:y;\
}
GENERIC_MAX(int)
int main()
{
	int x = 10, y = 20;
	int z = intmax(x, y);
	printf("%d\n", z);
	return 0;
}

运行结果为:
在这里插入图片描述
•命名约定:
一般来讲函数的宏的使用语法很相似。所以语言本⾝没法帮我们区分二者。
那我们平时的一个习惯是:把宏名全部大写,函数名不要全部大写

#undef

#undef NAME用于移除一个宏定义。

命令行定义

许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。
这样,这个程序在不同机器执行时,能定义不同的值,更具灵活性。

条件编译

•条件编译是当满足条件就编译,不满足条件就不参与编译
•基本的语法为:

#if 常量表达式
语句
#enif

需要注意的是,#if后面的一定是常量表达式,而不能出现变量。
•其他的条件编译函数:

#elif
#ifdef//等价于#if defined(symbol)
#ifndef//等价于#if !defined(symbol)

•显然,当我们调试信息不想删除的时候可以适用条件编译
•条件编译还可以嵌套:

#if defined(OS_UNIX)
 #ifdef OPTION1
 unix_version_option1();
 #endif
 #ifdef OPTION2
 unix_version_option2();
 #endif
#elif defined(OS_MSDOS)
 #ifdef OPTION2
 msdos_version_option2();
 #endif
#endif

头文件包含

•头文件包含包括两种方式:include"“,include<>;”“包含时会优先检索当前文件夹是否有该头文件,如果没有再检索标准位置;<>包含则会直接检索标准位置。
•这告诉我们,我们可以使用”"包含库函数的头文件。
•我们自定义的头文件显然可以和库函数头文件同名
•嵌套文件包含:当我们多次包含同一个头文件时,就会浪费空间,甚至发生错误。所以我们需要限定头文件包含时copy的次数,可以加入如下代码:

#ifndef __NAME_H__
content
#enif

或者

#pragma once
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值