ONEPIECE!程序环境和预处理——C语言最终章

news2024/10/4 10:20:32

时间过得飞快呀,从第一篇blog到现在,已经有三四个月的时间了,而我们终于也迎来了C语言的最终章——程序环境和预处理!加油吧朋友们,ONEPIECE就在眼前~

目录

 

一、程序的"翻译环境"和"运行环境"

 二、详解"编译环境"

三、"运行环境"

四、"预处理"详解

4.1预定义符号

4.2 小能手#define 

4.2.1 #define 定义标识符

4.2.2 #define 定义宏

4.2.3 #define 替换规则

4.2.4 巧用 # 和 ##

4.2.4.1 # 的使用

4.2.4.2 ##的使用

 4.2.5 带副作用的宏参数

4.2.6 宏和函数的对比

4.3 #undef

 4.4 条件编译

4.5文件包含

4.5.2 嵌套文件包含


 一、程序的"翻译环境"和"运行环境"

在ANSIC的任何一种实现中,存在两个不同的环境:

第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
第2种是运行环境,它用于实际执行代码。

test.c是文本信息的代码——源代码 || test.exe是可执行程序——二进制的指令

 二、详解"编译环境"

编译环境其实又分为编译和链接两部分:编译由编译器执行、链接由链接器执行。

编译器和链接器我们都可以在我们的电脑中搜索到:

  

下面我们介绍一下程序的编译过程: 

1.组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
2.每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
3.链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。

其实编译还可以继续细分:预编译、编译、汇编。

下面我们具体看一下每一步系统都干了些什么: 

我们可以看到几个阶段都强调了“符号”、“符号表”的操作,其实这些操作都是在为“链接”这个阶段做铺垫。

我们举个例子:假设我们的文件有两个 test.c 和 add.c


 符号汇总:

在 add.c 中,系统汇总了一个符号(系统一般只汇总应用全局的符号) add

在 test.c 中,系统汇总了符号:addmain、printf(这里忽略不谈) 

生成符号表:

注:这里汇总的每一个符号都是有其地址的,在这里我们假设一下。

合并段表:

而且,目标文件都有其特定的格式,在合并符号表时,相同段的数据会合并到一起:

合并符号表: 

合并两个符号表时,将 add.c 和 test.c 中的符号表合并到一起:

 最终系统都是通过最后这个符号表记录的地址来找到对应的符号位置。

三、"运行环境"

程序执行的过程:
1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
2. 程序的执行便开始。接着便调用main函数。                                                                                3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
4. 终止程序。正常终止main函数;也有可能是意外终止。

四、"预处理"详解

经过上面的学习,我们可能已经忘了,"预处理"是哪个阶段来着?

它是"编译"的第一个小阶段。 

4.1预定义符号

这些预定义符号都是语言内置的。 

用例: 

int main()
{
	printf("%s\n", __FILE__);//进行编译的文件
	printf("%d\n", __LINE__);//文件当前的行号
	printf("%s\n", __DATE__);//文件被编译的日期
	printf("%s\n", __TIME__);//文件被编译的时间
	//printf("%d\n", __STDC__);//当前使用是的VS2022不遵循ANSI C
	return 0;
}

4.2 小能手#define 

4.2.1 #define 定义标识符

语法:
#define name stuff

注意:define定义标识符时,最后不要加 ; 因为系统是完全把 stuff 替换到 name的位置。

相信小伙伴们对定义标识符已经很熟悉啦,下面我们来新介绍一个语法:

4.2.2 #define 定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。

下面是宏的申明方式:

#define name( parament-list ) stuff

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

注意:
参数列表的左括号必须与name紧邻

如果两者之间有任何空白存在,参数列表就会被解释为 stuff 的一部分

举个栗子:

#define ADD(x, y) (x) + (y)
int main()
{
	printf("%d\n", ADD(3, 22));
	return 0;
}

是不是很神奇,用宏也可以做函数的工作,但是宏在传参时也有可能会发生小错误:

 其实宏能做的不只是这些,宏能替换大部分的函数。

4.2.3 #define 替换规则

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

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

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

注意:

1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

4.2.4 巧用 # 和 ##

4.2.4.1 # 的使用

使用 # ,把一个宏参数变成对应的字符串

#define PRINT(x,format) printf(#x "的值是:"format"\n", x)
int main()
{
	float Love_date = 3.22;
	PRINT(Love_date, "%.2f");
	return 0;
}

4.2.4.2 ##的使用

##可以把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符。

注:这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。

 4.2.5 带副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果
例如:

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

4.2.6 宏和函数的对比

宏的优点:

1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。
    所以宏比函数在程序的规模和速度方面更胜一筹
2. 更为重要的是函数的参数必须声明为特定的类型,  宏是类型无关的
    所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点      型等可以用于>来比较的类型。

