网教网

搜索
查看: 123|回复: 1

给编程初学者编程习惯的十一条建议

[复制链接]

1

主题

5

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2023-3-3 22:05:08 | 显示全部楼层 |阅读模式
本文语言为 C++ ,编译器为 VS 2019
我所见很多没有被引导养成良好变成习惯的编程初学者,都或多或少有编程习惯的问题。
虽然这完全不影响程序的正常运行,但是良好的编程习惯会显著增强代码的可读性,以及降低后期维护的成本。代码可读性高,也有利于后人阅读和修改自己的代码。下面给新人一些编程习惯上的建议。
目录
养成缩进的好习惯
程序需要注释
大括号位置要统一
标识符不能随意起
循环变量的名称
语句中的空格
使用明确的括号,不要让人分析运算顺序
使用直观的赋值方式
以最新的标准为准(但考试的时候按照考试标准)
项目管理井井有条
错误自查的方法
<hr/>养成缩进的好习惯

//错误示范
#include <iostream>
using namespace std;
int main(){
size_t i, j;
   bool isPrime = false;
        for(i = 1; i <= 100; i ++)     {
        isPrime = true;
for(j = 2; j * j <= i; j ++)
                {
   if(i % j == 0)
                {
isPrime = false;
break;     }
                }
    if(isPrime)
                cout << i << endl;
}
return 0;
}
这是一个非常普通的判定 1 ~ 100 之间的素数的程序代码。阅读体验非常差。因为没有养成良好的缩进习惯。所谓「代码的缩进」,实际上就是位于同一级的代码统一向后推一列的做法。int main() 后的函数体隶属于 int main() ,所以统一向后推了 1 列。for、if 内的语句也是如此。
代码的缩进可以很好地展示代码之间的层级关系,有利于后期的维护,至少是在观感上比没有缩进或缩进不严格的代码要好得多:
//范例
#include <iostream>
using namespace std;

int main()
{
        size_t i, j;
        bool isPrime = false;
        //size_t 就是 unsigned int
        for(i = 1; i <= 100; i ++)
        {
                isPrime = true;
                for(j = 2; j * j <= i; j ++)
                {
                        if(i % j == 0)
                        {
                                isPrime = false;
                                break;
                        }
                }
                if(isPrime)
                        cout << i << endl;
        }
        return 0;
}
缩进的时候不需要打若干个空格,一般的编译器都会自动缩进。在需要手动调整缩进的时候,可以使用键盘左上角的 Tab 键缩进,或 Shift + Tab 取消一级缩进。
当然,对数量较少的语句,取消缩进也可以用 Backspace。



注意光标位置。接下来按下 Tab:



这是按下 Shift + Tab 的结果,接下来按下 Shift + Tab:



显然,Shift + Tab 取消了一级缩进。

这比手动敲若干个空格的好处是,Tab 和 Shift + Tab 可以对多个语句进行操作:



选中这一段语句,接下来按下 Tab:



按下 Tab 后,这部分语句统一缩进了一列,这比一行一行加空格高效多了;接下来按下 Shift + Tab:



显然,Shift + Tab 也可以对一段语句进行操作,这些语句统一取消了一级缩进。

程序需要注释

程序员阅读代码是一项复杂的工作。如果有的代码难以理解,最好加上注释。如上面素数的程序就使用了注释。注释也不一定是要写提醒的内容。注释也可用来暂时删除一段代码,非常方便。
不加注释有时候很可怕,如:
int a, b;
a = 5;
b = 6;
a = a ^ b;
b = a ^ b;
a = a ^ b;
这段代码不加注释难读得可怕。如果加上注释就不难理解:
int a, b;
a = 5;
b = 6;
//以下三行交换 a 和 b 的值
a = a ^ b;
b = a ^ b;
a = a ^ b;
//当然,现实生活中交换变量的值肯定不会这样写。
加注释并不是必须要手动输入 「//」或「/*  */」,编译器也提供了添加注释的快捷键。
DevC++ 的是:选中代码,按下「 Ctrl + , 」或者「Ctrl + .」或「Ctrl + /」。VS 需要自己设置。

大括号位置要统一

一般来说有两种大括号位置的写法:
//第一种
#include <iostream>
using namespace std;

