概述
前言:
(1) 知错就改
(2)经常温故知新
(3)坚持学习,天天向上
1.头文件
(1) 为了防止头文件被重复引用,应当引用ifdef/define/endif产生预处理块
#ifndef GRAPHICS_H // 防止 graphics.h 被重复引用
#define GRAPHICS_H
#include <math.h>// 引用标准库的头文件
…
#include “myheader.h” // 引用非标准库的头文件
…
void Function1(…); // 全局函数声明
…
class Box // 类结构声明 { … };
#endif
(2)头文件中只存放 声明 而不存放 定义
函数的定义与声明分开写
(3)不提倡使用全局变量
2.空行
(1)在每个类声明之后、每个函数定义结束之后都要加空行
(2)在一个函数体内,逻辑上密切相关的语句之间不加空行,其他地方应加空行分隔。
3.代码行
(1)一行代码只做一件事情,如只定义一个变量,或只写一条语句。
(2)if,for,while,do等语句各自占一行,执行语句不得紧跟其后,无论执行语句有多少都要加{ }。
(3)尽可能定义变量得同时初始化该变量
(4)关键字后要留一个空格
(5)函数名之后不留空格,紧跟左括号‘(’;已于关键字区别。
(6)'(' 向后紧跟 ')',';' 向前紧跟,紧跟处不留空格
(7)','之后要留空格,如果';'不是一行结束的标志,其后要留空格
(8)一元操作符 "!" "~" "++" "--" "&"等前后不加空格
(9)二元操作符前后要加空格 "=" "+=" "<=" ">=" "<="
"+" "*" "%" "&&" "||" "<<" "^"
(10) "[]" "." "->" 前后不加空格
(11)for语句和if语句为了紧凑一点可适当去掉些空格
4.对齐
(1)程序的分界符"{"和"}" 应该单独占一行并且位于同一列,同时与引用他们的语句左对齐
(2) { }之内的代码块在" { " 右边数格出对齐。
5. 长行拆分
代码行最大长度宜控制在 70 至 80个字符以内。代码行不要过长,否者眼睛看不过来,也不方便打印。
6.修饰符的位置
修饰符 * 和 & 紧靠变量名
7.注释
(1)注释是对代码的“提示”,而不是文档。程序中的注释不可喧宾夺主,注释太多会让人眼花缭乱。注释的花样要少
(2)如果代码本身就很清楚,则不必加注释
(3)尽量避免在注释中使用缩写,特别是不常用的缩写
(4)注释的位置应与描述的代码相邻,可以放在代码的上方或有方,不可放在下方
(5)当代码比较长,特别是有多重循环,应当在一些段落的结束处加注释,便于阅读
8.类的版式
采用“以行为为中心”的书写方式,即首先考虑类应该提供什么样的函数
9.命名规则
(1)标识符应当直观且可以拼读,望文知意。最好采用英文单词或其组合,便于阅读和记忆。切忌使用汉语拼音来命名。
(2)命名规则尽量与所采用的操作系统或开发工具的风格保持一致
windows应用程序的标识符通常采用“大小写”混排的方式,如AddChild。而Unix应用程序的标识符通常采用“小写加下划线”的方式,如add_child。别把这两种风格混用在一起
(3)程序中不要出现仅靠大小写区分的相似标识符
(4)程序中不要出现标识符完全相同的全局变量和局部变量,尽管二者作用域不同而不会发生语法错误,但会使人误解。
(5)变量的名字应当使用"名词"或"形容词+名词"。
(6)全局函数的名字应当使用"动词"或"动词+名词"
类的成员函数应当只使用"动词",被省略掉的名词就是对象本身
(7)用正确的反义词组命名具有互斥意义的变量或相反动作的函数
(8)尽量避免名字中出现数字编号,除非逻辑上的确需要编号。
10.window应用程序命名规则
(1)类名和函数名用大写字母开头的单词组成
(2)变量和参数用小写字母开头的单词组成
(3)常量全用大写字母,用下划线分割单词
(4)静态变量加前缀s_(表示static)
(5)如果不得已使用全局变量,则使全局变量加前缀g_(表示global)
(6)类的数据成员加前缀m_(表示member),这样可以避免数据成员与成员函数的参数同名
11.运算符的优先级
(1)如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免使用默认的优先级
(2)熟记比较困难,为了防止产生歧义并提高可读性,应当用括号确定表示式的操作顺序
12.符合表达式
(1)不要编写太复杂的符合表达式
(2)不要用多用途的复合表达式
(3)不要把程序中的复合表达式和"真正的数学表达式"混淆。
13.if 语句
(1)整型变量与零值比较
应当将整型变量用"=="或"!="直接与0比较
(2)浮点变量和零值比较
不可将浮点变量用"=="或"!=" 与任何数字比较。
无论是float还是double类型的变量,都有精度限制。所以一定要避免将浮点变量用"=="或"!="与数字比较,应当设法转化为">="或"<="的形式。
EPSINON是e的负10次方,该数表示接近于0的小正数
(3)指针变量和零值比较
应当用将指针变量用"=="或"!="与NULL比较。
(4)补充说明
14.循环语句的效率
(1)在多重循环,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少cpu跨切循环层的次数。
(2)如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体外面。
15.for循环控制变量
(1)不可在for循环内修改循环变量,防止for循环失去控制
(2)建议for循环控制变量的取值采用"半开半闭区间的写法"。
16.switch语句
(1)每个case语句的结尾不要忘了加break,否则将导致多个分支重叠(除非有意使用多个分支重叠)
(2)不要忘记最后那个default分支。即使程序真的不需要default处理,也应该保留语句default:break;那样做并非多此一举,而是为了防止别人误以为你忘了default处理。
17.goto
主张少用,慎用,而非禁用
18. 常量
尽量使用含义直观的常量来表示那些将在程序中出现的数字或字符串
19. const与#define的比较
(1) const常量有数据类型,而宏常量没有类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且字符替换可能会产生意料不到的错误(边际效应)。
(2) 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
(3) 在c++程序中只使用const常量而不使用宏常量,即const常量完全替代宏常量。
20.常量定义规则
(1) 需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。
(2) 如果某一常量与其他常量密切相关,应在定义中包含这种关系,而不应给出一些孤立的值。
例:
21.类中的常量
(1) 不能在类声明中中初始化const数据成员。
因为类的对象未被创建时,编译器不知道SIZE的值是什么。
(2) const 数据成员的初始化只能在类构造的初始化表中进行
(3) 建立整个类中都恒定的常量,应该用类中的枚举常量来实现。
枚举常量不会占用对象的存储空间,他们在编译时被全部赋值。枚举常量的缺点是:他的隐含数据类型是整数,其最大值有限,且不能表示浮点数。
22.函数设计
函数接口的两个要素是参数和返回值。
c语言中,函数的参数和返回值的传递方式有两种:值传递和指针传递。
c++语言中多了引用传递。
23.参数
(1) 参数的规则:参数的书写要完整,如果函数没有参数,则用void填充。
(2) 参数命名要恰当,顺序要合理
一般的,应将目的参数放在前面,源参数放在后面。
(3) 如果参数是指针,且仅做输入用,则应在类型前加const,以防止该指针在函数体内被意外修改。
(4) 如果输入参数以值传递的方式传递对象,则宜用"const &" 方式来传递,这样可以省去临时对象的构造和分析过程,从而提高效率。
(5) 避免函数有太多参数,参数尽量控制在5个以内。
(6) 尽量不要使用类型和数目不确定的参数。
24.返回值
(1) 不要省略返回值的类型
c语言中,凡不加类型说明的函数,一律自动按整型处理。这样做易被误认为是 void类型
c++语言有严格的类型安全检查,不允许上述情况发生。由于c++可以调用C函数,为了避免混乱,规定任何c/c++函数都必须有类型。如果函数没有返回值,那么应声明为void类型。
(2)函数名字与返回值类型在语义上不可冲突。
(3) 不要将正常值和错误标志混在一起返回。正常值用输出参数获得,而错误标志用return语句返回。
(4) 有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,可以附加返回值。
(5) 如果函数的返回值是一个对象,有些场合用"引用传递"替换"值传递"可以提高效率。而有些场合只能用"值传递"而不能用"引用传递",否则会出错。
25.函数内部实现的规则
(1) 在函数的"入口处",对参数的有效性进行检查。
很多程序错误是由非法参数引起的,应当充分理解并正确使用断言"assert" 来防止此类错误。
(2) 在函数的"出口处",对return语句的正确性和效率进行检查。
a. return语句不可返回指向"栈内存"的"指针"或者"引用",因为该内存在函数体结束时被自动销毁。强行使用,会造成非法访问(等于访问了一块不再属于你的空间)
b. 要搞清楚返回的究竟是"值","指针"或者"引用"。
c. 如果函数返回值是一个对象,要考虑return语句的效率。
26.其他建议
(1) 函数的功能要单一,不要设计多用途的函数。
(2) 函数体的规模要小,尽量控制在50行代码以内。
(3) 尽量避免函数带有"记忆"功能。相同的输入应当产生相同的输出。带有"记忆"功能的函数,其行为可能是不可预测的,因为他的行为可能取决于某种"记忆状态"。这样的函数即不容易理解又不利于测试和维护。在c/c++语言中,函数的static局部变量是函数的"记忆"存储器。尽量少用static局部变量,除非必需。
(4) 不仅要检查输入参数的有效性,还要检查通过其他途径进入函数体内的变量的有效性,例如全局变量,文件句柄等。
(5) 用于出错的返回值一定要清楚。
27. 使用断言
程序一般分为Debug版本和Release版本,Debug版本用于内部调试,Release版本发行给用户使用。
断言assert是仅在Debug版本起作用的宏,他用于检查"不应该"发生的情况。在运行过程中assert的参数为假,那么程序就会终止(一般地,还会出现提示对话,说明那个地方引发了ressert)。
如果程序在assert处终止了,并不是说含有该assert的函数有错误,而是调用者出了差错,assert可以帮助我们找到发生错误的原因。
(1) 使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,错误情况是必然存在的并且一定要做出处理。
(2) 在函数的入口处,使用断言检查参数的有效性(合法性)。
(3) 在编写函数时要进行反复考察,如果不可能的事情的确发生了,则要用断言进行报警。
28.引用与指针的比较
引用的规则
(1) 引用被创建的同时必须被初始化(指针则可以任何时候被初始化)
(2) 不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)
(3) 一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
(4) 引用的主要功能是传递函数的参数和返回值。c++中,函数的参数和返回值的传递方式有三种:值传递,指针传递和引用传递。
值传递: 函数体内的x是外部变量n的一份临时拷贝,改变x的值不会影响n。
指针传递: 函数体内的x是指向外部变量n的指针,改变该指针的内容将导致n的值改变。
引用传递: 函数体内的x是外部变量n的引用,x和n是同一个东西,改变x等与改变n。
29.内存管理
内存分配的三种方式
(1) 从静态存储区分配。内存在程序编译的时候就已经分配好了,这块内存在程序的整个运行期间都存在。例如:全局变量,static变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配,也称内存动态分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活。
30.常见的内存错误
(1)内存分配未成功,却使用了它。
解决方法:在使用内存之前检查指针是否NULL。
如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。
如果是用malloc或new来申请内存,应该用if(p=NULL)或if(p!=NULL)进行防错处理
(2)内存分配成功,但尚未初始化就引用它。
(3)内存分配成功并且已初始化,但操作越过了内存的边界。
(4)忘了释放内存
动态内存的申请与释放必须配对,程序中malloc与free的次数一定要相同,否则肯定有错误(new与delete同理)
(5)内存释放了却继续使用它。
a. 实在难以搞清楚某个对象究竟是否已经释放了内存,此时应重新设计数据结构,从根本解决对象管理的混乱局面。
b. 函数的return语句返回了指向"栈内存"的"指针"或者"引用",因为该内存在函数体结束时被自动销毁。
c. 使用free或delete释放内存后,没有将指针设置为NULL。导致产生"野指针"。
小结:
1.malloc或new申请内存后,立即检查指针值是否为NULL
2.为数组和动态内存赋初值
3.避免数组或者指针下标越界
4.动态内存的申请与释放必须配对,以防内存泄漏
5.用free或delete释放内存之后,立即将指针置NULL。
31.指针与数组的对比
数组在静态存储区(全局数组)或者在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不 变,只有数组的内容可以改变。
指针可以随时指向任意类型的内存块,它的特征是"可变",常用指针来操作动态内存。指针远比数组灵活,但也更危险。
32.修改内容
字符数组a之所以能改变字符串的内容,是因为数组a是将字符串从字符常量区拷贝了一份放在栈中,改变的是栈中拷贝的一份而不是字符常量区中的字符串本身。
33.内容的复制与比较
不能对数组名进行直接复制与比较。若想把数组a的内容复制给数组b,应该用标准库函数strcpy进行复制。比较a与b的内容是否相等,应该也用标准库函数strcmp进行比较。
若要复制数组a的内容,应用malloc申请strlen(a)+1个字节的内存空间,再用strcpy进行字符串复制。
34.计算内存容量
用运算符sizeof可以计算出数组的容量(字节数)。
c/c++没有办法知道指针所指的内存容量,除非在申请内存时记住它。
注意当数组作为函数参数进行传递时,该数组自动退化为同类型的指针。不论数组a的容量是多少,sizeof(a)始终等于
sizeof(char*)。
35.指针参数传递内存
(1) 如果函数的参数是一个指针,不要指望用该指针去申请动态内存。
指针p是在栈上开辟的空间,当void GetMemory函数调用结束,指针p的空间就被销毁,p中存放的malloc在堆上申请的空间的首地址丢失,所以str仍为NULL
(2) 要用指针参数去申请内存,那么应该改用"指向指针的指针"
*p改变了str的内容(为其申请了num个字节大小的空间),但并没有改变str的地址。
*p操作的str的空间,**p操作的是str内存放地址对应的空间,又str为NULL,所以**p无法操作
a.
不要用return语句返回指向"栈内存"的指针,因为该内存在函数结束时自动销毁。
例如:
str内存放的是p内存放的地址,函数调用结束被销毁,空间已被回收,若使用str将造成非法访问内存。
b.
Test运行虽然不会出错,但函数Getstring2的设计概念却是错误的。因为Getstring2内的"hello world"是常量字符串,位于静态存储区,他在程序生命期内恒定不变。无论什么时候调用Getstring2,它返回的始终是一个"只读"的内存块。
36.free和delete
free和delete只是把指针所指向的空间释放掉,但并没有把指针本身干掉。
37.动态内存释放
1) 指针消亡了,并不表示他所指的内存会被自动释放。
(2) 内存被释放了,并不表示指针会消亡或者成了NULL指针。
(3) 程序终止运行,一切指针都会消亡,动态内存会被操作系统回收。
38.杜绝"野指针"
(1) 指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,他的缺省值是随机的。指针变量在创建的同时应当被初始化,要么指针设置为NULL,要么让他指向合法的空间。
(2)指针p被free或者delete之后,没有置空,让人误以为p是个合法的指针。
(3)指针操作超越了变量的作用范围。(指针越界,或者指向了已被释放的空间)
39.malloc/free 与 new/delete
40.内存耗尽
如果在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针。
(1) 判断指针是否为NULL,如果是则马上用return语句终止本函数。
(2) 判断指针是否为NUL,如果是则马上用exit(1)终止整个程序的运行。
(3) 为new和malloc设置异常处理函数。
41.malloc/free的使用要点
(1)malloc返回值是void* ,在调用malloc时要显式地进行类型转换,将void*转换成所需要的指针类型。
(2)malloc函数本身不识别要申请的内存是什么类型,他只关心内存的总字节数。
(3)在malloc的"()"中使用sizeof运算符是良好的风格。
(由于c++不是很会,这块不太清楚,等学会c++再继续完善)
42.new/delete
最后
以上就是温暖世界为你收集整理的C语言之编程规范及细节知识(学C必看)前言:1.头文件2.空行3.代码行4.对齐5. 长行拆分6.修饰符的位置7.注释8.类的版式9.命名规则10.window应用程序命名规则11.运算符的优先级12.符合表达式13.if 语句14.循环语句的效率15.for循环控制变量16.switch语句17.goto18. 常量19. const与#define的比较20.常量定义规则21.类中的常量22.函数设计23.参数24.返回值25.函数内部实现的规则26.其他建议27. 使用断言28.引用与指针的全部内容,希望文章能够帮你解决C语言之编程规范及细节知识(学C必看)前言:1.头文件2.空行3.代码行4.对齐5. 长行拆分6.修饰符的位置7.注释8.类的版式9.命名规则10.window应用程序命名规则11.运算符的优先级12.符合表达式13.if 语句14.循环语句的效率15.for循环控制变量16.switch语句17.goto18. 常量19. const与#define的比较20.常量定义规则21.类中的常量22.函数设计23.参数24.返回值25.函数内部实现的规则26.其他建议27. 使用断言28.引用与指针所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复