C++感受2-逐字逐句,深入理解C++最小例程

以 “Hello World” 例程为载体、线索,在完成 “间接名字空间限定” 写法转换到“直接名字空间限定”的过程,同时掌握函数、主函数、函数调用、级联操作、声明、类型、int、字符串类型、头文件包含、行为数据、流输出操作符、标准输出流对象、标准库名字空间、ADL等20多个C++知识点,打好基础,让后续的学习事半功倍。

 0. 视频

逐行逐句深入讲解cpp最小例程

1. 函数

1.1 基本概念

在编程语言中,函数通常用于表达“做事情”的概念,它需要描述清楚做事情的三个要素:

  1. 做事情的“初始材料”
  2. 做事情的“操作过程”
  3. 做事情的“返回结果”

“初始材料”指对在做事情之前,需要从调用者处得到的数据的描述;
“操作过程”指对做事情的完整过程描述;
“返回结果”指对做事情可能得到的结果的描述;

除此之外,函数还有一个重要的外在属性:调用者。函数对应做事情,有时为了把“大事化小”,需要将一件大事,拆分成多个小事,然后再由大事调用小事(此处的“大”和“小”仅为相对概念,有时候“小”事也可能调用“大”事)。比如:“妈妈炒菜”是一件事,对应一个函数,在炒菜过程中,如果发现家里没有酱油了,炒菜无法进行下去,此时可以调用另一件事所对应的函数:“小明打酱油”。在这一例子中,“小明打酱油”函数的调用者,即“妈妈炒菜”函数。

1.2 基本语法

除此之外,函数还需要有名字,四者的组成语法为:

返回结果类型 函数名 ( 所需初始材料声明 )
{
       操作过程描述;
       return 返回数据;
}

其中:

  • “所需材料声明” 整体上构造函数的参数表(也称参数列表);
  • return 语句用于结束函数的操作过程(简称结束函数), “return” 正是 “返回”的意思;
  • 当一个函数声明为必须有返回结果时,对应的 return 语句后面也必须带相应类型的数据。

1.3 函数头、函数声明

三要素中的“初始材料”描述和“返回结果”描述,再加上函数名字,构成了“函数头”。如在函数后面直接加分号(语句分隔符),称为该函数的声明,即:对函数的基本约定,形如:

返回结果类型 函数名 ( 形参表 ); 

函数声明只是对函数所要做的事的基本描述和约定。即:描述了这件事的“入”和“出”:“入”是指需要“拿到”什么初始数据(材料),“出”是指事情完成后,可能返回什么结果数据。

对于同一个函数,函数声明可以多次出现。因此函数声明可视为一个人的“名片”。
函数声明有时也称为“函数原型”,意为:这个函数“大概长什么样子”。

1.4 函数体、函数定义

函数体用于实现函数,它是函数头之后的一对花括号,花括号内是实现具体操作过程的代码。
函数头加函数体,即代表了一个函数的真实实现。以下是一个真实的C++函数定义例子:

int add(int a, int b)
{
     return a+b;
}

 它表示有一个函数,名为 “add”,它在执行之前,需要从调用者得到两个入参:a和b,二者的类型一个是整数类型,另一个也是整数类型。这个函数执行之后,也会返回(得到)一个整数数据。从实现上看,即返回 a+b 的结果。

一个函数应该只有一个真实实现,称为(函数的)定义。

2. 主函数

主函数可理解为:对应到整个程序需要做的那件“大事”。

主函数是一个特殊的函数。它的第一个特殊之处,即名字必须固定为 main,小写(C++代码区分大小写)。

主函数的第二个特殊之处,它的调用者是程序的外部运行环境,比如操作系统(但并不一定是操作系统,有些程序会运行在虚拟机内,也有些时候,程序由另一个程序调用)。

主函数之所以函数名需要固定,正是为了和外部环境,也就是它的调用是约定好:请将主函数视为程序的开始位置,因此主函数也时常被称为“入口函数”。

