在ANSI C的任何一种实现中,存在两个不同的环境。
第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
第2种是执行环境,它用于实际执行代码
翻译环境:
成都创新互联公司长期为千余家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为天门企业提供专业的成都做网站、网站建设,天门网站改版等技术服务。拥有10余年丰富建站经验和众多成功案例,为您定制开发。展示了在Linux系统下程序的翻译过程
1. 预处理 选项 gcc -E test.c -o test.i
预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。
2. 编译 选项 gcc -S test.c
编译完成之后就停下来,结果保存在test.s中。
3. 汇编 gcc -c test.c
汇编完成之后就停下来,结果保存在test.o中。
4.链接过程是根据程序所用函数的名称,将C函数库或其他函数库中的函数链接到程序中,最终形成可执行文件。
运行环境:
程序执行的过程:预处理指令 预定义符号
1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序
的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
2. 程序的执行便开始。接着便调用main函数。
3.开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回
地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程
一直保留他们的值。
4. 终止程序。正常终止main函数;也有可能是意外终止。
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
用法:
printf("file:%s",__FILE__); //输出当前编译的源文件名
printf("line:%d",__LINE__); //输出当前行号
printf("date:%s",__DATE__); //输出文件被编译的日期
printf("time:%s",__TIME__); //输出文件被编译的时间
#define的用法定义标识符
用法:
#define 名字 被定义的具体内容
如:
#define MAX 100
#define name "张三"
定义宏
在 C 语言中,可以采用命令 #define 来定义宏。该命令允许把一个名称指定成任何所需的文本,例如一个常量值或者一条语句。在定义了宏之后,无论宏名称出现在源代码的何处,预处理器都会把它用定义时指定的文本替换掉。 惯例将宏名称每个字母采用大写,这有助于区分宏与一般的变量。
用法:
#define 名字(参数列表) 被定义的具体内容
例:
#define MAX(a,b) (a>b?a:b)
注意事项:
参数列表的左括号必须与name紧邻。
如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分
在程序中,如果我们想求出两个数中的大值,通常一个方法就是编写函数Max,计算结果并输出;
同样,该程序也可以采用定义宏来计算结果。
另外,为避免宏使用时出现争议性错误,还应该注意它定义时的规范性
例如:
定义一个宏,求一个数的平方
#define SQUARE( x ) x * x
当程序中出现
int a = 5;
printf("%d\n" ,SQUARE( a + 1) );
由于宏是直接替换,参数x被替换成a+1,所以该语句实际上是printf("%d\n",a+1*a+1);
代入a=5,经计算结果为11,并非主观上的6*6=36
经修正:
宏应该被定义为
#define SQUARE( x ) (x) * (x)
所以用于对数值表达式进行求值的宏定义都应该考虑充分,在合适的位置加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。
#和##的用法通过观察发现,C语言的字符串有自动连接的特点,即两个“”紧密相连的字符串可以一并输出
#define PRINT(FORMAT, VALUE)\
printf("the value is "FORMAT"\n", VALUE);
...
PRINT("%d", 10); //输出结果是什么
这里的宏参数为“%d”和10,分别替换FORMAT 和 VALUE,其中FORMAT在传参时必须传入带有“”的参数,否则便不能输出。
使用 # ,把一个宏参数变成对应的字符串
上面代码可更改为
#define PRINT(FORMAT, VALUE) printf("the value is "#FORMAT"\n", VALUE);
....
PRINT(%d, 10);
#FORMAT被预处理器预处理为"FORMAT"--->"%d"
##可以把位于它两边的符号合成一个符号。带副作用的宏参数
它允许宏定义从分离的文本片段创建标识符。
#define ADD_TO_SUM(num, value) sum##num += value;
...
ADD_TO_SUM(5, 10);
//先进行宏替换-->sum##5+=10 ##将两边的符号合并-->sum5+=10 -->给sum5增加10.
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能
出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
例如:
x+1 //无副作用,x没有变化
x++ //有副作用,x本身会+1
思考下面宏替换后输出的结果是什么?
#define MAX(a, b) ( (a) >(b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?
//6 9 9
宏替换后,MAX(x++,y++)-->((x++)>(y++)?(x++):(y++))-->
((5++)>(8++)?(5++):(8++))-->9
在使用宏定义时 尽量不要使用 x++ 等会影响原来参数的值的宏参数
宏和函数的对比属性 | #define定义宏 | 函数 |
代码长度 | 程序中每次定义宏时,都会将宏代码替换到程序中。若宏的代码过长且程序中出现多段宏,则会增加代码长度。 | 函数的代码只出现在一个地方,每次函数调用都会调用同一个地方的代码,多次调用函数不会增加代码长度。 |
执行速度 | 更快 | 函数的调用和返回消耗一定时间,会稍慢 |
操作符优先级 | 宏参数的求值是在所有周围表达式的上下文环境里, 除非加上括号,否则邻近操作符的优先级可能会产生 不可预料的后果,所以建议宏在书写的时候多些括 号。 | 函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。 |
带副作用的参数 | 参数会被替换到宏的多个位置,所以应该避免使用带副作用的参数 | 函数参数只在函数调用时求值一次,结果更易预测 |
参数类型 | 宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型。 | 函数的参数是与类型有关的,参数的类型不同,就需要不同 的函数。 |
调试 | 不方便 | 方便 |
递归 | 不可递归 | 可递归 |
移除前面#define NAME的宏定义
#undef NAME条件编译
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件
编译指令。比如在程序测试时想添加一些代码,但测试过后不想删除,可以采用更改其为注释的方法,也可以使用条件编译,仅让其在测试时编译。
常见的条件编译指令:
1.单分支
#if 常量表达式
....
#endif
....
#define __DEBUG__ 1
#if __DEBUG__
printf("测试中。。。”);
#endif
printf("测试完毕”);
2.多个分支的条件编译
#if 常量表达式
...
#elif 常量表达式
...
#else
...
#endif
3.判断是否被定义
//如果定义了symbol
#if defined(symbol)
#ifdef symbol
//如果没有定义symbol
#if !defined(symbol)
#ifndef symbol
#define symbol 100
#ifdef symbol
printf("%d",symbol);
#ifndef symbol
printf("not define");
4.嵌套指令
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