您的位置:首页 > 其它

如何将二维数组作为函数的参数传递

2013-12-19 10:02 309 查看
自身体会:
二维数组,用malloc开辟,用指针传入,直接在函数中用a[i][j]形式使用:
函数声明:

void BP_train(double **train_sample, //train_sample_array N0

int **train_output, //ideal_ouput_array N0

double min_sqare_error,

int Max_train_num,

int train_sample_num,

int features_num,

int p,

int kind_num,

double learn_rate,

double (*Wih)[p0],

double (*Who)[q0],

double *bh,

double *bo,

double *a); //BP神经网络训练的函数

函数定义:

void woodfunction::BP_train(double **train_sample,

int **train_output,

double min_sqare_error,

int Max_train_num,

int train_sample_num,

int features_num,

int p,

int kind_num,

double learn_rate,

double (*Wih)[p0],

double (*Who)[q0],

double *bh,

double *bo,

double *a)

数组定义:

double **features_array = (double **) malloc( n0*sizeof(double*));

for(i=0;i<n0;i++){

features_array[i]=(double *)malloc(database_size*sizeof(double));

memset(features_array[i],0,database_size*sizeof(double));

}

数组带入和使用:

带入:features_array

使用:features_[i][j]

前几日用C编写DSP程序时,遇到一个问题:如何向C函数中传递指向二维数组的指针参数。初接触以为很简单,直接声明一个二维数组,然后把数组名传进去。但是一经编译便报错。后来仔细想了一下,并查找了一些相关资料,发现二维数组在概念上远比一维数组复杂,或者说二维数组以一种晦涩的方式构建在一维数组之上。

先来回顾一下一维数组。一维数组的数组名即为指向该数组的指针,该指针值保存了数组存放在内存中的一块连续区域的起始地址;数组的下标表示了这片内存区域的某存储区相对于起始地址的偏移量。简单来讲就是:指向一维数组的指针,指向数据存放区域的起始位置。

事实上,计算机系统的多维数组其实最终还是以一维数组的形式实现的。就N x M的二维数组来讲,设其数组名为array。指针array指向一个数组,该数组存放的是一系列指针,这些指针分别指向相应的一维数组,而这些数组中存放的才是我们的数据。

array -> [一维数组指针1] -> [ 一维数组,M长]

[一维数组指针2] -> [ 一维数组,M长]

…… ……

[一维数组指针N] -> [ 一维数组,M长]

由此array是第i个指针变量地址,array[j]则表示相对于第i个指针变量偏移j*sizeof(数组类型)。系统通过这种机制访问了该二维数组的第i行,第j列的内容。

有上述可知,指向二维数组的指针其实是指向“指针变量地址”的指针变量。所以在声明指向二维数组的指针时,用 int ** array的形式。

有以下两种方式来对二维数组分配内存:

///// 方法一

#include <stdlib.h> // 必须包含该头文件,里面定义了malloc的实现

int ** array = malloc( N * sizeof(int *) );

for (int k=0;k<N;k++)

array[k] = malloc( M * sizeof(int) );

///// 方法二

#include <stdlib.h>

int ** array = malloc( N * sizeof(int *) );

array[0] = malloc( M * sizeof(int) );

for (int k=1;k<N;k++)

array[k] = array[0]+M*k;

上述两种方法的区别在于:前者在内存中分配的区域有可能是不连续的;而后者则在内存中的一片连续区域为该数组分配空间。

我们还可以通过一维数组模拟二维数组。在这中间要进行下标转换。如对于模拟的NxM数组,访问其第i行,第j列元素时,在一维数组中对应的位置是i*M+j。当然为了更简捷,我们可以把这个数组下标转换过程定义为一个宏,交由编译系统来处理。

#define Arr2 ( array_name, row,col ) array_name[row*M+col]

定义该宏后,访问Arr2( array, i, j)等价于访问 array[i*M+j]

如何将二维数组作为函数的参数传递
今天写程序的时候要用到二维数组作参数传给一个函数,我发现将二维数组作参数进行传递还不是想象得那么简单里,但是最后我也解决了遇到的问题,所以这篇文章主要介绍如何处理二维数组当作参数传递的情况,希望大家不至于再在这上面浪费时间。
正文:

