c++难点(较高级使用技巧)总结

本文深入探讨了C++中数组名、指针和函数的关系。首先,详细阐述了数组名不是指针,但在某些情况下可以转换为指针常量。接着,介绍了函数名和函数指针的概念,解释了如何声明、使用和调用函数指针。然后,讨论了函数参数传递中的指针问题,特别是关于一级指针和二级指针的区别。此外,还对比了`new int(5)`与`new int[5]`在内存分配上的差异。最后,总结了数组作为函数参数的几种方式,并展示了如何利用模板解决数组长度问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、数组名和指针的深入理解

指针是C/C++语言的特色,而数组名与指针有太多的相似,甚至很多时候,数组名可以作为指针使用。于是乎,很多程序设计者就被搞糊涂了。

魔幻数组名

请看程序(本文程序在WIN32平台下编译):

复制代码
#include <iostream>
using namespace std;

int main()
{
    char str[10];
    char* pStr = str;
    cout << "sizeof(str): \t" << sizeof(str) << endl;
    cout << "sizeof(pStr): \t" << sizeof(pStr) << endl;
    
    system("pause");
    return 0;
} 
复制代码

 
1、数组名不是指针

我们先来推翻"数组名就是指针"的说法,用反证法。

证明 数组名不是指针

假设:数组名是指针;

则:pStr和str都是指针;

因为:在WIN32平台下,指针长度为4;

所以:sizeof(str)和sizeof(pStr)的输出都应该为4;

实际情况是:

图片内容为:

sizeof(str):10

sizeof(str):4

所以:假设不成立,数组名不是指针


2、数组名神似指针

上面我们已经证明了数组名的确不是指针,但是我们再看看程序的这一:char* pStr = str;。该行程序将数组名直接赋值给指针,这显得数组名又的确是个指针!

我们还可以发现数组名显得像指针的例子:

复制代码
#include <iostream>
using std::cout;
using std::endl;

int main()
{
    char str1[10] = "I love U.";
    char str2[10];
    
    strcpy(str2, str1);
    
    cout << "string array 1: " << str1 << endl;
    cout << "string array 2: " << str2 << endl;
    
    system("pause");
    return 0;
}
复制代码


标准C库函数strcpy的函数原形中能接纳的两个参数都为char型指针,而我们在调用中传给它的却是两个数组名!

函数输出:

图片内容为:

string array 1:I Love U.

string array 2:I Love U.

数组名再一次显得像指针!

既然数组名不是指针,而为什么到处都把数组名当指针用?


揭密数组名(对上面的总结)

现在到揭露数组名本质的时候了,先给出三个结论:

(1)数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组;

(2)数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量

(3)指向数组的指针则是另外一种变量类型(在WIN32平台下,长度为4),仅仅意味着数组的存放地址!

1、数组名指代一种数据结构:数组(数组也是一种数据结构)

现在可以解释为什么第1个程序的这一行:sizeof(str);的输出为10的问题,根据结论1,数组名str的内涵为一种数据结构,即一个长度为10的char型数组,所以sizeof(str)的结果为这个数据结构占据的内存大小:10字节。

再看:

1. int intArray[10];
2. cout << sizeof(intArray) ;


第2行的输出结果为40(整型数组占据的内存空间大小)。

如果C/C++程序可以这样写:

1. int[10] intArray;
2. cout << sizeof(intArray) ;


我们就都明白了,intArray定义为int[10]这种数据结构的一个实例,可惜啊,C/C++目前并不支持这种定义方式。

2、数组名可作为指针常量

根据结论2,数组名可以转换为指向其指代实体的指针,所以程序1中的第5行数组名直接赋值给指针,程序2第7行直接将数组名作为指针形参都可成立。

下面的程序成立吗?

1. int intArray[10];
2. intArray++;
读者可以编译之,发现编译出错。原因在于,虽然数组名可以转换为指向其指代实体的指针,但是它只能被看作一个指针常量,不能被修改。

而指针,不管是指向结构体、数组还是基本数据类型的指针,都不包含原始数据结构的内涵,在WIN32平台下,sizeof操作的结果都是4。
顺 便纠正一下许多程序员的另一个误解。许多程序员以为 sizeof是一个函数,而实际上,它是 一个操作符,不过其使用方式看起来的确太像一个函数了。语句sizeof(int)就可以说明sizeof的确不是一个函数,因为函数接纳形参(一个变量),世界上没有一个C/C++函数接纳一个数据类型(如int)为"形参"。

