程序环境和预处理

集成开发环境(IDE,Integrated Development Environment )是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。

将所有的功能全部封装在一块,比如常用的VS。我们看不到程序的翻译过程,程序运行时直接就被翻译运行,显示结果了。

程序的翻译环境和执行环境:

翻译环境:源代码被转换为可执行的机器指令
执行环境:实际执行代码

 
 
 
 
翻译环境:
程序在编译过程中,程序的源文件会被转换成目标文件(windows为.obj,linux为.o文件),头文件在编译期间会进行包含展开。

编辑器(vim)—写代码
编译链接器(gcc)—将代码变为二进制
调试器(gdb)—调试器
这是在linux中,将各个工具都分开了。

程序在编译大致分为4个过程:

  1. 预处理:包括对头文件展开,去注释,宏替换,条件编译

例如:

   #define DOUBLE(N) N*2
   int main()
   {
     int a=4;
     printf("%d\n",DOUBLE(a));
     return 0;//sample                                                         
   }

在linux中,预处理操作指令为:

gcc -E test.c(文件名)-o test.i(转化文件名为临时文件)

在这里插入图片描述
查看该文件,宏被进行了替换,注释也被取消。预处理之后,还是C语言

  1. 编译:将C语言转换为汇编代码
    操作指令为:

gcc -S test.i -o test.s

查看该文件为汇编代码:
在这里插入图片描述

  1. 汇编:将汇编代码编程二进制目标文件(不可执行)
    操作指令为:

gcc -c test.s -o test.o

c一定要为小写的
查看文件为:二进制乱码
在这里插入图片描述
此时,我们生成的二进制文件还不能进行运行,因为还未进行和库文件的链接。

  1. 链接:将目标文件和C库文件进行链接形成可执行文件。
    操作指令为:

gcc test.o -o mytest(任意名字)

查看结果为:
在这里插入图片描述

 
 
 
运行环境:

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

 
 
 
 
 
 
预处理详解:

  1. 预定义符号:有一些符号是程序本身就定义好了的,比如编译的源文件
__FILE__   	      //进行编译的源文件
__LINE__          //文件当前的行号
__DATE__         //文件被编译的日期
__TIME__         //文件被编译的时间
__STDC__        //如果编译器遵守ANSI C,其值为1,否则未定义

在这里插入图片描述
预处理之后:
在这里插入图片描述

  1. #define:执行简单的文本替换,只进行替换,不会做其他操作
    在使用时,不要对后面加上

例如:这样一个代码
在这里插入图片描述
在执行的时候,就会发生错误。
预处理看一下:
在这里插入图片描述

在这里,a就携带了两个分号,这时就会出现语法错误。如果对ifelse语句带上大括号,就不会有错。

  1. #define定义宏:在这里,宏只做简单的文本替换,不会进行任何操作,若宏中有优先级运算问题,一般建议先带上括号。

替换规则:

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号,如果是,先被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,重复上述操作。

 
 
 
 
 
注意:

  1. 宏中不能出现递归。
  2. 字符串常量和宏名一样时,不被替换。

#和##:字符串有自动连接特性!

	char *p = "hello ""world!\n";
	printf("hello ""world!\n");
	printf("%s", p);

这样的字符串,在输出的时候,可以自动连接。
在这里插入图片描述

  1. 使用#把一个宏参数变成对应的字符串
#define PRINT(FORMAT,VALUE) printf("the value of "#VALUE" is "FORMAT"\n",VALUE)
	int i = 10;
	PRINT("%d", i + 3);

在这里插入图片描述

  1. ##可以把位于它两边的符号合成为一个符号。允许定义从分离的文本片段创建标识符。
#define ADD_TO_SUM(num,value) sum##num += value
	int sum5 = 10;
	ADD_TO_SUM(5, 10);
	printf("%d\n", sum5);

要注意先定义,否则这样的标识符是没有定义的,就会报错。

 
 
 
在使用宏中,尽量不要使用带有副作用的参数,这个副作用会使表达式求值的时候出现永久性效果,出现的结果是不可预测的。
比如传入的参数为x++ x--这样的,就会出现问题。
 
 
 
宏和函数相比较:
优势:

  • 宏的执行速度更快,因为宏是在预处理阶段进行文本替换,所以速度会更快。而函数的执行要开辟栈帧。
  • 宏的参数和类型无关。只要参数的操作是合法的,可以适用于任何参数类型。(宏参数可以出现类型)

劣势:

  • 宏每次执行,都会将代码插入到程序。如果宏比较长,就会使程序的长度大幅度增长。
  • 宏在书写里要尽量带上括号,因为邻近操作符会有优先级问题。
  • 宏不能调试。
  • 宏没有办法递归。

在命名时,尽量将宏名全部大写,函数名不要全部大写。 易于区分二者。

宏从定义开始,往下都是有效的。 (宏在前,程序在后,后面的程序都可使用宏,宏前面的程序不行)

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

命令行定义:编译时定义宏。
指令为:gcc 文件名 -D 宏名和值 在linux中。
这样一个代码:
在这里插入图片描述
操作上述指令,结果为:
在这里插入图片描述
在编译一个相同程序的不同版本的时候,就可以用到这个特性。

 
 
 
 
条件编译: 根据条件,选择执行的语句。选择性编译。

常见的条件编译指令:

  1. 定义了宏,但不知道宏的真假,进行判定,选择性编译。
#if 常量表达式

#endif
  1. 和上面同样,不过分支更多了
#if 常量表达式

#elif 常量表达式

#elif 常量表达式

...

#endif
  1. 判定是否被定义
#if defined(宏名)
#ifdef 宏名                 //前两个一样

#if !defined(宏名)
#ifndef 宏名              //后两个一样
  1. 条件编译时,也可以嵌套定义这样的指令。

 
 
 
 
文件包含: #include指令可以使另一个文件被包含。在预处理阶段,先删除这段指令,并用包含文件的内容进行替换。

比如我们常用的#include <stdio.h>->会把头文件的相关内容直接插入到我们的源代码当中,替换掉#include

头文件被包含的方式:

  • 本地文件包含:#include “filename.h”
    查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像在查找库函数头文件一样在标准位置查找头文件。如果找不到就提示编译错误。
  • 库文件包含:#include <filename.h>
    查找头文件直接去标准路径下查找,如果找不到就提示编译错误。

对于库文件是否也可以用“”的形式去查找?

可以这样做。但是这样做会使查找效率变低,也不容易区分是本地文件还是库文件。

  • 嵌套文件包含:多个文件重复包含,如果没有进行处理,就会出现文件内容的重复。

怎么解决这个问题?
进行条件编译。

#ifndef __TEST_H__
#define __TEST_H__

#enif __TEST_H__

如果没有定义,对该宏进行定义,文件重复包含时,内容也不会重复。这里面的宏,尽量使用文件名。

一般我们使用头文件时,尽量加上这个条件编译。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值