"数组引用"以避免"数组降阶"(本文曾贴于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. Sicily 1790. Single Round Match

    高进度求余 或者 将一个数奇位上的数字与偶位上的数字分别加起来,再求它们的差,如果这个差是11的倍数(包括0),那么,原来这个数就一定能被11整除. #include <iostream> ...

  2. 屏蔽鼠标右键功能JS

    <script language="Javascript">     function hiderightKey(){    return false; } docum ...

  3. Android studio 配置JNI环境

    Android studio配置jni开发环境,主要配置是两个build文件,以及新建一个jni文件,放c代码. 代码如下1: apply plugin: 'com.android.model.app ...

  4. 引用 - PHP手册笔记

    引用是什么 PHP中的引用意味着,用不同的变量名访问同一变量内容,类似于Unix的文件名和文件本身(变量名是目录条目,变量内容是文件本身,即用不同的目录条目访问同一文件),可以看做Unix文件系统中的 ...

  5. ModelMap和ModelAndView(转)

    转自:http://bao1073740756-126-com.iteye.com/blog/1549597 首先介绍ModelMap和ModelAndView的作用 ModelMap ModelMa ...

  6. LINUX 暂停、继续进程

    LINUX 暂停.继续进程 kill -STOP 1234 将该进程暂停. 如果要让它恢复到后台,用kill -CONT 1234 (很多在前台运行的程序这样是不行的) 如果要恢复到前台,请在当时运行 ...

  7. 关于SubclassWindow()和SubclassDlgItem

    msdn上的解析 CWnd::SubclassWindowBOOL SubclassWindow( HWND hWnd ); Return Value Nonzero if the function ...

  8. linux服务器WEB环境一键安装包lanmp教程之五

    在我们安装了linux服务器WEB环境一键安装包lanmp后,可能会有不少疑问还有就是使用过程中出现的问题,下面为大家总结几点比较常见的,如若还有其他疑问,可到wdlinux论坛寻找相关教程. 1.增 ...

  9. 当浏览器不支持placeholder,所执行的函数

    $(function(){ //判断浏览器是否支持placeholder属性 supportPlaceholder='placeholder'in document.createElement('in ...

  10. C#中Linq延迟执行问题

    本文来自:http://msdn.microsoft.com/zh-cn/library/bb399393(v=vs.110).aspx http://www.cnblogs.com/zhanglin ...