首先,我引用了谭浩强先生编著的《C程序设计》上面的一节原文,它简要介绍了如何
将二维数组作为参数传递,原文如下(略有改变,请原谅):

[原文开始]
可以用二维数组名作为实参或者形参,在被调用函数中对形参数组定义时可以指定所有维数的大小,也可以省略第一维的大小说明,如:
void Func(int array[3][10]);
void Func(int array[][10]);
二者都是合法而且等价,但是不能把第二维或者更高维的大小省略,如下面的定义是不合法的:
void Func(int array[][]);
因为从实参传递来的是数组的起始地址,在内存中按数组排列规则存放(按行存放),而并不区分行和列,如果在形参中不说明列数,则系统无法决定应为多少行多少列,不能只指定一维而不指定第二维,下面写法是错误的:
void Func(int array[3][]);实参数组维数可以大于形参数组,例如实参数组定义为:
void Func(int array[3][10]);
而形参数组定义为:
int array[5][10];
这时形参数组只取实参数组的一部分,其余部分不起作用。
[原文结束]
大家可以看到,将二维数组当作参数的时候,必须指明所有维数大小或者省略第一维的,但是不能省略第二维或者更高维的大小,这是由编译器原理限制的。大家在学编译原理这么课程的时候知道编译器是这样处理数组的:
对于数组 int p[m]
;
如果要取p[i][j]的值(i>=0 && i<m && 0<=j && j < n),编译器是这样寻址的,它的地址为:
p + i*n + j;
从以上可以看出,如果我们省略了第二维或者更高维的大小,编译器将不知道如何正确的寻址。但是我们在编写程序的时候却需要用到各个维数都不固定的二维数组作为参数,这就难办了,编译器不能识别阿,怎么办呢?不要着急,编译器虽然不能识别,但是我们完全可以不把它当作一个二维数组,而是把它当作一个普通的指针,再另外加上两个参数指明各个维数,然后我们为二维数组手工寻址,这样就达到了将二维数组作为函数的参数传递的目的,根据这个思想,我们可以把维数固定的参数变为维数随即的参数,例如:

void Func(int array[3][10]);
void Func(int array[][10]);
变为:
void Func(int **array, int m, int n);

在转变后的函数中,array[i][j]这样的式子是不对的(不信,大家可以试一下),因为编译器不能正确的为它寻址,所以我们需要模仿编译器的行为把array[i][j]这样的式子手工转变为:
*((int*)array + n*i + j);
在调用这样的函数的时候,需要注意一下,如下面的例子:
int a[3][3] =
{
{1, 1, 1},
{2, 2, 2},
{3, 3, 3}
};
Func(a, 3, 3);

根据不同编译器不同的设置,可能出现warning 或者error,可以进行强制转换如下调用:
Func((int**)a, 3, 3);
其实多维数组和二维数组原理是一样的,大家可以自己扩充的多维数组,这里不再赘述。写到这里,我先向看了这篇文章后悔的人道歉,浪费你的时间了。下面是一个完整的例子程序,这个例子程序的主要功能是求一个图中某个顶点到其他顶点的最短路经,图是以邻接矩阵的形式存放的(也就是一个二维数组),其实这个函数也是挺有用的,但是我们这篇文章的重点在于将二维数组作为函数的参数传递。
完整的例子程序包括三个文件,在Microsoft Visual C++ 6.0下调试通过。如下:

//

// mian.c 为主程序入口,并且调用了示范了如何调用求一个图中某个顶点到其他顶点

的最短路经

// 的函数

//

#include "short.h"

#include <stdio.h>

int main()

{

int i = 0, v = 0;

float AdjoinMatrix[5][5]=

{

{0, 10,NO_PATH,30,100},

{NO_PATH,0,50,NO_PATH,NO_PATH},

{NO_PATH,NO_PATH,0,NO_PATH,10},

{NO_PATH,NO_PATH,20,0,60},

{NO_PATH,NO_PATH,NO_PATH,NO_PATH,0}

};

int Path[5];

float Length[5];

ShortestPath(AdjoinMatrix, Length, Path, 5, 0);

for(i = 1; i < 5; i++)

{

v = i;

while(v != 0)

{

printf("%d ", v);

v = Path[v];

}

printf("%d\n", v);

}

return 0;

}

//

// ShortestPath.h 中定义了求一个图中某个顶点到其他顶点的最短路经的函数,还定