宏的缺点:

1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。

    除非宏比较短,否则可能大幅度增加程序的长度(每使用一次都要替换一次)。
2. 宏是没法调试的。宏直接替换,我们无法逐步分析。

3. 宏由于类型无关,也就不够严谨。
4. 宏可能会带来运算符优先级的问题,导致程容易出现错

另外,为了区分宏和函数,在宏和函数的命名时,宏一般全大写,如MAX、DOUBLE...;而函数名不全大写。

4.3 #undef

#undef 用于移除一个宏定义

 4.4 条件编译

在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。

常见的条件编译指令:

1.(同if else)

#if  常量表达式

//操作

#endif
//常量表达式由预处理器求值。

如:

#if 1
    printf("u r a genius!\n");
#endif
//...

2.多个分支的条件编译(同if elseif else)
#if 常量表达式
//...
#elif 常量表达式    //可以无限多
//...
#else
//...
#endif        //结束标志,必须有

3.判断是否被定义
#if defined(symbol)
#ifdef symbol

//以上两种写法相同
#if !defined(symbol)
#ifndef symbol

//以上两种写法相同

4.嵌套指令
#if defined(OS_UNIX)
    #ifdef 条件1
        //...
   
#endif
   
#ifdef 条件2
        //...
 
  #endif

#elif defined(OS_MSDOS)
    #ifdef 条件3
        //...
 
  #endif
#endif

4.5文件包含

4.5.1 头文件被包含的方式

头文件的包含有2中形式:

1.包含本地文件(自己的.h文件)

#include"xxx.h"

2.包含标准库的头文件

#include<xxx.h>

本地文件查找:

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

标准库文件查找:

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

4.5.2 嵌套文件包含

当出现以上这种场景时,就造成了文件内容的重复。 

这时我们就可以使用条件编译。或者在VS中我们创建.h文件中,它也会生成#pragma once ,这也可以避免头文件的重复引入。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1124971.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用gen 结合gorm 生成表模型文件

# 创建一个目录 用于执行 自动生成model 的代码 和存储 生成的model文件 mkdir gengormmodel && cd gengormmodel go mod init gengormmodel go get -u gorm.io/genv0.3.16 #最终的目录结构package mainimport ("fmt""gorm.io/driver/mysql""…

【进程概念③】:进程环境变量/进程切换

深入篇【Linux】学习必备&#xff1a;进程环境变量/进程切换 Ⅰ.环境变量Ⅱ.深层意义Ⅲ.全局属性Ⅳ.进程切换 Ⅰ.环境变量 1.环境变量是什么&#xff1f;&#xff1a;环境变量是系统提供的一组name/value形式的变量&#xff0c;不同的环境变量有不同的用户。 一般是用来指定操作…

AIGC笔记--基于DDPM实现图片生成

目录 1--扩散模型 2--训练过程 3--损失函数 4--生成过程 5--参考 1--扩散模型 完整代码&#xff1a;ljf69/DDPM 扩散模型包含两个过程&#xff0c;前向扩散过程和反向生成过程。 前向扩散过程对一张图像逐渐添加高斯噪声&#xff0c;直至图像变为随机噪声。 反向生成过程…

--initialize specified but the data directory has files in it. Aborting. 问题解决

当电脑输入这条命令以试图初始化数据库的时候&#xff0c;出现这样的错误。 2023-10-23T09:04:21.258180Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more deta…

Spark SQL概述与基本操作

目录 一、Spark SQL概述 &#xff08;1&#xff09;概念 &#xff08;2&#xff09;特点 &#xff08;3&#xff09;Spark SQL与Hive异同 &#xff08;4&#xff09;Spark的数据抽象 二、Spark Session对象执行环境构建 (1)Spark Session对象 &#xff08;2&#xff09;代码演…

Python-字符串(切片操作与内建函数)

目录 一、字符串介绍 1、什么是字符串 2、转义字符 二、字符串的输入和输出 1、字符串输出 2、字符串输入 三、访问字符串中的值 1、字符串的存储方式 2、使用切片截取字符串 四、字符串内建函数 1、find 2、index 3、count 4、replace 5、split 6、capitalize …

Centos 7 Zabbix配置安装

前言 Zabbix是一款开源的网络监控和管理软件&#xff0c;具有高度的可扩展性和灵活性。它可以监控各种网络设备、服务器、虚拟机以及应用程序等&#xff0c;收集并分析性能指标&#xff0c;并发送警报和报告。Zabbix具有以下特点&#xff1a; 1. 支持多种监控方式&#xff1a;可…

Docker容器引擎的介绍