int main()//大括号开头位于单独一行
{
        size_t i, j;
        bool isPrime = false;
        //size_t 就是 unsigned int
        for(i = 1; i <= 100; i ++)//大括号开头位于单独一行
        {
                isPrime = true;
                for(j = 2; j * j <= i; j ++)//大括号开头位于单独一行
                {
                        if(i % j == 0)//大括号开头位于单独一行
                        {
                                isPrime = false;
                                break;
                        }
                }
                if(isPrime)
                        cout << i << endl;
        }
        return 0;
}

//第二种
#include <iostream>
using namespace std;

int main(){//大括号在上一行的末尾
        size_t i, j;
        bool isPrime = false;
        //size_t 就是 unsigned int
        for(i = 1; i <= 100; i ++){//大括号在上一行的末尾
                isPrime = true;
                for(j = 2; j * j <= i; j ++){//大括号在上一行的末尾
                        if(i % j == 0){//大括号在上一行的末尾
                                isPrime = false;
                                break;
                        }
                }
                if(isPrime)
                        cout << i << endl;
        }
        return 0;
}
这两种风格没有优劣之分,但是要注意自己只能选择一种风格(至少在一个项目里使用一种风格),不要出现两种风格在一个项目里面交错的做法,这很让人头疼:
#include <iostream>
using namespace std;

int main(){
        size_t i, j;
        bool isPrime = false;
        //size_t 就是 unsigned int
        for(i = 1; i <= 100; i ++){
                isPrime = true;
                for(j = 2; j * j <= i; j ++)
                {
                        if(i % j == 0)
                        {
                                isPrime = false;
                                break;
                        }
                }
                if(isPrime)
                        cout << i << endl;
        }
        return 0;
}
标识符不能随意起

标识符(identifier)是指用来标识某个实体的一个符号,在不同的应用环境下有不同的含义。在计算机编程语言中,标识符是用户编程时使用的名字,用于给变量、常量、函数、语句块等命名,以建立起名称与使用之间的关系。标识符通常由字母和数字以及其它字符构成。
简单地说,标识符就是你定的各种名字。包括但不限于函数名、变量名。
初学者可能非常喜欢用 a, b, c...... aa, bb, cc.... fun1, fun2, fun3... 这种不知含义的变量名,只是图个方便。这是非常不好的习惯!
为什么我们不建议使用拼音作为变量名标识符的原因,主要是我们希望开发人员在开发、维护、重构的过程中能够尽量的从变量名中发现该变量的实际用途。

这要求我们,尽量的使用易于阅读、无二义性的词语。拼音作为汉字的音标,往往是一音多字,单单是 yi 这个拼音就能代表很多个字,比如一,比如义,再比如亿。我们往往在看到一串由拼音组成的字符串时需要经过一轮确定字形才能确定意思。再者拼音并不是世界通用语言,从多语言合作的角度而言使用汉语并不方便交流。

当然,无论如何使用各种命名方式取决于你自己在开发项目中自己的意向。如果你倾向于编写一个开源的项目的话,我建议从命名规范这一步开始规范项目开发。

* 不完全收录;作者:Acoris
点击前往上述言论来源
最好不要使用拼音,最好使用英文。比如学生姓名,你可以命名为 Student_Name ,不要命名为 xueshengxingming ,更不要是 a,甚至是 xsxm(Xue Sheng Xing Ming 的拼音简写)
当然,每次都输入长单词也很麻烦。我们可以使用一些单词的开头,比如 stu 就指学生,num 就指数字,prev 就指上一个。上述简写就是目前约定俗成的做法。也有其他的命名方法,比如 p 开头的一般是指针变量;也有一些命名规则,如 小驼峰、大驼峰。遵从这些做法,对你的程序可读性有很大帮助
上面我多次引用的输出素数的程序,isPrime 就是能够见名知意的变量名。

* 循环变量的名称

