Чтение строк с неопределенной длиной в C - PullRequest
8 голосов
/ 30 августа 2010

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

Я не очень хорош в программировании на C, и меня попросили прочитать «строковый» ввод с неопределенной длиной.

Это мое решение

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

char *newChar();
char *addChar(char *, char);
char *readLine(void);

int main() {
  char *palabra;
  palabra = newChar();

  palabra = readLine();
  printf("palabra=%s\n", palabra);

  return 0;
}

char *newChar() {
  char *list = (char *) malloc(0 * sizeof (char));
  *list = '\0';
  return list;
}

char *addChar(char *lst, char num) {
  int largo = strlen(lst) + 1;
  realloc(&lst, largo * sizeof (char));
  *(lst + (largo - 1)) = num;
  *(lst + largo) = '\0';
  return lst;
}

char *readLine() {
  char c;
  char *palabra = newChar();

  c = getchar();
  while (c != '\n') {
    if (c != '\n') {
      palabra = addChar(palabra, c);
    }
    c = getchar();
  }
  return palabra;
}

Пожалуйста, я буду признателен, если вы поможете мне, сообщив мне, если это хорошая идея, или дайте мне какую-то другую идею (а также сообщите мне, если это «правильное» использование для указателей).

Заранее спасибо


РЕДАКТИРОВАТЬ: Ну, спасибо за ваши ответы, они были очень полезны.Теперь я публикую отредактированный (и я надеюсь, что лучше) код, может быть, он может быть полезен для новичка в C (как я) и получит обратную связь.

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


void reChar(char **, int *);
void readLine(char **, int *);

int main() {
    char *palabra = NULL;
    int largo = 0;

    reChar(&palabra, &largo);
    readLine(&palabra, &largo);
    printf("palabra=%s\n", palabra, largo);

    system("pause");
    return 0;
}

void reChar(char **lst, int *largo) {
    (*largo) += 4;
    char *temp = (char*) realloc(*lst, (*largo) * sizeof (char));

    if (temp != NULL) {
        *lst = temp;
    } else {
        free(*lst);
        puts("error (re)allocating memory");
        exit(1);
    }
}

void readLine(char **lst, int *largo) {
    int c;
    int pos = 0;

    c = getchar();
    while (c != '\n' && c != EOF) {
        if ((pos + 1) % 4 == 0) {
            reChar(lst, largo);
        }
        (*lst)[pos] =(char) c;
        pos++;
        c = getchar();
    }
    (*lst)[pos] = '\0';
}

PS:

  • Кажется, достаточно замедлить увеличение размера "палабры".

  • Я не уверен, что захват getchar() в int, а затем приведение его в char - правильный путь к ловушке EOF ловушка

Ответы [ 5 ]

23 голосов
/ 30 августа 2010
  1. Посмотрите определение POSIX getline().

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

  3. Знайте, что malloc(0) может возвращать нулевой указатель или ненулевой указатель, который нельзя использовать (поскольку он указывает на ноль байтов памяти).

  4. Вы не можете писать '*list = '\0';, когда список указывает на ноль байтов выделенной памяти; у вас нет разрешения писать там. Если вы получите NULL, вы, скорее всего, получите дамп ядра. В любом случае вы вызываете неопределенное поведение, которое называется « A Bad Idea ™». ( Спасибо )

  5. palabra = newChar(); в main() приводит к утечке памяти - при условии, что вы исправите другие проблемы, которые уже обсуждались.

  6. Код в readLine() не учитывает возможность получения EOF до получения новой строки; это плохо и приведет к дампу ядра при сбое выделения памяти (наконец).

  7. Ваш код будет демонстрировать низкую производительность, поскольку он выделяет один символ за раз. Как правило, вы должны выделять значительно больше, чем один дополнительный символ за раз; лучше начать с начального выделения, возможно, 4 байта и удваивать распределение каждый раз, когда вам нужно больше места. Сохраняйте начальное распределение небольшим, чтобы код перераспределения был правильно протестирован.

  8. Возвращаемое значение из getchar() является int, а не char. На большинстве машин он может возвращать 256 различных положительных символьных значений (даже если char является типом со знаком) и отдельное значение EOF, которое отличается от всех char значений. (Стандарт позволяет возвращать более 256 различных символов, если в машине есть байты размером более 8 бит каждый.) ( Спасибо ) Стандарт C99 §7.19.7.1 говорит о fgetc():

    Если индикатор конца файла для входного потока, на который указывает поток, не установлен и следующий символ присутствует, функция fgetc получает этот символ как неподписанный char преобразуется в int и увеличивает индикатор положения соответствующего файла для поток (если определен).

    (выделение добавлено). Он определяет getchar() в терминах getc() и getc() в терминах fgetc().

  9. (Заимствовано: Спасибо ). Первый аргумент realloc() - это указатель на начало выделенной в данный момент памяти, а не указатель на начало выделенной в данный момент памяти. Если вы не получили предупреждение о компиляции, вы не компилируете достаточно предупреждений, установленных на вашем компиляторе. Вы должны включить предупреждения максимально. Вы должны учитывать предупреждения компилятора - они обычно указывают на ошибки в вашем коде, особенно когда вы все еще изучаете язык.

  10. Часто проще сохранить строку без нулевого терминатора, пока вы не узнаете, что достигли конца строки (или конца ввода). Если больше нет символов для чтения (на данный момент), тогда добавьте ноль, чтобы строка была правильно завершена, прежде чем она будет возвращена. Эти функции не нуждаются в правильном завершении строки во время чтения, если вы отслеживаете, где находитесь в строке. Убедитесь, что у вас всегда достаточно места, чтобы добавить NUL '\0' в конец строки.