仅是“视为”,C++程序实际上仍然可以要求在进入主函数之前,就“偷偷”做点其它事情(通常用于准备工作)。

主函数也是函数,因此也需要考虑“初始材料”和“返回结果”。

既然调用者是外部运行环境,因此主函数如果需要获得初始材料,也是由外部运行环境传入,方法也正是主函数的参数列表。此处,主函数又有特殊之处,它的参数列表可以二选一:

  1. 不需要参数,即空的参数表;
  2. 需要参数,形如: (int argc, char* argv[]);
    我们在很长时间内,会仅使用第1种形式,因此暂不解释第2种形式。

C++主函数必须返回一个整数,因此在函数头中,需写明其返回数据的类型为整数类型,在C++中使用int(即 integer 缩写)。主函数返回的整数有以下含义:

  • 0:表示程序运行正常结束;
  • 非0:表示程序因出错而意外结束,各整数值可用于不同的错误编号。

在函数体中,主函数又有一个“特权”:可以不写任何 return 语句。该情况下表示该主函数永远执行正确,永远返回0。事实上,这个“特权”的背后,只是编译器在发现一个主函数没有返回语句时,就主动帮我们添加了 “return 0;” 而已。

再次强调:这是主函数的“特权”,其它函数如果在声明自己需要有返回值(哪怕返回的类型也是int)却不写(或忘记写) “return XXX;” 语句,会带来严重后果。

3. 认识“流”

上一节课我们所写的代码中,主函数的操作过程就句(事实上还有隐藏的一句“return 0;”):

cout << "Hello World" << endl;

3.1 cout - 标准输出流对象

这其中cout读作c-out,c 是 “character”,out 是 “output-device”,合起来意为“字符输出设备”,即 cout 对应者程序的输出屏幕。

当然,cout 仅用于windows下有控制台或linux/unix下拥有终端的程序,图形界面程序 GUI
的屏幕输出,有另外的实现机制。

由于C++的标准输入输出基于“流”的概念设计及实现,因此cout也称为C++的“标准输出流对象”。关于“流”的概念,本站《STL和boost之“流” 视频辅导》 课程有深入讲解。现在大家可以简单理解为,就像水流、电流一样,C++中的“流”就是将待输出或待输入的数据,排成一串,在某个“管道”中,按某个方向“流”动。

3.2 << - 流输出操作符

两个小于号组成的符号,称为C++的“流输出操作符”。
以小学所学的加号 + 类比:

  • 需要两个操作数、<< 也是;
  • 执行的操作是将左右两数相加,<< 执行的操作是将右边的数据,输出到左边的对象身上;
  • 的操作结果是相加之和那个数,<< 操作结果则是继续得到它左边的对象,在本例中,即 cout;
  • 支持级联操作,比如: 1+2+5,可先计算出 3,然后3继续参与计算: 3+5。<< 也支持级联操作: cout << "Hello World"后,得到的仍然是 cout,于是继续参与计算 cout << endl;

在C++中,操作符本质上也是一种函数。

3.3 endl - 操作控制符、“行为”数据

endl 也是一个函数,但此处我们将它当作数据使用,将它输出到 cout 身上。作用是将之前已经送到 cout 身上的数据,完完整整地送到真实的屏幕上,并加上换行。

每次往真实的显示屏幕上输出,比较耗性能(无关数据量);因此,为提高性能,输出的数据可能被cout对象“攒”起来,积累到一定程度再一次性真实输出。

3.4 ; - 语句分隔符(语句结束符)

在编辑器中,我们可以把一行的C++语句,拆成多行写,比如:

cout <<
    "Hello World"
    << endl;

通常仅在这一语句确实很长时使用,上面的例子显然是在强行换行,不推荐。

 也可以将多个语句合成一行(这就很不推荐了):

cout << "Hello World" << endl; return -1;

