64-引用与函数执行

原创 2016年04月27日 13:22:29

64-引用与函数执行

一个函数的执行结果要返回给调用者,除了使用return功能,还有一种办法,那就是以引用的形式传递参数,然后在内部修改这个参数的值。前一种方法往往只能返回一个值,如果我们的函数执行结果具有多种数据,便需要把这些数据打包到一个数组、类等复合类型的变量中才能得以实现;但后一种方法相比而言就简单一些了。

运行时传递引用:Call-time Pass-by-ref

标题有点绕口,其实很简单,功能如以下php代码所示:

<?php
function byref_calltime($a) {
    $a = '(modified by ref!)';
}

$foo = 'I am a string';

//使用&传递引用
byref_calltime(&$foo);
echo $foo;
//输出'(modified by ref!)'
?>

我们在传递参数的时候使用&操作符,便可以传递foocopyfoo也跟着被一起修改了。同样的功能我们如何在扩展里实现呢,其实很简单,请看下面的源码:

ZEND_FUNCTION(byref_calltime)
{
    zval *a;

    //我们我接收的参数传给zval *a;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &a) == FAILURE)
    {
        RETURN_NULL();
    }

    //如果a不是以应用的方式传递的。
    if (!a->is_ref__gc)
    {
        return;
    }

    //将a转成字符串
    convert_to_string(a);

    //更改数据
    ZVAL_STRING(a," (modified by ref!)",1);
    return;
}

编译时的传递引用Compile-time Pass-by-ref

如果每一次都在调用函数时候都对参数加一个&符号真是太罗嗦了,有没有一个简单的办法呢,比如在定义函数的时候便声明这个参数是引用形式的,而不用用户自己加&符号表示引用,而由内核来完成这步操作?这个功能是有的,我们在PHP语言中可以这样实现。

<?php
// 在定义函数参数的时候加了引用符
function byref_compiletime(&$a) {
    $a = ' (modified by ref!)';
}
$foo = 'I am a string';

//这个地方我们没有加&引用符
byref_compiletime($foo);
echo $foo;
//输出 (modified by ref!)
?>

上面的代码中,我们只是把引用符号从函数调用里转移到函数定义里。此功能在扩展里面实现的话就颇费周折了,我们需要提前为它定义一个arginfo结构体来向内核通知此函数的这个特定行为。添加此函数到module_entry里需要这样:

ZEND_FE(byref_compiletime, byref_compiletime_arginfo)

byref_compiletime_arginfo十一个arginfo结构体,我们在前面的章节中已经用过一次了。

在Zend Engine 2 (PHP5+)中,arginfo的数据是由多个zend_arg_info结构体构成的数组,数组的每一个成员即每一个zend_arg_info结构体处理函数的一个参数。zend_arg_info结构体的定义如下:

typedef struct _zend_arg_info {
    const char *name;               /* 参数的名称*/
    zend_uint name_len;             /* 参数名称的长度*/
    const char *class_name;         /* 类名 */
    zend_uint class_name_len;       /* 类名长度*/
    zend_bool array_type_hint;      /* 数组类型提示 */
    zend_bool allow_null;           /* 是否允许为NULL */
    zend_bool pass_by_reference;    /* 是否引用传递 */
    zend_bool return_reference;     /* 返回值是否为引用形式 */ 
    int required_num_args;          /* 必要参数的数量 */
} zend_arg_info;

生成zend_arg_info结构的数组比较繁琐,为了方便PHP扩展开发者,内核已经准备好了相应的宏来专门处理此问题,首先先用一个宏函数来生成头部,然后用第二个宏生成具体的数据,最后用一个宏生成尾部代码。

#define ZEND_BEGIN_ARG_INFO(name, pass_rest_by_reference)   ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, ZEND_RETURN_VALUE, -1)
#define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args)   \
    static const zend_arg_info name[] = {                                                                       \
        { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args },


#define ZEND_ARG_INFO(pass_by_ref, name)        { #name, sizeof(#name)-1, NULL, 0, 0, 0, pass_by_ref, 0, 0 },
#define ZEND_ARG_PASS_INFO(pass_by_ref)         { NULL, 0, NULL, 0, 0, 0, pass_by_ref, 0, 0 },
#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, sizeof(#name)-1, #classname, sizeof(#classname)-1, 0, allow_null, pass_by_ref, 0, 0 },
#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, sizeof(#name)-1, NULL, 0, 1, allow_null, pass_by_ref, 0, 0 },


#define ZEND_END_ARG_INFO()     };

//这里我们先看
ZEND_BEGIN_ARG_INFO(name, pass_rest_by_reference)
ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference,required_num_args)

这两个宏函数的前两个参数的含义是一样的,name便是这个zend_arg_info数组变量的名字,这里我们定义它为:byref_compiletime_arginfo。pass_rest_by_reference如果被赋值为1,则代表着所有的参数默认都是需要以引用的方式传递的(在arginfo中单独声明的除外)。而对于ZEND_BEGIN_ARG_INFO_EX的后两个参数:

  • name和pass_rest_by_reference的含义同上。
  • return_reference:声明这个函数的返回值需要以引用的形式返回,这个参数已经在前面章节用过了。
  • required_num_args:函数被调用时,传递参数至少为前N个函数(也就是后面参数都有默认值),当设置为-1时,必须传递所有参数