目录 Docker概述 容器受欢迎的原因 Docker与虚拟机的区别 Docker三个核心概念 Docker的安装 1、环境准备 2、安装依赖包 3、设置阿里云镜像源 4、安装 Docker-CE并设置为开机自动启动 Docker命令 1、查看 docker 版本信息 2、docker 信息查看 3、Docker 镜像操作命…

GoLong的学习之路(五)语法之数组

书接上回&#xff0c;上回书说到&#xff0c;循环语句&#xff0c;在go中循环语句的少了whlie这个关键词&#xff0c;但是与之for可以改这个改这个特点。并且在终止关键词中&#xff0c;又有标签可以方便&#xff0c;停止。这次说数组 文章目录 Array(数组)数组的初始化方法一方…

数据结构堆详解

[TOC]堆详解 一&#xff0c;堆 1.1堆的概念 堆的性质&#xff1a; 堆中某个节点的值总是不大于或不小于其父节点的值&#xff1b; 堆总是一棵完全二叉树。 1.2堆的存储模式 我们前面的文章提到过&#xff0c;二叉树的两种存储模式&#xff0c;一个是顺序存储&#xff0c;一…

网络第一颗

✍ 如何理解局域网和广域网&#xff1f; ✍ 路由器和交换机是怎样工作的&#xff1f; ✍ 三层交换机能不能代替路由器&#xff1f; -- 1.局域网 2. 广域网 -- -- 企业网络 运营商架构 数据中心架构 -- 局域网 - 内网 - 私网 -- 通过交换机连接的 转发相同IP地址段的…

NVIDIA显卡算力表--nvidia显卡算力表

参考链接&#xff1a;https://blog.csdn.net/qq_41070955/article/details/108269915 官方链接&#xff1a;https://developer.nvidia.com/cuda-gpus

电压放大器在工业领域有哪些用途

电压放大器在工业领域中有广泛的应用&#xff0c;其主要功能是将传感器或其他信号源的微小电压信号放大为更大幅度的电压信号&#xff0c;以便进行后续的信号处理、控制和监测。以下是电压放大器在工业领域中的一些常见用途&#xff1a; 传感器信号放大&#xff1a;工业生产中经…

Java 通过反射修改字符串 String 类型变量的取值而不改变字符串变量的指向

注意点 由于 JDK 8 中有关反射相关的功能自从 JDK 9 开始就已经被限制了&#xff0c;如&#xff1a;通过反射修改 String 类型变量的 value 字段(final byte[])&#xff0c;所以要能够使用运行此方法&#xff0c;需要在运行项目时&#xff0c;添加虚拟机(VM)选项&#xff1a;-…

map set 使用快速上手【C++】

目录 一&#xff0c;关联式容器 二&#xff0c;键值对 三&#xff0c;set 1&#xff09;使用参考此文档 2&#xff09;count 函数 3&#xff09;multiset类 四&#xff0c;map 1. 模板参数介绍 2.operator[]介绍 3. multimap 英语比较好的同学可以自行查找文档 学…

springboot+avue框架开发的医院绩效考核系统全套源码

医院综合绩效核算系统全套源码 &#xff08;应用案例自主版权演示&#xff09; 医院绩效考核系统以医院的发展战略为导向&#xff0c;把科室、员工的绩效考核跟战略发展目标紧密结合&#xff0c;引导医院各个科室、各员工的工作目标跟医院的发展目标结合在一起&#xff0c;实现…

代码随想录Day26 贪心01 LeetCode T53 最大子数组和

LeetCode T53 最大子数组和 题目链接:53. 最大子数组和 - 力扣&#xff08;LeetCode&#xff09; 题目思路: 贪心贪的是哪里呢&#xff1f; 如果 -2 1 在一起&#xff0c;计算起点的时候&#xff0c;一定是从 1 开始计算&#xff0c;因为负数只会拉低总和&#xff0c;这就是贪…

VPN访问外网的原理

一.前言 许多人都用VPN翻墙&#xff0c;那么VPN为什么可以做到访问外网&#xff1f; VPN的全称叫“Virtual Private Network”意思就是虚拟私人专用网络&#xff0c;是专用网络的延伸&#xff0c;通过VPN&#xff0c;可以模拟点对点专用连接的方式&#xff0c;通过共享和公共网…

对知识蒸馏的一些理解

知识蒸馏是一种模型压缩技术&#xff0c;它通过从一个大模型&#xff08;教师模型&#xff09;中传输知识到一个小模型&#xff08;学生模型&#xff09;中来提高学生模型的性能&#xff0c;知识蒸馏也要用到真实的数据集标签。 软损失soft loss就是拿教师模型在蒸馏温度为T的…

Ai写作创作系统ChatGPT网站源码+图文搭建教程+支持GPT4.0+支持ai绘画(Midjourney)/支持OpenAI GPT全模型+国内AI全模型

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统AI绘画系统&#xff0c;支持OpenAI GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署…