对于简单的独立语句,C++编译器都是通过分号来分隔前后两行语句。注意,哪怕如本例中只有一行语句,也需要使用。

反过来,也并不是所有形式语句都需要分隔,后面将学习到复合语句,就是使用一对 {} 包含多行简单语句,此时 {} 就是天然的分隔。在本例中你可以将函数体的那一对花括号视为一种复合语句。

更不是所有代码行都需要语句分隔符。比如本例中的第一行:

#include <iostream>

深入区分的话,这是一行“预处理”代码,“预处理”是指在正式编译之前,先读一下代码做一些比较基础的准备工作,因此这些代码不被视为正式的C++语句,如此,这些代码也被硬性按格式要求书写,比如上面的 include 就只能写在一行,不能折行,下面是错误示例:

#include 
     <iostream>

 

4 出处!出处!!出处!!!

和现实生活中一样,任何一件出处不明的物体,或者人物,都是危险的,所以请再看一眼代表做“大事情”的主函数代码:

int main()
{
  cout << "Hello World" << endl;
}

请问:cout 是谁? << 又是谁? endl 呢?这三个家伙来自哪里?也就是它们的可信出处是?
如果没有出处,严谨的C++编译器不会让这些代码通过编译;而,如果有出处,编译通过了,但程序执行后所要做的事,却不一定真的如我们前面辛苦解释那样的:往屏幕上输出一行对世界的问候——如果它们的出处是由“坏人”提供的话。

4.1 头文件 - 相关符号的统一出处

头文件就像家族族谱,或者松散一点,就像一个名片录或通讯录,可以记载许多符号(比如人名)的出处——在我们名片录或通讯录的人,确实比完全的陌生人,要安全那么一点点了。

#include <XXXX>

 这一行代码中 #include 是一种硬性规定,它是一行预编译代码,用于指示编译器:在你正式工作(正式开始编译当前源文件后续内容)之前,请先找到并打开XXXX文件(通常被称为“头文件”),读入其中的内容——因为这些内容对后续的编译工作有关键作用。

一对 <> 也是一种硬性规定,它进一步要求编译器:当你需要在电脑中成千上万的文件中搜索XXXX文件时,一!定!不!要!到!处!乱!翻!(怕你不小心翻到主人的各种学习资料),你只能在编译环境事情约定,或编译参数中配置好的文件夹里去寻找。

 

4.2 iostream 头文件

iostream 正是来自C++自带的标准库的一个头文件,主要是包含了和“流”相关的各种符号的声明。从字面上看:i 是 input / 输入 ,o 是 output / 输出,stream 就是 “流” 的英文单词。

iostream 正是包含我们在本例中用到的 cout 、<< 、endl 这三个符号的声明,于是这三者相当于了合法的出处:我们出自iostream文件,而后者出自C++自带的标准库,而当前机器上的C++标准库,就是你自己下载、安装到你的电脑上的。

当然,我们的课程用的是线上编程环境,因此iostream等文件,实际在网络所在后台服务器,我们当然也选择相信。

4.3 namespace 名字空间

代码中的各个符号(通常称为标识符)本质就是一个个名字,而一牵涉到名字,就和现实生活一样,会有重名的情况。C++可以“名字空间 / namespace”来避免重名,方法就是将所有相关的名字,统一放在一个“名字空间”内。正如视频所讲,学校中的每个班级,天生就是一个个名字空间,因此“三年一班的李国庆”和“四年二班的李国庆”就得以区分。

当然,这样的前缀,通常只需在两人出现在同一场合的情况下需要。另外,这也解决不了同一名字空间下的重名问题。

标准库也用到了大量的符号,并且它因为是“标准库”,所以就很霸道地占用了很多极为常用的符号名字,比如 “sort”、“find”、“search”、“string”、“begin”、“end”、“list” ……所以,标准库问世后,很快就给自己搞了一个名字空间:std (来自 standard)。

