c语言中的预处理指令

时间:2022-02-02 01:11:56

预处理指令包括宏定义,文件包含,条件编译等

#define ,#undef,#include,#ifdef,#else,#endif,#ifndef,#if,#elif,#line,#error,#pragma

1.#define 宏定义,在预处理阶段,会进行简单的文本替换

#include<stdio.h>
#define TWO 2
#define SAY "Any problem in computer science can be solbed by\
                   another layer of indirection."

#define FOUR TWO*TWO
#define PX      printf("X = %d\n",x)
#define FMT    "X = %d\n"

int main(void)
{
  int x =TWO;
  
  PX;
  x=FOUR;
  printf(FMT,x);
  printf("%s\n",SAY);
  printf("TWO:SAY\n"); 
  return 0;
}


result: x is 2
x is 4
Any problem in computer science can be solbed by another layer of indirection.
TWO:SAY

 

(1)在双引号中的宏没有被替换,宏也存在一些缺陷

#include<stdio.h>

#define N 2+3
#define CHARP char *

int main(void)
{
    CHARP p,q;
    
   int num =N*2;
   printf("%d\n",num);
   printf("p=%d,q=%d\n",sizeof(p),sizeof(q));

  return 0;
}

32位机子编译结果  gcc define.c -m32
result :    8
            p=4,q=1 

N被 简单的替换成了  2+3*2     //(2+3)*2 
指针的大小为 4个字节,char 大小为 1个字节

 

(2)利用宏创建字符串   #运算符

#include<stdio.h>
#define str(x)  "aaaaa"#x"aaaaa"
#define PTR(x) printf("the result is "#x"=%d\n",(x)*(x))

int main(void )
{
   int  y=5;
  printf("%s\n",str(10));
  PTR(y);
  PTR(2+4);
 return 0;
}

(3)预处理的粘合剂  ##运算符(解决了,参数变量与宏展开,无法一一对应的问题)

#include<stdio.h>

#define XNAME(n)  x##n
#define  sum(a,b) (a##a+b##b)

int main(void)
{
  int XNAME(1) = 14;
  int XNAME(2) = 20;
   
   printf("%d\n",sum(2,3));
   printf("x1=%d,x2=%d\n",x1,x2);
  return 0;
}

result :  55
            x1=14,x2=20
           

 (4)可变宏 ... 和 __VA_ARGS__

    可变参数的宏可以实现参数的可变性,如printf(...) ,  ...前面可以有参数,后面不能有参数

#include<stdio.h>

#define PF(...)     printf(__VA_ARGS__)
#define PT(x,...)  printf("message "#x" :" __VA_ARGS__)

int main(void)
{
 PF("china");
 PF("weight = %d height = %f\n",100,120.0)
 
 PT(1,"china\n");
 PT(2,"weight =%d heith =%f\n",100,120.0);

 return 0;
}

result:   china
            weight = 100 height =120.000000
            message 1:china
            message 2:weight = 100 height = 120.000000

 (5)宏定义一年有多少秒,并且打印输出

#include <stdio.h>

#define SECOND_PER_YEAR (60*60*24*356UL)
int main(void)
{
    unsigned int a;
    a=SECOND_PER_YEAR+1;
    printf("%u\n",a);
    printf("Hello World!\n");
    return 0;
}

 

 

2.条件编译 依据条件,判断哪些程序段参与编译

(1)#ifdef(#ifndef)  #else   #endif

#include<stdio.h>

#define x86

int main(void)
{
 #ifdef x86
    printf("**********\n");
  #else
    printf("$$$$$$$$$$\n");
  #endif
 return 0;
}

(2)#if #elif  #endif
在写跨平台程序的时候,经常见到这样的语句,易于程序的移植

#include<stdio.h>

#define x86   100
#define MIPS  200
#define POWERPC 300
#define MACHINE  POWERPC

