• 【7】C语言进阶--程序的编译(预处理操作)+链接


    test.c    --源文件(源程序)

    经过编译生成   ****.exe文件        二进制文件

    1.程序的编译和执行环境

    2.详解C语言程序的编译+链接

    编译处理的过程        编译器(预编译    编译     汇编)

    链接处理的过程        链接

    • 组成一个程序的每个源文件都通过编译过程分别转换成目标代码(object code)
    • 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序
    • 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。
    1. 预处理:预处理之后产生的结果都放在test.i文件中,(#include  头文件包含,删除注释,使用空格替换注释,预处理阶段做的为文本操作)
    2. 编译:编译完成之后就停下来,结果保存至test.c中(把C代码翻译为汇编代码)
    3. 汇编:汇编完成之后就停下来,结果保存在test.o中(形成符号表)

    3.预定义符号介绍

    1. // 预定义符号
    2. __FILE__ //进行编译的源文件
    3. __LINE__ // 文件当前的行号
    4. __DATE__ // 文件被编译的日期
    5. __TIME__ // 文件被编译的时间
    6. __STDC__ // 如果编译器遵循ANSIC,其值为1,否则未定义
    1. #include
    2. // 预定义符号
    3. /* __FILE__ //进行编译的源文件
    4. __LINE__ // 文件当前的行号
    5. __DATE__ // 文件被编译的日期
    6. __TIME__ // 文件被编译的时间
    7. __STDC__ // 如果编译器遵循ANSIC,其值为1,否则未定义
    8. */
    9. int main()
    10. {
    11. printf("%s\n",__FILE__);
    12. printf("%d\n",__LINE__);
    13. printf("%s\n",__DATE__);
    14. printf("%s\n",__TIME__);
    15. return 0;
    16. }

    运行结果: 

     把数据写进文件txt中(详细内容看--文件操作这一节内容)

    1. #include
    2. // 预定义符号
    3. /* __FILE__ //进行编译的源文件
    4. __LINE__ // 文件当前的行号
    5. __DATE__ // 文件被编译的日期
    6. __TIME__ // 文件被编译的时间
    7. __STDC__ // 如果编译器遵循ANSIC,其值为1,否则未定义
    8. */
    9. int main()
    10. {
    11. int i = 0;
    12. int arr[10] ={0};
    13. FILE *pf = fopen("log.txt","w"); // 打开目标文件
    14. for (i=0;i<10;i++){
    15. arr[i]=i;
    16. fprintf(pf,"file:%s line:%d date:%s time:%s i=%d\n",
    17. __FILE__ ,__LINE__,__DATE__,__TIME__, i)
    18. }
    19. fclose(pf);
    20. pf = NULL;
    21. for(i=0;i<10;i++){
    22. printf("%d",arr[i]);
    23. }
    24. return 0;
    25. }

    4.预处理指令#define

    #define        可以定义标识符,也可以定义宏

    1. #define MAX 100
    2. #define reg register //给register重新起个名字叫做reg
    3. #define do_forever for(;;) //用更形象的符号来替换一种实现

    问题:在define定义标识符的时候,要不要在最后加上分号(;)

     不要加分号,因为在后面语句中会加分号

    #define替换规则

    • 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号,如果是,他们首先被替换。
    • 替换文本随后被插入到程序中原来文本的位置,对于宏,参数名被他们的值替换。
    • 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号,如果是,就重复上述处理过程。

    注意:

    • 宏参数和#define定义中可以出现其他#define定义的变量,但是对于宏,不能出现递归。
    • 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

    宏定义的参数是完整的代入表达式 ,宏通常被应用于执行简单的运算(比如比较两个数的大小),宏比函数在程序的规模和速度方面更胜一筹

    宏定义的函数无法被调试

    ##的作用:可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符

    5.宏和函数的对比

    属性#define定义宏函数
    代码长度每次使用时,宏代码都会被插入到程序中,除了非常小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方,每次使用这个函数,都调用那个地方的同一份代码
    执行速度更快存在函数的调用和返回的额外开销,所以相对慢一些
    操作符优先级宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则临近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号函数参数只在函数调用的时候求值一次,它的结果值传递给函数,表达式的求值结果更容易预测
    带有副作用的参数参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果函数参数只在传参的时候求值一次,结果更容易控制
    参数类型宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是不同的
    调试不方便调试可以逐语句调试
    递归不能递归可以递归

    6.命令定义

    宏名全部大写,函数名不要全部大写

    7.预处理指令#include

    1. #include // 官方库文件
    2. #include "****.h" // 自己写的文件

    ""查找的范围广,但是如果所有的头文件都用“”查找,速度会比较慢

     防止头文件被多次重复引用

    方法1:传统的写法

    1. #ifndef __TEST_H__
    2. #define __TEST_H__
    3. int ADD(int x,int y);
    4. #endif

    方法2:更简洁,现在的写法,不过在老的编译器里会编译不通过,出错

    1. #pragma once
    2. int ADD(int x,int y);

    8.预处理指令#undef

    用于移除一个宏定义

    9.条件编译

    条件满足,进行编译

    用途:调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译

    1. #include
    2. #define DEBUG
    3. int main()
    4. {
    5. int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    6. int i = 0;
    7. for (i = 0; i < 10; i++)
    8. {
    9. arr[i] = 0;
    10. #ifdef DEBUG
    11. printf("%d ", arr[i]);
    12. #endif
    13. }
    14. return 0;
    15. }

    常见的条件编译指令

    1. 1.
    2. #if 常量表达式
    3. //
    4. #endif
    5. 如:
    6. #define __DEBUG__ 1
    7. //
    8. #endif
    9. 2.多个分支的条件编译
    10. #if 常量表达式
    11. //
    12. #elif 常量表达式
    13. //
    14. #elif 常量表达式
    15. //
    16. #endif
    17. 3.判断是否被定义
    18. #if defined(symbol) // 这两条语句等价
    19. #ifdef symbol
    20. #if !defined(symbol) // 这两条语句等价
    21. #ifndef symbol
  • 相关阅读:
    itk配准整理(1)
    Android Glide照片宫格RecyclerView,点击SharedElement共享元素动画查看大图,Kotlin(1)
    Linux命令(二)(文件相关)
    在x86虚拟机搭建arm64交叉编译环境记录
    Linux学习第23天:Linux中断驱动开发(二): 突如其来
    Maven仓库Nexus安装部署
    HIVE自定义UDTF函数
    111.网络安全渗透测试—[权限提升篇9]—[Windows 2008 R2内核溢出提权]
    Django框架之模板层template的一些介绍和使用
    C# 第八章『多线程』◆第1节:进程与线程
  • 原文地址:https://blog.csdn.net/qq_45355603/article/details/126173159