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

【C语言】程序环境,预处理,编译,汇编,链接详细介绍,其中预处理阶段重点讲解

目录

程序环境

翻译环境

1. 翻译环境的两个过程

2. 编译过程的三个阶段 

执行环境 

预处理(预编译) 

1. 预定义符号

2. #define 

2.1 用 #define 定义标识符(符号)

2.2 用 #define 定义宏 

2.3 #define 的替换规则 

2.4 # 和 ## 的用法

2.5 宏和函数

2.6 #undef

3. 命令行定义

4. 条件编译

5. 文件包含

5.1 两种头文件的包含

5.2 嵌套文件包含


程序环境

在ANSI C(标准C)的任何一种实现中,存在两个不同的环境。

1. 翻译环境,在这个环境中源代码被转换为可执行的机器指令。

2. 执行环境,它用于实际执行代码。

.

我们写出的C语言代码是文本信息,计算机不能直接理解,计算机是执行二进制指令的,翻译环境负责将C语言代码转成二进制指令,执行环境负责执行二进制代码。


翻译环境

1. 翻译环境的两个过程

1. 一个工程可以有多个.c(源文件)文件,每个源文件都会单独经过编译器处理生成自己对应的目标文件(.obj),这个过程叫做编译。

2. 多个目标文件和链接库经过链接器的处理,最后生成可执行程序,这个过程叫做链接。

链接库的意思是链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。

2. 编译过程的三个阶段 

翻译环境分为编译和链接两部分,编译又有预处理,编译,汇编三个阶段。

1. 预处理:gcc -E test.c -o test.i

预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。

2. 编译:gcc -S test.c

编译完成之后就停下来,结果保存在test.s中。

3. 汇编:gcc -c test.c

汇编完成之后就停下来,结果保存在test.o中。

.

符号

1. 查看符号,这些符号都是全局的。

2. 每个源文件自己编译阶段符号汇总,汇编阶段形成符号表(符号,对应地址),链接进行所有源文件的符号表合并(相同符号合并,函数定义的符号的地址为有效地址,函数声明的符号的地址为无效地址,选取有效地址)。

比如:下图main.c符号表中_sum地址是无效的,合并的时候选择sum.c符号表的_sum地址。


执行环境 

程序执行的过程:

1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排(例如单片机的烧录),也可能是通过可执行代码置入只读内存来完成。

2. 程序的执行便开始。接着便调用main函数。 

3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack)也就是函数栈帧,存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。

4. 终止程序。正常终止main函数;也有可能是意外终止。


预处理(预编译) 

1. 预定义符号

以下预定义符号会在预处理阶段被替换。

__FILE__ 替换为当前进行编译的源文件名称

__LINE__ 替换为当前的行号

__DATE__ 替换为文件被编译的日期

__TIME__ 替换为文件被编译的时间

__STDC__ 如果编译器遵循ANSI C,其值为1,否则未定义

.

2. #define 

2.1 用 #define 定义标识符(符号)

语法:

#define name stuff

例子:

用 MAX 代表1000。

#define MAX 1000

为 register 这个关键字,创建一个简短的名字。

#define reg register

用更形象的符号来替换一种实现。

#define do_forever for(;;)

在写 case 语句的时候自动把 break 写上。

#define CASE break;case

如果定义的 stuff 过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。

#define DEBUG_PRINT printf("file:%s\tline:%d\t\date:%s\ttime:%s\n" ,\__FILE__,__LINE__ ,\__DATE__,__TIME__ )

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

答:不会直接报错,但没必要加,因为

1. 这只是单纯的替换,如果你加了分号,万一代码那边也写了分号,就会出现两个分号。

2. 不方便进行运算和逻辑执行,替换后你多一个分号有时候会影响原先代码的逻辑。

2.2 用 #define 定义宏 

语法:

其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在 stuff 中。

#define name(parament-list) stuff 

用法:

#define MAX(x, y) (x>y ? x : y)

这个写法其实不够严谨,因为传进来的可能是多项式,所以尽量加上括号。

#define MAX(x, y) ((x)>(y) ? (x) : (y))

这个替换会发生在预处理阶段。


带副作用的宏参数 

x+1;//不带副作用
x++;//带有副作用

例子

#define MAX(a, b)  ( (a) > (b) ? (a) : (b) )x = 5;
y = 8;
z = MAX(x++, y++);

z 就会替换为 z = ( (x++) > (y++) ? (x++) : (y++));

副作用就是表达式求值的时候出现的永久性效果。

