当前位置: 首页 > news >正文

【一天一个计算机知识】—— 【编程百度】翻译环境与运行环境

在这里插入图片描述
在这里插入图片描述

🌊🌊点击进入C语言专栏🌊🌊
🌊🌊如何收集自己的代码仓库🌊🌊

文章目录

  • 一、什么是ANSI C?
  • 二、翻译环境与运行环境
    • 2.1)翻译环境
      • 2.1.1)编译
        • 2.1.1.1)预编译(预处理)
        • 2.1.1.2)编译
        • 2.1.1.3)汇编
        • 2.1.1.4)链接
    • 2.2)运行环境

一、什么是ANSI C?

ANSI C 在 C 语言从一个灵活但有点狂野的语言走向标准化、稳定、可移植的里程碑

  • 内存管理 (<stdlib.h>)

ANSI C 正式确立了动态内存分配的标准函数:

void* malloc(size_t size):分配指定大小的内存。

void* calloc(size_t num, size_t size):分配指定数量和大小的内存,并初始化为0。

void* realloc(void* ptr, size_t new_size):重新调整已分配内存的大小。

void free(void* ptr):释放之前分配的内存。

它还引入了 size_t 类型,用于表示内存大小。

  • 文件操作 (<stdio.h>)

ANSI C 对标准输入输出(Standard I/O)库进行了全面标准化。

文件指针:FILE*类型作为文件流的抽象。

文件打开/关闭:fopen(), fclose(), freopen()

格式化 I/O:printf(), scanf(), fprintf(), fscanf()

字符 I/O:fgetc(), fputc(), getc(), putc()

行 I/O:fgets(), fputs()

块 I/O:fread(), fwrite()

文件定位:fseek(), ftell(), rewind()

错误处理:feof(), ferror()

  • 新的关键字和类型

void 关键字:

void func(void):明确表示函数不接受任何参数。

void func():在C中表示接受任意参数(为了兼容K&R C),但在C++中表示不接受参数。推荐使用 (void)。

void*通用指针类型。它可以指向任何类型的数据,但不能直接解引用。malloc 的返回值就是void*

const 关键字:

用于声明一个变量为只读,const int x = 10;

volatile 关键字:

告诉编译器该变量可能随时被外部(如硬件、多线程)修改,禁止编译器对其进行优化(如缓存到寄存器中)。

signedunsigned
明确了 charint 等类型的符号性。

enum (枚举类型):
提供了一种创建命名整数常量集的标准方法。

  • 预处理器 (Preprocessor)

ANSI C 改进和标准化了C预处理器 (CPP)。

#if defined(...):比 #ifdef 更强大的条件编译。

#(Stringizing Operator): 字符串化操作符,将宏参数转换为字符串字面量。

##(Token-Pasting Operator): 标记连接操作符,将两个标记(token)粘合成一个新的标记。

__FILE____LINE__: 标准化的预定义宏

ANSI C通过引入函数原型和标准化库 (如 stdlib.hstdio.h),极大地提高了C代码的可移植性、健壮性和安全性



二、翻译环境与运行环境

ANSI C的任意一种实现中,存在两种不同的编译环境:

第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令(二进制指令)
第2种是执行环境,它用于实际执行代码
在这里插入图片描述



2.1)翻译环境

C源代码转换成可执行机器代码的整个过程
翻译环境由编译和链接两大部分组成。而编译过程通常又可细分为:预处理(有些资料中也称为预编译)、编译和汇编三个阶段

  • 编译
test.c
编译器
cl.exe
test.obj

  • 链接
test.obj
链接器
link.exe
链接库
可执行程序
test.exe


一个 C 语言项目中,如果有多个 .c 文件需要共同构建,那么它们是如何一步步生成可执行程序的呢?

  • 首先,每个.c文件都会单独经过编译器处理,编译成对应的目标文件。
  • 注意:在 Windows 环境下,目标文件的后缀是 .obj;而在Linux环境下,则是.o
  • 接着,这些目标文件会与所需的链接库一起,交给链接器进行处理,最终生成可执行程序。
  • 这里所说的“链接库”,主要包括运行时库(即支持程序运行的基本函数集合)以及任何可能使用的第三方库
链接
编译
链接库
链接器 link.exe
可执行程序 xxx.exe
test.c
add.c
xxx.c
编译器 cl.exe
test.obj
add.obj
xxx.obj


如果再把编译器展开成3个过程,那就变成了下面的过程:

在这里插入图片描述



2.1.1)编译

2.1.1.1)预编译(预处理)
  • 输入:hello.c(C 源代码文件)

  • 输出:hello.i(一个临时的、经过处理的、但仍然是C语言代码的文本文件)

  • 预编译的四大主要任务
    预处理器的工作主要就是处理 C 代码中那些以#开头的指令

任务一: 文件包含 (#include)
任务二: 宏定义与替换 (#define)

时光卷轴:#define宏定义

任务三: 条件编译 (#if, #ifdef, #ifndef 等)

时光卷轴:预处理指令

任务四: 移除注释
任务五: 保留所有的#pragma的编译器指令,编译器后续会使⽤

时光卷轴:预处理指令

经过预处理后生成的 .i 文件将不再包含任何宏定义,因为所有宏都已被展开。同时,所有包含的头文件内容也已被插入到 .i 文件中。因此,当我们无法确定宏定义或头文件是否包含正确时,查看预处理后的 .i 文件是一个有效的确认方法。



2.1.1.2)编译

编译器将语言代码一步步转换到机器可执行的指令,其中词法、语法、语义分析确保了代码的正确性,而优化步骤则致力于提升执行效率

array[index] = (index+4)*(2+6);
  • 词法分析
    作用: 将源代码字符流分解成有意义的词素

在这里插入图片描述

  • 语法分析
    作用: 接下来,语法分析器将对扫描器产生的记号进行语法分析,从而产生语法树。这些语法树是以表达式为节点树
    在这里插入图片描述

  • 语义分析
    作用: 语义分析器负责进行语义分析,其任务是在语法分析的基础上,进一步检查程序结构的正确性。编译器进行的是语义的静态分析,主要包括声明与类型的匹配、类型转换等。此阶段会报告程序中存在的语义错误
    在这里插入图片描述



2.1.1.3)汇编

汇编器负责将汇编代码转换为机器可以执行的指令(二进制指令)。每一个汇编语句几乎都对应一条机器指令。其转换过程基本上是按照汇编指令与机器指令的对照表逐一翻译,通常不进行指令优化



2.1.1.4)链接

