6

I'd like to create and return an array of arrays in a PHP extension. From what I understand, I should allocate the space for the array elements using emalloc(), but what I don't understand is when it is appropriate to free it. I have a PHP function similar to this:

PHP_FUNCTION(test)
{
    int i;
    zval **pt = emalloc(sizeof(zval*) * 10);

    array_init(return_value);

    for (i = 0; i < 10; ++i) {
        MAKE_STD_ZVAL(pt[i]);
        array_init(pt[i]);
        add_index_double(pt[i], 0, 1);
        add_index_zval(return_value, i, pt[i]);
    }
}

Where should I free the memory allocated for pt?

rid
  • 61,078
  • 31
  • 152
  • 193

2 Answers2

6

In this case, you don't have to. When the variable that you're returning is destroyed, its memory is freed. Since you're returning an array, all the elements of the array will be destroyed by that time as well (to be more precise, their reference count is decreased when the array is deleted, only if they have no other references by that time will they be freed).

You can manually decrease the reference count of a zval by calling zval_ptr_dtor. When its reference count reaches 0, this will free its memory as well.

Technically, an array variable is backed by a HashTable. When the variable is destroyed, the hash table is destroyed as well. By this, the "destructor callback" associated with the HashTable as called as well, once with each of the hash table elements as an argument. When you call array_init, it also creates a hash table with zval_ptr_dtor as the destructor function.

Also note that you make calls to emalloc in two places here. The first is explicit, the other is via MAKE_STD_ZVAL. The first one is unnecessary, but if you use it, you should call efree before your function returns otherwise its memory leaks because it's not associated with any automatic memory management mechanism like PHP variables are.

Artefacto
  • 96,375
  • 17
  • 202
  • 225
  • I am calling `array_init(pt[i]);` right after `MAKE_STD_ZVAL()`, forgot to add it to the example. So, if I understand correctly, I shouldn't call `emalloc()` at all, because `MAKE_STD_ZVAL()` is responsible for allocating the memory, and the memory gets deallocated when its reference count arrives at zero (and that should happen when it gets out of scope in PHP, if it's only referenced once). – rid Apr 10 '12 at 18:01
  • @Radu Yes, `MAKE_STD_ZVAL` allocates (with `emalloc`) the zval as well. However, your first call to `emalloc` is not allocating a zval, it's allocating array of 10 zval*, which is not the same thibg. Like `actual` said, you can use a local variable for that, or you can abandon the array completely and do: `{ zval *zv; MAKE_STD_ZVAL(zv); add_index_double(zv, 0, 1); add_index_zval(return_value, i, zv); }`. – Artefacto Apr 10 '12 at 18:23
1

There is no need to allocate memory using emalloc in this case, just use zval *pt[10] or reduce it to single reusable zval, MAKE_STD_ZVAL will handle all memory (de)allocation and reference counting stuff.

actual
  • 2,370
  • 1
  • 21
  • 32