预处理和环境 4个月前

编程语言
636
预处理和环境

一、翻译环境和执行环境

翻译环境:在这个环境中源代码被转换为可执行的机器指令 执行环境:用于实际执行代码

具体什么意思我们通过画图来解释

image

1.翻译环境


翻译环境分为两步,编译和链接

image

那么编译和链接是什么呢,同样以图来说明

image

那么问题又来了,编译和链接的过程中发生了什么

由于编译和链接内部需要Linux环境深入了解,这里直接上定义

image

2.执行环境

简述一下程序执行的过程

1.程序必须载入内存中. 在有操作系统的环境中:一般由操作系统完成 在独立的环境中:程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存完成 2.程序执行便开始,调用main函数 3.开始执行程序代码.这个时候程序将使用一个运行的堆栈(stack)(堆栈就是栈),存储函数的局部变量和返回地址. 程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值. 4.终止程序.正常终止main函数,也可能是意外终止.

以上


二、预处理详解

1.预定义符号

__FILE__ //进行编译的源文件  %s
__LINE__ //文件当前的行号    %d
__DATE__ //文件被编译的日期  %s
__TIME__ //文件被编译的时间  %s
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义  %d

三、#define定义宏

#define允许把参数替换到文本中 勤加()

①#

#可以把参数替换到字符串中

引出一下常见情况

void print(int x)
{
    printf("??的结果是%d\n",x); // ?? 里面到底放什么呢
                               //显然函数并不能做到这个功能
}
int main()
{
  int a = 10;
  //打印出: a的结果是10
  print(a);
  
  int b = 20;
  //打印出: b的结果是20
  print(b);
  
  int c = 30;
  //打印出: c的结果是30
  print(c);
}

所以我们使用宏

首先我们要知道

printf("halo");
printf("ha""lo");
//这两个代码的结果是一样的,由此

#define print(X) printf(#X "的结果是%d",X)
int main()
{
  int a = 10;
  //打印出: a的结果是10
  print(a);

  //本质上来讲,替换后
  printf(#X "的结果是%d",X);
  printf("a" "的结果是%d",X);
  
  int b = 20;
  //打印出: b的结果是20
  print(b);
  
  int c = 30;
  //打印出: c的结果是30
  print(c);
}

image

//格式不同的处理
#define print(X,FORMATE) printf(#X "的结果是"FORMATE,X)
int main()
{

    int a = 20;
    //打印出: a的结果是20
    print(a, "%d\n");

    float b = 5.5;
    //打印出: b的结果是5.5
    print(b, "%f\n");
}

image

注意:#不能直接用在printf中,只能在宏内使用

②##

直接举个例子

#define CAT(X,Y) X##Y##Z
int main()
{
  int class1011 = 100;
  printf("%d\n",CAT(class,10,11));
  printf("%d\n",class1011);
  //两者打印结果相同,可以将##看作是一个连接符号
  
  return 0;
}

四、宏和函数的取舍

属性 #define定义宏 函数
代码长度 每次使用时,宏代码都会被插入程序中.除了非常小的宏除外,程序的长度会大幅提升 函数代码只出现于一个地方,每次调用这个函数时,都调用那个地方的同一份代码
执行速度 更快 存在函数的调用和返回的额外开销,所以相对较慢
操作符优先级 宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写时多些括号 函数参数只在函数调用的时候求值一次,它的结果值传递给函数.表达式的求值结果更容易预测
带有副作用的参数 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果 函数传参只在传参的时候求值一次,结果更容易控制
参数类型 宏的参数与类型无关,只要对参数的操作时合法的,它就可以使用于任何参数类型 函数的参数与类型有关,如果参数的类型不同,就需要不同的函数
调试 宏是不方便调试的 函数是可以逐语句调试的
递归 宏不能递归 函数可以递归

以上

五、undef

该指令可以移除一条宏定义 比如说

image

#undef NAME

六、条件编译

先上一个例子

#define ADD 
int main()
{
  #ifdef ADD       //如果定义了ADD宏,那么endif前就可以运行,反之则会在预处理时被略过
  printf("666");
  #endif
  
  #if 1            //非零则进入,否则在预处理时被略过
  printf("666");
  #endif
  
  return 0;
}

#ifdef 后跟宏名称 #if 后跟常量表达式 同样都是以#endif作为结尾

同样的还有#else 、#elif 其后面跟的都是常量表达式

int main()
{
 
  
  #if 1==1            //常量表达式
  printf("666");

  #elif 2==2          //常量表达式
  printf("999");

  #else 3==3          //常量表达式
  printf("114514");
  
  #endif
  
  //同if else 一样,这三条语句只能进入一个,不能同时进入
  
  return 0;
}

也有

#ifndef 作用与 #ifdef相反 #if defined() 括号内加入宏名,作用与 #ifdef相同 也可以#if !defined(),作用与 #ifdef相反

image
EchoEcho官方
无论前方如何,请不要后悔与我相遇。
1377
发布数
439
关注者
2222703
累计阅读

热门教程文档

QT
33小节
爬虫
6小节
Redis
14小节
Lua
21小节
Objective-C
29小节