内存管理&& 函数

1.NULL就是NULL,在许多编译器中它被定义为0。表示指针不指向任何地址。

 

2.NUL是ASCII码表中的第1个字符,表示的是空字符,其ascii码值为0。虽然两者的值都为0,但表示的意义不一样。

3.一般在函数 入口处使用assert(NULL!=p)对参数 进行检验 ,但是这只是建立在p不是野指针 的情况 下,如果 是野指针 ,这种做法也不无济于事。

4.  使用assert 宏的地方在Release版本中会被 编译 器完全 优化掉。所以assert宏通常只是帮助我们调试代码,用于定位错误。

5. RAII 资源获取即初始化

6. int a[10]={0};   memset(a,0,sizeof(a));   //三个参数依次是被设置 的内存起始地址,第二个是要被 设置 的值 ,第三个是要被 设置 的内存的大小 ,单位 是字节 。

7. for循环中尽量使用半开半闭的区间,即第二个分句不要带等号。而且循环初始化一般从0开始,不要从1开始 。

8.用malloc函数申请0字节的内存,并不会返回NULL,而是返回一个正常 的地址,但是无法使用。对这种情况 也无法 用if(NULL!=p)来检验 。

9.free函数 调用 后一定要把相应 的指针 置 NULL.

10.在函数中不要返回一片已经被释放 的内存 。

 

函数

1.每个函数 都 必须 有注释,注释包含的内容和次序如下:

/************************************************************************

* Function Name: nucFindThread

* Create Date: 2000/01/07

* Author/Corporation: your name/your company name

*

* Description: Find a proper thread in thread array.

*  Ifit’sanewthensearchanempty.

*

* Param: ThreadNo: someParam description

*  ThreadStatus: someParam description

*

* Return Code: Return Code description,eg:

ERROR_Fail: not find a thread

ERROR_SUCCEED: found

*

* Global Variable: DISP_wuiSegmentAppID

* File Static Variable: naucThreadNo

* Function Static Variable: None

*

*------------------------------------------------------------------------

* Revision History

* No. Date Revised by Item Description

* V0.5 2008/01/07 your name … …

************************************************************************/

static unsigned char nucFindThread(unsigned char ThreadNo,unsigned char ThreadStatus)

{

}

2.原则上少使用全局变量,如果必须使用,应对外提供访问全局变量的函数,避免直接读写全局变量,尤其是在多线程程序中,可以很方便地在函数中为变量读写加锁。

3。复制函数,一般将目的参数放在前面,源参数放后面。

4。如果参数是指针,且仅作输入参数用,则应在类型前加const,以防止该指针在函数体内被意外修改。

     如:void str_cpy(char * strDst, const char* strSrc);

5.assert宏与if检查

assert一般是用来检查bug,主要是用来检查致命的非正常的操作(如向PCI设备写东西,但是PCI设备根本就不存在),执行的最终结果是调用abort,即你的进程异常终止,这在软件发布上是致命的,不管怎么说你的程序至少要执行下去啊,别动不动就终止了.
if是常规性的检查,包括参数判断,异常条件等等,判断的结果处理随你,你可以仅仅打印异常信息,可以终止程序.几乎所有assert能干的事,if都可以干.只是有些同学觉得方便就喜欢用assert.
因此,assert尽量少用,有些公司的c编程规范直接规定不能用assert.
下面是对assert的描述:

DESCRIPTION

The assert() macro tests the given expression and if it is false, the

calling process is terminated.  A diagnostic message is written to stderr

and the function abort(3) is called, effectively terminating the program.

If expression is true, the assert() macro does nothing.

The assert() macro may be removed at compile time by defining NDEBUG as a

macro (e.g., by using the cc(1) option -DNDEBUG).

EXAMPLES

The assertion:

assert(1 == 0);

generates a diagnostic message similar to the following:

Assertion failed: (1 == 0), function main, file assertion.c, line 100.

-----------------------

6。函数的功能要单一,不要设计多用途的函数。微软的win32 API就是违反本原则的典型,其函数往往因为参数不一样而呈现不同的功能,导致初学者的疑惑。

7。函数体的规模尽量小,最好不超过80行。

8。使函数满足相同的输入产生相同的输出的要求,当然不些基于概率的算法除外。这就要求在函数中尽量避免使用全局变量、静态变量等带有”记忆“功能的量。

9。尽量不要使用类型和个数不确定的参数。

C标准库函数prinft是采用不确定参数的典型代表,其原型为:

int printf(const char* format[, argument]……);

这种风格的函数在编译时丧失了严格的类型检查。

10。有时候函数不需要返回值,但为了增加灵活性,如支持链式表达,可以附加返回值。如:

char str[10];

