printf的重入问题及解决方法

一、可重入函数
1)什么是可重入性?
可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误。相反, 不可重入(non-reentrant)函数不能由超过一个任务所共享。

可重入函数要么使用本地变量,要么在使用全局变量时保护自己的数据。

2)可重入函数:
为连续的调用持有静态数据
不返回指向静态数据的指针;所有数据都由函数的调用者提供。
使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。
如果必须访问全局变量,记住利用互斥信号量来保护全局变量。
绝不调用任何不可重入函数

3)不可重入函数:
函数中使用了静态变量,无论是全局静态变量还是局部静态变量。
函数返回静态变量
函数中调用了不可重入函数
函数体内使用了静态的数据结构
函数体内调用了malloc()或者free()函数
函数体内调用了其他标准I/O函数
函数是singleton中的成员函数而且使用了不使用线程独立存储的成员变量 。
总的来说,如果一个函数在重入条件下使用了未受保护的共享的资源,那么它是不可重入的。

常见的不可重入函数有:
printf --------引用全局变量stdout
malloc --------全局内存分配表
free    --------全局内存分配表
在unix里面通常都有加上_r后缀的同名可重入函数版本。

 

把一个不可重入函数变成可重入的唯一方法是用可重入规则来重写他。

二、函数编写规范
1 :对所调用函数的错误返回码要仔细、全面地处理
  
2 :明确函数功能,精确(而不是近似)地实现函数设计
  
3 :编写可重入函数时,应注意局部变量的使用(如编写C/C++ 语言的可重入函数时,应使用auto 即缺省态局部变量或寄存器变量)
说明:编写C/C++语言的可重入函数时,不应使用static局部变量,否则必须经过特殊处理,才能使函数具有可重入性。
  
4 :编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P 、V 操作)等手段对其加以保护
说明

5 :在同一项目组应明确规定对接口函数参数的合法性检查应由函数的调用者负责还是由接口函数本身负责,缺省是由函数调用者负责
说明

6 :防止将函数的参数作为工作变量
说明:将函数的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。
示例:如下函数的实现就不太好。
void sum_data( unsigned int num, int *data, int *sum )
{
     unsigned int count;
     *sum = 0;
  
     for (count = 0; count < num; count++)
     {
         *sum   += data[count]; // sum成了工作变量,不太好。
     }
}
若改为如下,则更好些。
void sum_data( unsigned int num, int *data, int *sum )
{
     unsigned int count ;
     int sum_temp;
     sum_temp = 0;
  
     for (count = 0; count < num; count ++)
     {
         sum_temp   += data[count];
     }
  
     *sum = sum_temp;
}
  
7 :函数的规模尽量限制在200 行以内
说明:不包括注释和空格行。
8 :一个函数仅完成一件功能

9 :为简单功能编写函数
说明:虽然为仅用一两行就可完成的功能去编函数好象没有必要,但用函数可使功能明确化,增加程序可读性,亦可方便维护、测试。
示例:如下语句的功能不很明显。
value = ( a > b ) ? a : b ;
改为如下就很清晰了。
  
int max (int a, int b)
{
     return ((a > b) ? a : b);
}
  
value = max (a, b);
  
或改为如下。
  
#define MAX (a, b) (((a) > (b)) ? (a) : (b))
  
value = MAX (a, b);
  
10:不要设计多用途面面俱到的函数
说明:多功能集于一身的函数,很可能使函数的理解、测试、维护等变得困难。
  
11:函数的功能应该是可以预测的,也就是只要输入数据相同就应产生同样的输出

 

你可能感兴趣的:(数据结构,工作,unix,测试,语言,任务)