C++核心准则边译边学-X.4:使用span解决数组退化和越界访问

数组是C++从C语言继承过来的特性,使用方便同时又可以提供绝佳的性能,因此被广泛使用。但是简便的另一面就是风险,其中最大的两个问题就是退化(array decay)和越界访问(range errors)。本文介绍如何提前使用C++20新特性span解决数组退化和越界访问的问题。

首先看使用数组的最常见代码:

 

int data[10];for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i) {    data[i] = 0;}

数组被定义时,同时有个元素个数信息。使用这个信息可以对数组进行操作。但是在将数组作为一个参数传递给某个函数时,只能以指针形式传递,这就是数组退化。为了正确把握数组的大小一般需要同时传递数组的大小信息。例如下面的初始化函数就是如此:

 

void init_data(int buffer[], size_t size){  cout << "size=" << size << endl;  for (gsl::index i = 0; i < size; ++i) {    buffer[i] = i;  }  buffer[4] = 40;  buffer[20] = 20;  //越界访问}

即使声明函数参数时形式上是数组,但所有的行为都和指针完全相同。还有一个问题就是,由于数组是一种完全暴露的数据结构,没有任何保护。例如代码中第8行,即使访问的第20个元素已经超过最初定义的10个元素,这种操作一般也会正常通过。但是接下来不知道哪个时刻,这个操作带来的影响就会以一种完全不相关的形式表现出来。数组大小信息获取,传递错误和越界操作具有引入容易、排查困难的特种,是许多程序员的噩梦。

为了解决这个问题,GSL引入了一个模板类span,它可以同时管理数组的地址和大小。这个类将从C++20开始成为C++的标准功能。

使用了span类的初始化函数如下:

 

void init_data(gsl::span<int> buffer){    cout << "size=" << buffer.size() << endl;    int value = 0;    for (auto it = buffer.begin(); it != buffer.end(); it++) {        *it = value++;    }    buffer[4] = 10;    buffer[20] = 20; //会触发断言}

只要函数参数声明为:gsl::span<int> buffer,大小信息就会由span模板类管理,接下来就可以像vector一样使用数组了。如果发生越界访问,会触发断言。

使用数组和span传递参数的示例代码如下:

 

int main(){    int data[10];    for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i) {        data[i] = 0;    }    //使用数组传递参数    init_data(data, 5);    //使用span传递参数    init_data(data);    return 0;}

和使用数组的调用相比,增强功能(范围检查等)的同时还简化了数组的用法!

完整代码已经上传到GitHub:

https://github.com/xueweiguo/ModernCpp/blob/master/Span.cpp

 

觉得本文有帮助,欢迎点赞并分享给更多的朋友。

阅读更多更新文章,请关注微信公众号【面向对象思考】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值