所以,前述的 cout、<<、endl,严格讲其实在 iostream 里声明的全称,解读起来应该是 std::cout、std::<<、std::endl。实际使用时,也应该用上这样的“全称”:

 std::cout std::<< "Hello World" std::<< std::end;

但是,为什么我们现在的代码:cout << “Hello World” << endl 可以编译并正确运行呢?这就是第三行代码的作用:

using namespace std;

它用来告诉编译器,如果在后面的代码,遇到实在找不到出处的符号,可以加上 std::再试试。以 cout 为例,当找不到它的出处时,可将它变成 std::cout 再尝试,于是就成功了。

4.4 关键字

并不是所有在代码中出现的符号都需要担心重名,C++语言(其它语言也一样)规定了一些特别重要的单词(符号)专门给自己使用,其他人(普通程序员)不能把这些单词随意拿作他用,自然,在如此“霸道”的规定下,这些单词就不会有重名的担心了。这样的单词就叫关键字/keyword。

本例中,int、using、namespace,以及被删除掉那一行的 return 都是C++的关键字,C++有近100个关键字。

iostream 则是用户(标准库作者)为文件取的名字,main也是(标准库)程序员为C++入口函数取的名字,std是标准库自用的名字空间的名字,它们要么来自标准库,要么是语言标准的硬性规定,显然我们在写代码时,为符号给的名字,还是尽量不要和它们重名的好。

4.5 直接名字空间限定

使用 using namespace XXX 的方法,称为“间接名字空间限定”,它会将XXX下的所有公开的符号名字,都引入当前代码下。这不是推荐的,名字空间的日常方法。我们的例程经常使用它,仅仅是因为我们的代码比较简单,而间接名字空间限定比较方便让代码行变短一些,从而有利于例程排版。

大多数情况下,我们推荐使用 “直接名字空间限定”的方法,下面就是使用这一方法的,新的Hello World 例程的完整代码:

#include <iostream>

int main()
{
    std::cout << "Hello World" << std::endl;
}

此时,cout 和 endl 都被直接加上 std:: ,相当于有一个特殊的前缀,直接接明它们来自std这个名字空间,又直观又安全。

  • 直观:把 :: 读成 “的”,就有: “std的cout”及“std的endl”,有如生活中的 “三年一班的李国庆”。
  • 安全:“间接名字空间限定”是在某一符号出处查找失败,才尝试使用间接指定的某个名字空间去套用,自然就有“套用”结果不是程序员本意的意外可能。直接名字空间限定出这种错误的可能性近乎零。

​4.5 ADL - 实参依赖查找

眼尖的同学会发现,在直接名字空间限定的写法中:

std::cout << "Hello World" << std::endl;

这行代码,只是用std::限定了 cout 和 endl,<< 却没有限定。

事实上,因为 << 是一个操作符,如果写作 std::<< 反倒会编译失败。
这就涉及到了 ADL(Argument-dependent lookup)这一语法知识点,它与编译规则也紧密相关。

 简单地说,和现实生活中类似,操作的对象,往往可以反过来帮助推导出操作的实际出处。比如“打屁股”、“打酱油”和“打车”的操作都是“打”,但其实各自含义大不相同(放在《辞海》里的出处自然也不同)。我们,也就是人类是通过打的对象,也就是“屁股”、“酱油”、“车” 倒过来推导出各自前面的“打”的含义。

编译器也有这样的智慧,它们也可以依据操作对象,倒过来推导操作的出处。比如:看到 << 这个操作符,因为没有加名字空间直接限定——事实上操作符的日常使用语法,也规定了不允许为它加名字空间前缀——而找不到其出处,自然无法知道它对应的真实操作……但是,编译器很快发现 << 所要操作的对象中,std::cout 是明确的,于是会到 std 这个名字空间下查找 <<,这就找到了,发现它是标准库的流输出操作符。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1~5节. 编程训练习题课

