"数组引用"以避免"数组降阶"(本文曾贴于VCKBASE\C++论坛)

受[hpho]的一段模板函数的启发,特写此文,如有雷同,实在遗憾。
数组降阶是个讨厌的事,这在C语言中是个无法解决的问题,先看一段代码,了解什么是"数组降阶"

#include <IOSTREAM>
using namespace std;

void Test( char array[20] )
{
    cout << sizeof(array) << endl; // 输出 4
}

int main( void )
{
    char array[20] = { 0 };
    cout << sizeof(array) << endl; // 输出 20
    Test( array );
}

为什么同样申明的array一个输出20一个输出4?这是因为void Test( char array[20] )中的array被降阶处理了,void Test( char array[20] )等同于void Test( char array[] ),也等同于void Test( char* const array ),如果你BT(开玩笑),它也等同于void Test( char array[999] )。
就是说
void Test( char array[20] )
{
    cout << sizeof(array) << endl;
}
被降成
void Test( char* const array )
{
    cout << sizeof(array) << endl; // 既然是char*,当然输出4
}
这样一来问题大了,你完全可以定义一个不足20个元素的数组,然后传给Test,坐等程序崩溃。在一些要求较高的场合就不能使用数组做参数,真TMD心有不甘。

那么在C语言中怎样解决这个问题?
没办法,应该说没有好办法。a:做个结构,其中仅一个char array[20],然后用这个结构指针代替char array[20]。可见这是个很繁琐的办法,且不直观;b:在Test内部使用_msize来计算array长度。这更不行,首先它使得错误的发现被推迟到运行期,而不是编译期,其次_msize长度/元素大小>=array长度,也就是说就是new char[19]和new array[20]分配的大小是一样的,这样一来,虽不至于导致程序崩溃,但运算结果却不正确。

感谢[hpho],受其启发,C++中有C所没有的"引用",但数组引用是怎样申明的呢?经过几番试验,Look

#include <IOSTREAM>
using namespace std;

void Test( char (&array)[20] ) // 是不是很像 char *p[20] 和 char (*p)[20] 的区别?
{
    cout << sizeof(array) << endl;
}

int main( void )
{
    char array[20] = { 0 };
    cout << sizeof(array) << endl;
    Test( array );
}

在 C++中,数组永远不会按值传递,它是传递第一个元素,准确地说是第 0个 的指针。

例如,如下声明 :
void putValues( int[ 10 ] );
被编译器视为 
void putValues( int* );
数组的长度与参数声明无关,因此,下列三个声明是等价的:
// 三个等价的 putValues()声明
void putValues( int* );
void putValues( int[] );
void putValues( int[ 10 ] );

因为数组被传递为指针 所以这对程序员有两个含义:

1. 在被调函数内对参数数组的改变将被应用到数组实参上而不是本地拷贝上,当用作实参的数组必须保持不变时,程序员需要保留原始数组的拷贝函数可以通过把参数类型声明为 const 来表明不希望改变数组元素。
void putValues( const int[ 10 ] );

2.  数组长度不是参数类型的一部分,函数不知道传递给它的数组的实际长度,编泽器也不知道,当编译器对实参类型进行参数类型检查时,并不检查数组的长度。例如:
void putValues( int[ 10 ] ); // 视为 int*
int main() {
int i, j[ 2 ];
putValues( &i ); // ok: &i 是 int*; 潜在的运行错误
putValues( j ); // ok: j 被转换成第 0 个元素的指针
// 实参类型为 int*: 潜在的运行错误
return 0;
}

参数的类型检查只能保证putValues()的两次调用都提供了int*型的实参,类型检查不能检验实参是一个 10元素的数组 。习惯上, C风格字符串是字符的数组,它用一个空字符编码作为结尾。但是所有其他类型,包括希望处理内含空字符的字符数组必须以某种方式在向函数传递实参时使其知道它的长度。

一种常见的机制是提供一个含有数组长度的额外参数。例如:
void putValues( int[], int size );
int main() {
       int i, j[ 2 ];
       putValues( &i, 1 );
       putValues( j, 2 );
       return 0;
}

另外一种机制是将参数声明为数组的引用

当参数是一个数组类型的引用时,数组长度成为参数和实参类型的一部分,编译器检查数组实参的长度与在函数参数类型中指定的长度是否匹配。 
// 参数为 10 个 int 的数组
// parameter is a reference to an array of 10 ints
void putValues( int (&arr)[10] );//不能写成&arr[10],因为下标操作符的优先级较高
int main() {
       int i, j[ 2 ];
       putValues( i ); // 错误: 实参不是 10 个 int 的数组
       putValues( j ); // 错误: 实参不是 10 个 int 的数组
       return 0;
}

