如何写出好的代码注释

如何写出好的代码注释

文章目录

一、引言1. 什么是代码注释?行内注释块注释文档注释特殊注释

2. 为什么要写代码注释?

二、注释的原则1. 简洁明了2. 写明意图3. 与代码保持一致

三、逻辑清晰、命名规范的代码胜于混乱和冗余的注释四、结论五、参考博文

一、引言

1. 什么是代码注释?

代码注释(以下简称注释)是嵌入在代码中的解释性文本,它们一般不会被编译或执行,但可以提供给开发者一些有用的信息。

不同编程语言的注释格式可能不同,本文使用C语言来介绍。

C语言有两种注释格式:

// 这是一段注释

/* 这也是一段注释 */

实际项目中的注释根据其形式和作用的不同,大体可以分为以下四类

行内注释

int a = 5; // 这是一个行内注释

// 这是一个行内注释

int a = 5;

块注释

/*

* 这是一个块注释

* 块注释可以跨越多行

*/

int b = 10;

文档注释

/**

* @brief 这是一个计算一个数的平方的函数

* @param x 要平方的数

* @return 输入数的平方

*/

int square(int x) {

return x * x;

}

文档注释可以有多种格式,这个例子使用的是Doxygen文档注释格式,它可以将程序中的注释转换成说明文档或者API参考手册,减少手动整理文档的时间

Doxygen格式文档参考链接:

官方文档: Doxygen Document

中文介绍: 代码注释规范之Doxygen

特殊注释

在开发过程中,我们常会用到某些特定的标记(如TODO、FIXME、NOTE等),它们可以帮助开发者管理和记录代码中的任务、问题和重要信息,我们可以通过全局搜索TODO、FIXEME这样的标记来了解代码中还有哪些地方没有完善。

int processData() {

// NOTE: 这是一个临时解决方案,之后会对其进行重构

// TODO: 增加异常处理功能

// FIXME: 优化循环性能

for (int i = 0; i < 1000; i++) {

// 一系列代码

}

}

2. 为什么要写代码注释?

代码的读者有两种:一种是编译器、解释器等计算机程序,另一种是开发者。编译器和解释器只关注代码本身,只要代码的语法正确,它们就能“读懂“代码,而我们人类开发者则不同,即使有语法分析工具帮助确保语法正确无误,当代码逻辑较复杂是,阅读别人写的代码,甚至自己写的代码,仍然要花费很多时间去理解。

注释的主要作用是增强代码的可读性、可维护性和可理解性。程序员可以通过注释快速理解代码的意图、功能和实现细节等。在团队合作开发或需要长时间维护的项目中,注释扮演着十分重要的角色,而坏的注释反而会带来负面影响。

例如,这里有一个没有注释的函数(并且变量和函数命名极其随意),你能一眼看出它的作用吗:

void c(int* a, int n, int* t, double* avg, int* h, int* l) {

*t = 0;

*h = a[0];

*l = a[0];

for (int i = 0; i < n; i++) {

*t += a[i];

if (a[i] > *h) {

*h = a[i];

}

if (a[i] < *l) {

*l = a[i];

}

}

*avg = (double)(*t) / n;

}

然后是加了注释后的代码:

/**

* @brief 计算学生成绩的总分、平均分、最高分和最低分

*

* @param a 存储学生成绩的数组

* @param n 数组的大小,即学生人数

* @param t 指向总分变量的指针

* @param avg 指向平均分变量的指针

* @param h 指向最高分变量的指针

* @param l 指向最低分变量的指针

*/

void c(int* a, int n, int* t, double* avg, int* h, int* l) {

*t = 0; // 初始化总分为0

*h = a[0]; // 初始化最高分为第一个学生的成绩

*l = a[0]; // 初始化最低分为第一个学生的成绩

// 遍历所有学生的成绩

for (int i = 0; i < n; i++) {

*t += a[i]; // 累加当前学生的成绩到总分

if (a[i] > *h) { // 更新最高分

*h = a[i];

}

if (a[i] < *l) { // 更新最低分

*l = a[i];

}

}

// 计算平均分

*avg = (double)(*t) / n;

}

这样看下来是不是容易懂多了,当然这段代码还是有很大的优化空间,通过以下提到的注释原则,可以很大程度上减少冗余的注释,让代码更加简洁明了。

二、注释的原则

在写代码注释时,最好遵守以下原则:

简洁明了写明意图与代码保持一致

1. 简洁明了

注释一般不会被编译或执行,因此注释尽量做到简明扼要,直击要点,不要过度注释,只需要注释那些复杂或不易理解的部分。

注意事项:

不要对每句代码都进行解释,这样不仅浪费时间,而且除了增加阅读量外毫无意义。如果你发现无法为某段代码写出简洁的注释,很可能是代码逻辑不够清晰,此时需要考虑对代码进行重构

现在对上面提到的代码的注释再进行优化

/**

* @brief 计算学生成绩的总分、平均分、最高分和最低分

*

* @param scores 存储学生成绩的数组

* @param size 数组的大小,即学生人数

* @param total 指向总分变量的指针

* @param average 指向平均分变量的指针

* @param highest 指向最高分变量的指针

* @param lowest 指向最低分变量的指针

*/

