目录
2.2 环境细化分析(环境可以自选,这里选用的是Linux)
一、程序环境
1.程序的翻译环境和执行环境
在ANSI C的任何一种实现中,存在两个不同的环境
(1)一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令(二进制指令)
(2)另一种是执行环境,它用于实际执行代码
2.编译&链接
2.1翻译环境
一个简单例子 :
源文件---------->(通过)编译器 ---------->目标文件(后缀 .obj)
目标文件+链接器 ---------->可执行程序(后缀 .exe)
2.2 环境细化分析(环境可以自选,这里选用的是Linux)
2.3运行环境
程序执行过程中:
(1)程序必须载入内存中,在有操作系统的环境中:一般这个由操作系统完成,在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存完成
(2)程序的执行便开始,接着使用调试main函数
(3)开始执行程序代码,这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址,程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值
(4)终止程序,正常终止main函数,也可能意外终止
二、预处理
1.预处理分析
1.1预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的符号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C标准,其值为1,否的话为未定义
#include<stdio.h>
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("file:%s line=%d date :%s time:%s i=%d \n",
__FILE__,
__LINE__,
__DATE__,
__TIME__, i);
}
return 0;
}
运行结果:
写入文件(代码日记)
#include<stdio.h>
int main()
{
FILE* pf;
pf = fopen("example", "w");
if (pf == NULL)
{
perror("fopen");
}
int i = 0;
for (i = 0; i < 10; i++)
{
fprintf(pf,"file:%s line=%d date :%s time:%s i=%d \n",
__FILE__,
__LINE__,
__DATE__,
__TIME__, i);
}
fclose(pf);
pf = NULL;
return 0;
}
运行结果:
1.2#define定义标识符
语法:
#define name stuff
使用例:
(1)#define MAX 100
(2)#define reg register //建立一个字节能记得住的名称
(3)#define do_forever for(;;) //更换一种符号实现
(4)#define CASE break;case //相当于省去了每次写break
(5)#define DEBUG+PRINT printf("file:%s\tline:%d\tdate:%s\ttime:%s\n", __FILE__, __LINE__, __DATE__, __TIME__)
预处理前后对于宏的作用
宏在预处理时做了替换,系统直接进行
用宏时,尽量不要在宏定义的后面加上分号(;)
1.3#define定义宏
宏的申明方式:
#define name(parament_list) stuff
其中的parament_list 是一个由逗号隔开的符号表,它们可能是出现在stuff中
注意:
name 旁边的括号必须是紧邻的
如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分
使用例:
#define SQUARE(x) x*x
此时的宏参数是x
但是当前的宏不是很稳定,容易发生变化,
得出的结果并不是我们想要的36,而是11,这就是宏当前执行的不稳定 所以这里改进就需要做好优先级的运算(加上括号)
宏又是如何执行的呢?宏是直接带入考虑算数的优先级进行
1.4#define替换规则
(1)在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号,如果是,他们首先是替换
(2)替换文本随后被插入到程序中原来文本的位置,对于宏,参数名被他们的值所替换
(3)最后,再次对结果文本进行扫描,是否包含#define定义符号,如果还有就重复上述操作
注意:
(1)宏参数和#define定义可以出现其他#define定义的符号,但是对于宏,不能出现递归
(2)当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索
2.#和##使用
2.1#使用
使用例:
#include<stdio.h>
#define PRINT(N) printf("the value of "#N" is %d\n",N)
int main()
{
int a = 10;
PRINT(a);
int b = 20;
PRINT(b);
return 0;
}
使用例解释:
printf("the value of "#N" is %d\n",N) 宏定义:
"the value of "前半段的字符串
" is %d\n"后半段字符串
#N代表的就是“N”,也是字符串,只是用#写
3.宏相关
3.1宏与函数对比
在较小型计算工作中不用函数解决问题
原因:
(1)用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多此时宏比函数更胜一筹(规模与速度)
(2)相比之下更好的是宏不需要特定的类型
所以函数只能在类型合适的表达式上使用,反之这个宏怎么可以适用于整型、常整型等等
宏的缺点:当前和函数相比宏也有劣势的地方:
(1)。每次使用宏的时候,一份宏定义的代码将插入到程序中,除非宏比较短,否则可能大幅度增加程序的长度。
(2)宏是不能调试的
(3)宏由于类型无关,也就不够严谨
(4)宏可能会带来运算符优先级的问题,导致程序容易出现错误
所以宏有些地方也是不能实现的,但是函数可以
3.2命名约定
一般来讲函数的宏的使用语法很相似,所以语言本身没法帮我们区分二者
习惯性命名:
把宏名全部大写
函数名不要全部大写
3.3 #undef
命令解释:移除一个宏定义
#undef NAME
通过#undef使用移除前面宏定义的M在这里就不在起作用了
3.4命令行定义
许多c的编译器提供一种能力,允许在命令行中定义符号,用于启动编译过程
例如:当我们根据同一个源文件要编译出一个程序不同版本的时候,这个特点有点用处
命令行定义:在编译的时候就将其指定值放入
3.5条件编译
在编译一个程序的时候如果条件可以就进行编译,简单点说就像在我们使用if函数的时候,可以选择性放弃,但是与if不完全相同,大家谨慎理解
注意:ifdef 使用的是定义__DEBUG__,条件可以编译,可以自己来改变条件,决定是否想要编译,如果将#define 屏蔽ifdef就不能执行了
(1)
#if 常量表达式
//...
#endif
//常量表达式由预处理器求值
如:
#define __DEBUG__ 1
#if __DEBUG__
//...
#endif
(2)多个分支的条件编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
(3)判断是否被定义
#if definedsymbol)
#ifdef symbol
#if !define(symbol) //否定
#ifndef symbol //否定
//结束时后面与前面一样加 #endif
(4)#if defined(OS_UNIX)
#ifdef OPTION1
unix1();
#endif
#ifdef OPTION2
unix2();
#endif
#elif defined(OS_MSDOS)
//...
#else
//..
#endif
3.6文件包含
文件包含里面是有头文件#include,多次包含头文件,拷贝的次数过多
所以在这里文件包含在当前部分编译器会给#pragma once在写头文件时,仅包含一次头文件
#pragma once
int ADD(int x,int y)
另一种方法:也是所有c编译器都可以使用的
#ifndef __TEST_H__
#define __TEST_H__
int ADD(int x,int y);
#endif
3.7文件查找
<>和""的区别是查找的策略不同
#include<stdio.h>
<>查找策略:直接去库函数目录小找
#include"test.h"
""查找策略:优先在当前文件下找,找不到后在库函数目录下查找
4.预处理指令
#error
#pragma
#line
有意向的可以了解