疯狂练一练 每一题都有非常详细的注释, 如果大家有其他更简单的思路, 可以在评论区交流, 或者私信一起讨论. 1、定义一个方法&#xff0c;该方法能够找出两个小数中的较小值并返回。 package com.itheima.lxh_exercise;public class Exercise {public static void main(Stri…

2024年,真的别裸辞....

作为IT行业的大热岗位——软件测试&#xff0c;只要你付出了&#xff0c;就会有回报。说它作为IT热门岗位之一是完全不虚的。可能很多人回说软件测试是吃青春饭的&#xff0c;但放眼望去&#xff0c;哪个工作不是这样的呢&#xff1f;会有哪家公司愿意养一些闲人呢&#xff1f;…

理论学习:Softmax层和全连接层 全连接层之前的数据

Softmax层和全连接层 Softmax层和全连接层在深度学习模型中通常是紧密相关的&#xff0c;经常一起使用。 全连接层&#xff08;也称为线性层或密集连接层&#xff09;是深度学习模型中常见的层之一&#xff0c;它将输入张量与权重矩阵相乘&#xff0c;并添加偏置项&#xff0c;…

PaddleOCR表格识别运行实例

目录 PaddleOCR 开源项目地址 一、数据集 1. 训练数据下载 2.数据集介绍 &#xff08;1&#xff09;PubTabNet数据集 &#xff08;2&#xff09; 好未来表格识别竞赛数据集 &#xff08;3&#xff09;WTW中文场景表格数据集 二、训练步骤 1.数据放置 2.环境配置 &…

k8s-生产级的k8s高可用(2) 25

部署containerd k8s2、k8s3、k8s4在配置前需要重置节点&#xff08;reset&#xff09;在上一章已完成 禁用所有节点docker和cri-docker服务 所有节点清除iptables规则 重置后全部节点重启 由于之前部署过docker&#xff0c;因此containerd默认已安装 修改配置 启动containe…

OpenCV学习笔记(一)——Anaconda下载和OpenCV的下载

OpenCV是图象识别中有巨大的应用场景&#xff0c;本篇文章以Python为基础。当初学OpenCV的时候&#xff0c;推使用在Anaconda编写代码&#xff0c;原因比较方便&#xff0c;下面我们对于Anaconda的下载过程进行演示。 Anaconda的下载 首先打开官网www.anaconda.com/download找…

Midjourney绘图欣赏系列(十)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子&#xff0c;它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同&#xff0c;Midjourney 是自筹资金且闭源的&#xff0c;因此确切了解其幕后内容尚不…

力扣701. 二叉搜索树中的插入操作

思路&#xff1a;往二叉搜索树中插入一个值&#xff0c;树的结构有多种符合的情况&#xff0c;那我们可以选一种最容易的插入方式&#xff0c;反正只需要插入一个值而已&#xff0c;我们不难发现&#xff0c;不管插入什么值&#xff0c;都可以安排插入到叶子节点上。 再利用二叉…

uview upicker时间选择器(附Demo)

目录 前言正文 前言 uniapp时间选择器&#xff0c;是upicker&#xff0c;与微信小程序还是有些区别 补充官网的基本知识&#xff1a;uview官网 官网的展示例子如下&#xff1a;&#xff08;但是没Demo&#xff09; 正文 通过上面的展示图&#xff0c;复刻一个类似Demo图&am…

小兔鲜鲜项目(前端vue3)

成果图 大家喜欢给一个赞被&#xff0c; 项目地址&#xff1a;gitee 注意&#xff1a;项目克隆下去之后先运行 npm i之后安装项目插件包之后在npm run dev 运行就可以了

【Mysql】事务与索引

