C/C++程序设计和预处理

个人主页:仍有未知等待探索_C语言疑难,数据结构,小项目-CSDN博客

专题分栏:C语言疑难_仍有未知等待探索的博客-CSDN博客

目录

一、引言

二、程序的翻译环境和执行环境

1、什么是程序

2、程序的翻译环境

3、程序的执行环境 

三、预处理 

1、预定义符号

2、#define 

1.#define定义标识符

2.#define定义宏

3.#define的替换规则

注意

思考题 

4.#和##

1.#的作用

2.##的作用

5.带副作用的宏参数

6.宏和函数对比

宏的优势

函数的优势

7.命名约定

 3、#undef

功能

4、命令行定义 

5、条件编译 

6、文件包含

1.头文件包含

2.嵌套文件包含 

问题解决一:

问题解决二:

7、其他预处理指令 


一、引言

到这篇文章开始,C语言迎来了最后结束。记住这是书的结束,而不是我们的学习C语言的结束!

但我们要知道C语言的内容不仅仅是这么一点点内容而已,还有很多更加高深,更加底层的知识等着我们去学习,去了解。

C语言的底层逻辑是怎么实现的,代码的执行逻辑又是怎么样的,函数栈帧的创建和销毁,还有编译器提供一系列的库函数等等……

革命尚未成功,同志仍需努力!

二、程序的翻译环境和执行环境

1、什么是程序

计算机程序是一组计算机能识别和执行的指令,运行于电子计算机上,满足人们某种需求的信息

工具。说白了互联网、智能移动设备、云计算、大数据的共同基础、共同的指挥官就是程序。

简而言之,程序=算法+数据结构。

2、程序的翻译环境

在ANSI C(C语言标准)中,存在两个不同的环境,一个是翻译环境,另一个是执行环境。

翻译环境:把程序员写的文本代码转化成机器可识别的二进制指令。

执行环境:说白了就是代码执行所需要的环境,用于代码的执行。

就比如说:文本文件、源文件(test.c)——>通过翻译环境可以转化成可执行文件,也就是二进制文件(test.exe),然后通过执行环境进行执行。 

1、test.c通过翻译环境和运行环境进行,来对文件进行可执行文件。

2、翻译环境会包括编译和链接。编译是指编译器(比如VS2019、devc++等),而链接器是链接目标文件和链接库生产的可执行程序(二进制程序)

3、编译又可以分为三个阶段:预编译、编译、汇编。

  1.  预编译:注释的替换(删除,注释会被替换成一个空格)、头文件的包含(#include<>)、#define符号的替换。这些都会在正式编译前会完成(对文本操作)。
  2. 编译:把C语言代码翻译成汇编代码。(通过:词法分析,语法分析,语义分析,符号汇总)。
  3. 汇编:把汇编代码翻译成二进制的指令,生成目标文件(.obj)、生成符号表(汇总的符号都是全局的)。
  4. 链接器:合并段表、符号表的合并和重定位。

其大体的过程如下: 

​  

3、程序的执行环境 

程序的执行过程:

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

三、预处理 

1、预定义符号

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

//预定义符号__FILE__ //进行编译的源文件__LINE__ //文件当前的行号__DATE__ //文件被编译的日期__TIME__ //文件被编译的时间__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
#include<stdio.h>
int main()
{//预定义符号//__FILE__ //进行编译的源文件//__LINE__ //文件当前的行号//__DATE__ //文件被编译的日期//__TIME__ //文件被编译的时间//__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义printf("%s\n", __FILE__);printf("%d\n", __LINE__);printf("%s\n", __DATE__);printf("%s\n", __TIME__);//printf("%d\n", __STDC__);不是所有的编译器都全部按照ANSI C进行编辑的return 0;
}

2、#define 

1.#define定义标识符

#include<stdio.h>
#define name stuff//语法
#define max 100
#define reg REG
#define do_forever for(;;)
//如果要替换的标识符太长了,可以换行写,但是必须在每行的末尾加上一个反斜杠(最后一行除外)
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
int main()
{return 0;
}

注:

  1. 如果要替换的标识符太长了,可以换行写,但是必须在每行的末尾加上一个反斜杠(最后一行除外)。
  2. 不要在语句的末尾加上分号。 

先存个疑,自己可以进行思考,在代替规则里,我会进行解释。 

#include<stdio.h>
#define max 30;
int main()
{//这么写是有语法错误的int n=0;if (max > 0)n -= max;elsen += max;return 0;
}

2.#define定义宏

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

宏的申明方式:
#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。
注意:
参数列表的左括号必须与name紧邻。
如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

其实就是将#define定义的宏直接往文本文件里面直接进行替换。

文本文件: 

#include<stdio.h>
#define Add(a,b) ((a)+(b))
int main()
{int a = 6;int b = 5;int sum = Add(a, b);printf("%d",sum);return 0;
}

被预编译后的文件 

#include<stdio.h>
#define Add(a,b) ((a)+(b))
int main()
{int a = 6;int b = 5;int sum = a+b;printf("%d", sum);return 0;
}

3.#define的替换规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

#define的使用规则比较简单,但是需要注意的是#define定义的标识符和宏是完全的遵循替换规则的。(替换规则就是将#define定义的标识符和宏给替换掉)

注意

1、#define定义宏和标识符不是计算好了之后再从文本中找到,然后进行替换。而是先进行替换,然后才计算。
2、宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
3、当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

#define的代替规则。

给标识符max进行#define定义的时候,在30后面多加了一个分号,导致回代的时候会多一个分号。而if——else没有花括号的话,默认只有一条语句,而空语句也算一条语句,导致if——else的语法不符合。

思考题 

下面代码的值是多少?

#include<stdio.h>
#define c a+b
int main()
{int a = 3;int b = 5;int ret1 = b * c;int ret2 = c * b;printf("ret1 = %d\nret2 = %d", ret1, ret2);return 0;
}

c先会被替换成a+b,然后才开始进行编译。

 

4.#和##

1.#的作用

如何把宏的参数插入到字符串中?

#define PRINT(FORMAT, VALUE) printf("the value is "#FORMAT"\n", VALUE);//使用 # ,把一个宏参数变成对应的字符串
#include<stdio.h>
int main()
{PRINT("%d", 10);return 0;
}
2.##的作用

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

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

#include<stdio.h>
#define CAT(v,n) v##n
int main()
{int v10 = 100;printf("%d", CAT(v, 10));return 0;
}

 

5.带副作用的宏参数

这里的副作用不是坏的意思,而是他除了运行这一串代码后,也可能会有什么其他的变化。

例如:

副作用就像下图一样,当a++,b++执行完之后,a和b的值发生了改变。这就叫做“副作用”

#include<stdio.h>
#define MAX(a,b) ((a)>(b)?(a):(b))
int main()
{int a = 100;int b = 20;int max = MAX(a++, b++);printf("%d", max);return 0;
}

6.宏和函数对比

宏的优势
  1. 宏在一些代码简洁的功能上,时间短。函数写一些简单的功能时,函数调用、函数执行、传返回值会比想象得更加繁琐。
  2. 函数传参时是有固定的参数类型的,而宏没有这一限制。
函数的优势
  1. 当代码量大的时候,使用函数更加方便。
  2. 函数方便调试,宏没办法进行调试。
  3. 函数更加的严谨。
  4. 宏可能会带来优先级的问题,而函数不会。
属 性#define定义宏函数
代 码 长 度每次使用时,宏代码都会被插入到程序中。除了非常
小的宏之外,程序的长度会大幅度增长
函数代码只出现于一个地
次使用这个函数时,都调
地方的同一份代码
执 行 速 度更快存在函数的调用和返回的
销,所以相对慢一些
操 作 符 优 先 级宏参数的求值是在所有周围表达式的上下文环境里,
除非加上括号,否则邻近操作符的优先级可能会产生
不可预料的后果,所以建议宏在书写的时候多些括
号。
函数参数只在函数调用的
值一次,它的结果值传递
数。表达式的求值结果更
测。
带 有 副 作 用 的 参 数参数可能被替换到宏体中的多个位置,所以带有副作
用的参数求值可能会产生不可预料的结果。
函数参数只在传参的时候
次,结果更容易控制。
参 数 类 型宏的参数与类型无关,只要对参数的操作是合法的,
它就可以使用于任何参数类型。
函数的参数是与类型有关
果参数的类型不同,就需
的函数,即使他们执行的
相同的。
调 试宏是不方便调试的函数是可以逐语句调试的
递 归宏是不能递归的函数是可以递归的

7.命名约定

一般来讲,宏的用法和函数类似,没办法区分。

我们一般全用大写来表示宏名。

不全部为大写来表示函数名。

 3、#undef

功能

移除宏定义

#include<stdio.h>
#define MAX(a,b) ((a)>(b)?(a):(b))
int main()
{int a = 100;int b = 20;
#undef MAX//意思就是将名为MAX的宏给移除//int max = MAX(a++, b++);这条语句将会报错,MAX无法解析的外部符号printf("%d", max);return 0;
}

4、命令行定义 

许多C的编译器都提供了一种能力,可以在命令行中定义符号,用于编译。

5、条件编译 

通过条件编译,编译程序的时候,我们将一条语句进行编译和移除很方便。

当#if后面的条件语句真值为1的时候,执行后面的语句,到#endif结束。

#if 1printf("%d", max);
#endif
#if 1==1printf("haha");
#elif 2==1printf("hh");
#endif

判断某个宏名是否被定义

//如果SUM宏存在,执行语句;否则不执行//第一种写法
#if defined(SUM)
//语句
#endif//第二种写法
#ifdef SUM
//语句
#endif
//如果SUM宏不存在,执行语句;否则不执行
//第一种写法
#if !defined(SUM)
//语句
#endif//第二种写法
#ifndef SUM
//语句
#endif

6、文件包含

1.头文件包含

#include的指令就是在文件里面包含其他文件。

这个不陌生吧,包含标准输入输出文件。

这个包含语句写法有两种:#include<>和#include""。

#include<>(标准库中包含)#include""(本地文件包含)
会在标准库中查找,如果没有则报错首先编译器会先在本地文件中进行查找,如果本地文件没有,则会在标准库中查找。如果最终没找到,则报错

2.嵌套文件包含 

嵌套文件包含会造成文件内容的重复。

问题解决一:

在每个头文件写的时候加上这句话。

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__
问题解决二:

在头文件的最前面写上这句话。

#pragma once

7、其他预处理指令 

#error
#pragma
#line
……

谢谢大家的支持! 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Banana Pi BPI-W3(Armsom W3)RK3588开当板之调试UART

前言 本文主要讲解如何关于RK3588开发板UART的使用和调试方法&#xff0c;包括UART作为普通串口和控制台两种不同使用场景 一. 功能特点 Rockchip UART (Universal Asynchronous Receiver/Transmitter) 基于16550A串口标准&#xff0c;完整模块支持以下功能&#xff1a; 支…

Chromium源码由浅入深(一)

工作中需要对Chromium源码、尤其是源码中图形部分进行深入研究&#xff0c;所以借此机会边学习边写文章&#xff0c;分享一下我的实时学习研究Chromium源码的由浅入深的过程。 闲言少叙&#xff0c;书归正传。 通过命令行启动Chrome浏览器&#xff0c;命令及结果如下&#xf…

系统升级数量超微软预期,Win10/11盗版激活被封杀

声明&#xff1a;本文提供的命令、工具来自第三方网站&#xff0c;仅供学习交流使用&#xff0c;下载后24小时内删除&#xff0c;一切非法使用责任由使用者自行承担。 上月底 Win11 迎来了 Moment 4 功能更新&#xff0c;任务栏取消合并居然真的回归了。 巨硬终于妥协&#x…

asp.net网上商城系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio协同过滤设计

一、源码特点 asp.net网上商城系统是一套完善的web设计管理系统系统采用协同过滤算法进行商品推荐&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库 为sqlserver2008&#xff0c;使用c#语言开发 ASP…

ilr normalize isometric log-ratio transformation

visium_heart/st_snRNAseq/05_colocalization/create_niches_ct.R at 5b30c7e497e06688a8448afd8d069d2fa70ebcd2 saezlab/visium_heart (github.com) 更多内容&#xff0c;关注微信&#xff1a;生信小博士 The ILR (Isometric Log-Ratio) transformation is used in the anal…

面试算法40:矩阵中的最大矩形

题目 请在一个由0、1组成的矩阵中找出最大的只包含1的矩形并输出它的面积。例如&#xff0c;在图6.6的矩阵中&#xff0c;最大的只包含1的矩阵如阴影部分所示&#xff0c;它的面积是6。 分析 直方图是由排列在同一基线上的相邻柱子组成的图形。由于题目要求矩形中只包含数字…

网络安全https

http是明文的&#xff0c;相当于在网上裸奔&#xff0c;引出了https&#xff0c;大多数网站都转为了https&#xff0c;连非法的赌博网站有的都是https的。 1.https的网站是不是必须让用户装数字证书&#xff1f; 答&#xff1a;分两种&#xff0c;一种是单向认证&#xff0c;像…

2023高频前端面试题-vue

1. 什么是 M V VM Model-View-ViewModel 模式 Model 层: 数据模型层 通过 Ajax、fetch 等 API 完成客户端和服务端业务模型的同步。 View 层: 视图层 作为视图模板存在&#xff0c;其实 View 就是⼀个动态模板。 ViewModel 层: 视图模型层 负责暴露数据给 View 层&…

移远通信5G RedCap模组拿下首个中国移动5G物联网开放实验室5G及轻量化产品能力认证

10月21日&#xff0c;在2023世界物联网博览会期间&#xff0c;中国移动举办了以“智融万物 创见未来”为主题的物联网开发者大会暨物联网产业论坛。作为中国移动在物联网领域重要的合作伙伴&#xff0c;移远通信应邀参加论坛。 随着千行百业数智化进程的不断加速&#xff0c;5G…

什么是web3.0?

Web 3.0&#xff0c;也常被称为下一代互联网&#xff0c;代表着互联网的下一个重大演变。尽管关于Web 3.0的确切定义尚无共识&#xff0c;但它通常被认为是一种更分散、更开放且更智能的互联网。 以下是Web 3.0的一些主要特征和概念&#xff1a; 1. 去中心化 Web 3.0旨在减少…

达芬奇MacOS最新中文版 DaVinci Resolve Studio 18中文注册秘钥

DaVinci Resolve Studio 18是一款专业的视频编辑软件&#xff0c;它具有多种强大的功能。首先&#xff0c;它提供了丰富的视频剪辑工具&#xff0c;如剪切、复制、粘贴、剪辑、缩放和移动等&#xff0c;使用户可以轻松地剪辑和组合视频素材。其次&#xff0c;该软件还支持多个轨…

搜维尔科技:伦敦艺术家利用Varjo头显捕捉盲人隐藏的梦想

在伦敦举行的弗里泽艺术博览会上,与专业级虚拟现实/XR硬件和软件领域的全球领先者Varjo合作,展示一个突破性的混合现实艺术装置, 皇家国家盲人学会 (rnib),英国领先的视力丧失慈善机构。 这个名为"公共交通的私人生活"的装置是一个互动的声音和图像雕塑,旨在让有眼光…

AI小百科 - 什么是词向量?

如何表示一个单词的意义&#xff1f;对人来说&#xff0c;一般用解释法&#xff0c;用一段话来解释词的含义。如“太阳”在新华字典中的释义是“太阳系的中心天体。银河系的一颗普通恒星。”然而&#xff0c;这样的解释计算机是听不懂的&#xff0c;必须用更简洁的方式来对词义…

Unity3D 打包发布时生成文件到打包目录

有时候需要自己创建批处理文件或日志文件&#xff0c;在启动程序的同级目录使用&#xff0c;减少手动操作的时间和错误率。主要使用到的是OnPostprocessBuild方法。 1、在工程中的Editor文件夹下创建脚本 2、将文件放入Plugins的相关目录 3.脚本内容 using System.Collection…

智慧垃圾站:AI视频智能识别技术助力智慧环保项目,以“智”替人强监管

一、背景分析 建设“技术先进、架构合理、开放智能、安全可靠”的智慧环保平台&#xff0c;整合环境相关的数据&#xff0c;对接已建业务系统&#xff0c;将环境相关数据进行统一管理&#xff0c;结合GIS技术进行监测、监控信息的展现和挖掘分析&#xff0c;实现业务数据的快速…

3ds Max2023安装教程(最新最详细)

目录 一.简介 二.安装步骤 软件&#xff1a;3ds Max版本&#xff1a;2023语言&#xff1a;简体中文大小&#xff1a;6.85G安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU3GHz 内存16G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a; …

UMMKD

方法 对于“Y”形模型&#xff0c;绿线之前的层是分开的&#xff0c;绿线之后的层在模态之间共享。对于“X”形模型&#xff0c;第一条蓝线之前和第二条蓝线之后的层是分开的&#xff0c;蓝线之间的层在模态之间共享 作者未提供数据

Android Studio新功能-设备镜像Device mirroring-在电脑侧显示手机实时画面并可控制

下载最新的灰测版本-蜥蜴 成功运行到真机后&#xff0c;点击右侧Running Devices选项卡&#xff0c;再点击号 选中当前设备&#xff1b; 非常丝滑同步&#xff0c;在电脑侧也可以顺畅控制真机 该功能大大方便了我们视线保持在显示器上专注开发&#xff0c;并且便于与UI视觉进行…

Guacamole Web端配置使用

文章目录 项目目的下载需要的docker镜像配置数据库并启动服务访问并配置web页面连接windows系统 项目目的 使用Guacamole搭建&#xff0c;类似腾讯云那样的web远程控制页面 下载需要的docker镜像 guacamole和guacd都下载最新版&#xff0c;mysql则使用5.6的版本 docker pul…

报错:SSL routines:ssl3_get_record:wrong version number

一、问题描述 前后端联调的时候&#xff0c;连接后端本地服务器&#xff0c;接口一直pending调不通&#xff0c;控制台还报以下错误&#xff1a; 立马随手搜索了一下解决方案&#xff0c;但是emmm&#xff0c;不符合前端的实际情况&#xff1a; 二、解决方法&#xff1a; 实际…