#c
#c
Вопрос:
Я пытаюсь написать очень простой lexxer на C и имею следующий код, который должен просто делать что-то вроде следующего:
Ввод: "12 142 123"
Вывод:
NUMBER -- 12
NUMBER -- 14
NUMBER -- 123
Однако у меня возникла проблема, из-за которой, если я не включу начальный printf("")
оператор перед циклом ввода, я получу такой результат:
Вывод:
NUMBER --
NUMBER -- 14
NUMBER -- 123
где первое число просто пустое. Я действительно не понимаю, почему это происходит, и был бы очень признателен за помощь в этом!
У меня есть следующий код (с опущенным рядом нерелевантных функций)
#define MAX_LEN 400
char* input;
char* ptr;
char curr_type;
char curr;
enum token_type {
END,
NUMBER,
UNEXPECTED
};
typedef struct {
enum token_type type;
char* str;
} Token;
void print_tok(Token t) {
printf("%s -- %sn", token_types[t.type], t.str);
}
char get(void) {
return *ptr ;
}
char peek(void) {
return *ptr;
}
Token number(void) {
char arr[MAX_LEN];
arr[0] = peek();
get();
int i = 1;
while (is_digit(peek())) {
arr[i] = get();
i;
}
arr[ i] = '';
Token ret = {NUMBER, (char*)arr};
return ret;
}
Token unexpected(void) {
// omitted
}
Token next(void) {
while (is_space(peek())) get();
char c = peek();
switch (peek()) {
case '0':
// omitted
case '9':
return number();
default:
return unexpected();
}
}
int main(int argc, char **argv) {
printf(""); // works fine with this line
input = argv[1];
ptr = input;
Token tokens[MAX_LEN];
Token t;
int i = 0;
do {
t = next();
print_tok(t);
tokens[i ] = t;
} while (t.type != END amp;amp; t.type != UNEXPECTED);
return 0;
}
Комментарии:
1.
Token ret = {NUMBER, (char*)arr};
Вы используете там указатель на локальную переменную. Вам нужно выделитьmalloc
пробел для строки term и скопировать.2. То, что вы описываете, имеет неопределенное поведение, записанное повсюду.
Ответ №1:
In number
— arr
локальная переменная. Локальная переменная уничтожается, когда ее функция завершается, и ее содержимое становится непредсказуемым. Тем не менее, ваша программа затем печатает его значение с помощью указателя в Token
структуре.
Выводимое значение непредсказуемо. Дополнительный printf("")
оператор может заставить компилятор изменить код таким образом, чтобы переменная не перезаписывалась или что-то в этом роде. Вы не можете полагаться на это.
У вас есть несколько других вариантов выделения памяти для каждого токена:
- Измените
str
,token
чтобы это был массив символов вместо указателя. Тогда у каждого токена есть свое собственное пространство для хранения строки. - Выделите строку с
malloc
помощью . Затем он остается выделенным до тех пор, пока выfree
его не получите. - Создайте массив в
main
, чтобы он действителен для обоихnext
иprint_tok
. Вам нужно будет указатьnext
указатель на массив, чтобы он знал, где он должен хранить строку. Одновременно будет храниться только одна строка токена. - В принципе, любой другой способ создания массива, кроме превращения его в локальную переменную
next
. - Сделайте так, чтобы указатель указывал на то, где находится токен в исходной строке. Добавьте еще одну переменную, в
Token
которой хранится длина токена.
Я думаю, что первый вариант самый простой, а последний вариант использует наименьшее количество памяти, но я включил некоторые другие варианты для полноты.
Комментарии:
1. Спасибо, это имеет смысл. Было бы решением выделить нелокальную память для структуры, а затем вернуть указатель? Или есть другой предпочтительный метод в подобной ситуации?
2. @mlz7 вы можете: (1) ‘malloc’ массив символов (и освободить его где-нибудь еще), (2) объявить arry локально в main (или как глобальную переменную …) и передать его в качестве параметра
number
, (3) возможно, самое быстрое решение — просто использоватьstatic
хранилище втакnumber
что время жизни массива будет равняться времени жизни всей программы3. @mlz7 первое решение является более «общим», поскольку оно применимо и к многопоточной среде. В любом случае, если ваше приложение является однопоточным, решения 2 и 3 являются третьими по быстродействию.
4. @mlz7 всегда пожалуйста. Ps: этот ответ был бы отличным и получил бы мое одобрение, если бы он содержал также некоторые предложения о том, как устранить проблему (вместо этого раздел кода кажется идентичным коду OP). Вы можете адаптировать мои комментарии, если хотите.