首页 > 其他分享 >C语言之编译链接

C语言之编译链接

时间:2024-03-29 16:32:53浏览次数:21  
标签:__ int MAX C语言 预处理 编译 链接 define

目录

一、翻译环境和运行环境

二、预编译

1.预定义符号

2.#define

3.头文件的包含

三、翻译环境

1. 预编译

2.编译

3.汇编

4.链接

一、翻译环境和运行环境

在ANSI C的任何⼀种实现中,存在两个不同的环境

第一种是翻译环境,在这个环境中,源代码被转换为可执行的二进制指令。翻译环境即我们日常使用编译器,将一个 " xxx.c " 的文件最终变成一个 " xxx.exe " 的可执行文件的一个过程。

第二种是运行环境,它用于实际执行代码。运行环境一般是由操作系统对 " xxx.exe " 可执行文件进行解析执行的结果。

二、预编译

1.预定义符号

C语言设置了⼀些预定义符号,可以直接使用,预定义符号也是在预处理期间处理的。

__FILE__ //进⾏编译的源⽂件 
__LINE__ //⽂件当前的⾏号 
__DATE__ //⽂件被编译的⽇期 
__TIME__ //⽂件被编译的时间 
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义 

2.#define

(1)#define定义常量

在define定义标识符的时候,不要在最后加上;

#define MAX 1000

if(condition)
 max = MAX;
else
 max = 0;

加了分号后,if和else之间就是2条语句,而没有大括号的时候if后边只能有一条语句。这⾥会出现语法错误。 

(2)#define定义宏

#include <stdio.h>
#define MAX(x,y) (x>y?x:y)

int main() {
	int a = 1;
	int b = 5;
	int E = MAX(a, b);
	// 预处理后变成 int E = (1>5?1:5);
	printf("%d\n", E); 
	return 0;
}

(2.1)宏的陷阱 

#include <stdio.h>
#define SQ(x) x*x

int main() {

	int a = 6;
	int e1 = SQ(a);
	// 预处理后:e1 = 6*6;  //36

	int e2 = SQ(a + 1);
	// 预处理后:int e2 = 6+1*6+1;  //8
	printf("%d\n", result1 );
	printf("%d\n", result2);
	return 0;
}

宏带来了运算符优先问题。由于 #define 在定义宏的时候,是直接对参数进行替换的。所以我们第二个预期为 " 49" 的结果,最终变成了 8.

#include <stdio.h>
#define SQ(x) (x)*(x)

int main() {

	int a = 6;
	int e1 = SQ(a);
	// 预处理后:e1 = (6)*(6);  //36

	int e2 = SQ(a + 1);
	// 预处理后:int e2 = (6+1)*(6+1);  //49
	printf("%d\n", result1 );
	printf("%d\n", result2);
	return 0;
}

可以利用加括号的方式,避免掉入宏的陷阱。解决优先级的运算符的问题 

(2.2)带有副作用的宏参数

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
int main()
{
  x = 5;
  y = 8;
  z = MAX(x++, y++);
  printf("x=%d y=%d z=%d\n", x, y, z);
}
z = ( (x++) > (y++) ? (x++) : (y++));
//x=6 y=10 z=9

(3)#define 定义宏和函数的区别

(4)#undef

#undef 用于移除一个宏定义,在err的上一行,就是移除了 MAX 这个宏。之后再使用的时,就会报错

#include <stdio.h>
#define MAX(x,y) (x>y?x:y)

int main() {
	int a = 1;
	int b = 25;
	int E = MAX(a, b);
	printf("%d\n", E);
	#undef MAX
	int E = MAX(a, b); 	// err
	return 0;
}

(5)#和##

1.#运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带参数的宏的替换列表中。 #运算符所执行的操作可以理解为”字符串化“。有⼀个变量 int x = 10; 的时候,打印出 the value of x is 10 . 就可以写:

#define PRINT(n) printf("the value of "#n " is %d", n);

 PRINT(x);当我们把x替换到宏的体内时,就出现了#x,而#x就是转换为

 printf("the value of ""x" " is %d", x);//预处理之后

2.## 可以把位于它两边的符号合成⼀个符号,它允许宏定义从分离的文本片段创建标识符。 ## 被称 为记号粘合 这样的连接必须产⽣⼀个合法的标识符。否则其结果就是未定义的。

#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \
 return (x>y?x:y); \
}
GENERIC_MAX(int) //替换到宏体内后int##_max ⽣成了新的符号 int_max做函数名 
GENERIC_MAX(float) //替换到宏体内后float##_max ⽣成了新的符号 float_max做函数名 
int main()
{
 //调⽤函数 
 int m = int_max(2, 3);
 printf("%d\n", m);
 float fm = float_max(3.5f, 4.5f);
 printf("%f\n", fm);
 return 0;
}

3.头文件的包含

#include 头文件包含属于预编译的过程,它其实也是进行了相关的文本替换。但C语言 的头文件分为两种,第一种是和库相关的库文件;第二种是本地文件包含。

本地头文件的查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头⽂件⼀样在 标准位置查找头⽂件。 如果找不到就提示编译错误。对于库⽂件也可以使用 “” 的形式包含,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

#include<stdio.h> //库文件
#include"SList.h" //本地文件

#include 指令可以使另外⼀个⽂件被编译。就像它实际出现于 #include指令的地方⼀样。这种替换的方式很简单:预处理器先删除这条指令,并用包含文件的内容替换。 ⼀个头⽂件被包含10次,那就实际被编译10次,如果重复包含,对编译的压力就比较大。

如果xxx.h⽂件比较大,这样预处理后代码量会剧增。使用条件编译指令来防止多次调用xxx.h。 

#ifndef __TEST_H__
#define __TEST_H__
//头⽂件的内容 
#endif 

或者使用pragme

#pragma once
//文件

三、翻译环境

其实翻译环境是由编译和链接两个大的过程组成的,而编译又可以分解成:预处理(预编译)、编译、汇编三个过程

1. 预编译

预处理阶段主要处理那些源文件中#开始的预编译指令。如:#include,#define,处理的规则如下:
(1)将所有的#define 删除,并展开所有的宏定义。
(2)处理所有的条件编译指令,如: #if、#ifdef、#elif、#else、#endif 。
(3)处理#include预编译指令,将包含的头文件的内容插⼊到该预编译指令的位置,这个过程是递归进行的,也就是说被包含的头文件也可能包含其他文件。
(4)删除所有的注释
(5)添加行号和文件名标识,方便后续编译器生成调试信息等。或保留所有的#pragma的编译器指令,编译器后续会使用。经过预处理后的.i⽂件中不再包含宏定义,因为宏已经被展开。并且包含的头⽂件都被插⼊到.i⽂件中。所以当我们无法知道宏定义或者头⽂件是否包含正确的时候,可以查看预处理后的.i⽂件来确认。
 

2.编译

编译过程就是将预处理后的文件进行⼀系列的:词法分析、语法分析、语义分析及优化,生成相应的汇编代码文件。

词法分析:将源代码程序被输⼊扫描器,扫描器的任务就是简单的进行词法分析,把代码中的字符分割成⼀系列 的记号(关键字、标识符、字⾯量、特殊字符等)

语法分析:接下来语法分析器,将对扫描产生的记号进行语法分析,从而产生语法树。这些语法树是以表达式为节点的树。

语义分析:由语义分析器来完成语义分析,即对表达式的语法层⾯分析。编译器所能做的分析是语义的静态分 析。静态语义分析通常包括声明和类型的匹配,类型的转换等。这个阶段会报告错误的语法信息

3.汇编

汇编器是将汇编代码转转变成机器可执行的指令,每⼀个汇编语句⼏乎都对应⼀条机器指令。就是根 据汇编指令和机器指令的对照表⼀⼀的进行翻译,也不做指令优化

4.链接

链接是⼀个复杂的过程,链接的时候需要把⼀堆文件链接在⼀起才生成可执行程序。 链接过程主要包括:地址和空间分配,符号决议和重定位等这些步骤。 链接解决的是⼀个项目中多⽂件、多模块之间互相调用的问题。

标签:__,int,MAX,C语言,预处理,编译,链接,define
From: https://blog.csdn.net/2301_78029441/article/details/137141383

