2013-11-17 5 views
0

Я пытаюсь получить выражение от пользователя и поместить его в динамически создаваемую строку. Вот код:Динамически созданная строка C

char *get_exp() { 
    char *exp, *tmp = NULL; 
    size_t size = 0; 
    char c; 
    scanf("%c", &c); 

    while (c != EOF && c != '\n') { 
     tmp = realloc(exp, ++size * sizeof char); 
     if (tmp == NULL) 
      return NULL; 

     exp = tmp; 
     exp[size-1] = c; 
     scanf("%c", &c); 
    } 
    tmp = realloc(exp, size+1 * sizeof char); 
    size++; 
    exp = tmp; 
    exp[size] = '\0'; 
    return exp; 
} 

Однако первый считанный символ является символом новой строки символ каждый раз, когда по какой-то причине, так что выходит из цикла. Я использую XCode, может быть, это и есть причина проблемы?

+0

Проверено на Ubuntu/ССАГПЗ - не проблема, если ехр инициализируется NULL (или это будет крах) , Вы пробовали самый простой тест - просто главный() + вызов get_exp()? И как вы это называете, в командной строке в XCode? – Michael

+1

В отличие от 'c = getc()', 'scanf ("% c ", & c)' никогда не может привести к значению 'EOF'. Поэтому тестирование для 'EOF' не нужно. – glglgl

+0

Обратите внимание, что стандартные функции ввода-вывода буферизируются в строке и не возвращают ничего, пока вы не нажмете enter. В этом нет стандартного способа, это зависит от ОС, и я не знаю, как это сделать в MacOS. Другое дело, в таком случае вы можете избежать дублирования кода с помощью 'for (;;) {something ...; if (endcondition) break; Нечто большее...; } '. – hyde

ответ

1

Нет, XCode не является частью вашей проблемы (это плохой рабочий, который обвиняет его инструменты).

Вы не инициализировали exp, что вызовет проблемы.

Ваш код для обнаружения EOF полностью сломан; вы должны проверить возвращаемое значение scanf() для обнаружения EOF. Вы бы лучше с помощью getchar() с int c:

int c; 

while ((c = getchar()) != EOF && c != '\n') 
{ 
    ... 
} 

Если вы чувствуете, что вы должны использовать scanf(), то вам необходимо проверить каждый вызов scanf():

char c; 

while (scanf("%c", &c) == 1 && c != EOF) 
{ 
    ... 
} 

Вы не проверить результат realloc() в петля; это хорошо. Вы не проверяете результат realloc() после цикла (и вы не сокращаете свое распределение); пожалуйста, проверьте каждый раз.

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

Конечно, если целью является просто прочитать строку, то было бы проще использовать POSIX getline(), который обрабатывает все выделение для вас. Кроме того, вы можете использовать fgets(), чтобы прочитать строку. Вы можете использовать фиксированный буфер для сбора данных, а затем скопировать его в соответствующий динамически распределенный буфер. Вы также позволили бы предположить, что линия очень длинная, поэтому вы должны убедиться, что у вас действительно есть новая линия.

0

Здесь, в Windows XP/cc, как сказал Майкл, он работает, если exp инициализируется значением NULL.

0

Вот фиксированный код, с комментариями, объясняя, что отличается от кода в вопросе:

char *get_exp() 
{ 
    // keep variables with narrowest scope possible 
    char *exp = NULL; 
    size_t size = 0; 

    // use a "forever" loop with break in the middle, to avoid code duplication 
    for(;;) { 

     // removed sizeof char, because that is defined to be 1 in C standard 
     char *tmp = realloc(exp, ++size); 
     if (tmp == NULL) { 
      // in your code, you did not free already reserved memory here 
      free(exp); // free(NULL) is allowed (does nothing) 
      return NULL; 
     } 
     exp = tmp; 

     // Using getchar instead of scanf to get EOF, 
     // type int required to have both all byte values, and EOF value. 
     // If you do use scanf, you should also check it's return value (read doc). 
     int ch = getchar(); 
     if (ch == EOF) break; // eof (or error, use feof(stdin)/ferror(stdin) to check) 
     if (ch == '\n') break; // end of line 
     exp[size - 1] = ch; // implicit cast to char 
    } 

    if (exp) { 
     // If we got here, for loop above did break after reallocing buffer, 
     // but before storing anything to the new byte. 
     // Your code put the terminating '\0' to 1 byte beyond end of allocation. 
     exp[size-1] = '\0'; 
    } 
    // else exp = strdup(""); // uncomment if you want to return empty string for empty line 
    return exp; 
} 
+0

Как сказано другими, обратите внимание, что увеличение буфера на один байт на один байт очень неэффективно. Вы должны удвоить буфер с каждым realloc, а затем, наконец, перераспределить его, чтобы исправить размер, когда вся строка читается, и точный размер известен. – hyde

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