有人习惯循环一层层使用 i、j、k,也有人习惯用具体的名字,比如 stuIndex,Index_stu 等;行业上的程序员两种做法都有,但是不要写这样的程序:
for (i = 0; i < 6; i++)
{
        for (ii = 0; ii < i; ii ++)
        {
                for(iii = 0; iii < ii; iii ++)
                {
                        for(iiii = 0; iiii < i; iiii ++)
                        {  ...
这样 iiii...  下去,到最后自己也看不懂自己的程序。
需要提示新人的是,输入大写字母不必每次都按两次 CapsLock 切换大小写状态,按下 Shift 同时按下键盘上的字母就可以输入与当前输入状态相反的字母(比如当前是大写状态就能打出小写,小写状态就能打出大写)。

语句中的空格

前面所有案例都是有语句中的空格的。一般来说「1+1==2」和「1 + 1 == 2」对编译器没有什么不同,但是后者加了空格的式子显然更容易阅读:
size_t i,j;
bool isPrime=false;
//size_t就是unsigned int
for(i=1;i<=100;i++)
{
        isPrime=true;
        for(j=2;j*j<=i;j++)
        {
                if(i%j==0)
                {
                        isPrime=false;
                        break;
                }
        }
        if(isPrime)
                cout<<i<<endl;
}
这段代码的语句中没有在运算符两边插入空格,虽然也能运行,但是可读性不强。代码很密,看的时间长了会不想看下去。
我遵循的规则是 =、+、-、*(乘号)、/、==、!=、<=、>=、->、--、++、%、&、|、&&、|| 等两边都有空格。
这样一来,代码就成了这样,对比一下:
size_t i ,j;
bool isPrime = false;
//size_t就是unsigned int
for(i = 1; i <= 100; i ++)
{
        isPrime = true;
        for(j = 2; j * j <= i; j ++)
        {
                if(i % j == 0)
                {
                        isPrime = false;
                        break;
                }
        }
        if(isPrime)
                cout << i << endl;
}
这样的代码阅读起来很舒服。初学者可能不能理解(我当年也是,后来……真香),时间长了就会懂了。

使用明确的括号,不要让人分析运算顺序

考试可能会出这种恶心的 i+++++i+++++i+i 的未定义行为的分析。只是为了考运算符结合优先级,这种运算符多的式子最好不要这样写。如果非要写,最好写成 (i ++) + (++ i) + ( ++ ( ++ i)) + i。使用明确的括号表达你希望的计算顺序远比运算符自带的结合顺序要直观得多。
同理,((i << (5 + j) ) != true ) && (i == 7 || i != 5) 也是如此,加了括号,多直观。

使用直观的赋值方式

比如需要做一个最多容纳 500 个学生的学生管理系统,表格长度你设置为 500,你可以这么写:
for (stuIndex = 0; stuIndex < 500; stuIndex ++)
{ ... }
这样写没有问题,但是你在开头写一个更明确的 stuNum 会更直观和易于维护:
//程序开头
int stuNum = 500;

...

for (stuIndex = 0; stuIndex < stuNum; stuIndex ++)
{ ... }
这样程序更容易修改,那个突如其来的 500 也获得了解释。后人在阅读你的代码,也会容易理解。
就像判定素数的程序里,使用的是 isPrime = true 或者 isPrime = false 这样的语句。虽然我知道 true 和 false 的值是什么,可以直接写 isPrime = 1 或者 0,但是都不如 true 或者 false 直观。

以最新的标准为准(但考试的时候按照考试标准)

很多学校使用的教材都是谭浩强,或是以这本书为代表的老书。这本书介绍的很多函数,如 scanf,fopen,strcpy 都没有问题,但是都有安全隐患(请查阅「缓存区溢出攻击」等)。Visual Studio 默认禁止用户使用这些危险的函数,并建议使用 scanf_s,fopen_s、strcpy_s 这些安全的函数。最好去适应这些新做法。不是课本上的都是对的,而是 VC++ 6.0 已经过时了。
同样,有的老师会建议学生编写主函数一律使用「void」但实际上这是不合理的。主函数理应拥有返回值,回馈运行状态。这个返回值在将来会用到,最好使用带返回值的类型,如 int 。
我较为推荐 Visual Studio 作为编译器,它不愧为宇宙最强 IDE(IDE 即集成开发环境),当然其他编译器也是可以的,但是不要使用老旧的 VC++ 6.0 为代表的过时产品。

项目管理井井有条

不知道有多少人编写新项目的时候用默认的「未命名1」或「project1」做名字,甚至不知道项目?
一个项目对应一个程序。初学者所学的小型程序可能只对应一个文件,一个文件对应一个项目,因而懒得创建一个一个项目。这个习惯是很不好的。
一个商用的较大的程序肯定不会是一个人完成的。这群人肯定不能都把自己的代码放在一个文件里面,管理起来非常麻烦。因此,需要为整个项目创建一个文件夹,将这个项目的所有有关文件整整齐齐地放在这里。
编程初学者也应该养成这样良好的习惯,每次编写程序都创建一个工程,并把工程放在一个特定的,名字好好起而不是「新建文件夹」的文件里。
在你的电脑里,应该专门划分一个区域存放编程文件。



比如我就是存放在「E:\software engineer」

每个工程放在一个文件里。这样会让你的每个文件管理井井有条。
前面的素数程序是在项目「Test_for_ZHIHU」里的(项目名字也要好好起!)



所有「Test_for_ZHIHU」的文件都在这个文件夹里面

下面以 VS 2019 为例,简单讲述一下创建项目的过程:



从这里找到「新建项目」的选项,或者按下 Ctrl + Shift + N



初学者一般是选择「空项目」



注意红框的项目名称和项目位置。VS会自动在这个路径下创建一个以项目名称命名的文件夹作为你项目的文件夹。黄框的勾可勾可不勾。

上述操作结束后,就成功创建了一个项目。在项目里添加文件,可以在右侧的「解决方案资源管理器」里操作。



在黄色框内会显示项目名称;右键「解决方案资源管理器」的「源文件」,点击添加「新建项」,或者按下 Ctrl + Shift + A



根据自己的需要创建不同类型的文件就好了。创建 C / C++ 源文件一般是选择「C++ 文件」

错误自查的方法

通常,养成了上述良好的编程习惯,程序的错误会少并且好找些。编译不通过看错误报告也能改正,但是当遇到程序编译成功但是结果错误(或不出结果),不能总是问别人。下面介绍一一个几乎可以解决 99% 初学者的问题的方法 —— 中途输出。
中途输出的第一个用法:检查运行结果不正确或陷入死循环
这段代码出了问题,结果不正确。
for (i = 1; i <= 100; i++);
{
        isPrime = true;
        for (j = 2; j * j <= i; j++)
        {
                if (i % j == 0)
                {
                        isPrime = false;
                        break;
                }
        }
        if (isPrime)
                cout << i << endl;
}
看了半天找不到错误,运用中途输出的做法:
//这一段代码出现问题
for (i = 1; i <= 100; i++);
{
        //在这里中途输出 i 的值,便于检查 i 是否确实计算到了 100
        cout << "当前的 i = " << i << endl;
        isPrime = true;
        for (j = 2; j * j <= i; j++)
        {
                if (i % j == 0)
                {
                        isPrime = false;
                        break;
                }
        }
        if (isPrime)
                cout << i << endl;
}
运行程序,很容易发现只显示了一句「当前的 i = 1 」,说明循环并没有按要求执行;检查循环语句,发现第一个 for 括号后多了一个分号。问题解决。
类似地,当出现死循环的时候,可以从上往下输出不同的值(或字符串):
for (i = 0; i < 8; i++)
{
        cout << "AAA" << endl;
        for (j = 0; j < 50; j++)
        {
                cout << "BBB" << endl;
                for (k = 0; k = 50; k++)
                {
                        cout << "CCC" << endl;
                        ...
                }
        }
               
}
哪个陷入死循环,哪个循环的特征字符串(比如 AAA、BBB 和 CCC)就会不断输出。最后发现是不算输出「CCC」,说明最内层循环出错,检查发现循环条件写错,问题解决。
当循环到一半程序崩溃,可以采用循环的时候输出相关变量的值的做法。运行程序的时候就可以看到哪组数据会导致程序崩溃……中途输出的做法可以解决很多问题。
当然,类似地,程序运行到一半崩溃,也可以在一些可能导致程序崩溃的语句后输出对应的字符串检测程序是否执行到此行,这样就可以很方便地定位出错的位置。
本质上,「中途输出」就是检测程序中途运行情况的做法。实际上,编译器还会提供别的更好的工具,比如打断点调试等。这些初学者以后会了解的。
如果上述办法让你找到了出错位置但是不知道是什么原因,可以检查一下是否有这些错误:
1、for,while,函数定义等等,括号后面加了分号;
2、for 内循环变量输入错误,不一致,比如应该是 for(i = 0; i < 5; i ++) 写成了 for(i = 0; j < 5; i ++)
3、scanf 忘记取地址
4、前面正在运行的程序忘记关闭
5、忘记句尾分号
6、成对括号,如大括号,中括号或小括号不匹配
7、使用指针访问只读区
8、字符串结尾没有 \0 导致访问越界
<hr/>这个总结的错误和给新人的建议,来自我无数次给别人人工 debug 的结果。
希望看这篇文章的新人成功养成编程的良好习惯~

回复

使用道具 举报

1

主题

2

帖子

3

积分

新手上路

Rank: 1

积分
3
发表于 2023-3-3 22:05:57 | 显示全部楼层
椽子太聚了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表