襄阳市做网站 优帮云,百度搜索次数统计,网站建设的违约责任怎么写,西安手机定制网站建设本文介绍程序的环境#xff0c;在Linux下对编译链接理解#xff0c;较为简短#xff0c;着重在于编译的步骤。
C的环境
在ANSI C#xff08;标准C语言#xff09;的任何一种实现中#xff0c;存在两个不同的环境。
翻译环境#xff1a;在这个环境中#xff0c;源代码…本文介绍程序的环境在Linux下对编译链接理解较为简短着重在于编译的步骤。
C的环境
在ANSI C标准C语言的任何一种实现中存在两个不同的环境。
翻译环境在这个环境中源代码被转换为可执行命令的机器命令。执行环境用于实际执行代码。
编译和连接
windows环境下源文件经编译器处理变为目标文件.obj再由链接器和链接库生成可执行文件.exe。
Linux环境下源文件经编译器处理变为目标文件.o 运行环境
程序的执行的过程:
1. 程序必须载入内存当中。再有操作系统的环境中由操作系统完成。在独立的环境中程序的载入必须手动安排也可能是通过可执行代码置入只读内存来完成。
2. 程序执行的开始。调用main()函数。
3. 开始执行程序的代码。程序会使用一个运行堆栈存储函数的局部变量和返回地址。程序同时也可以使用静态(static内存存储静态内存中变量在程序的整个执行过程中一直保留它们的值。
4. 终止程序。正常终止main函数也有可能是异常抛出。
--------------------------------------------------C环境到此结束-----------------------------------------------------------
预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵守ANSI C,其值为1,否则未定义
#define定义符号
#define指令可以定义任何语句但是不同换行在换行是需要特殊的换行符
#define MAX 100
#define STR hello bit
#define CASE break;case
#define reg register
#define do_forever for(;;)
//define语句最好不要加 ;
//在预处理时,define会把 ; 替换到代码中
//define 定义的可以时任何语句//甚至可以时一串代码
#define DEBUG_PRINT printf(file:%s\tline%d\t time%s\n,\
__FILE__,\
__LINE__,\
__TIME__)\ 在define中,如果后面没有任何字符,代表续行符
#define定义宏
宏是替换的不是通过计算的。
定义宏
#define 宏名(参数列表) 宏体
/******************************/
#define SQUARE(X) ((X)*(X))
#define DOUBLE(X) ((X)(X))//宏在定义的时候,把括号都带上
//否则会出现难以预料的错误
//这些错误没有任何语法错误,代码也可以正常运行.
//但是,最终的结果和期待的值有这很大差异 参数列表的左括号必须与宏名紧邻。若果两者之间有空格那么参数列表会被认为是宏体的一部分。带参的宏体最好把括号都带上,以防止宏的逻辑错误。
使用宏
缺少括号产生的逻辑错误
#define SQUARE(X) X*Xint main()
{int r SQUARE(51);//11//等价于int r 5 1 * 5 1;//宏是替换的,不是计算的printf(%d\n, r);return 0;}/**修正**/
#define SQUARE(X) ((X)*(X))
#define的替换规则 在程序中扩展#define定义的符号和宏时(三步骤)
调用宏时首先对参数进行检查看看是否包含任何由#define定义的符号。如果有它们首先被替换。替换文本随后被插入到程序中原来的位置上。对于宏参数名被它们的值所替换。扫描其他文件重复上述步骤直到没有#define #define补充
1、#把参数插入到字符串当
#define PRINT(N) printf(the value of #N is %d\n,N)
#define PRINT2(X,FORMAT) printf(the value of #X is #FORMAT\n,X)
int main()
{int a 10;PRINT(a);//等价于printf(the value of a is %d\n,N)PRINT2(a, %d);//规定打印类型//printf(the value of a is %d\n, a);int b 20;PRINT(b);//printf(the value of b is %d\n, b);float c 3.02;PRINT2(c, %lf);return 0;
}
2、##把两边的符号合并 ##合并产生的表示符必须是合法且定义的否则该标识符非法。
#define CAT(A,B) A##B
int main()
{int a01 100;printf(%d\n, CAT(a, 01));//等价于printf(%d\n, a01);return 0;
}
3、带有副作用的宏参数 带有副作用的宏参数并不是说宏写的不好而是参数传入带有一定的危险。 当宏的参数在宏的定义中出现超过一次的时候如果参数带有副作用那么在使用这个宏的时候就有可能出现危险导致不可预测的后果。 副作用就是表达式求值时出现的永久性的效果。
#define MAX(a,b) ((a)(b)?(a):(b))
int main()
{//int m MAX(2, 3);int a 5;int b 4;int m MAX(a, b);//((a)(b)?(a):(b))//65?---6(后置先用m6) --- a7printf(m%d\n, m);//6?printf(a%d\n, a);//7printf(b%d\n, b);//5return 0;
}x;//有副作用
x1;//没有副作用
4、宏和函数
以上述宏定义的MAX轻量运算和函数定义Max相比宏的优势
宏没有参数限制任何类型都可作比较。因此宏是类型无关的。用于调用函数和函数返回所花费的时间更多宏在程序的规模(更小)和速度(更快)方面更胜一筹。
宏的缺点
除非宏比较短否则会插入大量的代码。宏是不能调试的。宏是类型无关的不够严谨eg:浮点型和整型比较。宏会带来优先级问题可能会导致错误。
函数是没办法以类型作为参数的。因此宏可以做到函数做不到的事情。
#define MALLOC(num,type) (type*)malloc((num)*sizeof(type))
int main()
{int* p MALLOC(10, int);int* ptr (int*)malloc(10*sizeof(int));return 0;
}
宏和函数对比 4、命名约定
为了区分宏和函数
宏名一般全大写。函数名一般全小写。 #undef移除宏定义 #undef可以通过宏名或标识符来撤销#define的定义
#define M 100
int main()
{printf(%d\n, M);
#undef Mprintf(%d\n, M);//编译器会报错return 0;
}
命名行定义 用Linux或者Windows系统的命令行操作指令来定义宏等。操作系统中学习
条件编译 在编译一个程序的时候我们可以通过编译指令来放弃某一条语句或一端代码的编译。
实例
#define __DEBUG__ 10
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 }return 0;
}
常见条件编译指令
1、单分支条件编译
int main()
{
#if 1 //#if 常量表达式printf(hehe\n);
#endif return 0;
}
2、多分支条件编译
#define M 3
int main()
{
#if M5printf(Hehe\n);
#elif M5printf(hah\n)
#else printf(heihei\n);
#endif
}
在预处理时这些未被编译代码直接被删除了。 3、 判断是否被定义
#define MAX 100
int main()
{
#ifdef MAXprintf(max\n);
#endif return 0;
}//等价于
int main()
{
#if defined(MAX)printf(max\n);
#endifreturn 0;
}//如果MAX未定义但要编译代码
int main()
{
#ifndef MAXprintf(max\n);
#endif return 0;
}//等价于
int main()
{
#if !defined(MAX)printf(max\n);
#endifreturn 0;
}
4、嵌套指令 #if......#endif多层嵌套实现。在库函数头文件常见。
文件包含
多重包含
头文件的多次包含会导致代码冗余。
如何防止头文件的多次重复的包含
1、方法一预编译指令控制 2、方法二#pragma once
#pragma once
int Add(int x, int y)
{return x y;
}
查找策略 -----------------------------------------------------C预处理到此结束----------------------------------------------------- #offsetof宏的实现在结构体对齐中计算相对于起始地址的偏移量
#define My_offsetof(Type,member) (size_t) (((Type*)0)-member)struct S
{char c1;int i;char c2;
};int main()
{printf(%d\n, My_offsetof(struct S, c1));//0printf(%d\n, My_offsetof(struct S, i));//4printf(%d\n, My_offsetof(struct S, c2));//8return 0;
}