C语言——C程序编译过程

C语言目录:

1. 概述

2. 数据类型

3. 量

4. 运算符

5. 流程控制

6. 函数

7. C程序编译过程

8. 文件

9. 内存管理


预处理
编译
汇编
链接
hello.c
hello.i
hello.s
hello.o
hello.exe
  1. 编写代码,保存后生成 hello.c 源文件

    # include<stdio.h>
    
    int main(int argc, char const *argv[]){
        printf("Hello World!");
        
        return 0;
    }
    

    在这里插入图片描述

  2. 对源文件 hello.c 进行预处理,生成预处理文件 hello.i 在这里插入图片描述

  3. 对预处理文件 hello.i 进行编译,生成汇编文件 hello.s
    在这里插入图片描述

  4. 对汇编文件 hello.s 进行汇编,生成二进制文件 hello.o 在这里插入图片描述

  5. 对二进制文件 hello.o 进行链接,生成可执行文件 hello.exe
    在这里插入图片描述

  6. 运行可执行文件 hello.exe
    在这里插入图片描述

7.1 预处理

C编译器在对源程序进行编译前,会将一些特殊指令作解释(如 #include),进而产生一个新的源程序,之后再进行通常的编译

  • 所有的预处理指令都以 # 开始,并且结尾不用分号

  • 预处理指令可以出现在程序的任何位置,它的作用范围从它出现的位置到文件尾,所以尽可能将预处理指令写在源程序开头

  • 包括:文件包含、宏定义、条件编译

7.1.1 文件包含处理

使用 <> ,仅在系统指定的磁盘路径下搜索所包含的文件

使用 "" ,现在当前工作目录中搜索,若找不到则在系统中寻找

7.1.2 宏定义

:用来替换重复出现的字符串

宏名:代替该字符串的标识符

宏代换(宏展开):在编译预处理阶段,对程序中出现的所有 宏名 用相应的字符串进行代换,宏代换由预处理程序自动完成

a. 不带参数的宏定义

# define 标识符 字符串

字符串可以是常数,表达式,格式串等

约定:宏名一般用大写字母作为标识符,以便与变量名区别开,但用小写也没有语法错误

#include <stdio.h>
#define PI 3.14

int main (){
    float S = PI * r * r;

    printf("%f", S);
    return 0;
}

若字符串中出现了某个宏名,不进行替换

#define PI 3.14

char *str = "PIE";//此时不会发生宏代换

宏代换只是简单的 字符串替换 ,不做语法检查。只有编译时才对源程序进行语法检查


宏定义的有效范围是从定义位置开始到文件结束。如果需要终止宏定义的作用域,可以用 #undef

#include <stdio.h>
#define PI 3.14

int main (){
    #undef PI
    float S = PI * r * r; //报错

    printf("%f", S);
    return 0;
}

宏定义之间可以相互引用

#define R  3.0
#define PI 3.14
#define C  2*PI*R
#define S  PI*R*R

可以用宏定义表示数据类型

# define String char*

int main(){
    String str = "The String";	
    
    return 0;
}
b. 带参数的宏定义

带参数的宏,在调用中,不仅要宏展开,而且要用实参数代替形参

#define 宏名(形参列表) 字符串

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

int main(){
    int sum = SUM(1,2);
    
    printf("%d\n",sum);
    return 0;
}
  • 宏标识符与形参列表之间不能有空格,否则会当被当做普通字符串

    # define SUM (a,b) (a+b)
    
    int main(){
        int sum = SUM(1,2);//会被替换成 (a,b) (a+b)(1,2)
        //编译不通过
        
        printf("%d\n",sum);
        return 0;
    }
    
  • 带参数的宏在宏代换时,只作简单的字符和参数的替换,不进行计算操作

  • 宏定义时,要用 () 将形参字符串括住

    # define Twice(a) 2*a
    
    int main(){
        int res = D(3+4);// 替换结果为:2*3+4
        
        printf("%d\n",sum);
        return 0;
    }
    
    # define Twice(a) 2*(a)
    
    int main(){
        int res = D(3+4);// 替换结果为:2*(3+4)
        
        printf("%d\n",sum);
        return 0;
    }
    
  • 宏定义时,要将计算结果也用 () 括住

    #  define Pow(a) (a)*(a)
    
    int main(){
        int res = Pow(10)/Pow(2);
        //宏代换后:10*10/2*2=100
        
        return 0;
    }
    
    #  define Pow(a) ((a)*(a))
    
    int main(){
        int res = Pow(10)/Pow(2);
        //宏代换后:(10*10)/(2*2)=25
        
        return 0;
    }
    

7.1.3 条件编译

程序中一部分代码在满足一定条件才进行编译,否则不参与编译

优点:按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件,有利于程序的移植和调试。生成的目标程序较短

#if 条件1
	...
#elseif 条件2
    ...
#else
    ...
#endif
#define SCORE 67
#if SCORE > 90
    printf("优秀\n");
	code 优秀
#elif SCORE > 60
    printf("良好\n");
	code 良好
#else
    printf("不及格\n");
	code 不及格
#endif

7.2 别名typedef

typedef 原类型名 新类型名;

  • 原类型名中含有定义部分,新类型名一般用大写表示

  • typedef 在编译时完成替换

7.2.1 typedef 的使用

a. 基本数据类型
typedef int Integer;
typedef Integer MyInteger;
int main(){
    Integer a;//等价于int a
}
b. 数组
typedef int NAME[20];
NAME a;//等价于 int a[20];
c. 结构体类型
 struct Person{
    int age;
    char *name;
};

typedef struct Person Person;
typedef struct Person{
    int age;
    char *name;
} Person;
typedef struct {
    int age;
    char *name;
} Person;
d. 枚举类型
enum Session{
    Spring,
    Summer,
    Autumn,
    Winter
};
typedef enum Session Session;
typedef enum Session {
    Spring,
    Summer,
    Autumn,
    Winter
}Session;
typedef enum {
    Spring,
    Summer,
    Autumn,
    Winter
}Session;
e. 指针

指向结构体的指针

typedef struct {
    float x;
    float y;
}Point;

typedef Point *PP;

指向函数的指针

int sum(int a, int b) {
    int c = a + b;
    printf("%d + %d = %d", a, b, c);
    
    return c;
}
typedef int (*Fn)(int, int);

// 定义一个指向sum函数的指针变量p
Fn p = sum;

7.3 宏定义与函数、typedef区别

7.3.1 宏定义与函数的区别

带参数宏定义与函数形式类似

  • 匹配问题:宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值

  • 执行阶段:函数调用在程序运行时执行,而宏代换只在编译预处理阶段进行

  • 执行效率:带参数的宏比函数具有更高的执行效率

7.3.2 宏定义与typedef 区别

宏定义与typedef 都可对数据类型进行说明

  • 宏定义只是简单的字符串替换,在预处理阶段完成
  • typedef 在编译时处理,是对类型说明符的重新命名
typedef char *String;//给char *起了别名String

int main(){
    String str = "This is a String";
    return 0;
}

#define String char * //用String对char *进行宏代换

int main(){
    String str = "This is a String";
    
    return 0;
}
  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AmosTian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值