C语言:写好代码减少debug

目录

一、如何写好代码减少debug

以编写图像Mat矩阵的基本操作为例

要求:

一些建议:

二.示例


一、如何写好代码减少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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值