3、数组名可能失去其数据结构内涵 (变成一个很平民的指针)

请看下面一段程序:
复制代码
#include <iostream>
using std::cout;
using std::endl;

void arrayTest(char str[])
{
     cout << "sizeof(str):" << sizeof(str) << endl;
}

int main()
{
    char str1[10] = "I love U.";
    arrayTest(str1);
    
    system("pause");
    return 0;
}
复制代码

程序的输出结果为:

图片内容为:

sizeof(str):4
一个可怕的数字,前面已经提到其为指针的长度!

结论1指出,数组名内涵为数组这种数据结构,在arrayTest函数体内,str1是数组名,那为什么sizeof的结果却是指针的长度?这是因为:

(1)数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针;

(2)很遗憾,在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。

所以,数组名作为函数形参时,其全面沦落为一个普通指针!它的贵族身份被剥夺,成了一个地地道道的只拥有4个字节的平民。
参考:http://www.cnblogs.com/wuzhenbo/archive/2012/05/29/2523777.html

 

 二、函数名与函数指针


一、 通常的函数调用

    一个通常的函数调用的例子:

复制代码
#include <iostream>
using std::cout;
using std::endl;

void MyFun(int x);      //此处也可以声明为:void Myfun(int); int main()
{
    MyFun(10);          //这里是调用MyFun(int x);函数 
    
    system("pause");
    return 0;
}

void MyFun(int x) 
{
      cout << "x: " << x << endl;
}
复制代码


    这个MyFun函数是一个无返回值的函数,他相当于把MyFun函数的主体放到主函数中。看主函数中调用MyFun函数的书写格式:MyFun(10);
  思考:函数名到底又是什么东西呢?
    (不要以为这是没有什么意义的事噢!)

二、 函数指针变量的声明
    就象某一数据变量的内存地址可以存储在相应的指针变量中一样,函数的首地址也以存储在某个函数指针变量里的。这样,我就可以通过这个函数指针变量来调用所指向的函数了。
    在C++系列语言中,任何一个变量,总是要先声明,之后才能使用的。那么,函数指针变量也应该要先声明吧?那又是如何来申明呢?以上面的例子为例,来声明一个可以指向MyFun函数的函数指针变量FunP。

  申明FunP变量的方法:void (*FunP)(int) ;   也可写成void (*FunP)(int x);

  整个函数指针变量的申明格式如同函数MyFun的申明处一样,只不过,把MyFun改成(*FunP)而已,这样就有了一个能指向MyFun函数的指针FunP了。(当然,这个FunP指针变量也可以指向所有其它具有相同参数及返回值的函数了。)

三、 通过函数指针变量调用函数

  有了FunP指针变量后,我们就可以对它赋值指向MyFun,然后通过FunP来调用MyFun函数了。

  如何通过FunP指针变量来调用MyFun函数的:

复制代码
#include <iostream>
using std::cout;
using std::endl;

void MyFun(int x);      //此处也可以声明为:void Myfun(int); 
void (*FunP)(int);   //声明一个用以指向同样参数,返回值函数的指针变量

int main()
{
    MyFun(10);          //这里是调用MyFun(int x);函数 
    FunP = &MyFun;      //将MyFun函数的地址赋给FunP变量 
    (*FunP)(20);        //这是通过函数指针变量FunP来调用MyFun函数的 
    system("pause");
    return 0;
}

void MyFun(int x)       //MyFun函数的定义 
{
      cout << "x: " << x << endl;
}
复制代码
    请看黑体字部分的代码及注释。
    运行看看。
    给我们的感觉是:MyFun与FunP的类型关系类似于int 与int *的关系。函数MyFun好像是一个如int的变量(或常量),而FunP则像一个如int *一样的指针变量。
  int i,*pi;
  pi=&i;    //与FunP=&MyFun比较。(你的感觉呢?)其实不然——

四、 调用函数的其它书写格式
函数指针也可如下使用,来完成同样的事情:
复制代码
#include <iostream>
using std::cout;
using std::endl;

void MyFun(int x);      //此处也可以声明为:void Myfun(int); 
void (*FunP)(int);      //申明一个用以指向同样参数,返回值函数的指针变量

