Сначала чтение строк с неопределенной длиной в C

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

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

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

#include 
#include 
#include 

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 
#include 
#include 


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:

  • Кажется, достаточно замедлить увеличение размера «palabra».

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

8
задан Marco Aviles 31 August 2010 в 22:22
поделиться

5 ответов

  1. Посмотрите определение POSIX getline().

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

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

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

  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() :

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

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

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

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

См. Керниган и Пайк «Практика программирования» для множества соответствующих дискуссий. Я также думаю, что Магуайр «Написание надежного кода» может дать полезный совет, несмотря на то, что он несколько устарел. Тем не менее, вы должны знать, что есть те, кто ругает книгу. Следовательно, я рекомендую 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, но оба сейчас (10 августа 2015 г.) не работают. См. также Википедию TPOP.

23
ответ дан 5 December 2019 в 05:18
поделиться

Первым аргументом вызова realloc в

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

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

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

char *new_lst = realloc(lst, largo * sizeof (char));
if(new_lst != NULL) {
  lst = new_lst;
}
2
ответ дан 5 December 2019 в 05:18
поделиться
  • Вы всегда выделяете на один байт меньше, чем используете. Например, вначале вы выделяете место для нулевых символов, а затем пытаетесь установить (несуществующий) первый символ в '\0'.

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

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

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

5
ответ дан 5 December 2019 в 05:18
поделиться

Вот рабочий пример для 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
ответ дан 5 December 2019 в 05:18
поделиться

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

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

typedef struct _mystring {
    char * native;
    size_t size;
    size_t capacity;
} String;

size_t String__len(String this)
{
    return this.size;
}

String String__create(char native[], size_t capacity) {
  String this;

  this.size = strlen( native );
  if ( capacity < ( this.size + 1 ) )
        this.capacity = this.size + 1;
  else  this.capacity = capacity;

  this.native = (char *) malloc( capacity * sizeof( char ) );
  strcpy( this.native, native );

  return this;
}

String * String__set(String *this, char native[]) {
    this->size = strlen( native );

    if ( this->size >= this->capacity ) {
        do {
            this->capacity <<= 1;
        } while( this->size > this->capacity );

        this->native = realloc( this->native, this->capacity );
    }

    strcpy( this->native, native );

    return this;
}

String * String__add(String *this, char ch) {
    ++( this->size );

    if ( this->size >= this->capacity ) {
        do {
            this->capacity <<= 1;
        } while( this->size > this->capacity );

        this->native = realloc( this->native, this->capacity );
    }

    char * zeroPos = this->native + ( this->size -1 );
    *( zeroPos++ ) = ch;
    *zeroPos = 0;

    return this;
}

void String__delete(String *this)
{
    free( this->native );
}

После того, как вы сделали эту реализацию, которая полезна для этой и многих других задач, вы можете создать функцию 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;
}
1
ответ дан 5 December 2019 в 05:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: