Проблемы с Лексом

Я пишу программу на C. Для этой цели я использую lex. Я написал фрагмент кода на C для создания таблицы символов и управления ею. Итак, всякий раз, когда lex находит новый символ, он помещает его в таблицу символов. Проблема в том, что когда я пытаюсь распечатать все результаты из таблицы символов, я получаю результат, которого не ожидал. Если, например, входной файл был:

int main(){}

вывод должен быть:

int
main
(
)
{
}

но вывод:

int main(){}
main(){}
(){}
...

и так далее. Функция, используемая для печати, выглядит примерно так

void print_entries(struct symtab *start) {
   struct symtab *s = start;
   while(s != NULL) {
      printf("%s\n", s->name);
      s = s->next;
   }
}

Вот код для добавления новых символов:

void add_entry(char* name, int type, struct symtab *start)
{
   struct symtab *new;
   new = malloc(sizeof(struct symtab));
   last_entry(start)->next = new;
   new->name = name;
   new->type = type;
   new->next = NULL;
}

Любые идеи?


person Melvin Smiley    schedule 30.05.2011    source источник
comment
(Это может быть глупый вопрос, но вы вызываете print_entries только один раз?)   -  person marnir    schedule 30.05.2011
comment
Да, я звоню только один раз :)   -  person Melvin Smiley    schedule 30.05.2011
comment
Извините, я подумал, что должен спросить :P   -  person marnir    schedule 30.05.2011
comment
Похоже, вы не правильно создаете свои символы. Если вы посмотрите на свой вывод, имя не содержит содержимого токена, а является просто указателем на текст. Вы должны выделить имена для хранения только токена.   -  person Jeff Mercado    schedule 30.05.2011
comment
Я заметил то же самое. Но я не вижу ошибки в своем коде.   -  person Melvin Smiley    schedule 30.05.2011
comment
А как add_entry() называется? Вы ничего не выделили для имени здесь.   -  person Jeff Mercado    schedule 30.05.2011
comment
Джефф прав. Вы прячете указатели во входной буфер в ваших ->name полях; вам нужно выделить место для правильного количества символов (которое, похоже, вы в настоящее время не знаете в add_entry - это нужно будет передать), скопировать и завершить нулем.   -  person zwol    schedule 30.05.2011
comment
Можете ли вы дать мне пример кода для этого? :)   -  person Melvin Smiley    schedule 30.05.2011
comment
проще всего было бы использовать strdup(yytext) в коде lex, который вызывает add_entry. В качестве альтернативы используйте new->name = strdup(name); в add_entry   -  person Chris Dodd    schedule 30.05.2011
comment
Я мог ошибаться, но я думал, что yytext не обязательно будет заканчиваться нулем.   -  person zwol    schedule 30.05.2011


Ответы (1)


Вам необходимо скопировать имена символов в записи таблицы символов. Если по какой-то причине в вашей системе еще нет strdup(), используйте:

#include <string.h>
#include <stdlib.h>

char *strdup(const char *str)
{
   size_t len = strlen(str) + 1;
   char *dup = malloc(len);
   if (dup != 0)
       memmove(dup, str, len);
   return dup;
}

(В этом контексте я мог бы безопасно использовать memcpy(); я использую memmove(), потому что он всегда работает, а memcpy() — нет. И я использую memmove(), потому что я точно знаю длину строки, поэтому копии не нужно проверять каждый символ на наличие нулевого значения, поскольку это идет.)

С strdup() под рукой:

void add_entry(char* name, int type, struct symtab *start)
{
   struct symtab *sym;
   sym = malloc(sizeof(struct symtab));
   last_entry(start)->next = sym;
   sym->name = strdup(name);
   sym->type = type;
   sym->next = NULL;
}

Обратите внимание, что это по-прежнему исключает проверку ошибок из двух распределений памяти, что не является хорошей привычкой. Я пересмотрел его, чтобы использовать sym, а не new, потому что последнее является ключевым словом C++, и я избегаю использования их в качестве идентификаторов даже в коде C.

person Jonathan Leffler    schedule 30.05.2011