2013-02-12 2 views
4

Мы имеем этот прототип функции:Обеспечение статической памяти для параметра функции

BNode *b_new_node(const char *name, int pos, int len, const char *val); 

Большая часть кода с помощью этого (и подобные) являются автоматически генерируемый код, и выглядит следующим образом:

b = b_new_node("foo.bar.id.1", 0, 10, some_data); 

Функция выделяет новый BNode и копирует в него строку val, но он просто назначает члену name указателю, например

b_strlcpy(new_node->val, val, sizeof new_node->val); 
new_node->name = name; 

Это разрушает опустошение, если первый аргумент в b_new_node ("foo.bar.id.1", 0, 10, some_data); не является строковым литералом или иным способом со статической продолжительностью хранения, но, например, буфер в стеке.

В любом случае, с gcc (другие компиляторы тоже представляют интерес), мы можем проверить время компиляции, чтобы этот аргумент передавался из статического хранилища?

(конечно, простой способ избежать этих возможных проблем - скопировать этот аргумент в узел - измерения, которые мы сделали с этим подходом, увеличивают потребность в памяти на 50% и замедляют работу программы на 10%, поэтому подход нежелательно).

+2

Извините, что не ответил на хороший вопрос, но: Вы уверены, что вам нужна * статическая * строка?Думаю, что строка, которая живет дольше, чем узел? Классическим способом решения этой проблемы является документация о ожидаемом времени жизни содержимого 'name' и программисты, читающие документацию. Субоптимальный, я знаю, но стандарт. – thiton

+0

@thiton Конечно, в принципе вам нужно только то, что живет дольше, чем узел. Но так как большая часть кода, использующего это, автоматически генерируется, с строковым литералом и тем фактом, что обнаружение строкового литерала или статического хранилища звучит как более легкое задание для компилятора, чем отслеживание срока действия сурового объекта - это будет подходящий подход. – user964970

+0

@larsmans уверен, что вопрос, похоже, о проверке времени выполнения? OP, похоже, требуется только проверка времени компиляции. – nos

ответ

0

Вы можете сделать BNode условно скопировать имя. Это потребует и дополнительный бит памяти в BNode. Например .:

typedef struct BNode { 
    char const* name; 
    unsigned char : own_name; 
} BNode; 

void b_copy_name(BNode* n) { 
    if(!n->own_name) { 
     char* p = strdup(n->name); 
     if(p) { 
      n->own_name = 1; 
      n->name = p; 
     } 
     else { 
      abort(); 
     } 
    } 
} 

void b_destroy(BNode* n) { 
    // ... 
    if(n->own_name) 
     free(n->name); 
} 
+0

В то время как это возможно, для этого требуется, чтобы программист поступал правильно, вместо того, чтобы компилятор проверял этот факт. Кажется, что __builtin_constant_p() может мне помочь, но у меня пока нет его работы. – user964970

+0

@ user964970 В этом случае '__builtin_constant_p' может использоваться только на сайте вызова, то есть снова программист должен поступать правильно, или обернуть его в макрос. –

1

Это определит строковых литералов:

#include <stdio.h> 

#define PRINT_IT(c) do {\ 
if (__builtin_constant_p(c))\ 
    print_it(c, 1);\ 
else \ 
    1/__builtin_constant_p(c);\ 
} while (0) 


void print_it(const char *c, int is_static) 
{ 
    printf("%s is a constant %d\n", c, is_static); 
} 

int main(int argc, char *argv[]) 
{ 
    char bar[] = "bar"; 
    PRINT_IT("Foo"); //line 19 
    PRINT_IT(bar); //line 20 

return 0; 

} 


$ gcc foo.c 
foo.c: In function ‘main’: 
foo.c:20: warning: division by zero 

Таким образом, вы можете обернуть вашу функцию b_new_node() в макросе, возможно, только для отладки сборки, и использовать деление на ноль предупреждений.

Обратите внимание, что он определяет только строковые литералы как «аргументы», а не статическое хранилище, например.

const char *foo = "foo"; 
PRINT_IT(foo); //will generate a warning 
PRINT_IT("foo"); //will not generate a warning 
PRINT_IT(global_foo); //will generate a warning, even when. 
         //global_foo is const char *foo = "foo"; or 
         //global_foo is const char foo[] = "foo"; 
1

Обычно нет; нет средства C-обеспечения, чтобы узнать, указывает ли указатель на что-то в статическом хранилище. Конкретные среды и структуры данных могут изменить обстоятельства - например, проверка, если указанный адрес указан в read-only memory segment.

Экономия пространства за счет противодействия дублирования

Чтобы устранить дублирующие значения, так как указывает name, вы могли бы использовать the flyweight pattern, который не сильно отличается от string interning.

В основном вы создаете центральный набор встреченных жетонов и сохраняете только ссылку на каждый токен. Ссылки могут быть индексами или указателями массива.

Для быстрой очистки вы можете комбинировать мухи с reference counting, где отсчет нуля означает отсутствие ссылок.

Чтобы сохранить производительность центрального хранилища высоко, используйте структуру данных, где поиск выполняется быстро, например, набор, или с map, если используется reference counting.

Смежные вопросы