В большинстве случаев вы захотите использовать @ dbu sh динамический c распределение решение . Поскольку вы, вероятно, в любом случае используете это с динамическим распределением памяти c в некотором роде, динамическое выделение блоков дескрипторов не должно быть огромными накладными расходами.
Однако, при некоторых действительно ограниченных обстоятельствах, которые вам придется Полицейский сам, и, предполагая, что вы не используете допотопную версию компилятора C, это можно сделать довольно просто с помощью составных литералов . Помимо ограничения версии C компилятора (C99 или выше, что не должно быть огромным бременем), это будет работать точно в тех же условиях, что и ваша правка # 1, использующая конкатенацию токенов для генерации уникального имени: то есть, если не используется макрос LT_mark
внутри ограниченного блока, подчиненного функции.
Причина этого ограничения, которое, как я уже говорил, относится и к вашему решению с конкатенацией токенов, заключается в том, что время жизни автоматических c распределений заканчивается, когда управление выходит из блока, в котором они были объявлены. Это важный аспект C (и многих других языков программирования), поэтому важно четко понимать, как он работает.
Вот простой пример:
int example (){
struct LifeTime *lt_head = NULL;
char *s = malloc(64); LT_mark(s, free);
for (int i = 0; i < 4; ++i) {
/* InnerBlock */
char *s2 = malloc(64); LT_mark(s2, free);
....
}
/* Lifetime of all variables declared in InnerBlock expires */
....
/* If lt_head points to a struct automatically allocated inside
* InnerBlock, it is now a dangling pointer and cannot be used.
* The next statement is Undefined Behaviour.
*/
freeTheMallocs(lt_head);
}
Обратите внимание, что проблема не в том, что внутренний блок выполняется более одного раза (хотя это, вероятно, гарантирует, что вы заметите проблему). То же самое произошло бы, если бы я написал это как условное выражение:
int example (int flag){
struct LifeTime *lt_head = NULL;
char *s = malloc(64); LT_mark(s, free);
if (flag) {
/* InnerBlock */
char *s2 = malloc(64); LT_mark(s2, free);
....
}
/* Lifetime of all variables declared in InnerBlock expires */
....
freeTheMallocs(lt_head); /* Dangling pointer */
}
Вышеуказанное не может работать с автоматическим c распределением блоков дескрипторов (но оно будет работать нормально с Dynami c выделение).
ОК, что произойдет, если вы абсолютно пообещаете использовать LT_mark
только во внешнем блоке своей функции, как в исходном примере:
int example (){
struct LifeTime *lt_head = NULL;
char *s = malloc(64); LT_mark(s, free);
char *s2 = malloc(64); LT_mark(s2, free);
freeTheMallocs(lt_head);
}
Это сработает. Ваша единственная проблема заключается в том, как обеспечить соблюдение ограничения, в том числе для всех программистов, которые будут изменять код после того, как вы покидаете проект, и, возможно, не будете иметь ни малейшего представления о том, почему им не разрешено вкладывать LT_mark
в блок ( или даже знаете, что им нельзя это делать).
Но если вам нравится играть с огнем, вы можете сделать это так:
#define LT_mark(NAME, DELETE) \
lt_head = &(struct LifeTime){ \
.delete=DELETE, \
.prev=lt_head, \
.ref=NAME \
}
Это будет работать, в ограниченный набор случаев, в которых он работает, потому что составной литерал, созданный макросом, «имеет автоматическую c продолжительность хранения, связанную с включающим блоком». (§6.5.2.5 / 5).
Честно говоря, я искренне надеюсь, что вы не используете приведенный выше код. Я даю этот ответ главным образом в надежде, что он дает какое-то объяснение важности понимания воплощений.