2.3 #define 的替换规则 

在程序中扩展 #define 定义的符号和宏时,需要涉及几个步骤。

1. 在调用宏时,首先对参数进行检查,看看是否包含任何由 #define 定义的符号。如果有,那它们首先被替换,替换后的文本被插入到程序中原来文本的位置。

2. 然后参数被宏的值所替换。

3. 最后,再次对结果文件进行扫描,看看它是否包含任何由 #define 定义的符号。如果有,就重复上述处理过程。

注意:

1. 宏参数和 #define 定义中可以出现其他 #define 定义的符号。但是宏不能出现递归。

2. 当预处理器搜索 #define 定义的符号时,字符串常量的内容并不被搜索。

2.4 # 和 ## 的用法

1. # 的用法:把宏的参数插入到字符串中。

例子:

#define PRINT(n) printf("the value of n is %d\n", n)int main()
{int a = 10;PRINT(a);int b = 20;PRINT(b);return 0;
}

问题:字符串中的n没有被替换。

解决办法:在 n 前面加一个 #

#define PRINT(n) printf("the value of "#n" is %d\n", n)int main()
{int a = 10;PRINT(a);int b = 20;PRINT(b);return 0;
}

题外话:字符串的双引号是可以连接的。

比如:


2. ## 的用法:可以把位于它两边的符号合成一个符号。

例子:

将 a 和 b 连起来变成 ab。

#define CAT(x, y) x##yint main()
{int ab = 10;printf("%d\n", CAT(a, b));printf("%d\n", ab);return 0;
}

2.5 宏和函数

宏的优点

1. 执行简单的运算时,选择用宏而不是函数。

原因:

用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多,所以宏比函数在程序的规模和速度方面更胜一筹。

函数的参数必须声明为特定的类型,宏是类型无关的,所以函数只能在类型合适的表达式上使用。

2. 宏的参数可以出现类型,但是函数做不到。

#define MALLOC(num, type) (type*)malloc(num*sizeof(type))MALLOC(10, int);
//预处理器替换之后:
(int*)malloc(10*sizeof(int));

宏的缺点

1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。

2. 宏是没法调试的。

3. 宏由于类型无关,也就不够严谨。

4. 宏可能会带来运算符优先级的问题,导致程容易出现错。


宏与函数对比


命名约定

1. 宏名全部大写

2. 函数名不要全部大写

2.6 #undef

这条指令用于移除一个宏定义。

如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。

例子:

3. 命令行定义

许多 C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。

例子:

 #include <stdio.h>int main(){int array [ARRAY_SIZE];int i = 0;for(i = 0; i< ARRAY_SIZE; i ++){array[i] = i;}for(i = 0; i< ARRAY_SIZE; i ++){printf("%d " ,array[i]);}printf("\n");return 0;}

编译指令:

gcc -D ARRAY_SIZE=10 programe.c

4. 条件编译

在编译一个程序的时候,条件编译指令可以将一条语句或一组语句进行编译或者放弃编译。

常见的条件编译指令:

1. 单分支的条件编译,常量表达式由预处理器求值。

 #if 常量表达式//...#endif如:
#define __DEBUG__ 1#if __DEBUG__//..
#endif

2. 多分支的条件编译

#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif

3. 判断是否被定义,各自有两种写法。

#if defined(symbol)//...
#endif#ifdef symbol//...
#endif#if !defined(symbol)//...
#endif#ifndef symbol//...
#endif

4. 嵌套指令

#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif

5. 文件包含

5.1 两种头文件的包含

本地文件包含

#include "filename"

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件,如果找不到就提示编译错误。


库文件包含

#include <filename.h>

查找策略:查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。


题外话

1. 其实库文件包含也能用双引号,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

2. #include 指令可以使另外一个文件被编译,就像它实际出现于一样。

这种替换的方式很简单:预处理器先删除这条指令,并用包含文件的内容替换。

这样一个源文件被包含10次,那就实际被编译10次。

5.2 嵌套文件包含

comm.h 和 comm.c 是公共模块。

test1.h 和 test1.c 使用了公共模块。

test2.h 和 test2.c 使用了公共模块。

test.h 和 test.c 使用了 test1 模块和 test2 模块。

这样最终程序中就会出现两份 comm.h 的内容,这样就造成了文件内容的重复。


解决办法有两种

1. 条件编译

#ifndef __TEST_H__#define __TEST_H__
... //头文件的内容#endif   

2. 头文件开头写:

#pragma once

这样就可以避免头文件的重复引入。

林宇恒/code_c - 码云 - 开源中国 (gitee.com)

相关文章:

【C语言】程序环境,预处理,编译,汇编,链接详细介绍,其中预处理阶段重点讲解

目录 程序环境 翻译环境 1. 翻译环境的两个过程 2. 编译过程的三个阶段 执行环境 预处理(预编译) 1. 预定义符号 2. #define 2.1 用 #define 定义标识符(符号) 2.2 用 #define 定义宏 2.3 #define 的替换规则 2.4 # 和 ## 的用法 2.5 宏和函数 2.6 #undef …...

人生低谷来撸C#--021 多线程

1、概念 线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作&#xff0c;那么设置不同的线程执行路径往往是有益的&#xff0c;每个线程执行特定的工作。 线程是轻量级进程。一个使用线程的常见实例是现代操作系统中…...

【优秀python django系统案例】基于python的医院挂号管理系统,角色包括医生、患者、管理员三种

随着信息技术的迅猛发展&#xff0c;传统的医院挂号管理方式面临着效率低下、排队时间长、信息不对称等诸多问题。这些问题不仅影响患者的就医体验&#xff0c;也加重了医院工作人员的负担。在此背景下&#xff0c;基于Python的医院挂号管理系统应运而生。该系统旨在通过信息化…...

硬盘数据丢失不再怕,四大恢复工具帮你轻松逆转局面!

硬盘故障、误删文件、病毒攻击等原因导致数据丢失的情况时有发生。面对这种情况&#xff0c;如何高效、快速地进行硬盘数据恢复呢&#xff1f;接下来几款好用的数据恢复软件推荐给大家。 一、福昕数据恢复&#xff1a;全方位恢复&#xff0c;让数据无遗漏 链接&#xff1a;ww…...

自定义封装日历组件

自定义日历 工作需要&#xff0c;但现有框架封装的日历无法满足需求&#xff0c;又找不到更好的插件&#xff0c;所以准备自己封装一个。 效果图和说明 一个很简易版的demo日历&#xff0c;本文只提供最基本的功能代码&#xff0c;便于阅读二开。 新建calendar.vue文件 <…...

【大模型】【面试】独家总结表格

问题解答你能解释一下Transformer架构及其在大型语言模型中的作用吗?Transformer架构是一种深度神经网络架构,于2017年由Vaswani等人在他们的论文“Attention is All You Need”中首次提出。自那以后,它已成为大型语言模型(如BERT和GPT)最常用的架构。 Transformer架构使用…...

C# 6.定时器 timer

使用控件&#xff1a; 开启定时器&#xff1a;timer1.Start(); 关闭定时器&#xff1a;timer1.Stop(); 定时间时间间隔:Interval timer1.Interval 1000; Interva等于1000是每一秒刷新一次 定时器默认时间间隔是100ms 代码创建定时器 ①创建 Timer t1 new Timer(); …...

有了 createSlice,还有必要使用 createReducer 吗?什么情况需要 createReducer 呢?

通常情况下&#xff0c;使用 createSlice 已经足够满足大多数需求&#xff0c;而不需要直接使用 createReducer。但是&#xff0c;在某些特定场景下&#xff0c;createReducer 仍然有其用处&#xff1a; 更细粒度的控制&#xff1a; 当你需要对 reducer 的行为进行更精细的控制…...

怎么搭建AI带货直播间生成虚拟主播?

随着电商直播带货的热潮不断升温&#xff0c;虚拟主播逐渐崭露头角&#xff0c;成为电商直播领域的新宠&#xff0c;相较于真人主播&#xff0c;虚拟主播具备无档期风险、人设稳定可控、24小时不间断直播等显著优势。 本文将深入探讨如何搭建一个AI带货直播间&#xff0c;并详…...

设计模式的原则

设计模式的原则通常包括以下几种核心原则&#xff1a; 单一职责原则 (SRP)&#xff1a;一个类应该只有一个单一的职责&#xff0c;即该类应该只有一个引起它变化的原因。这样可以减少类之间的耦合&#xff0c;使得系统更加易于维护和扩展。 开放/封闭原则 (OCP)&#xff1a;软…...

RocketMQ与RabbitMQ的区别:技术选型指南

在现代分布式系统和微服务架构中&#xff0c;消息队列&#xff08;Message Queue&#xff0c;简称MQ&#xff09;扮演着至关重要的角色。消息队列用于实现系统间的异步通信、解耦、削峰填谷等功能。目前常见的MQ实现包括ActiveMQ、RabbitMQ、RocketMQ和Kafka。本文将重点对比Ro…...