int main()
{
    MyFun(10);          //这里是调用MyFun(int x);函数 
    FunP = MyFun;      //将MyFun函数的地址赋给FunP变量 
    (*FunP)(20);        //这是通过函数指针变量FunP来调用MyFun函数的 
    system("pause");
    return 0;
}

void MyFun(int x)       //MyFun函数的定义 
{
      cout << "x: " << x << endl;
}
复制代码
改了黑体字部分(请自行与之前的代码比较一下)。
运行试试,啊!一样地成功。
啊?
FunP=MyFun;
可以这样将MyFun值同赋值给FunP,难道MyFun与FunP是同一数据类型(即如同的int 与int的关系),而不是如同int 与int*的关系了?(有没有一点点的糊涂了?)
看来与之前的代码有点矛盾了,是吧!所以嘛!
 

请容许暂不给你解释,继续看以下几种情况(这些可都是可以正确运行的代码哟!):
代码之三:
复制代码
#include <iostream>
using std::cout;
using std::endl;

void MyFun(int x);      //此处也可以声明为:void Myfun(int); 
void (*FunP)(int);      //申明一个用以指向同样参数,返回值函数的指针变量

int main()
{
    MyFun(10);          //这里是调用MyFun(int x);函数 
    FunP = &MyFun;      //将MyFun函数的地址赋给FunP变量 
    FunP(20);           //这里是通过函数指针变量来调用MyFun函数的 
    
    system("pause");
    return 0;
}

void MyFun(int x)       //MyFun函数的定义 
{
      cout << "x: " << x << endl;
}
复制代码

 
代码之四:

复制代码
#include <iostream>
using std::cout;
using std::endl;

void MyFun(int x);      //此处也可以声明为:void Myfun(int); 
void (*FunP)(int);      //申明一个用以指向同样参数,返回值函数的指针变量

int main()
{
    MyFun(10);          //这里是调用MyFun(int x);函数 
    FunP = MyFun;      //将MyFun函数的地址赋给FunP变量 
    (*FunP)(20);           //这里是通过函数指针变量来调用MyFun函数的 
    
    system("pause");
    return 0;
}

void MyFun(int x)       //MyFun函数的定义 
{
      cout << "x: " << x << endl;
}
复制代码
真的是可以这样的噢!
(哇!要晕倒了!)
还有呐!看:
复制代码
#include <iostream>
using std::cout;
using std::endl;

void MyFun(int x);      //此处也可以声明为:void Myfun(int); 
void (*FunP)(int);      //申明一个用以指向同样参数,返回值函数的指针变量

int main()
{
    (*MyFun)(10);          //看,函数名MyFun也可以有这样的调用格式 
    FunP = MyFun;          //将MyFun函数的地址赋给FunP变量 
    (*FunP)(20);           //这里是通过函数指针变量来调用MyFun函数的 
    
    system("pause");
    return 0;
}

void MyFun(int x)       //MyFun函数的定义 
{
      cout << "x: " << x << endl;
}
复制代码
你也许第一次见到吧:函数名调用也可以是这样写的啊!(只不过我们平常没有这样书写罢了。)
那么,这些又说明了什么呢?
嗯,依据以往的知识和经验来推理本篇的“新发现”,必定会由此分析并推断出以下的结论:
1. 其实,MyFun的函数名与FunP函数指针都是一样的,即都是函数指针。 MyFun函数名是一个函数指针常量,而FunP是一个函数数指针变量,这是它们的关系。
2. 但函数名调用如果都得如(*MyFun)(10);这样,那书写与读起来都是不方便和不习惯的。所以 C语言的设计者们才会设计成又可允许MyFun(10);这种形式地调用(这样方便多了并与数学中的函数形式一样,不是吗?)。
3. 为统一起见,FunP函数指针变量也可以FunP(10)的形式来调用。
4. 赋值时,即可FunP=&MyFun形式,也可FunP=MyFun。
上述代码的写法,随便你爱怎么着!
请这样理解吧!这可是有助于你对函数指针的应用喽!
最后——
补充说明一点:在函数的声明处:
void MyFun(int );    //不能写成void (*MyFun)(int )。
void (*FunP)(int );   //不能写成void FunP(int )。
(请看注释)这一点是要注意的。

