涉及的C/C++的知识点

1.二维数组作为函数参数传入

静态数组(维数确定)

二维数组作为函数的参数,实参可以直接使用二维数组名,在被调用函数中可以定义形参所有维数的大小,也可以省略以为大小的说明。例如:

1
2
void find(char a[3][10]);
void find (char a[ ][10]);

也可以使用数组指针来作为函数参数,例如:

1
void find (char (*p)[10]);

但是不能像下面这样使用,例如:

1
2
void find(char a[ ][ ]);
void find (char a[3][ ]);

因为从实参传递来的是数组的起始地址,如果在形参中不说明列数,编译器将无法定位元素的的位置。

栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <string.h>
void range(char str[ ][100]);//传进二维数组
int main(void)
{
char str[8][100];
int i;
for(i=0;i<8;i++)
gets(str[i]);
range(str);
for(i=0;i<8;i++)
printf("%s\n",str[i]);

}

void range(char str[ ][100])
{
int i,j;
char temp[100];
for(i=0;i<8-1;i++)
{
for(j=0;j<8-i-1;j++)
{
if(strcmp(str[j],str[j+1])>0)
{
strcpy(temp,str[j]);
strcpy(str[j],str[j+1]);
strcpy(str[j+1],temp);
}
}
}
}

动态数组(维数不确定)

如果不确定二维数组的维数的话,我们不能使用上面的方法,可以用下面的方法:

手工转变寻址方式
对于数组 int p[m][n];

如果要取p[i][j]的值(i>=0 && m<=0 && j>=0 && n<=0),编译器是这样寻址的,它的地址为:p + (i*n + j) ;

二维数组的定义并非一个确定的值,也就是动态数组,这时候我们如果像之前那样进行函数调用就会报错,错误提示为:

1
[Error] cannot convert 'int (*)[N]' to 'int**' for argument '2' to 'int threeOrder_evaluation(int, int**)'
1
void printf_int(int hhh[][Dim]);

所以我们只能这样来调用

1
void printf_int(int** hhh);

C++ Code:动态分配数组内存的六种方法

1、利用“malloc-free”动态分配一维数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include<stdlib.h> //该头文件为malloc必须
using namespace std;

int main()
{
int len;
int *p;
cout<<"请输入开辟动态数组的长度:"<<endl;
cin>>len;
//长度乘以int的正常大小,才是动态开辟的大小
p = (int*)malloc(len*sizeof(int));
cout<<"请逐个输入动态数组成员:"<<endl;
for(int i=0; i<len; ++i)
{
//此处不可以写成:cin>>*p[i]
cin>>p[i];
}
cout<<"您输入的动态数组为:"<<endl;
for(int i=0; i<len; ++i)
{
cout<<p[i]<<" ";
}
//时刻记住:有malloc就要有free
free(p);
}

2、利用“malloc-free”动态分配二维数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>  
#include<stdlib.h> //该头文件为malloc必须
using namespace std;

int main()
{
int row,col;
int **p;
cout<<"请输入开辟动态数组的行 & 列:"<<endl;
cin>>row>>col;
//开始开辟
p = (int**)malloc(row*sizeof(int*));//为数组的行开辟空间
for(int i=0; i<row; ++i)
{
*(p+i)=(int*)malloc(col*sizeof(int));//为数组的列开辟空间
}


//输入成员
cout<<"请逐个输入动态数组 各行各列 成员:"<<endl;
for(int i=0; i<row; ++i)
for(int j=0; j<col; ++j)
{
//此处不可以写成:cin>>*p[i] [j]
cin>>p[i][j];
}

//输出成员
cout<<"您输入的动态数组 各行各列 成员如下:"<<endl;
for(int i=0; i<row; ++i)
for(int j=0; j<col; ++j)
{
cout<<p[i][j];
}

//时刻记住:有malloc就要有free
for(int i=0; i<row; ++i)
{
free(*(p+i));
}

}

3、利用“new-delete”动态分配一维数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
using namespace std;

int main()
{
int len;
cout<<"请输入开辟数组的长度:"<<endl;
cin>>len;
int *p = new int [len];


//数据输入
cout<<"请逐个输入数据:"<<endl;
for(int i=0; i<len; ++i)
{
cin>>p[i];
}

//数据反馈
cout<<"您分配的动态数组为:"<<endl;
for(int i=0; i<len; ++i)
{
cout<<p[i]<<" ";
}

//释放内存:
delete []p;

}

4、利用“new-delete”动态分配二维数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
using namespace std;

int main()
{
int row,col;
cout<<"请输入开辟数组的行 & 列:"<<endl;
cin>>row>>col;
//行的开辟
int **p = new int*[row];
for(int i=0; i<row; ++i)
{
//列的开辟
p[i] = new int[col];
}


//数据输入
cout<<"请逐个输入 各行各列 数据:"<<endl;
for(int i=0; i<row; ++i)
for(int j=0; j<col; ++j)
{
cin>>p[i][j];
}

//数据反馈
cout<<"您分配的动态数组为:"<<endl;
for(int i=0; i<row; ++i)
for(int j=0; j<col; ++j)
{
cout<<p[i][j]<<"";
}

//释放内存:

delete []p;

}

5、利用“new-delete”动态分配二维数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include<vector>
using namespace std;

int main()
{
int row,col;
cout<<"请输入行 & 列:"<<endl;
cin>>row>>col;
//很复杂的结构:对于某些编译器,注意连空格都不可以忽略
vector<vector<int> > p(row,vector<int>(col));


//数据输入
cout<<"请逐一输入 各行各列 数据:"<<endl;
for(int i=0; i<row; ++i)
for(int j=0; j<col; j++)
{
cin>>p[i][j];
}

//数据输出
cout<<"您输入的数据:"<<endl;
for(int i=0; i<row; ++i)
for(int j=0; j<col; j++)
{
cout<<p[i][j]<<" ";
}

//该方法利用的是两重的vector而无需释放

}

6、利用while的极其简单输入实现求和、求平均之类算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;


int main()
{
int sum=0,value=0;
//实际上非数字就会结束循环
cout<<"请输入求和数字,以*号作为结束;"
while(cin>>value)
sum += value;
cout<<"您输入数据之和为:"<<sum<<endl;
}

————————————————
版权声明:本文为CSDN博主「Errors_In_Life」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Errors_In_Life/article/details/78889951

2.求逆矩阵

行列式的计算方法

1.对角线法

image-20220403125310025 image-20220403125429394

对角线法也是三阶行列式计算使用最广泛的方法

▍ 对角线法适用于二、三阶行列式,对于更高阶的行列式暂时未找到规律

2.代数余子式法

image-20220403125521787 image-20220403165311052

3.等价转化法

image-20220403165404225

4.逆序法

image-20220403165503001 image-20220403165532571

总结

本文讲述了四种行列式的计算方法:

▍其中对角线法,是使用最简单、最广泛的方法

▍代数余子式法和等价转化法,在特定情况下能极大程度上简便运算,但需要读者对行列式进行灵活地观察

▍逆序数法,是一种更加基础的方法,使用起来比较复杂

3.函数返回一个数组,二维数组

与其他高级语言相比,c语言,c++ 最大的不方便之处就是 函数只能返回一个数值。若要返回一个数组,必须用到指针以及动态分配内存。

方法之一:返回一个结构体,结构体里面包括一个数组。

因为结构体运行效率略慢,这个方法我干脆不用了。

方法之二:利用指针函数,并结合动态内存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int *fun()
{

声明一个动态数组 a;

return a;

}

void main()
{

用指针调用函数 int *p=fun();

释放该指针 free(p); (相当于释放了指针函数里面的动态数组)

}

举例:下面程序返回一个一维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "stdio.h"
#include "stdlib.h"

int * Max(int *arr,int n)
{
int *a=(int *)malloc(2*sizeof(int));
int maxNum=0,maxIndex,i;
for (i=0;i<n;i++)
if (arr[i]>maxNum)
{maxNum=arr[i];maxIndex=i+1;}
a[0]=maxNum;a[1]=maxIndex;
return a;
}

void main()
{
int a[2]={5,2};//a[0]=5,a[1]=2;
int *b=Max(a,2);
int i;
for(i=0;i<2;i++)
printf("b[%d]=%d\n",i,b[i]);

free(b);
}

这个程序中的max函数不仅能够返回一个数组的最大值,还能够显示这个最大值在数组中的位置。

下列程序返回一个二维数组,求出一个二维数组各行的最大值以及所在位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include "stdio.h"
#include "stdlib.h"

int **Max(int **arr,int n,int m)
{
int **data;
data=(int **)malloc(n*sizeof(int *));
for (int i=0;i<n;i++)
data[i]=(int *)malloc(2*sizeof(int));
for (int i=0;i<n;++i)
{
int maxNum=0;
for (int j=0;j<m;++j)
{
//printf("arr[%d][%d]=%d ",i,j,*((int *)arr+m*i+j));
if (*((int *)arr+m*i+j)>maxNum)
{
maxNum=*((int *)arr+m*i+j);
data[i][0]=maxNum;data[i][1]=j;
}
}
//printf("\n");
}
return data;
}

void main()
{
int a[2][3]={5,2,4,6,3,9};
int **b=Max((int **)a,2,3);
for (int i=0;i<2;i++)
{
printf("the maximum num for row %d is %d\n",i+1,b[i][0]);
printf("the maximum num for row %d is in %d",i+1,b[i][1]+1);
printf("\n");
}
for(int i=0;i<2;i++)
free(b[i]);
free(b);
}

4.头文件

#include<stdlib.h>

#include<math.h>

1、 三角函数

double sin(double);正弦

double cos(double);余弦

double tan(double);正切

2 、反三角函数

double asin (double); 结果介于[-PI/2,PI/2]

double acos (double); 结果介于[0,PI]

double atan (double); 反正切(主值),结果介于[-PI/2,PI/2]

double atan2 (double,double); 反正切(整圆值),结果介于[-PI,PI]

3 、双曲三角函数

double sinh (double);

double cosh (double);

double tanh (double);

4 、指数与对数

double frexp(double value,int exp);这是一个将value值拆分成小数部分f和(以2为底的)指数部分exp,并返回小数部分f,即f2^exp。其中f取值在0.5~1.0范围或者0。

double ldexp(double x,int exp);这个函数刚好跟上面那个frexp函数功能相反,它的返回值是x*2^exp

double modf(double value,double *iptr);拆分value值,返回它的小数部分,iptr指向整数部分。

double log (double); 以e为底的对数

double log10 (double);以10为底的对数

double pow(double x,double y);计算x的y次幂

float powf(float x,float y); 功能与pow一致,只是输入与输出皆为单精度浮点数

double exp (double);求取自然数e的幂

double sqrt (double);开平方根

5 、取整

double ceil (double); 取上整,返回不比x小的最小整数

double floor (double); 取下整,返回不比x大的最大整数,即高斯函数[x]

6 、绝对值

double fabs (double);求实型的绝对值

double cabs(struct complex znum);求复数的绝对值

7 、标准化浮点数

double frexp (double f,int *p); 标准化浮点数,f = x * 2^p,已知f求x,p (x介于[0.5,1])

double ldexp (double x,int p); 与frexp相反,已知x,p求f

8 、取整与取余

double modf (double,double*); 将参数的整数部分通过指针回传,返回小数部分

double fmod (double,double); 返回两参数相除的余数

9 、其他

double hypot(double x,double y);已知直角三角形两个直角边长度,求斜边长度

double ldexp(double x,int exponent);计算x*(2的指数幂)

double poly(double x,int degree,double coeffs []);计算多项式

int matherr(struct exception *e);数学错误计算处理程序

5.关于模取余

1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
using namespace std;

int main()
{
cout<<3%2<<endl;//1
cout<<-3%2<<endl;//-1
cout<<3%-2<<endl;//1
cout<<-3%-2<<endl;//-1
return 0;
}

多重for循环优化,提升运行效率

在循环次数较少的时候一般不会发现for循环的写法会对效率产生多大问题,但一旦循环次数较多,比如说上万,循环层数较多,效率问题就非常明显了,我是在做一个数据量非常大有三层for循环的项目的时候,为显示曲线出来太花费时间,客户体验会非常不好,才研究这个情况的,事实证明,优化后的多重for循环提升了一大半的效率,是不是很神奇。
当然,本文也有借鉴其他同胞的方法。

  1. 实例化变量放在for循环外,减少实例化次数,尽量只实例化一次;
  2. 普通变量 改为 寄存器变量
    i++ 改为 ++i
1
2
3
int i=0, j;
j=++i; //前置版本,运算对象先自增1,然后将改变后的对象作为求值结果,再赋值给j;
j=i++; //后置版本,先赋值给j;再运算对象自增1,但求值结果是运算对象改变之前那个值的副本.

C++Primer 中解释:前置版本的递增运算符避免了不必要的工作,它把值加1后直接返回改变了的运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容,如果我们不需要修改前的值,那么后置版本的操作就是一种浪费。

  1. for(int i = 0; i<50; i++)

循环条件使用<要快于<=,>和>=同理;

  1. 把外层可以计算的尽可能放到外层,减少在内层的运算,有判断条件的语句和与循环不相关的操作语句尽量放在for外面;

  2. 应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数;
    采用的是行优先访问原则,与元素存储顺序一致。

这里写图片描述

  1. 对于一个可结合和可交换的合并操作来说,比如整数的加法或乘法,
    我们可以通过将一组合并操作分割成 2 个或更多的部分,并在最后合并结果来提高性能。
    原理:
    普通代码只能利用 CPU 的一个寄存器,分割后可以利用多个寄存器。
    当分割达到一个数量时,寄存器用完,性能不再提升,甚至会开始下降。
    用代码来描述,如下:
1
2
3
4
5
6
7
8
9
10
// 一般情况下的代码 for (i = 1; i < n+1; i++)
{
res = res OPER i;
}

// 循环分割后代码for (i = 1; i < n; i+=2)
{
res1 = res1 OPER i;
res2 = res2 OPER (i+1);
}

int 整数加法,性能测试结果对比如下:
整数的加法,普通代码运行 26s,循环分割后,18s。
浮点数计算的性能提升,明显大于整数,乘法的性能提升,略大于加法。