目录
一、如何写好代码减少debug
以编写图像Mat矩阵的基本操作为例
要求:
1.全部使用C语言利用struct定义矩阵数据结构而不是class
2.创建一个矩阵
3.删除一个矩阵
4.拷贝矩阵
5.两个矩阵相乘
一些建议:
1.不要使用int定义mat.cols和mat.rows,改用size_t,在64位操作系统上是64位无符号整数,表达的数据范围更大,使用int矩阵最大不超过2GB×4(8GB),而且行数或列数超过了会越界
2.不要使用双重for循环拷贝图像数据,效率低下,建议使用memcpy()
3.不要用二维数组表达图像数据,现申请一个一维数组,里面的元素是float类型的指针,一行一行把数据申请下来,要申请行数加1次内存
不好:
1.很多次malloc,就要很多次free,出错概率增加,内存泄漏概率增加
2.这样申请的内存是不保证连续的,内存不连续读数据的效率会降低
3. memcpy()要拷贝行数加1次
建议:使用1维数组一次把内存申请下来,而且数据连续,自己去管理哪些数据在第一行,哪些在第二行,效率更高
4.在循环体里有多重复计算(有例子),核心代码要想有没有好的优化方式
5.写一个函数,函数体里第一件事就是参数检查(空指针,矩阵维数是否匹配)
6. 不要将创建矩阵函数与文件I/O绑定。不好通用和移植
7.文件名的规范,建议所以都是小写字母,在非Windows操作系统下对文件名大小写字母敏感
8.代码的改进提升是无止境的
二.示例
头文件:结构体的定义,函数的声明
matrix.h
#ifndef _MATRIX_H
#define _MATRIX_H//解决头文件重复包含的问题,第二次包含这个条件不成立,第二次就不会包含进去
#include <stdbool.h> //for bool
typedef struct Matrix_{ //use typedef to simplify type name//c语言中(struct Matrix_)是类型名
size_t rows; // use size_t, not int
size_t cols; // use size_t, not int
float * data;
} Matrix;
Matrix * createMat(size_t rows, size_t cols);//创建矩阵
bool releaseMat(Matrix * p);//释放矩阵
bool add(const Matrix * input1, const Matrix * input2, Matrix *output);//矩阵加法
#endif
matrix.c
#include <stdlib.h>
#include <stdio.h>
#include "matrix.h"
// return NULL if failed
Matrix * createMat(size_t rows, size_t cols)
{
Matrix * p = NULL;//创建对象的指针初始化
//参数检查
if(rows == 0 || cols == 0)
{
fprintf(stderr, "rows and/or cols is 0.\n");// stderr ,linux管道,通过pipline导向不同的文件
return NULL;
}
// allocate memory
p = (Matrix *) malloc(sizeof(Matrix));//一定是sizeof(结构体),结构体大小可能大于成员变量
if( p == NULL )
{
fprintf(stderr, "Failed to allocate memory for a matrix.\n");
return NULL;
}
p->rows = rows;
p->cols = cols;
//第二次内存申请,有更高的概率失败(可能内存泄漏导致)
p->data = (float*) malloc( p->rows * p->cols * sizeof(float));
if(p->data == NULL)
{
fprintf(stderr, "Failed to allocate memory for the matrix data.\n");
//不能退出,不然会内存泄漏
free(p); //Don't forget to free memory here!
return NULL;
}
return p;
}
bool releaseMat(Matrix * p)
{
//don't forget to check a pointer before using it
//这个检测不能检查出所以错误,如何p是随机值,那么将无法判断是有效地址还是无效地址,所以p一定要初始化null
if (!p) return false;
if(p->data) free(p->data);
free(p);
return true;
}
// Matrix * output
1.关于输出矩阵,进加法之前就创建准备好,不要再加法函数里面内存管理,不然逻辑混乱
2.不将output作为返回值返回,不然只能在内部创建,出错也不能判断
bool add(const Matrix * input1, const Matrix * input2, Matrix *output)
{
// You much check all parameters carefully first
// It's important, and can save a lot of time on debugging
//参数检查
if(input1 == NULL)
{
//use stderr for error messages
fprintf(stderr, "File %s, Line %d, Function %s(): The 1st parameter is NULL.\n", __FILE__, __LINE__, __FUNCTION__);
// __FILE__, __LINE__, __FUNCTION__宏
//__FILE__字符串,文件名
//__LINE__这个宏所在的地方
//__FUNCTION__函数名
return false;
}
else if(input1->data == NULL )
{
fprintf(stderr, "%s(): The 1st parameter has no valid data.\n", __FUNCTION__);
return false;
}
if(input2 == NULL)
{
fprintf(stderr, "File %s, Line %d, Function %s(): The 2nd parameter is NULL.\n", __FILE__, __LINE__, __FUNCTION__);
return false;
}
else if(input2->data == NULL )
{
fprintf(stderr, "%s(): The 2nd parameter has no valid data.\n", __FUNCTION__);
return false;
}
if(output == NULL)
{
fprintf(stderr, "File %s, Line %d, Function %s(): The 3rd parameter is NULL.\n", __FILE__, __LINE__, __FUNCTION__);
return false;
}
else if(output->data == NULL )
{
fprintf(stderr, "%s(): The 3rd parameter has no valid data.\n", __FUNCTION__);
return false;
}
//矩阵尺寸检查
if( input1->rows != input2->rows || input2->rows != output->rows ||
input1->cols != input2->cols || input2->cols != output->cols)
{
fprintf(stderr, "The input and the output do not match. They should have the same size.\n");
fprintf(stderr, "Their sizes are (%zu, %zu), (%zu, %zu) and (%zu, %zu)\n",
input1->rows, input1->cols,
input2->rows, input2->cols,
output->rows, output->cols);
return false;
}
//version 1, the best one,只有一个for循环
size_t length = input1->rows * input1->cols;//矩阵所有元素个数
//矩阵数据首地址
const float * p1 = input1->data;
const float * p2 = input2->data;
float * p3 = output->data;
//相互之间没有依赖操作,可以并行操作,编译器可能会使用SIMD指令去并行操作0
for(size_t i = 0; i < length; i++)
*(p3++) = *(p1++) + *(p2++);
//version 2
for(size_t r = 0; r < input1->rows; r++)
{
// to calculate (cols * r) here, don't put it into the inner loop
//计算每一行的首地址
const float * p1 = input1->data + input1->cols * r;
const float * p2 = input2->data + input2->cols * r;
float * p3 = output->data + + output->cols * r;
//计算量向外移
for(size_t c = 0; c < input1->cols; c++)
*(p3++) = *(p1++) + *(p2++);
}
//version 3, 差,双重循环
for(size_t r = 0; r < input1->rows; r++)
{ // ->cols * r + c一层循环就要做3次运算,效率低
for(size_t c = 0; c < input1->cols; c++)
output->data[output->cols * r + c] =
input1->data[input1->cols * r + c] +
input2->data[input2->cols * r + c];
}
return true;
}
c文件:函数的实现
main.c
#include <stdio.h>
#include "matrix.h"
int main()
{
Matrix * matA = createMat(2, 3);
Matrix * matB = createMat(2, 3);
Matrix * matC = createMat(2, 3);
Matrix * matD = createMat(3, 2);
Matrix * matNULL = NULL;
//initialization
//You should have your own method to do it
//非完整测试
matA->data[3] = 2.3f;
matB->data[3] = 3.1f;
if(! add(matA, matB, matC))
fprintf(stderr, "Matrix addition failed.");
else
{
//You can have a better method to show the results
printf("result=%f\n", matC->data[3]);
}
//more tests
//尺寸不匹配测试
add(matA, matB, matD);
//空指针测试
add(matNULL, matB, matC);
return 0;
}