相关文章

  • 【拯救大学牲】人民邮电出版社C语言程序设计第四章编程题答案
     注:本文所有程序均为笔者自己编写,仅供交流学习使用,欢迎一切纠错与指正。目录1.根据x的值,计算y。2.输入4个整数,从小到大排序。3.求解一元二次方程的实根4.输入一个整数,判断它能否被3和5整除,并输出一下信息之一:5.输入整存整取金额及存期,计算出利息(不计利息税)。利息=金额×......
  • 【QT+QGIS跨平台编译】040:【geos_c+Qt跨平台编译】(一套代码、一套框架,跨平台编译)
    点击查看专栏目录文章目录一、geos_c介绍二、文件下载三、文件分析四、pro文件五、编译实践一、geos_c介绍  GEOS_C(GEOSC++接口)是GEOS库的C语言版本,它提供了一套丰富的API,允许开发者在C++程序中执行复杂的几何形状处理和空间关系分析。GEOS_C是基于JTS(Jav......
  • C语言:文件操作
    1.什么是文件磁盘(硬盘)上的文件是文件。但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。1.1 程序文件程序文件包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程(windows环境后缀为.exe)。1.2数据文件文件的内容不一定......
  • 你问我答!手把手教学,银河麒麟桌面操作系统编译安装BIMP插件过程详解
    (引言:银河麒麟应热心用户后台提问,推出银河麒麟桌面操作系统编译安装BIMP插件详解过程详解专题。如有其它问题和需求,欢迎后台留言咨询……)1.GIMP简介GIMP是GNU图像处理程序(GNUImageManipulationProgram)的缩写。包括几乎所有图象处理所需的功能,号称Linux下的PhotoS......
  • C语言运算符和表达式——增1和减1运算符
    目录增1和减1运算符一元运算符前缀增1/减1运算符后缀增1/减1运算符前缀与后缀对变量和表达式的影响稍微复杂一点的例子增1和减1运算符的优缺点增1和减1运算符增1运算符(Increment)++*使变量的值增加1个单位减1运算符(Decrement)--*使变量的值减少1个单位注意:操作数......
  • H5网页调用APP原生分享菜单 方法:mcloudshare://advert?imgUrl=图标链接&link=分享的链
    要在H5网页中调用APP原生的分享菜单,你可以通过以下步骤实现:创建分享按钮或触发分享的交互元素,例如一个按钮或链接。在按钮的点击事件处理程序中,使用JavaScript生成一个调用APP分享功能的URL。根据你提供的信息,生成的URL格式如下:mcloudshare://advert?imgUrl=图......
  • c语言例题,判断闰年
    首先,我们要判断闰年,去写判断闰年的函数,那我们要先知道闰年是如何判断的。普通闰年的判断,一般是公历年份是4的倍数,且不是100的倍数的,以及公历年份是整百数的且必须是400的倍数的才是闰年。根据这些闰年的信息,我们可以构想,那闰年的判断方法就是:闰年必须是能被4整除,并且不能被100......
  • c语言例题,逐个打印数字
    今天来分享个比较简单的程序例题,也是比较经典的一个新手例题,逐个打印输入的数字。我们直接从主函数看起,先定义一个num变量,同时变量的类型是unsignedint,这个类型的意思是无符号的整型变量,unsigned(无符号)是用来修饰int的,说明了num这个变量只能是正数,然后我们用scanf输入想要的数......
  • 新手c语言笔记
    第1章认识C语言C语言是国际流行的使用最广泛的感激程序设计语言。它既可以用来写系统软件,也可以用来写应用软件。1.1C语言的特点(1)c语言简洁紧凑,编写的程序短小精悍。(2)运算符丰富,数据结构丰富。c语言程序生成代码的质量较高,程序执行效率高。(3)C语言限制不太严格,程序设计自......
  • 【编译原理】手工打造词法分析器
    难点:如何拆词?如何定义分隔符?匹配的优先级是什么?关键点:有限自动机元素拆分解析age>=45为了入门字词是如何拆分识别的,我们举一个最简单的例子age>=45只有三种类型:标识符(age)、大于号(GE)、数字字面量(IntLiteral)使用空格分隔不同的元素思路:从左到右依次读取字符......