C++中函数访问数组的方式
在书写C++代码时,往往为了令代码更加简洁高效、提高代码可读性,会对定义的函数有一些特殊的要求:比如不传递不必要的参数,以此来让函数的参数列表尽可能简短。
当一个函数需要访问一个数组元素时,出于上述原因,往往也希望令传入的参数尽可能的少(至少我是这样...)。
首先,引出一个例子,对于std::vector<typename>来说,往往只需要传递一个参数就足够了(当只涉及单独访问该vector时的确如此),比如要编写一个show函数,这个函数的功能是打印传入容器的所有元素,并用空格将这些元素分隔开来。那么当传入容器为vector时,这个show函数就会简单无比:
void show(const std::vector<int> &ivec) {
for (size_t i = ; i < ivec.size(); ++i)
std::cout << ivec[i] << " ";
std::cout << std::endl;
}
可以看到,由于标准库中的vector包含size成员,使得我们很容易获取这个vector对象的大小,进而方便对这个vector进行访问。此时,这个函数只需要一个参数就可以完成容器的访问工作(当然,对于这种容器,也可以用迭代器进行访问,此方式下函数的参数个数不变)。
那么... 内置数组呢?
很遗憾,答案是:不行。
在《C++ Primer 5th》中,作者指出:“因为数组是以指针的形式传递给函数的,所以一开始函数并不知道数组的确切尺寸,调用者应该为此提供一些额外的信息。”(中文版第193页,英文版第216页)
书中继而阐述了三种函数访问数组的方式(原文为:“管理指针形参的三种技术”):
1. 使用标记指定数组长度
通俗地说,就是在传入的这个数组中,含有一些标记数组结束的元素,比如C风格字符串(const char *),显式地以'\0'字符作为结尾标识符。那么当遍历到一个'\0'时,即可认定这个字符串结束,此时判定访问结束。但显而易见,这样的访问方式不具有普适性,毕竟很多容器并不会包含一个指定的结尾标识,甚至一些容器都不能保证存入元素的顺序固定。
这里,当然也可以预定数组就是指定大小的(例如在源文件中声明const LEN = 20; 或者预处理 #define LEN 20),尽管这样和上述方法一样,可以使得函数只需要一个形参:
#define LEN 20
// const int LEN = 20; void show(const char *cp) {
if (cp)
while (*cp)
std::cout << *cp++;
} void show(const int lst[]) {
for (size_t i = ; i < LEN; ++i)
std::cout << lst[i] << " ";
std::cout << std::endl;
}
但是,这样的程序是不具有普遍性的,我们可以很肯定地说,我们的数组一定不会正好包含20或更少的元素。
2. 使用标准库规范
在标准库中,对数组定义了begin和end方法,和迭代器不同的是,函数返回的是指针类型;和迭代器相同的是,用法基本一致...
void show(const int *beg, const int *end) {
for (auto iter = beg; iter != end; ++iter)
std::cout << *iter << std::endl;
std::cout << std::endl;
}
/* 很遗憾,玩儿不转
void show(const int lst[]) {
for (auto beg = std::begin(lst); beg != std::end(lst); ++beg)
std::cout << *beg << " ";
std::cout << std::endl;
}
*/
可以看到,这时的show函数,至少需要两个参数。当然,说到begin和end函数,很多人都会想到上面第二个这种方式,然而很遗憾,这种方式不行!此时,只能通过显式地调用
show(std::begin(lst), std::end(lst));
来实现第一个函数的调用。但... 这并未达到“调用函数只包含一个参数”的期望。
至于这里的第二种方式不行的原因,我认为是:在C++调用函数时,数组被自动转换为数组元素类型的指针类型(比如int lst[]就被转换成了int *lst),这个过程是如此自然,就像是内置类型默认类型转换一般(比如sum = 1 + 2.0,这里的1就自然从int类型被转换成了double类型)。因此,函数对这样一个int *类型,自然无从对之继续调用begin和end函数以得到其首尾位置(这里重点是无法得到尾位置)。故... 玩儿不转。
3. 显式传递一个表示数组大小的形参
这可能是每个学过C/C++的人最常用的方法了。通过显式控制访问元素的个数,保证指针前进的深度:
void show(const int lst[], size_t length) {
for (size_t i = ; i < length; ++i)
std::cout << lst[i] << " ";
std::cout << std::endl;
}
形式很简单,无须解释。这里需要注意的就是形参处的length的类型是size_t的,因此调用者传入一个负值,很有可能就美滋滋了。当然,也可以修改为其他类型,但这个问题不容忽视(见之前博客)。
我想说的是,这种方式下,依然没达到“调用函数只包含一个参数”的期望...
可能只有强迫症才会有这样的纠结吧,但很欣慰的是,通过这个纠结,将调用函数访问数组的三种方式系统地进行了说明阐述(方式2的第二种形式真的让我很惋惜...)。
当然,如果读者有更好的方式(如果能只传入一个参数即可访问任意类型任意大小的数组,看在我纠结这么久的份儿上,千万要告诉我...),欢迎评论区分享,若有写的不足的地方,敬请评论区斧正,在此感谢。
C++中函数访问数组的方式的更多相关文章
- python中函数参数的引用方式
值传递和引用传递时C++中的概念,在python中函数参数的传递是变量指向的对象的物理内存地址!!! python不允许程序员选择采用传值还是传引用.Python参数传递采用的肯定是“传对象引用”的方 ...
- MVC 控制器中直接访问url 的方式
public void ShowDetailsImg() { //生成MD5码 string path = @"D:\其他\Test\WebApplication2\WebApplicati ...
- js中如何访问对象和数组
js中如何访问对象和数组 一.总结 一句话总结:js访问对象点和中括号,访问数组的话就是中括号 对象 . [] 数组 [] 1.js访问对象的两种方式? . [] 可以使用下面两种方式访问对象的属性和 ...
- C#委托与C语言函数指针及函数指针数组
C#委托与C语言函数指针及函数指针数组 在使用C#时总会为委托而感到疑惑,但现在总新温习了一遍C语言后,才真正理解的委托. 其实委托就类似于C/C++里的函数指针,在函数传参时传递的是函数指针,在调用 ...
- JS的一些总结(函数声明和函数表达式的区别,函数中的this指向的问题,函数不同的调用方式,函数也是对象,数组中的函数调用)
一.函数声明和函数表达式的区别: 函数声明放在if——else语句中,在IE8中会出现问题 函数表达式则不会 <script> if(true){ function f1(){ conso ...
- JavaScript中常见的数组操作函数及用法
JavaScript中常见的数组操作函数及用法 昨天写了个帖子,汇总了下常见的JavaScript中的字符串操作函数及用法.今天正好有时间,也去把JavaScript中常见的数组操作函数及用法总结一下 ...
- smarty访问数组中的数据,如果是关联数组直接用点.
$tpl=new Smarty();//新建一个smarty对象,我使用的是Smarty-3.1.6版本 1.设置smarty模板路径$tpl->setTemplateDir():默认情况下是t ...
- C语言中的函数、数组与指针
1.函数:当程序很小的时候,我们可以使用一个main函数就能搞定,但当程序变大的时候,就超出了人的大脑承受范围,逻辑不清了,这时候就需要把一个大程序分成许多小的模块来组织,于是就出现了函数概念: 函 ...
- PHP中使用数组指针函数操作数组示例
数组的内部指针是数组内部的组织机制,指向一个数组中的某个元素.默认是指向数组中第一个元素通过移动或改变指针的位置,可以访问数组中的任意元素.对于数组指针的控制PHP提供了以下几个内建函数可以利用. ★ ...
随机推荐
- Hadoop3.1.1源码Client详解 : 写入准备-RPC调用与流的建立
该系列总览: Hadoop3.1.1架构体系——设计原理阐述与Client源码图文详解 : 总览 关于RPC(Remote Procedure Call),如果没有概念,可以参考一下RMI(Remot ...
- Markdown编辑器软件安装及问题处理
一.Markdown简介 MarkdownPad是Windows下的一个多功能Markdown编辑器 Markdown是一门编辑语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式,可以用来对 ...
- 使用Python处理非对称加密-测试大佬常用的处理方式
一.思考❓❔ 1.什么是非对称加密? 公钥加密系统,广泛用于数据加密传输 更详细的解释可参考维基百科( https://en.wikipedia.org/wiki/RSA_(cryptosystem) ...
- Tomcat 和 JVM 性能调优总结
Tomcat性能调优: 找到Tomcat根目录下的conf目录,修改server.xml文件的内容.对于这部分的调优,我所了解到的就是无非设置一下Tomcat服务器的最大并发数和Tomcat初始化时创 ...
- Git储藏工作现场
假设你现在在dev分支工作,此时你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,你当前工作只进行到一半,还没法提交,预计完成还需1天时间 ...
- Python socket day2
接收数据 需要一个端口 (端口大于1024 1024一下的端口为特殊端口) (当同一个端口同一时间只能被一个使用) 创建一个套接字 s = socket.socket(AF_INET,SOCK ...
- 关于GOM引擎启动时显示:windows socket error: 在其上下文中,该请求的地址无效。 (10049), on API 'bind'
GOM启动时网管登陆器显示:windows socket error: 在其上下文中,该请求的地址无效. (10049), on API 'bind'解决方法: 重新配置引擎控制台.在配置里取消双IP ...
- python opencv:绘图 基本图形
参数说明 • img:你想要绘制图形的那幅图像. • color:形状的颜色.以 RGB 为例,需要传入一个元组,例如:( 255,0,0)代表蓝色.对于灰度图只需要传入灰度值. • thicknes ...
- Linux : file命令
file xxx file命令用来探测给定文件的类型.file命令对文件的检查分为文件系统.魔法幻数检查和语言检查3个过程 命令选项: -b:列出辨识结果时,不显示文件名称: -c:详细显示指令执行过 ...
- java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time.....
SpringBoot 2.1.4启动时报错 java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecogniz ...