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
    
    int main(int argc, char const *argv[]){
        printf("Hello World!");
        
        return 0;
    }
    

    C语言——C程序编译过程_第1张图片

  2. 对源文件 hello.c 进行预处理,生成预处理文件 hello.i C语言——C程序编译过程_第2张图片

  3. 对预处理文件 hello.i 进行编译,生成汇编文件 hello.s
    C语言——C程序编译过程_第3张图片

  4. 对汇编文件 hello.s 进行汇编,生成二进制文件 hello.o C语言——C程序编译过程_第4张图片

  5. 对二进制文件 hello.o 进行链接,生成可执行文件 hello.exe
    C语言——C程序编译过程_第5张图片

  6. 运行可执行文件 hello.exe
    C语言——C程序编译过程_第6张图片

7.1 预处理

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

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

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

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

7.1.1 文件包含处理

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

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

7.1.2 宏定义

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

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

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

a. 不带参数的宏定义

# define 标识符 字符串

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

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

#include 
#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 
#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;
}

你可能感兴趣的:(编译型语言,#,C语言,c语言,c++,开发语言)