大理市建设局网站,上海协策网站制作,wordpress 链接,中国网站模板下载一、程序的翻译环境和执行环境在ANSIC的任何一种实现中#xff0c;存在两个不同的环境。第1种是翻译环境#xff0c;在这个环境中源代码被转换为可执行的机器指令。
第2种是执行环境#xff0c;它用于实际执行代码。二、详解编译链接1.翻译环境2.编译本身也分为几个阶段推荐…一、程序的翻译环境和执行环境在ANSIC的任何一种实现中存在两个不同的环境。第1种是翻译环境在这个环境中源代码被转换为可执行的机器指令。第2种是执行环境它用于实际执行代码。二、详解编译链接1.翻译环境2.编译本身也分为几个阶段推荐阅读《程序员的自我修养》3.运行环境程序执行的过程1.程序必须载入内存中。在有操作系统的环境中一般这个由操作系统完成。在独立的环境中程序的载入必须由手工安排也可能是通过可执行代码置入只读内存来完成。2.程序的执行便开始。接着便调用main函数。3.开始执行程序代码。这个时候程序将使用函数栈帧存储函数的局部变量和返回地址。程序同时也可以使用静态static内存存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。4.终止程序。正常终止main函数也有可能是意外终止。三、预处理1.预定义符号__FILE__//进行编译的源文件 __LINE__//文件当前的行号 __DATE__//文件被编译的日期 __TIME__//文件被编译的时间 __STDC__//如果编译器遵循ANSIC其值为1否则未定义#includestdio.h int main() { int i 0; for (i 0;i 10;i) { printf(\n\n%d\n, i); printf(%s\n, __FILE__); printf(%s\n, __TIME__); printf(%d\n, __LINE__); printf(%s\n, __DATE__); } return 0; }2.define①define定义标识符可以是字符串、整形甚至是一段代码#includestdio.h #define MAX 100 #define STR hello world #define print printf(hi!\n) //define后面不要加分号 int main() { printf(%d\n, MAX); //100 printf(%s\n, STR); //hello world print; //hi! return 0; }#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定义宏#define机制包括了一个规定允许把参数替换到文本中这种实现通常称为宏macro或定义宏(define macro)。下面是宏的申明方式#define name(parament-list) stuff //宏名 //参数列表 //文本其中的parament-list是一个由逗号隔开的符号表它们可能出现在stuff中注意参数列表的左括号必须与name紧邻。如果两者之间有任何空白存在参数列表就会被解释为stuff的一部分。#includestdio.h #define SQURE(x) ((x)*(x)) int main() { int n SQURE(5); printf(%d, n);//25 return 0; }③#define替换规则在程序中扩展#define定义符号和宏时需要涉及几个步骤。1.在调用宏时首先对参数进行检查看看是否包含任何由#define定义的符号。如果是它们首先被替换。2.替换文本随后被插入到程序中原来文本的位置。对于宏参数名被他们的值所替换。3.最后再次对结果文件进行扫描看看它是否包含任何由#define定义的符号。如果是就重复上述处理过程。注意:1.宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏不能出现递归。2.当预处理器搜索#define定义的符号的时候字符串常量的内容并不被搜索。④#Ⅰ.两个字符串会自动连接#includestdio.h int main() { printf(hello world\n); printf(hello world\n); //都得到hello world return 0; }Ⅱ.会替换成 the value of a is 5\n#N:把参数a插入字符串中#includestdio.h #define PRINT(N) printf(the value of #N is %d\n,N) int main() { int a 5; PRINT(a); int b 10; PRINT(b); return 0; }Ⅲ.打印多种类型的变量#includestdio.h #define PRINT(N,FORMAT) printf(the value of #N is FORMAT\n,N) int main() { int a 10; PRINT(a, %d); float f 3.14f; PRINT(f, %lf); return 0; }⑤####可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。#includestdio.h #define CAT(Class,Num) Class##Num int main() { int Class106 100; printf(%d\n, CAT(Class, 106)); //等价于 //printf(%d\n, Class106); return 0; }注这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。⑥带副作用的宏参数当宏参数在宏的定义中出现超过一次的时候如果参数带有副作用那么你在使用这个宏的时候就可能出现危险导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。int a10; //无副作用 int ba1; //带有副作用 //b改变的同时a也改变了 int ba;错误示范:#includestdio.h #define MAX(x,y)((x)(y)?(x):(y)) int main() { int a 5; int b 4; int m MAX(a, b); //mMAX((a)(b)?(a):(b)) // 6 5 7 //m6 //a7 //b5 printf(%d %d %d, m, a, b); return 0; }⑦宏和函数对比宏通常被应用于执行简单的运算。比如在两个数中找出较大的。#define MAX(a, b) ((a) (b)? (a): (b))那为什么不用函数来完成这个任务原因有二1.用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。2.更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于来比较的类型。宏是类型无关的。宏的缺点1.每次使用宏的时候一份宏定义的代码将插入到程序中。除非宏比较短否则可能大幅度增加程序的长度。2.宏是没法调试的。3.宏由于类型无关也就不够严谨。4.宏可能会带来运算符优先级的问题导致程容易出现错。函数不可以传类型但宏可以#includestdio.h #includestdlib.h #define MALLOC(num,type)(type*)malloc(num)*sizeof((type)) int main() { int* p MALLOC(10, int); //等价于 //int* p (int*)malloc((10) * sizeof(int)); return 0; }⑧命名约定一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。那我们平时的一个习惯是把宏名全部大写函数名不要全部大写3.#undef用于移除一个宏定义#includestdio.h #define M 100 int main() { printf(%d, M); #undef M printf(%d, M); //会报错未定义标识符M return 0; }4.命令行定义许多C的编译器提供了一种能力允许在命令行中定义符号。用于启动编译过程。例如当我们根据同一个源文件要编译出一个程序的不同版本的时候这个特性有点用处。假定某个程序中声明了一个某个长度的数组如果机器内存有限我们需要一个很小的数组但是另外一个机器内存大些我们需要一个数组能够大些。#includestdio.h int main() { int arr[SZ]; int i 0; for (i 0;i SZ;i) arr[i] i; for (i 0;i SZ;i) printf(%d , arr[i]); return 0; }linux环境演示gcc -D ARRAAY_SIZE10 programe.c5.条件编译在编译一个程序的时候我们如果要将一条语句一组语句编译或者放弃是很方便的。因为我们有条件编译指令。比如说调试性的代码删除可惜保留又碍事所以我们可以选择性的编译。#includestdio.h #define __DEBUG__ //如果删掉这行代码debug未定义 //条件为假中间的语句就不参与编译 int main() { int i 0; int arr[10] { 0 }; for (i 0;i 10;i) { arr[i] i; #ifdef __DEBUG__ //如果定义了debug条件为真参与编译 printf(%d\n, arr[i]); #endif //__DEBUG__ } return 0; }常见的条件编译指令1.#if 常量表达式//..#endif#includestdio.h int main() { #if 1 printf(hello\n); //hello #endif return 0; }#includestdio.h int main() { #if 0 printf(hello\n); //什么也没有 #endif return 0; }//常量表达式由预处理器求值。如#define _DEBUG_1#if _DEBUG//...#endif2.多个分支的条件编译#if 常量表达式//...#elif 常量表达式//...#else//..#endif#includestdio.h #define M 6 int main() { #if M5 printf(hello!!!\n); #elif M5 printf(Whats Your Name?\n); #else printf(I Love You!\n); //I Love You! #endif return 0; }3.判断是否被定义#if defined(symbol)#includestdio.h #define MAX 100 int main() { #if defined(MAX) printf(max\n); //max #endif return 0; }#ifdef symbol#includestdio.h #define MAX 100 int main() { #ifdef MAX printf(max\n); //max #endif return 0; }#if !defined(symbol)#includestdio.h //#define MAX 100 int main() { #if !defined(MAX) printf(max\n); //max #endif return 0; }#ifndef symbol#includestdio.h //#define MAX 100 int main() { #ifndef MAX printf(max\n); //max #endif return 0; }4.嵌套指令#if defined(oS_UNIX) #ifdef OPTIONL unix_version_optionl(); #endif #ifdef OPTION2 unix_version_option2(); #endif #elif defined(os_MSDOS) #ifdef OPTION2 msdos_version_option2(); #endif #endif6.文件包含我们已经知道#include指令可以使另外一个文件被编译。就像它实际出现于#include指令的地方—样。这种替换的方式很简单预处理器先删除这条指令并用包含文件的内容替换。这样一个源文件被包含10次那就实际被编译10次。①防止头文件被多次重复包含写法一#ifndef __TEST_H__ //如果没有定义__TEST_H__,条件为真 //下面的代码参与编译 #define __TEST_H__ //第一次包含头文件的时候没有定义过__TEST_H__ //先把__TEST_H__定义了 int ADD(int x, int y); #endif //第二次再想包含的时候ifndef条件为假 //不会产生效果写法二#pragma once②和 的区别是查找的策略不同#includestdio.h直接去库目录下查找#includetest.h先去代码所在的路径下查找如果上面找不到再去库目录下查找③头文件被包含的方式●本地文件包含#include filename查找策略先在源文件所在目录下查找如果该头文件未找到编译器就像查找库函数头文件一样在标准位置查找头文件。如果找不到就提示编译错误。Linux环境的标准头文件的路径/usr/includeVS环境的标准头文件的路径C:\Program Files (x86)\Microsoft Visual Studio 12.0\vc\include//这是vS2013的默认路径注意按照自己的安装路径去找。●库文件包含#include filename.h查找头文件直接去标准路径下去查找如果找不到就提示编译错误。这样是不是可以说对于库文件也可以使用“”的形式包含可以。但是这样做查找的效率就低些当然这样也不容易区分是库文件还是本地文件了。④嵌套文件包含四、其他预处理指令【拓】百度笔试题写一个宏计算结构体中某变量相对于首地址的偏移并给出说明考察offsetof宏的实现1.普通写法#includestdio.h #includestdlib.h struct S { char c1; int i; char c2; }; int main() { struct S s { 0 }; printf(%d\n, offsetof(struct S, c1));//0 printf(%d\n, offsetof(struct S, i));//4 printf(%d\n, offsetof(struct S, c2));//8 return 0; }2.宏的写法假设0地址处放一个结构体把它强制类型转换成结构体指针当成一个结构体地址拿到结构体成员取出成员地址#includestdio.h #define OFFSETOF(type,member_name) (size_t)(((type*)0)-member_name) struct S { char c1; int i; char c2; }; int main() { struct S s { 0 }; printf(%d\n, OFFSETOF(struct S, c1));//0 printf(%d\n, OFFSETOF(struct S, i));//4 printf(%d\n, OFFSETOF(struct S, c2));//8 return 0; }