义了一个

// 宏,#define NO_PATH 0x00ffffff,如果图中的两个顶点之间的直接路径的长度为

NO_PATH,

// 表示图中两个顶点是不直接相通的。

//

#ifndef INCLUDE_SHORTESTPATH_H

#define INCLUDE_SHORTESTPATH_H

#define IN

#define OUT

#define NO_PATH 0x00ffffff

/*++

Abstract:

该函数的功能是求得一个图中的某个顶点到其他所有顶点的最短路经,及其最

短路经的长度

Returen value:

类型是int,含义如下

0 成功

1 资源不够

Examples:

//你有一个图的邻接矩阵如AdjoinMatrix

和数组

Path
, Length
(n为图顶点的个数,

//然后你可以如下调用:ShortestPath(AdjoinMatrix,

Length, Path, 5, 0);

//调用后,Path
中存放最短路径,Length
中存放着最

短路径的长度

//下面的例子中我们求得从0顶点到其他定点的最短路经及

其长度

float AdjoinMatrix[5][5]=

{

{0, 10,NO_PATH,30,100},

{NO_PATH,0,50,NO_PATH,NO_PATH},

{NO_PATH,NO_PATH,0,NO_PATH,10},

{NO_PATH,NO_PATH,20,0,60},

{NO_PATH,NO_PATH,NO_PATH,NO_PATH,0}

};

int Path[5];

float Length[5];

ShortestPath(AdjoinMatrix, Length, Path, 5,

0);

int i = 0, int v =0;

for(i = 1; i < 5; i++)

{

v = i;

while(v != 0)

{

printf("%d ", v);

v = Path[v];

}

printf("%d\n", v);

}

--*/

int ShortestPath(

IN float **AdjoinMatrix, //存放图的邻接矩阵,是

一个二维数组

OUT float *Length, //用于返回到各

个点的最短路经的长度

OUT int *Path, //用于返回最短

路经,Path[i]表示在最短路经上顶点i前面的顶点

IN int VertexNum, //顶点的个数

IN int Vertex //起始顶点

);

#endif

//

// ShortestPath.c 中实现了求一个图中某个顶点到其他顶点的最短路经的函数。

//

#include "ShortestPath.h"

#include <stdlib.h>

/*++

Abstract:

该函数的功能是求得一个图中的某个顶点到其他所有顶点的最短路经,及其最短

路经的长度

Returen value:

类型是int,含义如下

0 成功

1 资源不够

--*/

int ShortestPath(

IN float **AdjoinMatrix, //存放图的邻接矩阵,是

一个二维数组

OUT float *Length, //用于返回到各

个点的最短路经的长度

OUT int *Path, //用于返回最短

路经,Path[i]表示在最短路经上顶点i前面的顶点

IN int VertexNum, //顶点的个数

IN int Vertex //起始顶点

)

{

int i = 0, j = 0, w = 0;

//

// 已经在最短路经中的点的集合,如果VertexSet[i]不为0,则表示第

i个点在该集合中

//

int *VertexSet = (int*)malloc(VertexNum);

if(VertexSet == NULL)

{

return 1; //缺乏内存资源

}

//

// 初始化

//

for(i = 0; i < VertexNum; i++)

{

Length[i] = *((float*)AdjoinMatrix + Vertex*VertexNum

+ i);

VertexSet[i]=0;

if(i != Vertex && Length[i] < NO_PATH)

{

Path[i]=Vertex;

}

else

{

Path[i] = -1;

}

}

VertexSet[Vertex] = 1;

Length[Vertex] = 0;

//

// 求得最短路经

//

for(i = 0; i < VertexNum-1; i++)

{

float min = NO_PATH;

int u = Vertex;

for(j = 0; j < VertexNum; j++)

{

if( !VertexSet[j] && Length[j] < min)

{

u = j;

min = Length[j];

}

}

VertexSet[u] = 1;

for(w = 0; w < VertexNum; w++)

{

if(!VertexSet[w] && *((float*)AdjoinMatrix +

u*VertexNum + w) < NO_PATH && Length[u]+*((float*)AdjoinMatrix + u*VertexNum +

w) < Length[w])

{

Length[w] = Length[u] +

*((float*)AdjoinMatrix + u*VertexNum + w);

Path[w] = u;

}

}

}

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: