c/c++ 二维数组指针参数传递 矩阵计算实例

before|正文之前:

c++实验代码及学习笔记(三)

你好! 这是一个高程实验课的代码记录及学习笔记。我将记录一些重要的知识点、易错点。但是作为大学生,水平很低,敬请指点教导、优化代码。

今天我将试图用沙雕文风解释二维数组的知识;D

1问题

首先我们来看一下问题
实验目的
FBIwarning:建议在阅读答案前,独立思考,先自行尝试,遇到问题再继续阅读。

怎么样!看起来就很难对吧!实际上!它确实不简单(流下了不学无术的泪水)
特别是,大家都知道作为程序媛每个月总有那么几天很暴躁(乱入的某皮:也就那么二三十天吧)。然后暴躁girl写这道题的时候就被debug折磨得炸毛了,头也快被薅秃了(震惊!程序媛秃头竟为哪般)……所以此题适合心平气和时来做。

好了,进入正题


2思路

这道题涉及的知识比较复杂,关于接口设计简直是一门艺术;而数组的内存存储则需要数据结构的知识(流泪);另外矩阵乘法还需要线性代数的基础知识(再一次流泪);最后是本文的关键,指针。

如果你恨他,请让他用指针,因为足够折磨;如果你爱他,也请让他用指针,因为一旦学会了就会爱上它。
——鲁·我没说过·迅

由于知识庞杂,我们还是看题说话。

题目详解

题目:预设一M*N的矩阵A:

  1. 计算每行均值
  2. 计算每列均值
  3. 移除矩阵的某一行
  4. 移除矩阵的某一列
  5. 输入一个N维向量X,计算A与X的乘积,结果为一M维向量。

预设

本次实验我延续了上次模块化的思想,依旧使用头文件、源文件的形式,看起来或许更为复杂,但是普适性更强。
数据类型:float

//main.cpp
#include <iostream>
#include "matrix.h"	//本次自定义头文件“matrix.h”
#include <stdlib.h>
using namespace std;

int main()
{
   
	float arr[][2] = {
    {
   1,2},{
   3,4},{
   5,6} };
	...
	return 0;
}

我将分为三个部分阐述:

1、计算每行/列均值
函数类型

没错,头号任务是在matrix.cpp中定义函数。
一看到这个题目,宝宝们是不是就想到<传入数组参数、行列数-遍历数组计算均值-返回均值> 呢? 没错,思路是正确的。但是用什么样的函数类型呢?
二狗:数组是float型的,返回的均值也应该是float的……那么我函数也float吧!
老师:你要想清楚,矩阵每行均值可不单单是一个值哇!
二狗:糟了,要返回数组!那就……float* 指针型数组?
老师:……指针型数组不是不可以啦,就是用到动态分配malloc,主函数里要free()释放内存,很多人都会忘哦,所以不推荐。(此处动态分配是真的不推荐,二狗以身试法惨遭吐血级bug)
老师:所以呢,我们推荐void型!想不到吧!(二狗:后面这句就不用说了喂!
之所以是void,是因为我们可以在常量区声明一个数组p,然后传入这个一维数组,直接对其操作,等函数运行完毕局部变量over之后,常量区的数组内存地址还在哦。(在上篇结尾提到了!大家有没有好好阅读呢~)

参数:传入二维数组

确定函数类型后,我们来观察一下参数吧!
第一个是预设的二维数组arr,我们如何传入一个二维数组呢?
不要理所当然认为是**arr,可以手动试一试。
代码引用自文章:c++ – 二维数组参数传递

#include <iostream>
using namespace std;

/*传二维数组*/

//第1种方式:传数组,第二维必须标明
/*void display(int arr[][4])*/ 
void display1(int arr[][4],const int irows)
{
   
    for (int i=0;i<irows;++i)
    {
   
        for(int j=0;j<4;++j)
        {
   
            cout<<arr[i][j]<<" ";     //可以采用parr[i][j]
        }
        cout<<endl;
    }
    cout<<endl;
}

//第2种方式:一重指针,传数组指针,第二维必须标明
/*void display(int (*parr)[4])*/
void display2(int (*parr)[4],const int irows)
{
   
    for (int i=0;i<irows;++i)
    {
   
        for(int j=0;j<4;++j)
        {
   
            cout<<parr[i][j]<<" ";    //可以采用parr[i][j]
        }
        cout<<endl;
    }
    cout<<endl;
}
//注意:parr[i]等价于*(parr+i),一维数组和二维数组都适用

//第3种方式:传指针,不管是几维数组都把他看成是指针
/*void display3(int *arr)*/
void display3(int *arr,const int irows,const int icols)
{
   
    for(int i=0;i<irows;++i)
    {
   
        for(int j=0;j<icols;++j)
        {
   
            cout<<*(arr+i*icols+j)<<" ";   //注意:(arr+i*icols+j),不是(arr+i*irows+j)
        }
        cout<<endl;
    }
    cout<<endl;
}

/***************************************************************************/
/*
//第2种方式:一重指针,传数组指针void display(int (*parr)[4])
//缺陷:需要指出第二维大小
typedef int parr[4];
void display(parr *p)
{
    int *q=*p;        //q指向arr的首元素
    cout<<*q<<endl;   //输出0
}

typedef int (*parr1)[4];
void display1(parr1 p)
{
    cout<<(*p)[1]<<endl;  //输出1
    cout<<*p[1]<<endl;    //输出4,[]运算符优先级高
}
//第3种方式:
void display2(int **p)
{
    cout<<*p<<endl;           //输出0
    cout<<*((int*)p+1+1)<<endl; //输出2
}
*/

int main()
{
   
    int arr[][4]={
   0,1,2,3,4,5,6,7,8,9,10,11};
    int irows=3;
    int icols=4;
    display1(arr,irows);
    display2(arr,irows);

    //注意(int*)强制转换.个人理解:相当于将a拉成了一维数组处理。
    display3((int*)arr,irows,icols);
    return 0;
}

可以看出,无论第一种还是第二种,都需要指明二维数组的列数。所以为了对任意M*N数组适用(普适性:我的函数不止可以用于一个文件、一类数组),我们只能选用第三种。
重要,请仔细阅读哦↓

第三种传参形式是(float*)arr,定义是*a
简单解释就是,传入一维数组指针是可行的,其实二维数组可以视为一维数组套一维数组,我们把二维数组拍扁,就可以假扮一维数组混入函数啦!
例:
arr[3][2]:{ {1,2},{3,4},{5,6}}
拍扁:
arr’[6]:{1,2,3,4,5,6}

那么我们可以写出函数了!

代码实现
void avgRows(float *a, int rows, int cols, float p[])
{
   	
	for (int i = 0; i < rows; i++)
	{
   
		p[i] = 0;
		for (int j = 0; j < cols; j++)
		{
   
			p[i] = p[i] + a[ i * cols + j];	//这里比较难理解,不是a[i][j],
											//它变为了一维数组,应该是a[i乘以列数+j]
		}
	}
	for (int i = 0; i < rows; i++) {
   
		p[i] = p[i] / cols;
	}
}

同理,我们可以写出求列的均值函数

void avgCols(float *a, int rows, int cols, float p[])
{
   
	for (int i = 0; i < cols; i++)
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值