详细的网站规划建设方案服务器,杭州网站建设机构,js 访问网站统计刷新不增加次数,成品网站安装目录
前言
一、程序环境
1. 翻译环境
1.1 主要过程
1.2 编译过程
2. 运行环境
二、预处理
1. 预定义符号
2. #define
2.1 #define定义标识符
2.2 #define定义宏
2.3 命名约定和移除定义 3. 条件编译 4. 文件包含
结束语 前言 每次我们写完代码运行的时候都…目录
前言
一、程序环境
1. 翻译环境
1.1 主要过程
1.2 编译过程
2. 运行环境
二、预处理
1. 预定义符号
2. #define
2.1 #define定义标识符
2.2 #define定义宏
2.3 命名约定和移除定义 3. 条件编译 4. 文件包含
结束语 前言 每次我们写完代码运行的时候都会弹出来一个黑框框这个黑框框实际上是一个可执行程序(.exe文件)。那么代码是如何被变成一个可执行文件的呢其实这就是编译器所做的事一起来了解了解吧。 一、程序环境
1. 翻译环境
1.1 主要过程 代码不可能凭空运行只有可执行程序才能在计算机上运行。因此在翻译环境下代码会经过编译链接形成一个可执行程序。如下图组成程序的各个源文件经过编译器的编译形成各自的目标文件再由链接器链接形成一个可执行程序。在链接过程之中链接器会从C语言标准库中引入程序中所用到的函数。
1.2 编译过程 上述过程中的编译过程又可细分为预编译预处理、编译。翻译三个阶段。
预处理这一阶段主要用来执行各种预处理指令例如#define定义标识符常量宏。之前在介绍枚举常量时与#define定义的标识符常量进行对比标识符常量无法进行调试这就是因为标识符常量在预处理时就已经被替换为常量值。
编译编译阶段主要对代码的语法词法语义进行分析检查。确保代码无语法错误后将各个文件中的符号函数名全局变量等进行汇总便于跨文件调用。
翻译计算机只能识别二进制的代码因此在链接之前编译器需要将C语言代码经汇编代码翻译为二进制代码 并形成目标文件。
2. 运行环境 程序在执行时必须载入内存中这一步一般由操作系统完成。若在无操作系统的环境中则需手动完成。程序开始执行会调用main函数这时候需使用一个运行的堆栈用以存储函数的局部变量和地址。程序也可用静态区存储静态变量和全局变量。最后程序正常结束也有可能异常结束。
二、预处理
1. 预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间 以上列举的预定义符号为C语言内置的符号使用效果如下图。 2. #define
2.1 #define定义标识符 #defien定义标识符的规则为”#define 标识符名称 内容“。 #define定义的标识符会在预处理阶段直接被替换为其内容例如下方为一段标识符的定义和使用。在预处理阶段MAX被替换为100即将100赋值给变量max。
#define MAX 100int max MAX; 同样地#define可以以任何数据为内容因此我们可以对switch语句中的casebreak进行如下改造。改造的依据是C语言外的其它语言中部分语言的switch语句不需要使用break结束情况。当其它语言的程序员写C程序时可能会使用如下方式写switch语句。
#define CASE break;caseswitch(x)
{case 1:printf(%d\n,1);CASE 2:printf(%d\n,2);CASE 3:printf(%d\n,3);CASE 4:printf(%d\n,4);default:printf(%d\n,0);break;
} 这段代码在预处理阶段会将CASE替换为break;case将各个情况分开并且不用在每种情况后手动加上break。 当然由于标识符是直接替换内容使用不熟练可能会造成逻辑错误例如下方代码
#define A 33int a 2*A; 很多新手会认为此时的a的值为12因为A就是“336”嘛那么2*A就是12。这段代码的中的a的值应该为9。由于A为标识符因此代码中的A直接替换为标识符内容33即int a 2*33结果为9。若想达到预期效果则需加上括号如下
#define A (33)int a 2*A;
2.2 #define定义宏 #define定义宏的规则与标识符相似#define 宏名称(宏参数) 内容。 宏的形式与函数相似都需要传入参数不同的是宏的参数是直接替换到宏的内容中而函数则是使用参数的值。例如下方代码 这段代码中宏的计算为21*35而函数的计算为3*39。
与函数相比宏的优点
1、 函数的调用需要在栈上开辟空间传参返回值销毁空间。这些准备工作可能比实际计算所需的时间要多得多相比之下宏在处理一些简单计算时的速度更快。
2、 另外类似于标识符宏的参数可以为任何内容这是函数无法做到的函数的参数必须是声明的指定类型的数据因此宏的使用较函数更加灵活例如向宏中传入一个关键字这是函数无法做到的。 宏的缺点
1. 每次使用宏的时候一份宏定义的代码将插入到程序中。除非宏比较短否则可能大幅度增加程序 的长度。
2. 宏是没法调试的。
3. 宏由于类型无关也就不够严谨。
4. 宏可能会带来运算符优先级的问题导致程容易出现错。
2.3 命名约定和移除定义
命名约定由于宏在使用时与函十分相似因此人们约定在定义宏名称时全部用大写字母而在定义函数名时不全用大写字母。
移除定义使用#undef指令移除一个宏定义如下图。 3. 条件编译 当有些代码需要在特定的条件下执行其它条件下不执行时就需要用到条件编译。条件编译类似于if分支语句区别是条件编译指令是在预处理阶段执行而分支语句是在函数内执行。这里列举一条常见的条件编译指令
#if 常量表达式//满足条件执行内容
#endif 这段指令就是在常量表达式为真时执行#if与#endif之间的代码。例如 第一段代码由于常量表达式为真故定义函数func并成功调用。而第二段代码由于常量表达式为假函数func未定义故调用失败报错。 与if分支语句类似#if条件编译指令也可出现分支如下
#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif 另外这里有两组判断表达式是否被定义的条件编译指令。若symbol已定义则上面两段指令的内容被执行若未定义则下面两段指令的内容被执行。
#if defined(symbol)
#ifdef symbol#if !defined(symbol)
#ifndef symbol4. 文件包含 当我们需要使用C语言标准库里的函数时或是使用自己所写的头文件里的内容时必须包含相应的头文件。包含头文件有以下两种写法(以stdio.h为例)
#includestdio.h
#includestdio.h 其中使用尖括号代表直接从C标准库中查找该头文件而使用引号则代表从程序内部和C标准库中查找。因此在我们包含C标准库中的头文件时通常使用尖括号以提高效率。 在大型项目中多个程序员可能会多次调用同一个头文件这样容易导致代码的运行效率降低。我们可以使用条件编译的方式来解决这个问题如下方代码
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif 这样的写法就可以避免同一个头文件的重复调用大大提高项目的效率。另外在头文件开头加上以下代码也可达到相同效果。
#pragma once 结束语 程序环境和预处理对于新手来说运用不多但也算是一块比较重要的内容有助于新手理解程序的编译形成过程熟悉预处理操作。