См. Kernighan & Pike «Практика программирования» для множества актуальных обсуждений. Я также думаю, что Maguire «Написание твердого кода» может предложить соответствующий совет, хотя он несколько устарел. Тем не менее, вы должны знать, что есть те, кто критикует книгу. Следовательно, я рекомендую TPOP вместо WSC (но у Amazon есть WSC от $ 0,01 + p & p, в то время как TPOP начинается с $ 20,00 + p & p - это может говорить о рынке).


TPOP ранее был на http://plan9.bell -labs.com / cm / cs / tpop и http://cm.bell -labs.com / cm / cs / tpop но оба (2015-08-10) повреждены. См. Также Википедию по TPOP .

5 голосов
/ 30 августа 2010
  • Вы всегда выделяете на один байт меньше, чем используете.Например, в начале вы выделяете пространство для нулевых символов, а затем пытаетесь установить (не существующий) первый символ на '\0'.

  • realloc без указателяна указатель в качестве первого параметра.Предполагается, что он будет использоваться следующим образом:

    lst = realloc(lst, largo * sizeof (char));
    
  • Если вы хотите обработать условия нехватки памяти, вам нужно проверить, вернул ли malloc() или realloc() значение NULL.

  • Было бы более эффективно выделять больший буфер в начале и увеличивать его большими шагами, а не перераспределять каждый добавленный символ отдельно.

2 голосов
/ 30 августа 2010

Первый аргумент для вызова realloc в

realloc(&lst, largo * sizeof (char));

должно быть lst, а не &lst

Также указатель, возвращаемый realloc, не всегда должен совпадать с его первым аргументом. Если свободной памяти рядом с существующей памятью не обнаружено, выделяется совершенно другой фрагмент и возвращается его адрес.

char *new_lst = realloc(lst, largo * sizeof (char));
if(new_lst != NULL) {
  lst = new_lst;
}
1 голос
/ 30 августа 2010

Вот рабочий пример для realloc и fgets. Его C89, POSIX не требуется. Вы можете установить параметр с вашей собственной предварительно выделенной памятью или NULL. Завершающий «свободный» всегда необходим.

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

char *getstringStdin(char *s)
{
  char buffer[9];
  s=realloc(s,1);
  *s=0;
  while( fgets(buffer,9,stdin) )
  {
    s=realloc(s,strlen(s)+1+strlen(buffer));
    strcat(s,buffer);
    if( strchr(s,'\n') )
    {
      *strchr(s,'\n')=0;
      break;
    }
  }
  return s;
}

main()
{
  char *s;
  while( *(s=getstringStdin(0)) ) /* a single Enter breaks */
  {
    puts(s);
    free(s);
  }
  free(s);
  puts("end");
  return 0;
}
1 голос
/ 30 августа 2010

Помимо ошибок в вашем коде, я думаю, что лучше создать строку переменной длины в C. Получив это, вы можете написать функцию getLine ().Эта строка переменной длины включает в себя понятие емкости, поэтому ее размер увеличивается в блоках степеней 2, а не один за другим.многие другие, вы можете создать функцию getLine:

String String__getLine()
{
    int ch;
    String this = String__create( "", 16 );

    do {
        ch = fgetc( stdin );
        String__add( &this, ch );
    } while( ch != EOF
          && ch != '\n' );

    size_t len = String__len( this );
    this.size = len -1;
    *( this.native + this.size ) = 0;

    return this;
}

Теперь вы можете просто использовать ее:

int main()
{
    printf( "Enter string: " );
    String str = String__getLine();
    printf( "You entered: '%s'\n", str.native );
    String__delete( &str );

    return EXIT_SUCCESS;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...