目录 MySQL事务 事务的特性 并发事务的问题&#xff1f; 事务隔离级别&#xff1f; MySQL索引 数据结构 索引类型 聚簇索引与非聚簇索引 聚集索引的优点 聚集索引的缺点 非聚集索引的优点 非聚集索引的缺点 非聚集索引一定回表查询吗(覆盖索引)? 覆盖索引 联合索…

识别恶意IP地址的有效方法

在互联网的环境中&#xff0c;恶意IP地址可能会对网络安全造成严重威胁&#xff0c;例如发起网络攻击、传播恶意软件等。因此&#xff0c;识别恶意IP地址是保护网络安全的重要一环。IP数据云将探讨一些有效的方法来识别恶意IP地址。 IP地址查询&#xff1a;https://www.ipdata…

springboot265基于Spring Boot的库存管理系统

基于Spring Boot库存管理系统 Inventory Meanagement System based on Spring Boot 摘 要 当下&#xff0c;如果还依然使用纸质文档来记录并且管理相关信息&#xff0c;可能会出现很多问题&#xff0c;比如原始文件的丢失&#xff0c;因为采用纸质文档&#xff0c;很容易受潮…

Redis底层核心对象RedisObject源码分析

文章目录 1. redis底层数据结构2. 插入KV底层源码流程分析 1. redis底层数据结构 redis 6数据结构和底层数据结构的关系 String类型本质是SDS动态字符串&#xff0c;即redis层面的数据结构底层会有对应的数据结构实现&#xff0c;上面是redis 6之前的实现 redis 7数据结构和底…

Terrace联合创始人兼CEO Jesse Beller确认出席Hack.Summit() 2024区块链开发者大会

在科技创新的浪潮中&#xff0c;区块链技术以其独特的去中心化、透明性和安全性&#xff0c;正逐渐成为引领未来发展的重要力量。在这样的背景下&#xff0c;备受瞩目的Hack.Summit() 2024区块链开发者大会即将于4月9日至10日在香港数码港盛大举行。本次大会的亮点之一&#xf…

程序员春招攻略:金三银四的求职智慧与机遇

文章目录 程序员的金三银四求职宝典方向一&#xff1a;面试技巧分享自我介绍的艺术技术问题的回答策略团队协作经验的有效展示压力面试的应对结束语的巧妙运用 方向二&#xff1a;面试题解析数据结构与算法题系统设计题编程题 方向三&#xff1a;公司文化解读腾讯&#xff08;T…

软件设计不是CRUD(14):低耦合模块设计理论——行为抽象与设计模式(上)

是不是看到“设计模式”四个字,各位读者就觉得后续内容要开始讲一些假大空的内容了?各位读者是不是有这样的感受,就是单纯讲设计模式的内容,网络上能找到很多资料,但是看过这些资料后读者很难将设计模式运用到实际的工作中。甚至出现了一种声音:设计模式是没有用的,应用…

机试:最大子序列的和

问题描述: 算法思想: 若第(i-1)个序列的小于0,则第i个序列的最大值为nums[i]; 若第(i-1)个序列的小于0,则第i个序列的最大值为max(i-1) nums[i]; 如果max(i-1)>0,max(i)max(i-1)Nums(i) 如果max(i-1)<0,max(i)Nums(i)代码示例: #include <bits/stdc.h> //该算法…

ULTRAL SCALE FPGA TRANSCEIVER速率

CPLL支持2-6.25速率 QPLL支持速率 实际使用CPLL最高可以超过这个&#xff0c;QPLL最低也可以低于这个&#xff0c;xilinx留的阈量还是比较大。

蓝桥杯真题讲解:子矩阵(二维滑动窗口)

蓝桥杯真题讲解&#xff1a;子矩阵&#xff08;二维滑动窗口&#xff09; 一、视频讲解二、正解代码 一、视频讲解 蓝桥杯真题讲解&#xff1a;子矩阵&#xff08;二维滑动窗口&#xff09; 二、正解代码 //二维单调队列 #include<bits/stdc.h> #define endl \n #def…