可重入函数及函数编写规范
一、可重入函数

结果就遗漏了合法性检查这一必要的处理过程
造成问题隐患;要么就是调用者和被调用者均对参数进行合法性检查
这种情况虽不会造成问题
但产生了冗余代码
降低了效率
6 :防止将函数的参数作为工作变量
说明:将函数的参数作为工作变量
有可能错误地改变参数内容
则必须是STATIC的局部变量的地址作为返回值
若为AUTO类
则返回为错针
示例:如下函数
其返回值(即功能)是不可预测的
unsigned int integer_sum( unsigned int base )
{
unsigned int index;
static unsigned int sum = 0; // 注意
如果必须访问全局变量
记住利用互斥信号量来保护全局变量
绝不调用任何不可重入函数
3)不可重入函数:
函数中使用了静态变量
无论是全局静态变量还是局部静态变量
函数返回静态变量
函数中调用了不可重入函数
函数体内使用了静态的数据结构;
函数体内调用了malloc()或者free()函数;
{
unsigned int count ;
int sum_temp;
sum_temp = 0;
for (count = 0; count < num; count ++)
{
sum_temp += data[count];
19 :避免使用无意义或含义不清的动词为函数命名
说明:避免用含义不清的动词如process、handle等为函数命名
单片机程序实现可重入和不可重入函数方法

单片机程序实现可重入和不可重入函数方法
可重入函数是指一个函数可以被多个任务同时调用而不会产生冲突的函数,不可重入函数是指一个函数在被同时调用时可能会产生冲突的函数。
实现可重入函数的方法如下:1. 避免使用全局变量和静态变量。
全局变量和静态变量可能会在函数调用期间被修改,导致并发访问时出现冲突。
如果需要使用全局变量,可以使用互斥锁保护对全局变量的访问。
2. 使用局部变量。
局部变量是每次函数调用时创建的,不会被其他任务访问到,因此可以保证并发访问的安全性。
3. 使用栈上的变量。
栈上的变量是每次函数调用时分配的,不会被其他任务访问到,因此可以保证并发访问的安全性。
4. 避免使用非可重入函数。
如果需要使用非可重入函数,可以使用互斥锁保护对非可重入函数的访问。
实现不可重入函数的方法如下:1. 使用全局变量和静态变量。
全局变量和静态变量是多个任务共享的,如果在函数中使用全局变量和静态变量,可能会导致并发访问时出现冲突。
2. 使用非线程安全的库函数。
一些库函数在多任务环境下可能不是线程安全的,如果在函数中使用这些库函数,可能会导致并发访问时出现冲突。
3. 使用非原子操作的指令。
一些指令在执行时可能是非原子的,如果在函数中使用这些指令,可能会导致并发访问时出现冲突。
需要注意的是,可重入函数不一定是线程安全的,因为线程安全还需要考虑共享资源的访问和同步。
而不可重入函数通常也不是线程安全的,因为它可能会对全局资源进行修改,需要使用互斥锁或其他同步
机制来保护对全局资源的访问。
linux中信号处理函数为可重入函数

在Linux 中,信号处理函数应该尽量设计为可重入函数。
可重入函数是指能够在多个线程同时调用时保持其行为正确的函数,无论这些线程是同一进程内的还是不同进程内的。
以下是一些编写可重入信号处理函数的指导原则:
1. 避免使用非可重入函数:在信号处理函数中尽量避免使用非可重入的函数,因为这些函数可能会使用全局变量或静态变量,如果多个信号处理函数同时调用它们,可能会导致数据竞争和意外的结果。
2. 使用信号安全的函数:在信号处理函数中只使用被认为是“信号安全”的函数。
信号安全的函数是指在信号处理函数中能够正常工作且不会破坏全局状态的函数。
例如,可以使用`write()` 函数来输出消息,但不可使用`printf()` 函数。
3. 如果必须使用非可重入函数:如果必须在信号处理函数中使用非可重入函数,确保使用保护措施来防止数据竞争。
例如,可以使用互斥锁来保护全局变量的读写操作,以避免多个信号处理函数之间的竞争条件。
4. 确保正确设置信号处理函数:在设置信号处理函数时,使用`sigaction()` 函数而不是`signal()` 函数。
`sigaction()` 函数提供了更精确的信号处理控制,可以指定信号处理函数的选项和属性。
通过遵循这些原则,可以编写出更安全和可靠的可重入信号处理函数,从而提高应用程序在处理信号时的稳定性和可维护性。
可重入函数

占先式(Preemptive)
低优先级任务 中断服务程序使 高优先级任务就绪
(1)
(2)
ISR
(5)
(3)
高优先级任务
TIME
(4)
(6)
高优先级任务得到 CPU使用权
可重入型函数
可以被一个以上的任务调用,而不必担心数据的破坏。可重 入型函数任何时候都可以被中断,一段时间以后又可以运行,而相 应数据不会丢失。可重入型函数只使用局部变量,即变量保存在 CPU寄存器中或堆栈中。 一个不可重入型函数的例子
什么是可重入代码
• 可重入的代码指的是一段代码(比 如:一个函数)可以被多个任务同 时调用,而不必担心会破坏数据。
• 也就是说,可重入型函数在任何时 候都可以被中断执行,过一段时间 以后又可以继续运行,而不会因为 在函数中断的时候被其他的任务重 新调用,影响函数中的数据。
可重入代码举例
程序1:可重入型函数 void swap(int *x, int *y) { int temp; temp=*x; *x=*y; *y=temp; }
占先式(preemptive)
当系统响应时间很重要时,要使用占先式 (preemptive)内核。最高优先级的任务一旦 就绪,总能得到CPU的控制权。 当一个运行着的任务使一个比它优先级高 的任务进入了就绪态,当前任务的CPU使用权 就被剥夺了,或者说被挂起了,那个高优先级 的任务立刻得到了CPU的控制权。 使用占先式内核时,应用程序不应直接使 用不可重入型函数。如果调入不可重入型函数 时,低优先级的任务CPU的使用权被高优先级 任务剥夺,不可重入型函数中的数据有可能被 破坏。
非可重入代码举例
程序2:非可重入型函数 int temp; void swap(int *x, int *y) { temp=*x; *x=*y; *y=temp; 返回 }
C_C++编程规范-华为标准-精

程的关系,如访问、修改及创建等。 5-4:当向公共变量传递数据时,要十分小心,防
止赋与不合理的值或越界等现象发生。 5-5:防止局部变量与公共变量同名。 5-6:严禁使用未经初始化的变量作为右值。
编程规范详解——函数、过程 处理。
1-11:在两个以上的关键字、变量、常量进行对等操作时,它们之间 的操作符之前、之后或者前后要加空格;进行非对等操作时,如果 是关系密切的立即操作符(如->),后不应加空格。
1-12: 程序结构清析,简单易懂,单个函数的程序行数不得超过100行。
编程规范详解——注释
2-1:一般情况下,源程序有效注释量必须在20%以上。 2-2:说明性文件(如头文件.h文件、.inc文件、.def文件、编译说明文件.cfg
编程规范详解——注释
2-9:对于所有有物理含义的变量、常量,如果其命名不是充分自注释的,在 声明时都必须加以注释,说明其物理含义。变量、常量、宏的注释应放在 其上方相邻位置或右方。
2-10:数据结构声明(包括数组、结构、类、枚举等),如果其命名不是充分自 注释的,必须加以注释。对数据结构的注释应放在其上方相邻位置,不可 放在下面;对结构中的每个域的注释放在此域的右方。
后进入下一个case处理,必须在该case语句处理完、下一个case语句前加 上明确的注释。
编程规范详解——标识符的命名
3-1:标识符的命名要清晰、明了,有明确含义,同时使用完 整的单词或大家基本可以理解的缩写,避免使人产生误解。
3-2:命名中若使用特殊约定或缩写,则要有注释说明 3-3:自己特有的命名风格,要自始至终保持一致,不可来回
延用华为标准
C/C++编程规范
可重入函数和不可重入函数

可重入函数和不可重入函数
可重入函数和不可重入函数是计算机编程中重要的概念,它们通常用于多线程编程。
可重入函数是指在多线程环境下可以安全地被多个线程同时调用,而不会出现不同线程之间的相互干扰或冲突。
不可重入函数则相反,不能被多个线程同时调用,因为会出现不同线程之间的相互干扰或冲突。
可重入函数通常具有以下特点:不使用或共享全局变量;不使用或共享静态变量;不使用或共享动态内存分配的内存;不使用或共享文件或设备等共享资源。
这些特点使得可重入函数可以被多个线程同时调用,而不会出现不同线程之间的相互干扰或冲突。
不可重入函数通常具有以下特点:使用或共享全局变量;使用或共享静态变量;使用或共享动态内存分配的内存;使用或共享文件或设备等共享资源。
这些特点使得不可重入函数不能被多个线程同时调用,因为会出现不同线程之间的相互干扰或冲突。
在多线程编程中,使用可重入函数可以更好地保证程序的正确性和稳定性。
因为多个线程同时调用可重入函数不会出现冲突,从而避免了程序崩溃或数据损坏
等问题。
而使用不可重入函数则需要进行同步操作,以避免不同线程之间的相互干扰或冲突。
总之,可重入函数和不可重入函数是多线程编程中非常重要的概念。
了解它们的特点和使用方式可以更好地提高程序的正确性和稳定性,从而保证程序的质量
和性能。
c++函数重载规则

c++函数重载规则
C++中的函数重载是指在同一个作用域内定义多个同名函数,但是它们的形参个数或类型不同。
函数重载使得程序员可以用同一个函数名来完成不同的操作,提高了代码的可读性和复用性。
函数重载的规则如下:
1.函数名称相同:重载函数的名称必须相同。
2.形参个数不同:重载函数的形参个数必须不同。
例如,可以定义一个带一个参数的函数和一个带两个参数的函数。
3.形参类型不同:重载函数的形参类型必须不同。
例如,可以定义一个带整型参数的函数和一个带浮点型参数的函数。
4.形参顺序不同:重载函数的形参顺序必须不同。
例如,可以定义一个带两个整型参数的函数和一个带一个整型参数和一个浮点型参数的函数。
5.返回值类型不同:重载函数的返回值类型可以不同,但不能仅仅是返回值类型不同。
例如,不能定义一个返回整型的函数和一个返回浮点型的函数,它们的形参和形参类型都必须不同。
需要注意的是,函数重载并非只有形参不同,例如下面的代码就不是函数重载:
int add(int a, int b) {
return a + b;
}
double add(int a, int b) {
return a + b + 0.5;
}
因为这两个函数的形参类型相同,不能被视为函数重载。
总之,函数重载是C++中非常重要的特性,它允许我们定义多个同名函数,并根据参数类型和个数自动选择合适的函数进行调用。
在编写C++程序时,我们应该充分利用函数重载,从而提高代码的可读性和可维护性。
可重入函数的概念

可重入函数的概念主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。
也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。
如果确实需要访问全局变量(包括static),一定要注意实施互斥手段。
可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代价。
编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。
说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。
示例:假设Exam是int型全局变量,函数Squre_Exam返回Exam平方值。
那么如下函数不具有可重入性。
unsigned int example( int para ) {unsigned int temp;Exam = para; // (**)temp = Square_Exam( );return temp;}此函数若被多个进程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使Exam赋与另一个不同的para值,所以当控制重新回到“temp = Square_Exam( )”后,计算出的temp很可能不是预想中的结果。
此函数应如下改进。
unsigned int example( int para ) {unsigned int temp;[申请信号量操作] //(1)Exam = para;temp = Square_Exam( );[释放信号量操作]return temp;}(1)若申请不到“信号量”,说明另外的进程正处于给Exam赋值并计算其平方过程中(即正在使用此信号),本进程必须等待其释放信号后,才可继续执行。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一、可重入函数1)什么是可重入性?可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误。
相反,不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在代码的关键部分禁用中断)。
可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。
可重入函数要么使用本地变量,要么在使用全局变量时保护自己的数据。
2)可重入函数:不为连续的调用持有静态数据。
不返回指向静态数据的指针;所有数据都由函数的调用者提供。
使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。
如果必须访问全局变量,记住利用互斥信号量来保护全局变量。
绝不调用任何不可重入函数。
3)不可重入函数:函数中使用了静态变量,无论是全局静态变量还是局部静态变量。
函数返回静态变量。
函数中调用了不可重入函数。
函数体内使用了静态的数据结构;函数体内调用了malloc()或者free()函数;函数体内调用了其他标准I/O函数。
函数是singleton中的成员函数而且使用了不使用线程独立存储的成员变量。
总的来说,如果一个函数在重入条件下使用了未受保护的共享的资源,那么它是不可重入的。
4)示例在多线程条件下,函数应当是线程安全的,进一步,更强的条件是可重入的。
可重入函数保证了在多线程条件下,函数的状态不会出现错误。
以下分别是一个不可重入和可重入函数的示例://c codestatic int tmp;void func1(int* x, int* y) {tmp=*x;*x=*y;*y=tmp;}void func2(int* x, int* y) {int tmp;tmp=*x;*x=*y;*y=tmp;}func1是不可重入的,func2是可重入的。
因为在多线程条件下,操作系统会在func1还没有执行完的情况下,切换到另一个线程中,那个线程可能再次调用func1,这样状态就错了。
二、函数编写规范1 :对所调用函数的错误返回码要仔细、全面地处理2 :明确函数功能,精确(而不是近似)地实现函数设计3 :编写可重入函数时,应注意局部变量的使用(如编写C/C++ 语言的可重入函数时,应使用auto 即缺省态局部变量或寄存器变量)说明:编写C/C++语言的可重入函数时,不应使用static局部变量,否则必须经过特殊处理,才能使函数具有可重入性。
4 :编写可重入函数时,若使用全局变量(除简单的写或读操作),则应通过关中断、信号量(即P 、V 操作)等手段对其加以保护说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。
示例:假设Exam是int型全局变量,函数Squre_Exam返回Exam平方值。
那么如下函数不具有可重入性。
unsigned int example( int para ){unsigned int temp;Exam = para; // (**)temp = Square_Exam( );return temp;}此函数若被多个进程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使Exam 赋与另一个不同的para值,所以当控制重新回到“temp = Square_Exam( )”后,计算出的temp很可能不是预想中的结果。
此函数应如下改进。
unsigned int example( int para ){unsigned int temp;[申请信号量操作] // 若申请不到“信号量”,说明另外的进程正处于Exam = para; // 给Exam赋值并计算其平方过程中(即正在使用此 temp = Square_Exam( ); // 信号),本进程必须等待其释放信号后,才可继 [释放信号量操作] // 续执行。
若申请到信号,则可继续执行,但其// 它进程必须等待本进程释放信号量后,才能再使// 用本信号。
return temp;}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:函数的功能应该是可以预测的,也就是只要输入数据相同就应产生同样的输出说明:带有内部“存储器”的函数的功能可能是不可预测的,因为它的输出可能取决于内部存储器(如某标记)的状态。
这样的函数既不易于理解又不利于测试和维护。
在C/C++语言中,函数的static局部变量是函数的内部存储器,有可能使函数的功能不可预测,然而,当某函数的返回值为指针类型时,则必须是STATIC的局部变量的地址作为返回值,若为AUTO类,则返回为错针。
示例:如下函数,其返回值(即功能)是不可预测的。
unsigned int integer_sum( unsigned int base ){unsigned int index;static unsigned int sum = 0; // 注意,是static类型的。
// 若改为auto类型,则函数即变为可预测。
for (index = 1; index <= base; index++){sum += index;}return sum;}12 :尽量不要编写依赖于其他函数内部实现的函数说明:此条为函数独立性的基本要求。
由于目前大部分高级语言都是结构化的,所以通过具体语言的语法要求与编译器功能,基本就可以防止这种情况发生。
但在汇编语言中,由于其灵活性,很可能使函数出现这种情况。
示例:如下是在DOS下TASM的汇编程序例子。
过程Print_Msg的实现依赖于Input_Msg 的具体实现,这种程序是非结构化的,难以维护、修改。
... // 程序代码proc Print_Msg // 过程(函数)Print_Msg... // 程序代码jmp LABEL... // 程序代码endpproc Input_Msg // 过程(函数)Input_Msg... // 程序代码LABEL:... // 程序代码endp13 :避免设计多参数函数,不使用的参数从接口中去掉说明:目的减少函数间接口的复杂度。
14 :非调度函数应减少或防止控制参数,尽量只使用数据参数说明:本建议目的是防止函数间的控制耦合。
调度函数是指根据输入的消息类型或控制命令,来启动相应的功能实体(即函数或过程),而本身并不完成具体功能。
控制参数是指改变函数功能行为的参数,即函数要根据此参数来决定具体怎样工作。
非调度函数的控制参数增加了函数间的控制耦合,很可能使函数间的耦合度增大,并使函数的功能不唯一。
示例:如下函数构造不太合理。
int add_sub( int a, int b, unsigned char add_sub_flg ){if (add_sub_flg == INTEGER_ADD){return (a + b);}else{return (a b);}}不如分为如下两个函数清晰。
int add( int a, int b ){return (a + b);}int sub( int a, int b ){return (a b);}15 :检查函数所有参数输入的有效性16 :检查函数所有非参数输入的有效性,如数据文件、公共变量等说明:函数的输入主要有两种:一种是参数输入;另一种是全局变量、数据文件的输入,即非参数输入。
函数在使用输入之前,应进行必要的检查。
17 :函数名应准确描述函数的功能18 :使用动宾词组为执行某操作的函数命名。
如果是OOP 方法,可以只有动词(名词是对象本身)示例:参照如下方式命名函数。
void print_record( unsigned int rec_ind ) ;int input_record( void ) ;unsigned char get_current_color( void ) ;19 :避免使用无意义或含义不清的动词为函数命名说明:避免用含义不清的动词如process、handle等为函数命名,因为这些动词并没有说明要具体做什么。
20 :函数的返回值要清楚、明了,让使用者不容易忽视错误情况说明:函数的每种出错返回值的意义要清晰、明了、准确,防止使用者误用、理解错误或忽视错误返回码。