五、 定义某一函数的指针类型:
就像自定义数据类型一样,我们也可以先定义一个函数指针类型,然后再用这个类型来申明函数指针变量。
我先给你一个自定义数据类型的例子。
typedef int* PINT;    //为int* 类型定义了一个PINT的别名
int main()
{
  int x;
  PINT px=&x;   //与int * px=&x;是等价的。PINT类型其实就是int * 类型
  *px=10;       //px就是int*类型的变量 
  return 0;
}
根据注释,应该不难看懂吧!(虽然你可能很少这样定义使用,但以后学习Win32编程时会经常见到的。)
下面我们来看一下函数指针类型的定义及使用:(请与上对照!)
复制代码
#include <iostream>
using std::cout;
using std::endl;

void MyFun(int x);          //此处也可以声明为:void Myfun(int); 
typedef void (*FunType)(int);    //这样只是定义一个指针类型 
FunType FunP;               //然后用FunType类型来声明全局Funp变量 

int main()
{
    //FunType Funp;        //函数指针变量当然也可以是局部的,那就请在这里声明 
    (*MyFun)(10);          //看,函数名MyFun也可以有这样的调用格式 
    FunP = &MyFun;          //将MyFun函数的地址赋给FunP变量 
    (*FunP)(20);           //这里是通过函数指针变量来调用MyFun函数的 
    
    system("pause");
    return 0;
}

void MyFun(int x)       //MyFun函数的定义 
{
      cout << "x: " << x << endl;
}
复制代码

看黑体部分:
首先,在void (*FunType)(int ); 前加了一个typedef 。这样只是定义一个名为FunType函数指针类型,而不是一个FunType变量。
然后,FunType FunP;  这句就如PINT px;一样地申明一个FunP变量。
其它相同。整个程序完成了相同的事。
这样做法的好处是:
有了FunType类型后,我们就可以同样地、很方便地用FunType类型来声明多个同类型的函数指针变量了。如下:
FunType FunP2;
FunType FunP3;
//……

六、 函数指针作为某个函数的参数:
既然函数指针变量是一个变量,当然也可以作为某个函数的参数来使用的。所以,还应知道函数指针是如何作为某个函数的参数来传递使用的。
给你一个实例:
要求:要设计一个CallMyFun函数,这个函数可以通过参数中的函数指针值不同来分别调用MyFun1、MyFun2、MyFun3这三个函数(注:这三个函数的定义格式应相同)。
实现:代码如下:

复制代码
#include <iostream>
using std::cout;
using std::endl;

void MyFun1(int x);
void MyFun2(int x);
void MyFun3(int x);
typedef void(*FunType)(int);        //2.定义一个函数指针FunType, 与1.函数类型一直 
void CallMyFun(FunType fp, int x);

int main()
{
    CallMyFun(MyFun1, 10);          //5.通过CallMyFun函数分别调用三个不同的函数 
    CallMyFun(MyFun2, 20);
    CallMyFun(MyFun3, 30);  
    
    system("pause");  
    return 0;
}

void CallMyFun(FunType fp, int x)   //3.参数fp的类型是Funtype 
{
     fp(x);                         //4.通过fp的指针执行传递进来的函数,注意fp所知的函数时有一个参数的 
}

void MyFun1(int x)
{
     cout << "MyFun1: " << x << endl;
}
void MyFun2(int x)
{
     cout << "MyFun1: " << x << endl;
}
void MyFun3(int x)
{
     cout << "MyFun1: " << x << endl;
}
复制代码

分析见注释。

参考:http://www.cnblogs.com/wuzhenbo/archive/2012/05/29/2524400.html

 

 

 

三、函数参数的传递问题(一级指针和二级指针)

程序1:

void myMalloc(char *s) //我想在函数中分配内存,再返回

{

  s=(char *) malloc(100);

}

void main()

{

  char *p=NULL;

  myMalloc(p); //这里的p实际还是NULL,p的值没有改变,为什么?

  if(p) free(p);

}

程序2:

void myMalloc(char **s)

{

  *s=(char *) malloc(100);

}

void main()

{

  char *p=NULL;

  myMalloc(&p); //这里的p可以得到正确的值了

  if(p) free(p);

}

程序3:

复制代码
#include <iostream>
using namespace std;

void fun(int *p)
{
     int b = 100;
     p = &b;
}

main()
{
      int a = 10;
      int *q;
      q = &a;
      printf("%d\n", *q);
      fun(q);
      printf("%d\n", *q);
      
      system("pause");
      return 0;
}
复制代码

结果:

图片内容为:

10

10

程序4:

复制代码
#include <iostream>
using namespace std;

void fun(int *p)
{
     *p = 100;
}

main()
{
      int a = 10;
      int *q;
      q = &a;
      printf("%d\n", *q);
      fun(q);
      printf("%d\n", *q);
      
      system("pause");
      return 0;
}
复制代码

结果为

图片内容为:

10

100

 

为什么?

---------------------------------------------------------------

1.被分配内存的是形参s,p没有分配内存;

2.被分配内存的是形参s指向的指针p,所以分配了内存。

---------------------------------------------------------------

 

不是指针没明白,是函数调用的问题!看看这段:

程序5指针参数是如何传递内存的?

     如果函数的参数是一个指针,不要指望用该指针去申请动态内存。程序5中,Test函数的语句GetMemory(str, 200)并没有使str获得期望的内存,str依旧是NULL,为什么?

复制代码
#include <iostream>
using namespace std;

void GetMemory1(char *p, int num)
{
     p = (char *)malloc(sizeof(char)*num);
}

int main()
{
    char *str = NULL;
    GetMemory1(str, 100);                  // str仍然为 NULL 
    strcpy(str, "hello")                  // 运行错误 
   system("pause"); return 0; 
}
复制代码

程序5 试图用指针参数申请动态内存

 

毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,因为没有用free释放内存。

如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,见程序6

复制代码
#include <iostream>
using namespace std;

void GetMemory2(char **p, int num)
{
     *p = (char *)malloc(sizeof(char)*num);
}

int main()
{
    char *str = NULL;
    GetMemory2(&str, 100);                  // 注意参数是&str,而不是str 
    strcpy(str, "hello");                  
    cout << str << endl;
    free(str); 
     
    system("pause");                     
    return 0;
}
复制代码

程序6用指向指针的指针申请动态内存

 

由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态内存。这种方法更加简单,见示例7-4-3。

复制代码
#include <iostream>
using namespace std;

char *GetMemory3(int num)
{
     char *p = (char *)malloc(sizeof(char)*num);
     return p;
}

int main()
{
    char *str = NULL;
    str = GetMemory3(100);                  
    strcpy(str, "hello");                  
    cout << str << endl;
    free(str); 
     
    system("pause");                     
    return 0;
}
复制代码

程序7 用函数返回值来传递动态内存

 

用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return语句用错了。这里强调不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡,见程序8。

复制代码
#include <iostream>
using namespace std;

char *GetString(void)
{
     char p[] = "hello world";
     return p;  //编译器将提出警告 
}

int main()
{
    char *str = NULL;
    str = GetString();             //str 的内容是垃圾 
    cout << str << endl;
     
    system("pause");                     
    return 0;
}
复制代码

结果:

图片内容为:

Q=放假啊克雷格(乱码)

程序8 return语句返回指向“栈内存”的指针

 

用调试器逐步跟踪主函数,发现执行str = GetString语句后str不再是NULL指针,但是str的内容不是“hello world”而是垃圾。

如果把示例程序7改写成程序8,会怎么样?

复制代码
#include <iostream>
using namespace std;

char *GetString2(void)
{
     char *p = "hello world";
     return p;  //编译器将提出警告 
}

int main()
{
    char *str = NULL;
    str = GetString2();             //str 的内容是垃圾 
    cout << str << endl;
     
    system("pause");                     
    return 0;
}
复制代码

程序8 return语句返回常量字符串

 

函数程序8运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。

---------------------------------------------------------------

看看林锐的《高质量的C/C++编程》,上面讲得很清楚的

---------------------------------------------------------------

对于1和2:

如果传入的是一级指针S的话,

那么函数中将使用的是S的拷贝,

要改变S的值,只能传入指向S的指针,即二级指针

---------------------------------------------------------------

程序1:

void myMalloc(char *s) //我想在函数中分配内存,再返回

{

  s=(char *) malloc(100); // s是值参, 函数返回后就回复传递前的数值,无法带回分配的结果

}

这个和调用 void func (int i) {i=1;}; 一样,退出函数体,i指复原的

程序2:void myMalloc(char **s)

{

  *s=(char *) malloc(100); // 这个是可以的

}

等价于

void int func(int * pI) {*pI=1;} pI指针不变,指针指向的数据内容是变化的

值参本身不变,但是值参指向的内存的内容发生了变化。

程序3:

void fun(int *p)

