本篇重点
认识程序环境和预处理
在ANSI C的任何一种实现中,存在两个不同的环境。
int add(int x, int y)
{
//add.c
return x + y;
}
#include
#define m 100
int val = 9;
//test.c
int main()
{
int x = 0;
int y = 0;
scanf("%d %d", &x, &y);
int ret=add(x,y);
printf("%d %d", val, ret);
return 0;
}
在vs2022上不好观察编译的过程,有兴趣的小伙伴可以用gcc编译器观察编译的过程:
一些借本指令:
1.
gcc test.c -E可以将预编译完成后终止编译
gcc test.c -O对编译好的文件进行重命名
预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。
2.
编译 选项 gcc -S test.c
编译完成之后就停下来,结果保存在test.s中
3.
汇编 gcc -c test.c
汇编完成之后就停下来,结果保存在test.o中(目标文件)
readelf,elf是一种文件格式(将.o文件分成一个一个段),test.o,test.exe都是以这种格式组织的。
程序执行的过程:
》1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
》2. 程序的执行便开始。接着便调用main函数。
》3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
》4. 终止程序。正常终止main函数;也有可能是意外终止
__ FILE__ //进行编译的源文件
__ LINE__ //文件当前的行号
__ DATE__ //文件被编译的日期
__ TIME__ //文件被编译的时间
__ FUNCTION__//打印函数名
__ STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
#include
int main()
{
printf("%s\n", __DATE__);
printf("%s\n", __TIME__);
printf("%s\n", __FILE__);
printf("%d\n", __LINE__);
printf("%s\n",__FUNCTION__);
printf("%d\n", __STDC__);
return 0;
}
这些都是在预处理中被替换。
#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定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
#define name( parament-list ) stuff
//其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中
宏是直接替换的,不会加括号的,要加括号避免不必要的麻烦,自己根据实际情况加括号。
一些宏的常见错误:
#include
#define mul(x) x*x//改法:(x)*(x)
int main()
{
int a = 5;
printf("%d", mul(a + 1));//5+1*5+1=11
return 0;
}
#include
#define mul(x) (x)+(x)//改法:((x)+(x))
int main()
{
int a = 5;
printf("%d", 10*mul(a + 1));//10*6+6=66
return 0;
}
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先
被替换。- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
#include
#define mul(x) ((x)+(x))
#define a 5
int main()
{
printf("%d", mul(a + 1));
return 0;
}
先替换a,即printf(“%d”, mul(5 + 1));,再替换x,即#define mul(5+1) ((5+1)+(5+1)),最后再替换mul(5+1),即printf(“%d”, ((5+1)+(5+1)));
注意:
#include
int main()
{
printf("hello" "world\n");
return 0;
}
打印出来的结果是什么?
helloworld
vs会将两个字符串合并在一起的。
如何把参数插入到字符串中?
#include
#define PRINT(x) printf("the value of "#x" is %d\n",x)
int main()
{
int a = 10;
PRINT(a);//将“a”插入
return 0;
}
#include
#define PRINT(format,x) printf("the value of "#x" is "format"\n",x)
int main()
{
int a = 10;
PRINT("%d", a);
float b = 3.14;
PRINT("%f", b);
return 0;
}
加了#相当于替换参数名,不加#号相当于替换参数的值
##的作用:
##可以把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符。
#include
#define Cat(x,y) x##y
int main()
{
Cat("Class", "-w302");
printf("%s\n", Cat("Class", "-w302"));
return 0;
}
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
#include
#define Max(x,y) (x)>(y)?(x):(y)
int main()
{
int a = 4;
int b = 6;
int ret = Max(a++, b++);
//(a++)>(b++)?(a++):(b++)
// a=5 b=7 b=8
printf("%d\n%d %d", a, b, ret);
return 0;
}
宏和函数的比较:
#include
#define MALLOC(num,type) (type*)malloc(num*sizeof(type))
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
int* pp = MALLOC(10, int);
return 0;
}
命名约定:
把宏名全部大写
函数名不要全部大写
许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。
例如:当我们根据同一个源文件要编译出一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一个机器内存大些,我们需要一个数组能够大些。)
#include
int main()
{
int array [ARRAY_SIZE];
int i = 0;
for(i = 0; i< ARRAY_SIZE; i ++)
{
array[i] = i;
}
for(i = 0; i< ARRAY_SIZE; i ++)
{
printf("%d " ,array[i]);
}
printf("\n" );
return 0;
}
//linux 环境演示
gcc -D ARRAY_SIZE=10 programe.c
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译
#include
int main()
{
#ifdef PRINT
printf("hehe");
#endif
return 0;
}
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 OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif
#include
#define M 9
int main()
{
#if M==1
printf("hehe");
#elif M==2
printf("haha");
#else
printf("heihei");
#endif//注意这个不要忘记
return 0;
}
#include
#define M 1
int main()
{
#ifdef M
printf("hehe");
#endif
#ifndef N
printf("haha");
#endif
return 0;
}
以上就是本篇的所有内容了,如果喜欢本篇,不妨点个赞,如有问题,欢迎评论区提问,谢谢大家的观看。