小白也能懂:SQL注入攻击基础与防护指南

SQL注入是一种针对数据库的攻击方式&#xff0c;攻击者通过在Web表单、URL参数或其他用户输入的地方插入恶意SQL代码&#xff0c;以此绕过应用程序的验证机制&#xff0c;直接与后台数据库交互。这种攻击可以导致攻击者无授权地查看、修改或删除数据库中的数据&#xff0c;甚至…...

【Hot100】LeetCode—76. 最小覆盖子串

题目 原题链接&#xff1a;76. 最小覆盖子串 1- 思路 利用两个哈希表解决分为 &#xff1a;① 初始化哈希表、②遍历 s&#xff0c;处理当前元素&#xff0c;判断当前字符是否有效、③收缩窗口、④更新最小覆盖子串 2- 实现 ⭐76. 最小覆盖子串——题解思路 class Solution …...

删除排序链表中的重复元素 II(LeetCode)

题目 给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 解题 class ListNode:def __init__(self, val0, nextNone):self.val valself.next nextclass Solution:def deleteDuplicates(self…...

【Java】解决如何将Http转为Https加密输出

目录 HTTP转HTTPS一、 获取 SSL/TLS 证书二、 安装证书2.1 Apache2.2 Nginx 三、更新网站配置四. 更新网站链接五. 检查并测试六. 自动续期&#xff08;针对 Lets Encrypt&#xff09; HTTP转HTTPS 将网站从 HTTP 转换为 HTTPS 能够加密数据传输&#xff0c;还能提高搜索引擎排…...

二叉树链式结构的实现(递归的暴力美学!!)

前言 Hello,小伙伴们。你们的作者菌又回来了&#xff0c;前些时间我们刚学习完二叉树的顺序结构&#xff0c;今天我们就趁热打铁&#xff0c;继续我们二叉树链式结构的学习。我们上期有提到&#xff0c;二叉树的的底层结构可以选为数组和链表&#xff0c;顺序结构我们选用的数…...

Python | Leetcode Python题解之第312题戳气球

题目&#xff1a; 题解&#xff1a; class Solution:def maxCoins(self, nums: List[int]) -> int:n len(nums)rec [[0] * (n 2) for _ in range(n 2)]val [1] nums [1]for i in range(n - 1, -1, -1):for j in range(i 2, n 2):for k in range(i 1, j):total v…...

远程访问mysql数据库的正确打开方式

为了安全&#xff0c;mysql数据库默认只能本机登录&#xff0c;但是在有些时候&#xff0c;我们会有远程登录mysql数据库的需求&#xff0c;这时候应该怎么办呢&#xff1f; 远程访问mysql数据&#xff0c;需要两个条件&#xff1a; 首先需要mysql服务器将服务绑定到0.0.0.0…...

网络6 -- udp_socket 实现 echo服务器

目录 1.server 服务端 1.1.完整代码展示&#xff1a; 1.2.代码解析&#xff1a; 1.2.1 给服务端创建套接字 1.2.2 绑定套接字 1.2.3 服务端接受数据并返回 2.客户端&#xff1a; 2.1 完整代码展示&#xff1a; 2.2 代码解析 2.2.1 客户端使用手则&#xff1a; 2.2.2 …...

ASUS/华硕幻15 2020 冰刃4 GX502L GU502L系列 原厂win10系统 工厂文件 带F12 ASUS Recovery恢复

华硕工厂文件恢复系统 &#xff0c;安装结束后带隐藏分区&#xff0c;一键恢复&#xff0c;以及机器所有驱动软件。 系统版本&#xff1a;windows10 原厂系统下载网址&#xff1a;http://www.bioxt.cn 需准备一个20G以上u盘进行恢复 请注意&#xff1a;仅支持以上型号专用…...

simulink绘制bode图

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…...

知识工程视角下的软件研发

知识工程 在我们的工作中存在两类知识&#xff1a;显式知识&#xff08;explicit knowledge&#xff09;、不可言说的知识&#xff08;tacit knowledge&#xff09;。 所谓显式知识就是能够直接表达且在人群中分享的知识。比如&#xff0c;地球的周长、水的密度、三角形面积公…...

深度学习------权重衰退

目录 使用均方范数作为硬性限制使用均方范数作为柔性限制演示最优解的影响参数更新法则总结高纬线性回归多项式的权重衰退从零开始实现初始化模型参数定义L2范数惩罚定义训练代码实现忽略正则化直接训练使用权重衰减从零开始代码实现 多项式的权重衰退的简洁实现简洁函数代码简…...

【算法】退火算法 Simulated Annealing

退火算法&#xff08;Simulated Annealing, SA&#xff09;是一种基于热力学模拟的优化算法&#xff0c;用于求解全局优化问题。它通过模拟物理退火过程来寻找全局最优解。以下是退火算法的基本原理和步骤&#xff1a; 一、基本原理 退火算法的灵感来源于金属在高温下缓慢冷却…...

深入理解 Git `git add -p` 命令中的交互选项

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119@qq.com] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? 专栏导…...

