读取C中未定义长度的字符串

时间:2010-08-30 06:32:55

标签: c string pointers

首先(一如既往)我想对我的英语道歉,可能不够清楚。

我不擅长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:

  • 似乎足以减缓“palabra”的增加幅度。

  • 我不确定是否将getchar()捕获到int然后将其投放到char中是正确的方法EOF pitfall

5 个答案:

答案 0 :(得分:23)

  1. 查找POSIX getline()的定义。

  2. 请记住,您需要从realloc()获取返回值;不能保证新的内存块从与旧内存块相同的位置开始。

  3. 知道malloc(0)可能返回空指针,或者它可能返回一个不可用的非空指针(因为它指向零字节的内存)。

  4. 当列表指向已分配内存的零字节时,您可能不会写'*list = '\0';;你没有在那里写的许可。如果您返回NULL,则可能会获得核心转储。在任何情况下,您都在调用未定义的行为,即“一个坏主意™”。 (Thanks

  5. palabra = newChar();中的main()泄漏内存 - 假设您已解决已讨论过的其他问题。

  6. readLine()中的代码未考虑在获取换行符之前获取EOF的可能性;这很糟糕,当内存分配(最终)失败时会导致核心转储。

  7. 您的代码会表现不佳,因为它一次分配一个字符。通常,您应该一次分配多于一个额外字符;从初始分配大约4个字节开始,每次需要更多空间时将分配加倍可能会更好。保持初始分配较小,以便正确测试重新分配代码。

  8. getchar()的返回值为int,而不是char。在大多数机器上,它可以返回256个不同的正字符值(即使char是有符号类型)和单独的值EOF,它与所有char值不同。 (如果机器的字节大于8位,则该标准允许它返回超过256个不同的字符。)(Thanks)C99标准§7.19.7.1说明fgetc()

      

    如果未设置stream指向的输入流的文件结束指示符,则为a   如果存在下一个字符,则fgetc函数将字符作为无符号字符获取   char转换为int 并前进相关的文件位置指示符   流(如果已定义)。

    (强调补充。)它根据getchar()定义getc(),并根据getc()定义fgetc()

  9. (借:Thanks)。 realloc()的第一个参数是指向当前分配的内存开头的指针,而不是指向当前分配的内存开头的指针。如果您没有从中获得编译警告,则表示您没有在编译器上设置足够的警告进行编译。您应该将警告调到最大。您应该注意编译器警告 - 它们通常表示代码中存在错误,尤其是在您仍在学习语言时。

  10. 在您知道已到达行尾(或输入结束)之前,通常更容易保持字符串不带空终止符。当没有更多要读取的字符时(暂时),然后追加null,以便在返回之前正确终止字符串。只要您跟踪字符串中的位置,这些函数就不需要字符串在读取时正确终止。但请确保您有足够的空间将NUL '\0'添加到字符串的末尾。

  11. 见Kernighan&amp;派克'The Practice of Programming'进行了很多相关的讨论。我也认为Maguire 'Writing Solid Code'有相关的建议,因为它有点过时了。但是,你应该知道有些人会亵渎这本书。因此,我推荐TPOP优于WSC(但亚马逊的WSC售价为0.01美元+ p&amp; p,而TPOP起价为20.00美元+ p&amp; p - 这可能是市场发言)。


    TPOP之前曾在 http://plan9.bell-labs.com/cm/cs/tpophttp://cm.bell-labs.com/cm/cs/tpop但现在(2015-08-10)都被打破了。 另见TPOP上的维基百科。 功能

答案 1 :(得分:5)

  • 您总是分配比您正在使用的字节少一个字节。例如,在开头为零字符分配空间,然后尝试将(不存在的)第一个字符设置为'\0'

  • realloc不会将指针指针作为第一个参数。它应该像这样使用:

    lst = realloc(lst, largo * sizeof (char));
    
  • 如果您想处理内存不足的情况,则必须检查malloc()realloc()是否返回NULL。

  • 在开头分配更大的缓冲区并以更大的步骤增加缓冲区而不是分别重新分配每个添加的字符会更有效。

答案 2 :(得分:2)

中对realloc的调用的第一个参数
realloc(&lst, largo * sizeof (char));

应该是lst而不是&lst

realloc返回的指针也不必总是与第一个参数相同。如果找不到与现有内存相邻的空闲内存,则会分配完全不同的块并返回其地址。

char *new_lst = realloc(lst, largo * sizeof (char));
if(new_lst != NULL) {
  lst = new_lst;
}

答案 3 :(得分:1)

除了你的代码中的错误,我认为最好在C中创建一个可变长度的字符串。一旦你有了,你可以写一个getLine()函数。这个可变长度的字符串包含容量的概念,因此它的大小以2的幂为单位而不是逐个增加。

#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;
}

答案 4 :(得分:1)

这里是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;
}