Featured image of post 一种实用的 C/C++/C# 代码注释方法

一种实用的 C/C++/C# 代码注释方法

按一个键就可以注释一大段代码,再按一次就可以取消,不觉得很有趣吗?

刚考完复变函数与积分变换,有一个星期可以喘息一下了。 我闲得没事干,就开始在 Microsoft Learn 上学习 C#。

# 普通的注释方法怎么你了?

和以前学习 C 和 C++ 一样,我又回到了那个照着例程抄写、反复调试运行的过程。 这里面将代码注释掉是一个常见的操作。 一般来说,我们要想注释一行 C 语言风格的代码(对 C/C++/C# 都适用),常用 //

1
// printf("Hello, world!\n");

如果是一大段的代码,我们常用 /* */

1
2
3
4
5
6
7
/*
int main()
{
    printf("Hello, world!\n");
    return 0;
}
*/

但是这两种方法都有一定的不方便的地方。 首先,对于第一种注释方法,即使 VS Code 有 Ctrl+/ 快捷键,但是每一行都要加上 // 还是有点麻烦。 而且对于像我这种书写格式强迫症来说,当我看到以下第 2 行的注释时,我会忍不住给它加个空格变成第 1 行的样子:

1
2
// printf("Hello, world!\n");
//printf("Hello, world!\n");

其次,对于第二种注释方法,一旦在一个文件里用得太多了,往往会使代码变得非常难看。

1
2
3
4
5
6
7
8
/*
int main()
{
    printf("Hello, world!\n");
    return 0;
}
*/
// ↑ 在一个 } 下面又是一个 */,让函数看上去非常不舒服

而且要想删掉这段注释,就必须既删掉这段注释的开头 /*,又要删掉注释的结尾 */,否则整个文件都会出问题,非常麻烦。

最近我又在一些库文件中看到了另一种注释方法,那就是利用 ifendif

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#if 0 // 以下代码块不会出现在编译后的文件中
int main()
{
    printf("Hello, world!\n");
    return 0;
}
#endif

#if 1 // 激活以下代码块
int main()
{
    printf("Hello, world!\n");
    return 0;
}
#endif

这种方法通过在 if 0if 1 之间切换来实现代码块的禁用和激活,严格意义上来说不属于注释语法,

# 一种实用的注释方法

这个时候我想起来了过去在 HITSZ-VEX 程序组培训时,Longbin 教给我们的一种注释方法。 它的内容是,在要注释的代码块开头和结尾都添加一行 //*/

1
2
3
4
5
6
//*/
int a = 0;
int b = 1;
printf("%d + %d = %d\n", a, b, a + b);
printf("Hello, world!\n");
//*/

在这种情况下,代码仍是有效的。 如果想要注释掉这段代码,只需将第一个 //*/ 改为 /*/

1
2
3
4
5
6
/*/ <- 只删掉了一个 '/'
int a = 0;
int b = 1;
printf("%d + %d = %d\n", a, b, a + b);
printf("Hello, world!\n");
//*/

如果想要再次激活这段代码,那么把 /*/ 改回 //*/ 就行了。

# 它的原理是什么?

原理很简单,就是利用了 C 语言的注释规则。

在 C/C++/C# 中,// 之后的内容都会被视为注释,直到这一行的结尾。 而 /* 之后的内容都会被视为注释,直到遇到 */

所以只需要一张图就可以解释这种注释方法的原理:

principle

下面我来介绍一下这种注释方法的几个用处。

# 有什么用呢?

# 学习 C/C++/C#

初学一种编程语言时,我们往往会照着例程抄写,然后反复调试运行。

例如我正在学习 C# 的逻辑运算,Microsoft Learn 上的培训给了若干示例代码。 我想把它们都记录下来,但测试运行效果时,我只想运行我最新抄写下来的那一段代码,因此我的文件看起来是这样的:

 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
/*/ 相等运算符
Console.WriteLine("a" == "a");
Console.WriteLine("a" == "A");
Console.WriteLine(1 == 2);

string myValue = "a";
Console.WriteLine(myValue == "a");
//*/

/*/
string value1 = " a";
string value2 = "A ";
Console.WriteLine(value1.Trim().ToLower() == value2.Trim().ToLower());
//*/

/*/ 比较运算符
Console.WriteLine(1 > 2);
Console.WriteLine(1 < 2);
Console.WriteLine(1 >= 1);
Console.WriteLine(1 <= 1);
//*/

//*/ 返回布尔值的方法
string pangram = "The quick brown fox jumps over the lazy dog";
Console.WriteLine(pangram.Contains("fox"));
Console.WriteLine(pangram.Contains("cow"));
//*/

当我想要回头测试前面的代码块时,我只需修改两处地方,一个是把要激活的代码块的 /*/ 改为 //*/,另一个是把要禁用的代码块的 //*/ 改为 /*/

概括下来,只需一次删除操作和一次添加操作,就可以实现代码块的切换。

读者可以设想或实验一下使用普通的注释方法 ///* */,以上的内容要怎么写才能达到同样的效果、至少会使用到多少次删除和添加的操作。

懂了吧?😏

# 调试程序

无论是机器人制作还是嵌入式开发,我们往往会对一些顺序执行的流程进行调试。

例如我曾经还在 HITSZ-VEX 的时候,我们的机器人自动程序大概是这样的:

 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
void autonomous() {
    //* ----- Begin -----
    SomeNamespace::SomeClass::SomeFunction();
    SomeNamespace::SomeTask *task = new SomeNamespace::SomeTask(someParams);
    // Some other code
    //*/

    //*/ ----- Stage 1 -----
    function1();
    ...
    //*/

    //*/ ----- Stage 2 -----
    function2();
    ...
    //*/

    //*/ ----- Stage 3 -----
    funtion3();
    ...
    //*/

    //*/ ----- End -----
    // 一组动作
    delete task;
}

我们在开发调试时,经常需要只测试其中的某个 stage,或者暂时跳过某些 stage,那么只需在 //*/ ----- Stage x -----/*/ ----- Stage x ----- 之间进行切换。 因此这种注释方法就能在尽可能减少对代码的破坏的同时,实现高效率的调试。

# 总结

总结一下,本文介绍了一种 C/C++/C# 风格的注释方法

1
2
3
4
5
6
7
//*/ 一行或多行激活的代码
int a = 0;
//*/

/*/ 一行或多行被注释的代码
int b = 0;
//*/

这种注释方法有以下几个优点:

  • 只需少量操作就可以实现代码块的注释与取消注释
  • 由于对注释的增删操作是在代码所在行之外进行的,所以不容易破坏代码块的内容
  • 美观,可以在 //*/ 后书写额外的内容,方便对代码块进行标注

希望读者能够寻找到这种注释方法的更多应用场景,为自己的 C/C++/C# 学习和开发带来便利。

LICENSED UNDER CC BY-NC-SA 4.0
Maxwell Jay at his most tender.
使用 Hugo 构建
主题 StackJimmy 设计