接下来让我们看生成具体数据的宏:

ZEND_ARG_PASS_INFO(by_ref)
//强制所有参数使用引用的方式传递


ZEND_ARG_INFO(by_ref, name)
//如果by_ref为1,则名称为name的参数必须以引用的方式传递,


ZEND_ARG_ARRAY_INFO(by_ref, name, allow_null)
ZEND_ARG_OBJ_INFO(by_ref, name, classname, allow_null)
这两个宏实现了类型绑定,也就是说我们在传递某个参数时,必须是数组类型或者某个类的实例。如果最后的参数为真,则除了绑定的数据类型,还可以传递一个NULL数据。

//我们组合起来使用:
ZEND_BEGIN_ARG_INFO(byref_compiletime_arginfo, 0)
    ZEND_ARG_PASS_INFO(1)
ZEND_END_ARG_INFO()

为了使我们的扩展能够兼容PHP4,还需要使用#ifdef进行特殊处理。

#ifdef ZEND_ENGINE_2
    ZEND_BEGIN_ARG_INFO(byref_compiletime_arginfo, 0)
        ZEND_ARG_PASS_INFO(1)
    ZEND_END_ARG_INFO()
#else /* ZE 1 */
static unsigned char byref_compiletime_arginfo[] =   { 1, BYREF_FORCE };
#endif

我们copy一份ZEND_FUNCTION(byref_calltime)的实现,并重名成ZEND_FUNCTION(byref_compiletime)就行了。或者直接弄个ZEND_FALIAS就行了:

ZEND_FE(byref_compiletime,byref_calltime,byref_compiletime_arginfo)

php源代码解析:引用与函数执行

一个函数的执行结果要返回给调用者,除了使用return功能,还有一种办法,那就是以引用的形式传递参数,然后在内部修改这个参数的值。前一种方法往往只能返回一个值,如果我们的函数执行结果具有多种数据,便需...
  • zkaipmoo
  • zkaipmoo
  • 2013年12月28日 00:47
  • 870

JAVA实现EXCEL公式专题(六)——查找引用函数

查找引用函数主要有match、lookup、vlookup、hlookup这4种,这几个常用的方法也就不解释啦,直接上干货: /** * 项目名称: * 文件说明: * 主要...
  • u011680348
  • u011680348
  • 2015年08月14日 18:22
  • 1752

VT-x/AMD-V 硬件加速在您的系统中不可用。您的 64-位虚拟机将无法检测到 64-位处理器,从而无法启动。

vagrant up启动virtual box 发现报错:VT-x/AMD-V 硬件加速在您的系统中不可用。您的 64-位虚拟机将无法检测到 64-位处理器,从而无法启动。     目前Inter和A...
  • JoeChao1003
  • JoeChao1003
  • 2017年08月18日 11:32
  • 1609

确保线程执行函数体正确使用引用传递

1、前言     在启动一个线程时,我们首先需要定义一个线程执行体,这个可执行体可以是函数、仿函数或者lambda表达式。并且它们可以拥有任意数量的参数。如,     auto thHandler...
  • acaiwlj
  • acaiwlj
  • 2015年10月28日 18:30
  • 1052

C++函数返回引用

首先需要明白:C++函数为什么要返回引用? 答:这样就不用返回结果的副本。因为返回副本需要做赋值拷贝函数,浪费时间。这时候,实际上,返回是结果的副本,而不是结果本身。如果要返回本身,就返...
  • gao1440156051
  • gao1440156051
  • 2015年01月31日 10:55
  • 633

函数的参数为引用和指针的区别

C++之所以增加引用类型, 主要是把它作为函数参数,以扩充函数传递数据的功能。 到目前为止我们介绍过函数参数传递的两种情况。 1) 将变量名作为实参和形参 这时传给形参的是变量的值,传递...
  • u014774853
  • u014774853
  • 2016年10月22日 17:37
  • 1086

c++函数与引用

要求:趣味取球。已知袋中有若干黑球和白球(具体个数,键盘输入),每次从中取出两个球。如果取出的两个球为同色,则放入袋中一个白球;如取出的两个球异色,则放入袋中一个黑球。通过取出球的颜色调用两种处理函数...
  • baozhenyeye
  • baozhenyeye
  • 2010年12月06日 22:39
  • 83

perl 回调必须是函数引用

[root@wx03 lib]# cat a1.pl use AE; use AnyEvent; ##定义watch my $t = AnyEvent->timer( after ...
  • zhaoyangjian724
  • zhaoyangjian724
  • 2016年07月14日 10:19
  • 389

引用与函数(C++)

引用是C++与C的一个重要区别,对于函数laishu
  • fanadong1
  • fanadong1
  • 2014年06月17日 17:23
  • 261

Delphi按名字调用方法

按名字调用方法似乎一直以来都是大家比较关注的技术,在论坛上有一个经典的答复:  type    TProcedure = procedure(Test: string) of object;  pro...
  • qinxiongwei
  • qinxiongwei
  • 2006年12月08日 11:42
  • 662
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:64-引用与函数执行
举报原因:
原因补充:

(最多只允许输入30个字)