您的位置:首页 > 编程语言 > C语言/C++

C 08 C语言的编译过程 条件编译和宏

2013-12-31 20:54 375 查看
1.编译过程
1.1 eg: gcc -E hello.c -o hello.i
·预处理器 file.c file.h->file.i
·去掉注释以空格代替
·删除#define 并且展开所有宏定义
·处理条件编译指令#if #ifdef #else
·处理#include 展开被包含的文件
·保留编译器需要使用的#pragma命令

1.2 eg:gcc -S hello.c -o hello.s
· 编译器gcc ->file.S
分析语法语义 并且进行优化同时生成汇编文件
1.3 eg:gcc –c file.s –o hello.o
·汇编器as->file.o
将汇编代码转换成机器代码
1.4·链接器linkerfile.o+libc.a ->file.out
链接器将各模块链接成可执行文件 与库文件相链接生成可执行文件

2.链接器的意义
·主要是把各个模块之间相互引用的部分处理好 使得各模块能过正确的衔接 像拼图一样
·静态链接:编译期间完成file1.o+file2.o+libc.a = a.out(链接器linker作用)
·动态链接:运行期间完成 *.so文件 但是有寻址的时间会减低效率

3.编译器将编译工作分为: 预编译 编译 汇编

4.#define 宏定义
4.1宏表达式 比函数更强大更容易出错



#include <stdio.h>

#define SUM(a, b) (a)+(b)

#define MIN(a, b) ((a<b)? (a):(b))

#define DIM(array) (sizeof(array)/(*array))

int main(void)

{

printf("%d\n",SUM(1,2)*SUM(1,2));//printf("%d\n",(1)+(2)*(1)+(2));

printf("%d\n",MIN(i++, j));//printf("%d\n",((i++<j)? (i++):(j)));

return 0;

}

#define SUM(a, b) ((a)+(b)) 结果为9




4.2 为了避免宏带来的副作用 建议使用简单的参数 避免使用符合表达式的参数
4.3 宏的巧妙运用 宏比函数强大的地方在于宏不仅可以像函数一样定义功能的还可以定义一些语句来替换 还可以是类型等等吧 理论上没有函数调用的消耗 并且宏表达式不能出现递归的调用

4.3 宏定义是没有局部这一说法的 在一个函数中定义的另一个函数也是可以使用的
4.4 C语言强大的内置宏
----------------------------
_FILE_ 被编译的文件名
_LINE_当前行号
_DATE_编译日期
_TIME_编译时间
_STDC_编译器是否遵循标准C规范

eg:

printf("%s:%d Enter main \n", __FILE__, __LINE__);

printf("%s:%d Exit main \n", __FILE__, __LINE__);

eg2:日志宏:用于打印系统日志 使用宏可以提高代码的复用性

#define LOG(s) do{\

time_tt;\

structtm *ti;          \

time(&t);           \

ti = localtime(&t);          \

printf("%s [%s:%d] %s\n", asctime(ti), __FILE__, __LINE__, s);  \

}while(0)


-----------------------------

4.5 定义宏常量


#define定义宏常量可以出现在代码的任何地方

#define从本行开始 之后的代码都可以使用这个宏常量

#define ERROR -1

#definePI3.1415926


5.条件编译 #if #else #endif
  5.1条件编译是预编译指示命令 用于控制是否编译某段代码

eg:

#if( 1 == C)

printf("c==1\n");

#else

printf("else\n");

#endif

shell:gcc -DC=1 -E  hello.c -o hello.i

int main(void)

{

printf("c==1\n");

return 0;

}


6.#include 随心所欲包含头文件
  6.1本质是将已经存在的文件内容嵌入到当前文件中 可能间接包含同样会产生潜入文件内如的动作

#ifndef _GLOBAL_H_

#define _GLOBAL_H_

int m = 3;

#endif


  6.3  条件编译使得我们可以按不同的条件编译不同的代码段 因而可以产生不同的目标代码
  6.4 通过编译器命令行能够定义预处理器使用的宏

  6.5 条件编译可以避免重复包含同一头文件
  6.6 条件编译是在工程开发中可以区别不同产品线的代码
  6.7 条件编译可以定义产品的发布版和调试版

7.#error 用于生成一个编译错误消息 并且停止编译
  7.1 用法:#error message message不需要用双引号包围
    可以用于自定义程序员特有的编译错误信息
    #warning用于生成编译警告 但是不会停止编译

  7.2 定义字符串宏: gcc -DCOMMAND=\"Test Cmd\" hello.c

8.#line的用法 #line number filename
8.1用于强制指定新的行号和编译文件名 并且对源文件的代码重新编号
本质是重定义 __FILE__ 和 __LINE__
8.2#line 10 "sky wang" 用于定义文件或者自己的位置等

9.#pragma
9.1 预处理将忽略它不认识的#pragma指令
9.2 不同的编译器可能以不同的方式处理#pragma
9.3 #pragma编译器指示字 用于指示编译器完成一些特定的动作
#pragma所定义的很多指示字是和编译器和操作系统特有的
#pragma在不同的编译器间是不可移植的
9.4 #pragma message 是VC特有的编译器指示字 GCC将其忽略
    message 参数在编译时输出消息到编译窗口中
    message 可用于代码的版本控制
  9.5 #pragma pack 能够改变编译器的默认对齐方式
  9.6 内存对齐:不同类型的数据在内存中按照一定的规则排列规定CPU只能读写偶地址
9.7 #pragma pack struct 占用内存的大小的计算
·第一个成员起始于0偏移量
    ·每个成员按其类型大小和指定对齐参数n中较小的一个对齐
    ·便宜地址和成员占用大小均需对齐
    ·结构体成员的对齐参数为其所有成员使用的对齐参数的最大值
    ·结构体总长度必须为所有对齐参数的整数倍即最小公倍数 可以除尽所有的对齐数

10.#运算符用于在预编译期将宏参数转换为字符串
#define CONV(s) #s 这里后面的# 将内容转换为一个字符串

11. ##运算符用于在预编译期粘连两个符号
##的巧妙运用 来自高通

file.c
#include <stdio.h>

#define STRUCT(type) typedef struct _tag_##type type;\
struct _tag_##type

STRUCT(Student)
{
char *name;
int id;
};

int main()
{
Student stu;
stu.name = "stu1";
stu.id    = 12;

printf(" %s\n", stu.name);
printf(" %d\n", stu.id);

return 0;
}
file.i预编译文件
typedef struct _tag_Student Student;struct _tag_Student
{
char *name;
int id;
};

int main()
{
Student stu;
stu.name = "stu1";
stu.id = 12;

printf(" %s\n", stu.name);
printf(" %d\n", stu.id);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: