В расширении 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
недопустимо в последней строке, потому что (при условии, что он возвращает значение) временный результат myclass->test()
существует только на протяжении первой строки. Результат myclass->test().c_str()
Таким образом, также, действителен только на протяжении первой строки. arg
сразу после этого становится висящим указателем.
Ваш обходной путь — правильный способ исправить это. Я бы рекомендовал, при использовании любого из этих макросов, убедиться, что вы передаете ему указатель на данные, которые конечно все еще существует, по крайней мере, до тех пор, пока выполняется макрос, независимо от того, сколько операторов / выражений может в нем содержаться.
Вот что я бы сделал:
if (myclass != NULL) {
const string& retval = myclass->test();
RETURN_STRING(retval.c_str(), 1);
}
Теперь независимо от того, что myclass->test()
это переживет все расширенные операторы макроса, и вам не нужно было копировать его в новый std::string
объект.
Других решений пока нет …