链接是计算机程序开发过程中的一个关键步骤,它发生在编译之后、程序运行之前
简单来说,它的任务是将程序的不同部分组合在一起,形成一个单一的、可执行的文件

链接解决的是⼀个项目中多文件、多模块之间互相调用的问题

我们通过一个简单的 C 语言程序来具体演示链接的过程
在这里插入图片描述


我们已经知道,每个源文件会通过单独通过一个链接器生成对应的目标文件

main.c
main.o
add.c
add.o

我们有两个目标文件:add.omain.o,它们都是独立的机器码文件

链接器会执行以下关键任务:

符号解析

  • 链接器检查 main.o,它发现 main.o 有两个未解决的引用:addprintf
  • 链接器查找 add.o,发现其中定义了 add 函数。它成功匹配了 main.o 中对 add 的引用。
  • 链接器查找 C 标准库(libc.solibc.agcc 默认会链接它)。它在标准库中找到 printf 函数的定义,成功匹配了 main.o 中对 printf 的引用

重定位

  • main.o 认为自己从地址 0 开始,add.o 也认为自己从地址 0 开始。但它们在最终的 myprogram 文件中将会有不同的实际内存地址
  • 链接器会决定 add 函数的代码在 myprogram 中最终位于哪个虚拟内存地址(例如 0x400500
  • 链接器会决定 main 函数的代码在 myprogram 中最终位于哪个虚拟内存地址(例如 0x400600
  • 然后,它会回到 main.o 中调用 add 函数的指令处,将原来的占位符替换为add函数的实际地址 (0x400500)
  • 同样,printf 函数的调用也会被修正为printf在标准库中的实际地址
  • 所有相对跳转和数据引用都会被修正为最终程序中的绝对地址或正确偏移

合并代码和数据

  • add.o.text 段(代码)和 main.o.text 段会被合并到 myprogram.text 段中。
  • add.omain.o 中的任何 .data.bss 段也会被合并到 myprogram` 相应的段中。
  • 最终生成一个完整的、可执行的 myprogram 文件

感兴趣的学者,可以考虑购买《程序员的自我修养》这本书学习学习



2.2)运行环境

  1. 程序载入内存: 任何程序都必须先加载到内存中才能运行。在通用操作系统的管理下,这项工作由系统自动完成。而在独立的嵌入式环境中,则需要开发者手动安排载入过程,或者直接将可执行代码写入只读内存。

  2. 控制权移交: 程序执行起点通常是 main 函数,操作系统将控制权移交至此,程序开始运行。

  3. 运行时内存模型: 程序运行时依赖两种主要内存区域:栈 负责管理函数调用时的局部变量和返回地址;静态存储区 则用于存放全局和静态变量,它们的生命周期与程序相同。

  4. 执行结束: 程序的结束分为两种方式:正常终止(main 函数返回)和意外终止(如发生严重错误)



http://www.dtcms.com/a/605393.html

相关文章:

  • 【Redis存储】Redis介绍
  • 计算机组成原理---总线与输入/输出系统
  • Python 的几个重要的相关概念
  • 零基础学AI大模型之Milvus核心:分区-分片-段结构全解+最佳实践
  • Spring AI Alibaba 自学习AI智能体实战:让AI越用越懂你
  • Springboot主配置文件
  • 家具电商网站建设一定要建设好网站才能备案吗
  • 医药建设网站wordpress 柚子皮下载
  • Java被裁后如何快速上岸?
  • 拥抱元宇宙:通过GoogleVR来了解VR到底是什么
  • 【UE5】- VR小技巧 :用PC处理代替频繁使用VR头显开发
  • 攻击者利用自定义GPT的SSRF漏洞窃取ChatGPT机密数据
  • 支付招聘网站套餐费用怎么做帐wordpress preg_replace 关键词 alt
  • GPT-5.1发布:深入解读与 GPT-5、GPT-4o 在性能与安全基准上的全面对比
  • 两台虚拟机搭建多机区块链网络
  • Vue.js栏目 - 目录一展
  • 网站采集怎么做莱芜金点子广告电子版2022最新
  • 2025 最硬核技术创新,重构 AI 感知与决策逻辑
  • flowable05外置表单和绘制流程图
  • UDP网络编程:从客户端封装到服务端绑定的深度实践
  • Arbess从初级到进阶(4) - 使用Arbess+GitLab实现React.js 项目自动化部署
  • 内网穿透技术
  • asp.net做织梦网站长沙商城网站开发
  • [免费]基于Python的深度学习豆瓣电影数据可视化+情感分析推荐系统(Flask+Vue+LSTM+scrapy)【论文+源码+SQL脚本】
  • SQL 分类
  • 微信小程序项目上传到git仓库(完整操作)
  • Vue 3响应式系统的底层机制:Proxy如何实现依赖追踪与自动更新?
  • 【MySQL】MySQL库的操作
  • 研发管理知识库(10)AWS云的核心DevOps工具介绍
  • PostgreSQL 备份导致的 Cache Pollution(缓存污染)