Printf в цикле не работает без начальной инструкции печати

#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). Вы можете адаптировать мои комментарии, если хотите.