【无标题】 <程序环境>和<预处理>(也许会迟到,但永不缺席)

一、程序环境

 1.编译环境

  各位帅哥美女,我又来了,我一直想写这篇关于 程序环境和预处理 的博客,不要问我为什么没写,问就是没时间,现在给你一巴掌让你进入正题:

  相信每一位敲过代码的同志在敲完代码准备运行时的画面是这样的:首先看一眼自己写完的代码,然后心里想着”这么无懈可击的代码一定可以实现我要的结果”,随后按下运行键,看着屏幕上滚动的信息,随即弹出运行窗口,满怀期待的看着它,漆黑的窗口承载着数不清的希望,有的人会因为这个窗口激动地手舞足蹈,感叹自己是个天才,而有的人会因为这个窗口抓耳挠腮、怀疑人生,关掉窗口再看一遍自己的代码,在调试无果后甚至会伤及可怜的键盘(盘盘那么可爱,你为什么要.....)

  扯远了,不知道大家有没有想过,从我们写好代码到运行,这中间到底发生了什么呢?

  直接上图!

详细来说,编译又分为如下几阶段:
预编译阶段: 预处理指令 --------------------------------------- 结果存储在(.i)文件中
编译阶段: 语法分析、词义分析、符号汇总 ------- 结果存储在(.s)文件中
汇编阶段: 形成符号表、汇编指令 -> 二进制指令… -> 结果存储在(.o)文件中

 2.运行环境

  (1).程序必须要载入内存中,而载入内存的方式可以简单分为两种
   ①我们可以选择在有操作系统的环境,操作系统会将程序载入内存中。②我们选择使用独立的环 境时,必须要手动去将程序载入内存,或者可以编写可执行代码将程序放入只读内存中。
  (2).程序开始执行后就开始调用main函数
  (3).执行程序代码,这个时候会调用堆栈,用来存储函数的参数和返回地址,也会在静态内存区开辟空间用来存储静态的局部变量,存储在静态区的变量在整个执行过程中将一直保存它的值。
  (4).程序终止:①正常运行完终止②意外终止

二、预处理

  大家应该都见过这东西:#define xxx ,这个就是预定义符号
   除此之外,还有一些内置的预定义符号:
  ①__FILE__ 当前被编译的源文件
  ②__LINE__ 当前被编译的源文件的行号
  ③__TIME__ 当前被编译的源文件的时间
  ④__DATE__ 当前被编译的源文件的日期
  ⑤__STDC__ 当前被编译的源文件是否遵循ANSI C,遵循值为1,否则未定义

 1.#define定义标识符

  语法:

   #define name stuff   -----   将neme替换成stuff(在预编译阶段完成)

 2.#define定义宏

  #define允许将参数替换到文本中,这种实现方式成为定义宏或者宏。
  语法:

  #define name(parament-list) stuff

   其中parement-list可以是一个由’ , ’(逗号)隔开的符号表,也可能只是一个符号,不管是二者中的哪一个,都可能会出现在stuff中。注意:name与右边的 ( 不能有空白格,否则会被当成stuff中的一部分
   总结一下#define作用:我想大家也大概明白了,#define就是起到一个替换的作用。#define定义标识符时其实就是将name替换成stuff。#define定义宏跟函数有点类似,将参数传过去然后进行替换,不同的是,函数传参需要类型,而宏不用。

 3.使用#define的注意事项

  ①用#define定义标识符和宏时要不要在末尾加上’ ; ’?
   答:按照语法来说是可以的,但是最好不要这样做,因为在C语言中, ""代表语句结束,而我们用#define就是想达到一种替换效果,而如果我们在#define定义时加了"",这就使我们在编写代码时简介的改变了编写时的语法习惯,所以最好不要再末尾加’ ; ’。
   例:

#define MY_NAME “songsong”;
int main()
{ 
  char* p = MY_NAME;//我们习惯的书写格式,这里在编译时就出现语法错误了   
  return 0;
}

   ②#define定义宏要加上( )
    答:一定要加!
   也许很多人在会将#define定义的宏与函数认为是同一种东西,这里可以很明确的告诉大家,并不是,尽管用法相同,长相相同,但是①从内存的角度说完全是两回事,#define定义的宏是在编译之前就进行处理了,也就是预处理阶段,而函数则是在编译时开辟堆栈②从性能来说也有区别,#define定义宏只是进行一个替换,而函数则是进行运算。这也是为什么#define定义宏加()的原因,因为它只起到了一个替换的作用。
例:

#define add(x) x + x // 正确: #define add(x) (x+x)
int main()
{
  int sum = 0;
  sum = 5*add(10); //原意:5*(10+10)=100
                 //实际:5*10+10=60
  return 0;
}

   因此,大家能看出()的重要性了吧!
   ③#define定义宏最好不要使参数自增

  #define fun(a,b) ((a+1)*(b+1)) 
int main()
{
  int x = 3;
  int y = 4;
  int z = fun(x++,y);//注意这里,不仅没有将x+1的值代入进去计算,反而还使x值发生
//变化。
  return 0;
}  

   #define宏 总结:
    ①在调用宏进行替换时,首先对宏的参数进行检查,若参数含有宏替换,那么它们首先被替换。
    ②替换后直接插入到文本位置。
    ③宏不能递归

 4.#和##

  值得一提的是,当#define定义宏的参数部分是字符串时,那么字符串中对应的参数就不会进行替换
  ①#
   作用将参数转换成对应的字符串
   例:
  修改前:

  #define PRINT(type,value) printf(“the value is” type”\n”,value)

  修改后:

 #define PRINT(type,value) printf(“the”# value “is”type”\n”,value)
int main()
{
  int i = 0;
  //PRINT(“%d”,i+3);//printf(“the value is””%d””\n”,i+3),可以看到value并没有被替换
  PRINT(“%d”,i+3); // printf(“the””i+3””is””%d””\n”,value) // 将i+3替换成字符串
  return 0;
}

  ②##
   作用将##两侧的符号合并成为一个符号
   例:

  #define add(x,y) (sum##x = x+y)
int main()
{
  int a = 5;
  int b = 3;
add(3,5);//sum##3 = 3+5 ---- sum3 = 3+5
  reuturn 0;
}

 5.宏和函数相比较

 6.#undef

   若需要将已经宏定义的名字进行重定义,那么首先需要移除这个名字,我们可以直接将原指令删除,然后重新#define,但是这样做太low了,我们只需要用#undef就可以将这个名字原本的定义移除

  7.条件编译指令:

   ①.#if

   #if 常量表达式    //常量表达式由预处理器求值 
   #elif 常量表达式
   #else 常量表达式
   #endif

   例:

   #define symbol...
   #if symbol
   #endif


     #if必须跟随#endif收尾
   ②.判断是否宏定义:

  #if defined(symbol)
  #ifdef symbol

  #if !defined(symbol)
  #ifndef symbol

 8.头文件的包含

  我们在使用函数时,都会引用头文件,引用的格式:#include <xxxx.h> or #include”xxxx.h”
也许大家只知道,使用库函数里的函数时采用前者的格式,使用自己编写的函数时,使用后者的格式,那么大家有没有想过为什么呢?
  原因很简单,前者是去标准位置进行查找,后者是先在源文件目录下查找,若找不到,再去标准位置查找。也许有的小伙伴会说,那我使用库函数里的函数,引用头文件时,也采用#include ”xxx.h”可以吗?当然可以,只不过我们明知道我们使用的是库函数里的函数,却还这么引用,虽然最后还是会在标准位置查找文件,但这会使运行速度降低,而且让我们不容易进行区分。
                                     好了,这部分内容就结束了,这几天好累!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值