{

  int b=100;

  p=&b;       // 等同于第一个问题, b的地址并没有被返回

}

程序4:

void fun(int *p)

{

  *p=100; // okay

}

结论:

1.       函数的返回值是指针类型的,检查是静态内存指针还是堆内存指针还是栈内存指针,栈内存指针是绝对要不得滴

2.       函数需要使用指针参数进行传入传出的,在函数中只能对指针的指向的值(*p)进行修改,而不能修改指针指向,也就是指针地址!(函数中不得修改指针参数的地址,否则请使用指针的指针!)

参考:http://www.cnblogs.com/wuzhenbo/archive/2012/06/13/2547664.html

 

 

 

四、int* pInt = new int(5);和int* pInt = new int[5];

int *p = new int(5);
这句是从堆上分配一个int型变量所占的字节内存,这个内存单元存放的整数值为5,然后让一个整形的指针变量p指向它的地址。
释放方式:delete p;
int *p = new int[5];
这句相当于从堆上分配一个含有5个元素的整形数组所占的字节内存,然后让一个整形的指针变量p指向它的首址。
释放方式:delete []p;(注意这个[]不能掉了,如果掉了就会只释放P[0]所占的空间,p[1]到p[4]不会被释放,产生内存泄露。)

 

 

五、数组作为函数参数及作为函数模版参数的总结

今天编程序时发生了个这样的错误:

在头文件里 定义了一个数组:

View Code
1 char s[]="1234567890";

又定义了一个现显示组的函数:

View Code
1 void Display(char* c);

通过下面这两条语句分别在现实函数和主函数中现实数组的大小:

View Code
1     sizeof(c);
2     sizeof(s);

现实结果却大相径庭,在主函数中为11,在现实函数中却为4。

经过思考发现,在主函数中s代表的是一个数组,而在现实函数中c代表的是一个指向数组头的指针。数组的大小为11,指针的大小为4。

主程序如下:

View Code
复制代码
 1 #include<iostream>
 2 #include<stdlib.h>
 3 using namespace std;
 4 
 5 char s[]="1234567890";
 6 void Display(char* c);
 7 
 8 void main()
 9 {
10     char a;
11     cout<<"这个是在主函数中对数组长度的测试:"<<sizeof(s)<<endl;
12     Display(s);
13     cin>>a;
14 }
15 
16 
17 void Display(char* c)
18 {
19     cout<<"这个是在子函数中对数组长度的测试:"<<sizeof(c)<<endl;
20 }
复制代码

现实结果:

我的问题是怎样可以在主函数和子函数中都指的是数组而不是一个指向数组头的指针???

 

在网上查了几种将数组作为函数参数进行传递的方法,列举如下:

View Code
复制代码
 1 #include<iostream>
 2 #include<stdlib.h>
 3 #include<vector>
 4 using namespace std;
 5 
 6 char s[]="1234567890";
 7 int a[10]={0,1};
 8 int b[10]={0,1};
 9 void Display(char* c);
