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
对象中。
在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
对象中。