int length= strlen(strcpy(str,”hello”);

11.在函数中对于经过各种途径进入函数体内的变量都要检查其有效性。如全局变量、文件句柄等。

12。函数名与返回值类型在语义上不可冲突。反例:C语言标准库函数getchar竟然无耻地返回了int.其原型为:int getchar(void);

13.汇编语言应该被封装并被隔离,最好同时定义成宏。

在需要汇编指令的地方建议以如下方式封装并隔离这些指令:

  • 汇编函数
  • C函数

对汇编语言进行封装原因:由于不同的CPU识别的汇编语言不完全相同,因此从程序可移植性角度考虑,汇编语言应该封装并隔离,这样在处理由于汇编引起的可移植性问题时,比较容易找到需要修改的地方。

内嵌汇编:在C程序中内嵌汇编提供了直接读/写硬件的能力,但编译器并不检查和分析内嵌汇编可能导致C变量的值改变或一些严重错误。(注:内嵌汇编也是一种引起变量值以意料之外方式改变的途径,此时需要volatile保留字)。

14。声明或定义一个数组时,它的大小应该显式声明,这样在查看代码时比较容易知道其长度。注意这里包括“声明和定义”

15。调用函数时尽量避免隐式类型转换。

16。一些需要强制类型转换的地方,除了最终对结果的强制类型转换,对相关操作数提前进行类型转换,可以避免一些错误,毕竟保持谨慎是明智的做法。

比如 short i=23; short j=xxxx;  int k= i+j;  根据本条原则应该写成  int k=(int)i+(int)j;实际上是引入了一些中间变量。

17。建议尽量少用多个返回路径,用一个临时变量存储返回值,在函数结尾再统一给出返回表达式。

18。使用strncpy库函数代替strcpy库函数。

strncpy 是 C语言的库函数之一,来自 C语言标准库,定义于 string.h,char *strncpy(char *dest, const char *src, int n),把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回dest。

如果n =dest串长度,则dest串没有NULL字符,会导致输出会有乱码。如果不考虑src串复制完整性,可以将dest 最后一字符置为NULL。

一般情况下,使用strncpy时,建议将n置为dest串长度(除非你将多个src串都复制到dest数组,并且从dest尾部反向操作),复制完毕后,为保险起见,将dest串最后一字符置NULL,避免输出乱码问题。当然喽,无论是strcpy还是strncpy,保证dest串容量(能容纳下src串)才是最重要的。

19。指针的数学运算(整数加减运算)只能用在指向同一个数组元素的指针上。对不是指向数组或数组元素的指针,做整数加减上会导致未定义的行为。指针在做减法、>、>=、<、<=等运算符时,只有指针指向同一个数组,结果才是可预知的。

20。传递给库函数的值必须检查其有效性。C标准库中的许多函数根据ISO标准,并不需要检查传递给它们的参数的有效性。所以程序员应该将为所有带有严格输入域的库函数(标准库、第三方库以及自己定义的库)提供适当的输入值检查机制。(???对自己编写的库进行输入值检查是否会显得多此一举,检查参数有效性的责任到底是谁的??可能作为程序员不应该轻易相信别人给你提供的参数,因为当你发布了一个第三方的库,别人没有看懂,就乱用气,参数根本不符合要求,但它不将责任揽到自己头上,而会说什么垃圾东西。所以还是谨慎一点,不在故障发生在调用自己代码的过程中)。

举例:

标准库中log函数、sqrt函数并没有进行有效性检查,所以在传递参数进入前,要自己进行参数有效性的检查。

fmod函数的第二个参数不能为0。

toupper和tolower:当传递给toupper函数的参数不是小写字母时,某些实现能产生非预期的结果(tolower的情况类似)

如果为ctype.h中的字符测试函数传递无效的值时,会给出未定义的行为。

---------有许多方法可以满足本规则的要求,包括:----------------

a).调用函数前检查输入值

b).产生函数的“封装”(wrapped)版本,在该版本中首先检查输入,然后调用原始的函数。注意这种方法对没有原有函数源码但能调用的库函数都有效。

C).自己设计带有内部检查的相同功能的函数。这种做法相比B中的做法难度会比较大,但有可能会带来效率的提升。

D).静态地声明输入参数 永远不会效的值 。

21。不使用任何变量编写strlen函数

  “不使用中间变量”是说程序员不能显式的申请内存,即不能有局部变量或者动态内存申请。如果函数自动申请栈内存或者使用寄存器存储变量,或者使用立即数寻址即常量,那么就相当于“不使用中间变量”。从函数原型看,返回值为int,那么在函数内部必定需要一个地方存储这个值,要么是常数要么是寄存器。长度不为1时不能一次就求出来,说明必须有递归调用,这样递归时函数会自动申请栈内存,这样就相当于程序员“不使用中间变量”了。

#include <iostream>
using namespace std;

int mystrlen(const char* str)
{
    if(str==NULL)
        return 0;
    if(*str!='\0')
        return 1+mystrlen(++str);   //函数调用的开销比递归 大得多,不到万不得已不要用递归 。
    else
        return 0;

}

void main()
{
    char *str="Diaoyu islands belong to china";
    cout<<mystrlen(str)<<endl;
}

转载于:https://my.oschina.net/ray1421/blog/685671

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值