HTML JavaScript 闪光涟漪

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>闪光涟漪</title><style>.ripple-conta…...

FastAPI之Depends

文章目录 基本概念基本用法复杂场景中的 Depends数据库会话管理处理请求用户嵌套依赖全局依赖 作用域与生命周期可选依赖类依赖总结 基本概念 在 FastAPI 中&#xff0c;依赖可以是&#xff1a; 一个函数&#xff0c;它的返回值会被传递给视图函数作为参数。可以被其他依赖函…...

AttributeError: module ‘jwt‘ has no attribute ‘decode‘解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

C++——C++11

前言&#xff1a;本篇文章将分享一些C11版本所产生的一些新的技术以及对老版本的优化。 目录 一.C11简介 二.统一的列表初始化 1.{}初始化 2.std::initializer_list 三.右值引用和移动语义 1.左值引用和右值引用 2.两者的比较 &#xff08;1&#xff09;左值引用 &#…...

day12 多线程

目录 1.概念相关 1.1什么是线程 1.2什么是多线程 2.创建线程 2.1方式一&#xff1a;继承Thread类 2.1.1实现步骤 2.1.2优缺点 2.1.3注意事项 2.2方式二&#xff1a;实现Runnable接口 2.2.1实现步骤 2.2.2优缺点 2.2.3匿名内部类写法 2.3方式三&#xff1a;实现cal…...

深圳最好的网站建设/如何在百度推广自己的产品

来源|新榜&#xff08;ID&#xff1a;newrankcn&#xff09;同样是在抖音里化妆、跳舞、才艺展示&#xff0c;甚至都是漂亮小哥哥小姐姐&#xff0c;但有人能拍出100W点赞爆款&#xff0c;有人只有5个&#xff0c;比如本人。虽然人类的本质是复读机&#xff0c;但你一定不甘心只…...

it公司怎么在国外网站做宣传/云浮seo

需求一&#xff1a;从键盘输入一串字符串&#xff0c;统计数字&#xff0c;字母&#xff0c;空格&#xff0c;其它字符的个数 1 import java.util.Scanner;2 3 public class Q1 {4 5 public static void main(String[] args) {6 // TODO Auto-generated method stu…...

网站模板下载软件/电商中seo是什么意思

在目前的内核版本中&#xff0c;存在三种流行的字符设备编程模型&#xff1a;杂项设备驱动模型&#xff0c;早期经典标准字符设备驱动模型&#xff0c; Linux 2.6 标准字符设备驱动模型。Linux 系统借鉴了面向对象的思想来管理设备驱动 &#xff0c;每一类设备都都会有定义一个…...

汽车音响网站建设/2022网站快速收录技术

————— 第二天 —————————————————首先&#xff0c;我们来定义一个Product类&#xff1a;public class Product {ArrayList<String> parts new ArrayList<String>();public void add(String part) {parts.add(part);}public void show() {S…...

soho在哪里做网站/产品网络营销策划

JavaScript&#xff0c;DOM操作表格 学习要点&#xff1a; 1.操作表格 DOM在操作生成HTML上&#xff0c;还是比较简明的。不过&#xff0c;由于浏览器总是存在兼容和陷阱&#xff0c;导致最终的操作就不是那么简单方便了。本章主要了解一下DOM操作表格和样式的一些知识。 一&am…...

wordpress怎么添加语言包/搜索引擎优化技术有哪些

默认的 dashboard 没啥用&#xff0c;我们用 kubesphere 可以打通全部的 devops 链路。 Kubesphere 集成了很多套件&#xff0c;集群要求较高 https://kubesphere.io/ Kuboard 也很不错&#xff0c;集群要求不高 官方文档&#xff1a;https://kuboard.cn/support/&#xff0c;但…...