编程中的代码维护、可移植性与安全性
代码可维护性与注释技巧
代码的可维护性并非自然而然形成,即使是面向对象的代码,也需要程序员付出额外努力才能具备良好的可维护性。在编写代码时,注释是一项重要的工作,但也有其原则和技巧。
大多数编程风格指南都源于一个基本规则:如果能用代码表达,就不要用注释。当代码能清晰传达注释的核心内容时,应省略注释。不过,这并不意味着完全不写注释,而是要把注释留给那些无法用代码清晰表达的重要思想。
注释应聚焦于解释代码的功能和目的,而非实现方式。通常代码本身应能说明其工作原理。例如,为每个函数、类或模块添加注释时,应解释其功能而不涉及实现细节。若无法在不解释实现方式的情况下说明程序单元的功能,这可能是该单元缺乏良好内聚性的警示信号。
以一个 C 函数
compress
为例:
size_t compress(char *s, size_t n, char c);
可以这样注释:
'compress' removes all occurrances of character 'c' from character
array 's' of length 'n,' and shifts the remaining characters of 's' left
(to lower addresses) to fill in the gaps. 'compress' returns the length of
the compressed array, that is, 'n' minus the number of characters removed.
注释应至少提及每个函数参数一次,最好按照参数在声明中的顺序。若顺序不一致,可调整参数列表或重写注释。
在代码审查中,协调参数列表和注释是一个需要简要讨论的事项。有些项目使用标准的注释模板,虽能提醒程序员注释应涵盖的主题,但对于大多数函数来说,固定模板并不适用,精心撰写的段落注释才是更好的选择。
注释的详细程度应适中,为同行提供足够的背景信息以便阅读代码,避免将注释写成教程。若读者需要更多细节,可提供相关教程的参考资料。
代码可移植性的提升方法
C 语言应用广泛,既可以作为高级语言编写易于跨平台移植的程序,也能作为低级语言与汇编语言竞争以操作硬件。然而,C 代码并非天生具有可移植性,需要程序员付出一定的思考和努力。
一般来说,在其他条件相同的情况下,可移植代码优于非可移植代码。应避免编写不必要的平台特定代码,当有同样优秀的平台无关代码可完成相同任务时,应优先选择后者。
以下是一些在 C 代码中提升可移植性的方法:
1.
定义
main
函数的正确形式
:应将
main
函数的头部定义为
int main(void)
或
int main(int argc, char *argv[])
,这是 C 标准明确认可的形式。其他常见形式如
void main(void)
仅在部分平台上可用。
2.
使用标准的程序退出代码
:使用 0(或
EXIT_SUCCESS
)和
EXIT_FAILURE
作为程序退出代码。调用
exit(0)
是可移植的,而
exit(1)
不是,应使用
EXIT_FAILURE
(在
<stdlib.h>
中定义)来表示失败。
main
函数中的
return
语句实际上相当于调用
exit
,因此
main
通常应返回 0 或
EXIT_SUCCESS
。
3.
使用
size_t
类型
:使用
size_t
(在多个标准头文件中定义)作为存储对象大小的类型。例如,标准
memcmp
函数的第三个参数和
strlen
函数的返回类型都是
size_t
。
size_t
始终是无符号整数类型,比使用
unsigned long int
更合适,因为在某些架构上,长整数可能会造成浪费。
4.
存储指针差值使用
ptrdiff_t
类型
:在极少数情况下,需要存储两个指针的差值时,应将其存储在
ptrdiff_t
类型的对象中(在
<stddef.h>
中定义)。每个实现都会将
ptrdiff_t
定义为适合目标架构的有符号整数类型。
5.
使用
offsetof
宏访问结构体成员
:若需要按相对偏移量访问结构体成员,可使用
offsetof
宏(在
<stddef.h>
中定义)。
offsetof(t,m)
返回一个
size_t
类型的常量表达式,表示成员
m
相对于结构体
t
起始位置的字节偏移量。例如:
struct date {
month mm;
unsigned char dd;
unsigned int yy;
};
size_t n = offsetof(date, yy);
在 C++ 中,
offsetof
不一定适用于类对象,可使用成员指针代替。
构建安全代码的策略
设计和实现安全的软件系统可能很复杂,但通过一些简单直接的方法可以取得很大的改进。以下是构建安全代码的一些策略:
消除常见的安全漏洞
- 处理输入溢出和缓冲区溢出问题 :输入语句常假设固定的最大输入长度,但未进行有效限制。例如,将输入读入一个 128 字符的数组,若发现错误就增加数组长度,这并不能真正解决问题。更安全的做法是对过大的输入进行适当的错误处理,无论数组大小如何,都要检查输入长度并正确处理错误。示例代码如下:
#define SIZE 1024
char x[SIZE];
...
read (0, x, 1023);
若后续修改
SIZE
使其变小,可能会导致输入溢出。
2.
避免过度依赖命令解释器调用外部程序
:通过命令解释器运行外部程序虽然方便,但存在风险。开发人员常因懒惰选择这种方式,这可能导致安全问题。例如,在 Perl 脚本中:
system(glimpse −H /database −y −i $Close $WordPart $d
'$string;$string2;$string3' | nor $dir");
若变量包含用户输入,可能会被恶意利用。
3.
去除不必要的功能
:避免为用户提供不必要的功能,如在 Word 中提供 Visual Basic 文件删除功能,这可能是导致病毒传播的原因之一。应禁用危险功能或在解释输入前进行检查。
4.
避免无意义的可执行内容
:不要为简单任务使用 Java、ActiveX 等通用功能,这些功能可能对用户没有实际益处,还可能危及安全。例如,网站等待鼠标移动到 logo 上才显示菜单的功能,就没有实际价值。
5.
加强人员信息保护培训
:程序员应了解常见的软件安全漏洞,避免因无知导致安全问题。例如,两个程序同时打开同一文件而未进行适当的文件锁定,可能会导致文件状态不一致;检查密码时按字符逐个检查,可能会被黑客利用决策时间猜出密码。
提供快速响应机制
- 加密和认证基于互联网的软件更新 :通过互联网更新软件是最快且低成本的方式,但要确保更新的安全性。应提供强加密以保护商业机密,强认证以防止用户安装错误的更新。
- 在影响客户前发现并修复错误 :将改进作为支持合同的一部分,及时修复产品中的错误,提高产品质量,让客户满意。
- 启用漏洞报告和快速响应 :鼓励客户报告漏洞,及时修复并告知客户。对于安全漏洞,应尽快提供修复方案。
采取合理谨慎的措施
- 发布前进行独立保护审计 :若产品具有安全特性,应让专业人员进行审查和测试,避免因安全漏洞导致尴尬局面。
- 在软件更新过程中使用良好的变更控制 :建立标准的检查流程,比较不同版本的差异,避免因优先级变更等问题导致系统崩溃。
- 提供安全的制造和分发机制 :在产品发货前,从生产线抽取样本与测试阶段的原始源进行比较,并使用标准测试套件重新测试,确保产品内容未被损坏。
- 跟踪错误并找出根本原因 :通过跟踪错误,找出问题的根源,以便在未来消除这些错误。
建立有效的 Beta 测试流程
- 内部 Beta 测试应积极寻找漏洞 :组建专业的安全测试团队,尽早发现安全漏洞,因为在现场修复安全漏洞的成本要比在发布前高得多。
- 使用随机输入测试 :像让两岁孩子随意操作程序一样,向程序输入随机数据,观察是否会导致崩溃,这可能是安全漏洞的迹象。
- 构建可重复的测试能力 :确保能够在相同条件下重复安全测试,以确定是否真正修复了漏洞。若无法购买可重复的测试流程,可自行构建。
- 吸引外部社区参与 :若自身无法进行充分的安全测试,可吸引外部人员帮助寻找漏洞,尽管他们的效率可能不如内部团队,但总比没有要好。
坚持改进,抓住机遇
不要轻易放弃解决安全问题,即使产品的安全性无法做到无懈可击,也应足够强大以在市场中竞争。通过持续改进产品质量来增强安全性,避免因版本控制问题撤销已修复的漏洞。同时,不要忽视“防御性编程”的重要性,要在各个阶段检查假设,避免因他人的错误导致自己的安全漏洞。
在当今时代,安全是一项有价值的资产,不安全则可能导致企业陷入困境。将安全软件视为超越竞争对手的机会,打造具有更低生命周期成本、更高投资回报率且无负面报道的优质产品。
编程中的代码维护、可移植性与安全性
关键要点总结
为了更清晰地呈现前面提到的各项编程要点,下面通过表格进行总结:
| 编程方面 | 具体要点 | 详细说明 |
| ---- | ---- | ---- |
| 代码可维护性 | 注释原则 | 能用代码表达的就不用注释,注释用于解释代码功能和目的,避免解释实现方式 |
| | 函数注释示例 | 如
compress
函数注释,应提及所有参数并尽量按声明顺序 |
| | 注释模板 | 固定模板不适合多数函数,应使用精心撰写的段落注释 |
| | 注释详细程度 | 为同行提供足够背景信息,避免写成教程 |
| 代码可移植性 |
main
函数定义 | 应定义为
int main(void)
或
int main(int argc, char *argv[])
|
| | 程序退出代码 | 使用 0(或
EXIT_SUCCESS
)和
EXIT_FAILURE
|
| |
size_t
类型使用 | 用于存储对象大小,比
unsigned long int
更合适 |
| |
ptrdiff_t
类型使用 | 存储两个指针的差值 |
| |
offsetof
宏使用 | 按相对偏移量访问结构体成员,C++ 中类对象可使用成员指针代替 |
| 代码安全性 | 消除常见漏洞 | 处理输入溢出、避免依赖命令解释器、去除不必要功能等 |
| | 快速响应机制 | 加密认证更新、提前修复错误、启用漏洞报告 |
| | 合理谨慎措施 | 发布前审计、变更控制、安全分发、跟踪错误根源 |
| | Beta 测试流程 | 内部测试找漏洞、随机输入测试、可重复测试、吸引外部参与 |
| | 持续改进 | 不放弃解决安全问题,持续提升产品安全性 |
编程流程整合
下面通过 mermaid 格式的流程图展示一个综合的编程流程,涵盖代码可维护性、可移植性和安全性的考虑:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
A([开始编程]):::startend --> B(编写代码):::process
B --> C{可维护性检查}:::process
C -->|是| D{可移植性检查}:::process
C -->|否| E(优化注释和代码结构):::process
E --> B
D -->|是| F{安全性检查}:::process
D -->|否| G(调整代码以提升可移植性):::process
G --> B
F -->|是| H(代码审查):::process
F -->|否| I(消除安全漏洞):::process
I --> B
H --> J(内部 Beta 测试):::process
J --> K(修复测试发现的问题):::process
K --> L(发布产品):::process
L --> M(持续监控和改进):::process
M --> B
各要点的操作步骤梳理
-
代码可维护性操作步骤
-
撰写注释
:
- 分析代码功能和目的,确定哪些内容需要注释。
- 优先用代码表达逻辑,将注释留给关键信息。
- 为函数、类或模块添加注释,解释功能但不涉及实现细节。
- 确保注释提及所有参数并按声明顺序。
- 避免使用固定模板,撰写有针对性的段落注释。
- 代码审查 :检查注释是否清晰、准确,是否符合上述原则。
-
撰写注释
:
-
代码可移植性操作步骤
-
定义
main函数 :将main函数头部定义为标准形式。 -
使用标准退出代码
:在
main函数和exit调用中使用 0(或EXIT_SUCCESS)和EXIT_FAILURE。 -
类型选择
:使用
size_t存储对象大小,ptrdiff_t存储指针差值,offsetof宏访问结构体成员。 - 代码检查 :审查代码,确保没有使用平台特定的代码结构。
-
定义
-
代码安全性操作步骤
-
消除常见漏洞
:
- 对输入进行长度检查和错误处理。
- 避免使用命令解释器调用外部程序。
- 去除不必要的功能,禁用危险功能。
- 避免使用无意义的可执行内容。
-
快速响应机制
:
- 对软件更新进行加密和认证。
- 建立漏洞报告渠道,及时修复漏洞并告知客户。
-
合理谨慎措施
:
- 发布前进行独立保护审计。
- 建立软件更新的变更控制流程。
- 确保产品制造和分发过程的安全性。
- 跟踪错误并找出根本原因。
-
Beta 测试流程
:
- 组建内部安全测试团队进行 Beta 测试。
- 使用随机输入测试程序。
- 构建可重复的测试能力。
- 吸引外部社区参与测试。
-
消除常见漏洞
:
总结与展望
在编程过程中,代码的可维护性、可移植性和安全性是三个至关重要的方面。通过遵循上述原则和操作步骤,程序员可以编写出更优质的代码,减少潜在的问题和风险。
代码可维护性确保了代码在未来的开发和维护过程中能够被轻松理解和修改;可移植性使得代码能够在不同的平台上正常运行,扩大了代码的应用范围;安全性则保障了软件系统的稳定运行,避免了因安全漏洞导致的损失。
在未来的编程工作中,程序员应始终将这三个方面放在重要位置,不断学习和实践,以提升自己的编程技能和代码质量。同时,随着技术的不断发展,新的编程规范和安全挑战也会不断出现,程序员需要保持敏锐的洞察力,及时调整自己的编程策略。
总之,通过综合考虑代码的可维护性、可移植性和安全性,程序员可以打造出更具竞争力的软件产品,为用户提供更优质的服务。
超级会员免费看

被折叠的 条评论
为什么被折叠?



