读取C中未定义长度的字符串
首先(一如既往)我想对我的英语道歉,可能不够清楚。
我不擅长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陷阱
-
查找POSIX
getline()
的定义。 -
请记住,您需要从
realloc()
捕获返回值; 不能保证新的存储器块与旧存储器块的起始位置相同。 -
知道
malloc(0)
可能返回一个空指针,或者它可能返回一个不可用的非空指针(因为它指向零字节的内存)。 -
你可能不会写’
*list = '\0';
当list列出分配内存的零字节时; 你没有在那里写的许可。 如果您返回NULL,则可能会获得核心转储。 在任何情况下,您都在调用未定义的行为,这是“ 一个坏主意 ™”。 ( 谢谢 ) -
palabra = newChar();
在main()
泄漏内存 – 假设你修复了已经讨论过的其他问题。 -
readLine()
中的代码不考虑在获取换行符之前获取EOF的可能性; 这很糟糕,当内存分配(最终)失败时会导致核心转储。 -
您的代码将表现出较差的性能,因为它一次分配一个字符。 通常,您应该一次分配多于一个额外字符; 从初始分配大约4个字节开始,每次需要更多空间时将分配加倍可能会更好。 保持初始分配较小,以便正确测试重新分配代码。
-
getchar()
的返回值是int
,而不是char
。 在大多数机器上,它可以返回256个不同的正字符值(即使char
是有符号类型)和单独的值EOF,它与所有char
值不同。 (如果机器的字节大于8位,则该标准允许它返回超过256个不同的字符。)( 谢谢 )C99标准§7.19.7.1说fgetc()
:如果未设置stream指向的输入流的文件结束指示符并且存在下一个字符,则fgetc函数将该字符作为转换为int的unsigned char获取,并为该流提前关联的文件位置指示符(如果定义)。
(重点补充。)它根据
getc()
定义了getchar()
,并用fgetc()
定义了getc()
fgetc()
。 -
(借: 谢谢 )。
realloc()
的第一个参数是指向当前分配的内存开头的指针,而不是指向当前分配的内存开头的指针。 如果您没有收到编译警告,则表示您没有在编译器上设置足够的警告进行编译。 您应该将警告调到最大。 您应该注意编译器警告 – 它们通常表示代码中存在错误,特别是在您仍在学习语言时。 -
在您知道已到达行尾(或输入结束)之前,通常更容易保持字符串不带空终止符。 当没有更多要读取的字符时(暂时),然后追加null,以便在返回之前正确终止字符串。 只要您跟踪字符串中的位置,这些函数就不需要字符串在读取时正确终止。 但是,确保你有足够的空间将NUL
'\0'
到字符串的末尾。
有关很多相关讨论,请参见Kernighan&Pike的“编程实践” 。 我也认为Maguire的“编写固体代码”提供了相关的建议,因为它有点过时了。 但是,你应该知道有些人会亵渎这本书。 因此,我建议TPOP而不是WSC(但亚马逊的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上的维基百科。
-
您总是分配比您正在使用的字节少一个字节。 例如,在开头为零字符分配空间,然后尝试将(不存在的)第一个字符设置为
'\0'
。 -
realloc
不会将指针指针作为第一个参数。 它应该像这样使用:lst = realloc(lst, largo * sizeof (char));
-
如果要处理内存不足的情况,则必须检查
malloc()
或realloc()
返回NULL。 -
在开头分配更大的缓冲区并以更大的步骤增加缓冲区而不是分别重新分配每个添加的字符会更有效。
调用realloc
的第一个参数
realloc(&lst, largo * sizeof (char));
应该是lst
而不是&lst
此外, realloc
返回的指针不必总是与其第一个参数相同。 如果找不到与现有存储器相邻的空闲存储器,则分配完全不同的块并返回其地址。
char *new_lst = realloc(lst, largo * sizeof (char)); if(new_lst != NULL) { lst = new_lst; }
除了你的代码中的错误,我认为最好在C中创建一个可变长度的字符串。一旦你有了,你可以写一个getLine()函数。 这个可变长度的字符串包括容量的概念,因此它的大小以2的幂块而不是逐个增加。
#include #include 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; }
这里是realloc和fgets的一个工作示例。 它的C89,不需要POSIX。 您可以使用自己的预分配内存或NULL设置参数。 始终需要终止“免费”。
#include #include #include 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; }