int main(void)
{
  #if MACHINE == x86
       printf("**********");
  #elif MACHINE == MIPS
       printf("%%%%%%%");
   #elif  MACHINE == POWERPC
        printf("$$$$$$$$$$$");
   #endif
  return 0;
}

用条件编译来注释,解决了/**/不支持嵌套的特点

3.文件包含 #include 支持嵌套,递归进行的

(1) #include<stdio.h>

       从系统指定路径中搜索包含头文件 Linux中的系统路径为/usr/include

 (2)#include "myString.h"

       从工程当前路径中搜索包含头文件,当前路径没有,就到系统路径下搜索包含

  (3)c语言是以文件为单位进行编译的,编译期只需要函数声明即可

  (4)定义头文件

      自包含可以免去多余的前向声明,避免头文件重复包含采用如下格式:

      #ifndef  __XX_H__

      #define __XX_H__

       //数据类型声明

       //函数声明

       #endif

 

4.其他

(1)系统自定义宏

    __DATE__  进行预处理的日期 ("Mmm dd yyyy"形式的字符串文字)

    __FILE__    代表当前源代码文件名的字符串 文字

    __LINE__    代表当前源代码中的行号的整型常量

    __TIME__    源文件编译时间,格式“hh: mm: ss”

    __func__     胆怯所在函数名

 __FILE__  __LINE__ 对开发者非常有用的提示,可以写在日志里

#include<stdio.h>
#include<stdlib.h>

void why_me()
{
 printf("The file is %s\n",__FILE__);
 printf("The date is %s\n",__date__);
 printf("The time is %s\n",__TIME__);
 printf("This is line %d\n",__LINE__);
 printf("This function is %s\n",__func__);
 
  why_me();
 return 0;
}

void why_me()
{
   printf("This function is %s\n",__func__);
   printf("The file is %s\n",__FILE__);
   printf("This is line %d\n",__LINE__);
}

(2)#line 和 #error

#line 用于重置由 __LINE__ 和 __FILE__宏定义的行号和文件名

#line 1000  //把当前行号重置为1000

#line 10 "cool.c"  //把行号重置为10 ,文件名重置为 cool.c

 

#error 指令使预处理器发出一条错误信息,该信息包含指令中的文本,可能的话,编译过程应该中断

#if __STDC_VERSION__ != 199901L

          #error   Not c99

#endif

 

(3)pragma

在现在编译器中,可以用命令行参数或IDE菜单修改编译器的某些设置,也可用#pragma将编译器指令置于源代码中

#pragma message ("消息文本")

#ifdef  _X86

#pragma message("_X86 macro ativated!")

#endif

 

#pragma code_seg(["section-name"[,"section-class"]])

能够设置程序中函数代码存放的代码段,驱动程序开发中用到

 

#pragma once 只要在头文件的最开始加入这条指令就能保证头文件被编译一次,这条指令

vs中就已经有了,但是考虑到兼容性并没有太多的使用它

 

#pragma pack(n)  //按n字节对齐

#pragma apop()   //取消n 字节对齐方式

 

#pragma warning(disable:450734) //不显示4507 和34号警告信息

#pragma warning(once:4385)  //4385警告信息仅报告一次

#pragma warning(error:164)  //把164号警告信息作为一个错误

 

#pragma disable

在函数前声明,只对一个函数有效。该函数调用过程中将不可被中断。一般在C51中使用

 

#pragma data_seg 建立一个新的 数据段 并定义共享数据

#pragma data_seg("shareddata")

HWND sharedwnd=NULL; //共享数据

#pragma data_seg()

一般用于DLL中。这个数据段中的全局变量可以被多个进程共享,否则多个进程之间无法共享DLL中的全局变量;共享数据必须出示化,否则微软编译会把没有初始化的数据放在.bss段中,导致共享失败

#pragma hdrstop

表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占用太多磁盘空间,所有使用这个选项可以排除一些头文件

#pragma resource  "*.dfm"  把*.dfm 文件中的资源加入工程。 *.dfm中包括窗体外观的定义