临沂网站,wordpress社交平台主题,重庆建设人才网站,专业网站建设首选公司目录
一#xff1a;#x1f512;程序环境
程序的翻译环境和执行环境
#x1f4a1;1.1翻译环境
预编译阶段#xff1a;
编译阶段#xff1a;
汇编阶段#xff1a;
链接阶段#xff1a;
#x1f4a1;1.2运行环境 二#xff1a;#x1f512;预处理详解
程序环境
程序的翻译环境和执行环境
1.1翻译环境
预编译阶段
编译阶段
汇编阶段
链接阶段
1.2运行环境 二预处理详解
2.1预处理符号
2.2#define
#define定义标识符
#define定义宏
#define替换规则 #和##
#的作用
##的作用
带副作用的宏参数
三宏与函数的对比
命名约定 四条件编译
五文件包含
头文件的包含方式
避免头文件被重复引用 一程序环境
程序的翻译环境和执行环境 在ANSIC任何一种实现中存在两种不同的环境 1.翻译环境在这个环境下源代码被转换成可执行的机器指令 2.执行环境用于实际代码运行 1.1翻译环境 1.组成一个程序的每个源文件通过编译过程分别转换成目标代码object code。 2.每个目标文件由链接器linker捆绑在一起形成一个单一而完整的可执行程序。 3.链接器同时也会引入标准C函数库中任何被该程序所用到的函数而且它可以搜索程序员个人的程序库将其需要的函数也链接到程序中。 预编译阶段 1.头文件包含 #include 预处理指令 2.define定义的符号替换 #define 预处理指令 3.注释删除 以上这些都是文本操作 编译阶段 把c语言代码翻译成了汇编代码 1、语法分析 2、词法分析 3、语义分析 4、符号汇总 汇编阶段 把汇编指令翻译成了二进制的指令 形成符号表这样就能够找到源文件外部的符号只能汇总全局符号 链接阶段 1、合并段表 2、符号表的合并和重定位 1.2运行环境
程序执行的过程 1. 程序必须载入内存中。在有操作系统的环境中一般这个由操作系统完成。在独立的环境中程序 的载入必须由手工安排也可能是通过可执行代码置入只读内存来完成。 2. 程序的执行便开始。接着便调用main函数。 3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈stack存储函数的局部变量和返回地址。程序同时也可以使用静态static内存存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。 4. 终止程序。正常终止main函数也有可能是意外终止。 二预处理详解
2.1预处理符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C其值为1否则未定义
这些预定义符号都是语言内置的。
int main()
{printf(file:%s line:%d\n, __FILE__, __LINE__);return 0;
}2.2#define
#define定义标识符
#define MAX 1000
#define reg register //为 register这个关键字创建一个简短的名字
#define do_forever for(;;) //用更形象的符号来替换一种实现
#define CASE break;case //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长可以分成几行写除了最后一行外每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf(file:%s\tline:%d\t \date:%s\ttime:%s\n ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ ) 注在宏定义时最好不要加分号 ( ; ) 因为宏定义标识符并不会进行计算在编译阶段进行的是内容替换 #define MAX 100;
int main()
{int max 0;if (1)max MAX; //errorelsemax 0;return 0;
}
这里宏定义会直接替换将 100; 替换到 MAX位置 if (1)max 100;;elsemax 0;
#define定义宏 #define 机制包括了一个规定允许把参数替换到文本中这种实现通常称为宏macro或定义宏define macro。 #define name(parament-list) stuff
//name表示名字
//parament-list 是以逗号隔开的参数
//宏的具体内容例子
//#define max(a,b) ab 注意 参数列表的左括号必须与name紧邻。 如果两者之间有任何空白存在,参数列表就会被认为是要替换的部分参数列表就会被解释为stuff的一部分 同样也要注意因为宏是直接进行文本替换然后才在程序中发生计算所以如果不按照标准规定写宏可能会产生bug #define SQUAREx x*xint c5SQUARE(51);//我们预期这里的内容是36但是最终结果是11是因为实际计算的是//51*5111 所以在写的时候我们应该尽可能带上括号防止因为优先级的问题出现bug #define SQUAREx x*x #define替换规则
在程序中扩展 #define 定义符号和 宏 时需要涉及几个步骤。 1. 在调用宏时首先对参数进行检查看看是否包含任何由#define定义的符号。如果是它们首先 被替换。 2. 替换文本随后被插入到程序中原来文本的位置。对于宏参数名被他们的值替换。 3. 最后再次对结果文件进行扫描看看它是否包含任何由#define定义的符号。如果是就重复上 述处理过程。. 注意 宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏不能出现递归。 当预处理器搜索#define定义的符号的时候字符串常量的内容并不被搜索。 #和##
#的作用 如何把参数插入到字符串中 int main()
{printf(who say!!!\n);printf(who say!!!\n);return 0;
} 字符串是具备自动连接的特点。 而define定义的符号在X 里面不会被识别我们可以用 #X 解决此问题 #define PRINT(n, format) printf(the value of #n is format\n, n)
int main()
{int a 10;PRINT(a, %d);//printf(the value of a is %d\n, a);//printf(the value of a is %d\n, a);int b 20;PRINT(b, %d);return 0;
} ##的作用 ##可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符。 #define CAT(v, n) v##n
int main()
{int value10 100;printf(%d\n, CAT(value, 10));printf(%d\n, value10);return 0;
} 注 这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。 带副作用的宏参数
简单来讲就是宏在执行的过程中参数自身的值会发生变化这个就叫做带副作用的宏的参数
#define MAXab a(b)?(a):(b)int main()
{int x5,y8;int cMAX(x,y);printf(%d ,c);return 0;
}
//这里输出的是多少9嘛
//首先带入 (5(8)?(a):(b)) 首先是进行ab大小的比较在这里比较之后
//a跟b跟别变成了6和9然后执行后面的b最终的结果应该a6 c9 b10 要避免写出这样的代码宏是无法调试的 三宏与函数的对比 宏通常被应用于执行简单的运算。 比如在两个数中找出较大的一个。 宏定义和函数的比较
属性#define宏定义函数代码长度每次使用时宏代码都会被插入到程序中。除了非常 小的宏之外程序的长度会大幅度增长函数代码只出现于一个地方每 次使用这个函数时都调用那个、地方的同一份代码执行速度 相对更快简单的程序存在函数的调用和返回的额外开 销所以相对慢一些 操作符 优先级 宏参数的求值是在所有周围表达式的上下文环境里 除非加上括号否则邻近操作符的优先级可能会产生 不可预料的后果所以建议宏在书写的时候多些括 号。函数参数只在函数调用的时候求 值一次它的结果值传递给函 数。表达式的求值结果更容易预 测。带 有 副 作 用 的 参 数惨数可能被替换到宏体中的多个位置所以带有副作 用的参数求值可能会产生不可预料的结果。函数参数只在传参的时候求值一 次结果更容易控制。参数类型 宏的参数与类型无关只要对参数的操作是合法的 它就可以使用于任何参数类型。函数的参数是与类型有关的如 果参数的类型不同就需要不同 的函数即使他们执行的任务是 不同的。调试无法调试函数可以逐语句调试的递归无法递归可以递归 1.用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。相对简单定义 2.更为重要的是函数的参数必须声明为特定的类型。 所以函数只能在类型合适的表达式上使用。 反之这个宏怎可以适用于整形、长整型、浮点型等可以 用于来比较的类型。 宏是类型无关的。 命名约定 一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。 那我们平时的一个习惯是 把宏名全部大写 函数名不要全部大写一般单词首字母大写 四条件编译
条件编译顾名思义就是满足条件才进行读取
1.
#if 常量表达式//...
#endif
//常量表达式由预处理器求值。
如
#define __DEBUG__ 1
#if __DEBUG__//..
#endif
2.多个分支的条件编译
#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif
五文件包含 #include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方 一样。 这种替换的方式很简单 预处理器先删除这条指令并用包含文件的内容替换。 这样一个源文件被包含10次那就实际被编译10次。 头文件的包含方式 本地文件 #includetest.h 先从源文件所在目录进行查找头文件然后再到标准函数库头文件所在目录下查找 #includestdio.h 直接从标准函数库头文件所在目录下查找 总的来说就是的引用方式查找范围更广 但是 本地文件还是按照 #includetest.h 的方式 库文件按照 #includetest.h的方式 否则查找的效率就低些当然这样也不容易区分是库文件还是本地文件了。 避免头文件被重复引用
防止多次头文件频繁引用
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__
或者
#pragma once以上便是我对【C语言】程序编译的介绍文中不足之处还望得到指点得以改善。感谢