10 void PutArray1(int *p,int length1);
11 void PutArray2(int p[],int length1);
12 void PutArray3(int p[10]);
13 void PutArray4(int (&p)[10]);
14 void PutArray5(vector<int>verc);
15 
16 void main()
17 {
18     char q;
19     cout<<"这个是在主函数中对数组长度的测试:"<<sizeof(s)<<endl;
20     Display(s);
21     cout<<"*********************************************"<<endl;
22     PutArray1(a,10);
23     PutArray2(a,10);
24     PutArray3(a);
25     PutArray4(b);
26     cin>>q;
27 }
28 
29 
30 void Display(char* c)
31 {
32     cout<<"这个是在子函数中对数组长度的测试:"<<sizeof(c)<<endl;
33 }
34 void PutArray1(int *p,int length1)
35 {
36     int length2=sizeof(p);
37     cout<<"第一种方法的输出:"<<endl;
38     cout<<"第一种方法数组的长度为:"<<length2<<endl;
39     for(int i=0;i<length1;i++)
40     {
41         cout<<p[i];
42     }
43     cout<<endl;
44 }
45 void PutArray2(int p[],int length1)
46 {
47     int length2=sizeof(p);
48     cout<<"第二种方法的输出:"<<endl;
49     cout<<"第二种方法数组的长度为:"<<length2<<endl;
50         for(int i=0;i<length1;i++)
51         {
52             cout<<p[i];
53         }
54     cout<<endl;
55 }
56 void PutArray3(int p[10])
57 {
58     int length2=sizeof(p);
59     cout<<"第三种方法的输出:"<<endl;
60     cout<<"第三种方法数组的长度为:"<<length2<<endl;
61         for(int i=0;i<9;i++)
62         {
63             cout<<p[i];
64         }
65     cout<<endl;
66 }
67 void PutArray4(int (&p)[10])
68 {
69     int length2=sizeof(p);
70     cout<<"第四种方法的输出:"<<endl;
71     cout<<"第四种方法数组的长度为:"<<length2<<endl;
72         for(int i=0;i<9;i++)
73         {
74             cout<<p[i];
75         }
76     cout<<endl;
77 }
78 void PutArray5(vector<int>verc)
79 {
80     vector<int>::iterator begin_iter=verc.begin();
81     vector<int>::iterator end_iter=verc.end();
82     int size=verc.size();
83     cout<<"第五种方法的输出:"<<endl;
84     cout<<"第五种方法数组的长度为:"<<size<<endl;
85     cout<<"下面这种方法是采用向量遍历的方法遍历数组:"<<endl;
86     for(vector<int>::iterator iter=begin_iter;iter!=end_iter;iter++)
87     {
88         cout<<*iter;
89     }
90     cout<<endl;
91     cout<<"下面这种方法是采用普通遍历数组的方法遍历数组:"<<endl;
92     for(int i=0;i<size-1;i++)
93     {
94         cout<<verc[i];
95     }
96     cout<<endl;
97 }
复制代码

在这里,int *arr和int arr[]的含义相同,编译器自动将 int arr[]替换为int *arr,所以这也解释了上面在主函数和子函数中利用数组名求数组长度会得到不同结果的原因。这种情况只有在数组作为函数参数进行传递时才会发生(C++ Primer Plus,P192)。

其中第四种方法没有理解,疑问暂时留在这里吧。

另外虽然上面四种方法都可以正确地在子函数中传递数组作为参数,但是仍然不能满足博客刚开始的要求:在子函数中可以测的参数数组的长度。后来查看C++ Primer Plus发现书上已经明确指出没有实现这种想法的方法,数组的长度必须在函数中作为参数进行传递。


另外由于第五种方法需要重新定义向量模版,和主题不符,所以在主函数里并没有调用它。例程中给的程序如下所示:

View Code
1 vector<int> verc1(a,a+10);
2 vector<int> verc2(b,b+8);
3 PutArray5(verc1);
4 PutArray5(verc2);

 上面这五种调用数组的方法只是在传递数组的方式上不同,可以归纳为传递数组的一种方法,即:传递指向数组头的指针和数组的长度。另外一种传递数组的方法是将指向数组头的指针和指向数组尾的指针作为两个参数进行传递,函数定义如下:

View Code
1 int sum_arr(const int* begin,const int* end);

在学习这个知识点时是有一些感想的,虽说C++ Primer已经看了一遍,里面的知识点也认真地消化过,但是一到用的时候就使不上劲。看来应该在看书的同时多实践书上的代码,这样才能对代码的应用有更好的理解。

本篇收集自:http://www.cnblogs.com/tziyachi/archive/2012/02/11/2347073.html
我看了下,方法4在方法声明时就固定了数组的使用长度了,没有任何灵活性可言,方法5没看懂,有空再说把。不过发现一个终极解决方案如下:

我们知道,在C/C++中,向一个函数传递数组时,实际上传送的是这个数组的首地址,也即是一个指针类型。所以,在函数中,我们没有办法知道这个传递进来的数组的长度到底为多长,一个普遍的方法就是再增加一个参数,来记录传递进来的数组的长度。
不过在C++中,使用模板机制,可以很好地解决这个问题,如下面的代码所示:
  #include <iostream>
  using namespace std;
  template<class T, int N>
  void array(T (&param)[N])
  {
  cout << "N= " << N << endl;
  }
  int main()
  {
  int i[100];
  double d[20];
  char c[6];
  array(i);
  array(d);
  array(c);
  return 0;
  } 来源:

【补充个东西,函数模版的模版非类型参数,原始定义在书中是这样的:

template<class T,int size>

T max(T(&array)[size])

{

      //函数体

上面模版方法定义中,size是一个模版非类型参数,专门用来表示数组array的长度,需要在编译之前确定他的值。



 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值