[转]数组引用:C++ 数组做参数 深入分析的更多相关文章

  1. C++二维数组(指针)做参数

    一.问题描述 使用C++编程过程中经常需要使用到二维数组,然而初级程序员在使用过程中经常会出错使程序崩溃.下面就二维指针的定义,初始化,以及二维指针做参数给出简单介绍. 1.二维数组的定义与初始化 在 ...

  2. 数组引用:C++ 数组做参数 深入分析

    转载:https://blog.csdn.net/jiangxinyu/article/details/7767065 在 C++中,数组永远不会按值传递,它是传递第一个元素,准确地说是第 0个 的指 ...

  3. C++数组引用

    C++数组引用 一.数组引用 C++数组的引用:引用即别名这样比指针传地址方便多了 形参中的(&a)[10]可以就看做a数组的别名,肯定要指定数组大小,如果没有后面的数组大小,天知道是变量还是 ...

  4. C++数组做参数

    首先,看一下下面这段代码: void changearr(int a[],int n){    cout<<sizeof(a)<<endl;         // 输出4}in ...

  5. C++ 中数组做参数的分析

    C++ 中数组做参数的分析 1.数组降价问题? "数组引用"以避免"数组降阶",数组降阶是个讨厌的事,这在C语言中是个无法解决的问题,先看一段代码,了解什么是& ...

  6. C语言 函数理解(以数组做参数)

    #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> int run(int *p){ // ...

  7. 引用、数组引用与指针引用、内联函数inline、四种类型转换运算符

    一.引用 (1).引用是给一个变量起别名 定义引用的一般格式:类型  &引用名 = 变量名: 例如:int a=1;  int  &b=a;// b是a的别名,因此a和b是同一个单元 ...

  8. perl 引用(数组和hash引用) --- perlreftut - Mark 的一个简单的'引用'教程 ---Understand References Today. --Mark Jason Dominus, Plover Systems (mjd-perl-ref+@plover.com)

    https://blog.csdn.net/fangwei1235/article/details/8570886 首页 博客 学院 下载 论坛 APP 问答 商城 活动 VIP会员 招聘 ITeye ...

  9. js 数组引用 发现的问题

    最近做项目时,要对返回的数据[保存在json数组中]做一次修改,但原数据要保留一次做备用.首先想到,原数据不动,用一个临时的变量来修改,大致模型就是这样: // 原始: a=[1,2,3,4,5,.. ...

随机推荐

  1. weblogic配置jdbc数据源

    weblogic配置jdbc数据源的过程 方法/步骤   启动weblogic 管理服务器,使用管理用户登录weblogic管理控制台   打开管理控制台后,在左侧的树形域结构中,选择服务->数 ...

  2. Android的Drawable

    1.获取Drawable的内部宽\高:getIntrinsicHeight/Width.但是并不是所有Drawable都有内部宽高(比如说一个颜色形成的宽高,Drawable的宽高不等于大小,大小是根 ...

  3. Javascript自定义类

    JavaScript并不是严格的面向对象的语言,但是带有面向对象的一些特性,我们可以通过这些特性创建js中的自定义类. JavaScript中的类其实是function关键字包裹的一系列变量和方法. ...

  4. ZOJ问题--hdu3788

    ZOJ问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  5. Oracle EBS-SQL (INV-9):检查搬运单分配异常.sql

    select h.request_number,         l.line_number,         msib.segment1 item_code,         t.transacti ...

  6. android的listview组件

    http://www.cnblogs.com/menlsh/archive/2013/03/15/2962350.html http://www.tuicool.com/articles/IveeI3 ...

  7. [置顶] mkdir函数-linux

    tyle="margin:20px 0px 0px; font-size:14px; line-height:26px; font-family:Arial">

  8. 反思java web的发展

    本来网站都是一个个静态HTML的,但很快我们就不满足于这样了.动态网页应运而生.一开始是Servlet.其代码类似于下面这样.主要是Java代码,然后用out一点一点输出HTML代码.当然代码无比丑陋 ...

  9. cdecl、pascal、stdcall、fastcall

    Directive Parameter order   Clean-up Passes parameters in registers?register   Left-to-right         ...

  10. ActiveX异步回调JavaScript

    ActiveX异步回调JavaScript 开发环境:VC6.0. 背景知识:COM/ActiveX/JavaScript/MFC/Thread 想必用过Ajax的童鞋们都知道xmlhttp这个东西吧 ...