void calculateScores(int scores[], int size, int* total, double* average, int* highest, int* lowest) {

*total = 0;

*highest = scores[0];

*lowest = scores[0];

// 遍历所有学生成绩,并计算总分、最高分、最低分

for (int i = 0; i < size; i++) {

*total += scores[i];

if (scores[i] > *highest) {

*highest = scores[i];

}

if (scores[i] < *lowest) {

*lowest = scores[i];

}

}

// 计算平均分

*average = (double)(*total) / size;

}

具有良好命名方式和清晰的逻辑的代码本身就可以提供很好的可读性,因此,为了使注释简洁,养成良好的命名习惯和编写清晰代码逻辑是非常重要的。

2. 写明意图

行内注释或块注释的主要作用是提供代码的意图而非实现细节。对于开发人员来说,注释应该说明这段代码的目的或作用,而具体的实现细节应放在文档注释中。

行内注释和块注释:应重点说明代码的意图,帮助开发人员理解代码的作用。

文档注释:用于详细解释代码的实现细节和工作原理。

继续回到之前那个例子,我们可以将计算总分、最高分和最低分的实现细节写到文档注释当中

/**

* @brief 计算学生成绩的总分、平均分、最高分和最低分

*

* 这个函数遍历输入的学生成绩数组,计算总分、最高分和最低分,

* 循环结束后,通过total / size 计算平均分,最后将结果通过指针参数返回。

*

* @param scores 存储学生成绩的数组

* @param size 数组的大小,即学生人数

* @param total 指向总分变量的指针

* @param average 指向平均分变量的指针

* @param highest 指向最高分变量的指针

* @param lowest 指向最低分变量的指针

*/

3. 与代码保持一致

确保注释随代码的变化而变化,避免注释和代码不一致。如果注释不能与代码保持一致,随着时间的推移,注释可能会误导开发者,从而影响代码的可读性和维护性。因此,良好的编程习惯之一就是在修改代码时,同时更新相关的注释,确保注释始终对阅读代码有帮助而不是造成困惑。

三、逻辑清晰、命名规范的代码胜于混乱和冗余的注释

代码与注释是相辅相成的,只有二者都恰到好处,才能让开发者更高效地理解代码。

逻辑清晰

逻辑清晰的代码更易于理解和维护。这样的代码不仅减少了对注释的依赖,还能使开发者更快地定位和修复问题。

模块化设计:将代码分成小而独立的模块或函数,每个模块或函数只负责一项特定的任务

void validateOrder(Order order) {

// 验证订单

}

void updateInventory(Order order) {

// 更新库存

}

void sendConfirmationEmail(Order order) {

// 发送确认邮件

}

void processOrder(Order order) {

validateOrder(order);

updateInventory(order);

sendConfirmationEmail(order);

}

简化控制流程:保持代码结构简单,避免深层嵌套的条件和循环。使用提前返回的方式来减少嵌套层级。

具体方法可以参考这个视频:

Bilibili: 【代码美学】为何要成为“不嵌套主义“YouTube: Why You Shouldn’t Nest Your Code

命名规范

命名规范可以显著提高代码的可读性和理解性。好的命名能直观地反应变量、函数或类地用途和功能,使代码自解释,减少对注释的依赖。

有意义的命名:使用描述性的名称,使变量、函数或类的用途一目了然

// 不推荐

int a = 10; // 被减数

int b = 5; // 减数

int c = a - b; // a - b的结果

// 推荐

int minuend = 10;

int subtrahend = 5;

int difference = minuend - subtrahend;

一致的命名约定:遵循团队或项目的命名约定,如使用小驼峰命名法(camelCase)或下划线命名法(snake_case)。

// 不推荐(混合命名约定)

int first_number = 5;

int SecondNumber = 10;

// 推荐(统一命名约定)

int firstNumber = 5;

int secondNumber = 10;

避免缩写和冗长:避免使用晦涩难懂的缩写(除非是公认的),名称不应过长,保持简洁但有意义。

// 不推荐

int num = 5;

int MaximumCharacterLimitForUsername = 15;

// 推荐

int userCount = 5;

int maxUsernameLength = 15;

这里两个视频讲的更详细点:

Bilibili:

【代码美学】在代码中取名【代码美学】勿写注释 YouTube:

Naming Things in CodeDon’t Write Comments

四、结论

良好的代码注释和清晰易读的代码风格相辅相成,编写代码时,最好遵循一定的原则,以便后续能更高效地理解和维护代码,减少因阅读源代码浪费的时间。

五、参考博文

Code comment best practices every developer should know

How to Comment Your Code Like a Pro: Best Practices and Good Habits

Best practices for writing code comments

相关推荐

国产车大厂吉利、奇瑞、长安、长城,哪家发动机更好用?
DNF史诗装备获取全攻略:数据支撑,案例实证,权威玩家怎么说?
夫妻冷战多久不回微信才算严重?解读冷战背后的沟通危机