PHP 扩展 - RETURN_STRING

PHP Extensions - RETURN_STRING

在PHP扩展中,这有什么区别:

PHP_METHOD(TestExtension, test)
{
  MyClass *myclass;
  MyClass_Object *obj = (MyClass_Object*)zend_object_store_get_object(getThis() TSRMLS_CC);
  myclass = obj->myclass;
  if (myclass != NULL) 
  {
    string retval = myclass->test();
    RETURN_STRING(retval.c_str(), 1);
  }
  RETURN_NULL();
}

还有这个:

PHP_METHOD(TestExtension, test)
{
  MyClass *myclass;
  MyClass_Object *obj = (MyClass_Object*)zend_object_store_get_object(getThis() TSRMLS_CC);
  myclass = obj->myclass;
  if (myclass != NULL) 
  {
    RETURN_STRING(myclass->test().c_str(), 1);
  }
  RETURN_NULL();
}

?

两者 似乎 都可以工作,但是当我 运行 valgrind 时:

valgrind --tool=memcheck --num-callers=30 --log-file=./php.log /usr/bin/php test.php

其中 test.php 是:

<?php
  $obj = new TestExtension("testing");
  echo $obj->test() . "\n";
?>

然后后者给我一大堆错误,都是:

Address 0xe4c3e98 is 24 bytes inside a block of size 66 free'd

valgrind总结如下:

126 ==23067== HEAP SUMMARY:
127 ==23067==     in use at exit: 9,031 bytes in 15 blocks
128 ==23067==   total heap usage: 25,131 allocs, 25,116 frees, 4,435,757 bytes allocated
129 ==23067==
130 ==23067== LEAK SUMMARY:
131 ==23067==    definitely lost: 0 bytes in 0 blocks
132 ==23067==    indirectly lost: 0 bytes in 0 blocks
133 ==23067==      possibly lost: 0 bytes in 0 blocks
134 ==23067==    still reachable: 9,031 bytes in 15 blocks
135 ==23067==         suppressed: 0 bytes in 0 blocks
136 ==23067== Rerun with --leak-check=full to see details of leaked memory
137 ==23067==
138 ==23067== For counts of detected and suppressed errors, rerun with: -v
139 ==23067== ERROR SUMMARY: 48 errors from 5 contexts (suppressed: 0 from 0)

尽管 RETURN_STRING 宏的文档中没有说明(这似乎是一个遗漏:它肯定会说明生命周期要求),但宏似乎扩展到不止一行。

比如说:

RETURN_STRING(myclass->test().c_str(), 1);

变为:

const char* arg = myclass->test().c_str();
someCode();
someMoreCode();
finalCode(arg);

arg 在最后一行无效,因为(假设按值 returns)myclass->test() 的临时结果仅在第一行期间存在。因此,myclass->test().c_str() 的结果也仅在第一行期间有效。 arg 之后立即变为悬挂指针。

您的解决方法是解决此问题的正确方法。我建议,在使用这些宏中的任何一个时,确保向它传递一个指向数据的指针,该指针至少在宏运行期间 肯定 仍然存在,无论有多少statements/expressions可能涉及。

我会这样做:

if (myclass != NULL) {
   const string& retval = myclass->test();
   RETURN_STRING(retval.c_str(), 1);
}

现在,无论 myclass->test() 是什么,它都会在宏的所有扩展语句中继续存在,您不必将它复制到新的 std::string 对象中。