2014-02-19 8 views
0

В настоящее время, у меня есть очень простая функция для освобождения массива двойников в моей программе:Функция Variadic в C99 для освобождения нескольких массивов?

void deallocate(double** array) 
{ 
    free(*array); 
} 

Я хотел бы эту функцию, чтобы быть VARIADIC для того, чтобы занять несколько массивов, и освободить их один за другим. Я никогда не писал вариационную функцию, и, поскольку могут существовать трюки с указателями, я хотел бы знать, как это сделать.

+2

Нравится? http://stackoverflow.com/a/20993120/2327831 – this

+0

Это невозможно сделать без #define? – Vincent

+0

Конечно, вам просто нужно каждый раз вводить первый и последний аргументы. В этом случае я предпочел бы сделать массив и передать его, а не использовать variadic f. – this

ответ

3

Не делайте этого с помощью вариационной функции, эта концепция должна быть удалена. В частности, это не имеет никакого смысла для чего-то, что должно принимать аргументы одного и того же типа, void*.

Просто простую функцию, первый, который принимает массив указателей

void free_arrays(void* a[]) { 
    for (size_t i = 0; a[i]; ++i) free(a[i]); 
} 

Затем вы можете обернуть, что с помощью макроса, как тот

#define FREE_ARRAYS(...) free_arrays((void*[]){ __VA_ARGS__, 0 }) 

Это предполагает, что ни один из ваших указателей нет уже 0, так как обработка остановится в этой точке.

Если у вас есть необходимость в работе, даже если некоторые из указателей равны 0, вам необходимо передать количество элементов в качестве первого параметра вашей функции. Это немного утомительно, но может быть определено и в макросе.

void free_arrays0(size_t n, void* a[]) { 
    for (size_t i = 0; i < n; ++i) free(a[i]); 
} 

#define FREE_ARRAYS0(...)         \ 
    free_arrays(            \ 
       sizeof((void*[]){ __VA_ARGS__})/sizeof(void*), \ 
       (void*[]){ __VA_ARGS__}      \ 
      ) 
+0

«немного утомительно»? 'sizeof ((void * []) ​​{__VA_ARGS__})/sizeof (void *)' должен работать, правильно? – hvd

+0

@hvd, да, конечно, см. Мое редактирование. Многие люди, вероятно, считают такой код неразборчивым. –

+0

Ах, ладно, я не из тех, но для повышения удобочитаемости вы можете обернуть его как '#define LENGTHOF (x) (sizeof (x)/sizeof (* (x))' (уже вообще полезно в других ситуаций), а затем выполните 'LENGTHOF ((void * []) ​​{__VA_ARGS__})' – hvd

2

Вы можете сделать это следующим образом:

void deallocate(double *p, ...) 
{ 
    va_list ap; 

    va_start(ap, p); 
    do { 
     free(p); 
     p = va_arg(ap, double *); 
    } while (p); 
    va_end(ap); 
} 

вызов как deallocate(p1, p2, p3, (double *)NULL). Вам нужен NULL (или какое-то другое значение) в качестве часового, чтобы сигнализировать конец списка аргументов; ни один из других указателей не должен быть NULL, или цикл остановится преждевременно.

Я не говорю, что это хорошая идея: функции varargs имеют свои прецеденты, но они подвержены ошибкам с указателями, потому что некоторые неявные преобразования не происходят (поскольку компилятор не выполняет знать тип аргументов за пределами первого).

+0

Вам нужно фактически передать '(double *) NULL', если вы прочитали его как' double * 'inside' deallocate'. – hvd

+0

@hvd Нет, вам это не нужно. 'void *' совместим с любым указателем, и вы никогда не разыгрываете его. – this

+0

@self. Да, да. Компилятор не знает, как неявно преобразовывать 'NULL' в любой другой тип при передаче через' ... '. Легкий пример того, когда он ломается: 'NULL' определяется как' 0'. 'int' имеет 32 бита. 'double *' имеет 64 бита. Как ты думаешь, что произойдет? (Кроме того, 'void *' не обязательно должен быть совместим с другими типами указателей, он должен поддерживать только конверсии из других типов указателей. Но реализации, где *, * breaks, намного реже.) – hvd