在C语言中,如何动态地重新分配未知数量和大小的字符串表?
更新2:Dave在他的建议中添加了错误检查,我采纳了它,并在单独的回答中添加了一些扩展功能 更新1:问题已解决(不是realloc()问题,而是我没有为空终止分配额外的表槽……我已经纠正了它)。另请参见第一个答案中Dave的建议 原创帖子: 不使用中间数据结构,是否可以在c中动态地重新分配一个完全未知的字符串表 我要做的是生成一个func sarr_make_tokens()来标记任意c字符串,并返回一个c字符串标记表(以NULL结尾) 例如,使用s[]=“你好,残酷的世界!”并调用:char**tokens=s_make_tokens(s,“\t!”);我想得到一个以NULL结尾的令牌表在C语言中,如何动态地重新分配未知数量和大小的字符串表?,c,string,memory,resize,C,String,Memory,Resize,更新2:Dave在他的建议中添加了错误检查,我采纳了它,并在单独的回答中添加了一些扩展功能 更新1:问题已解决(不是realloc()问题,而是我没有为空终止分配额外的表槽……我已经纠正了它)。另请参见第一个答案中Dave的建议 原创帖子: 不使用中间数据结构,是否可以在c中动态地重新分配一个完全未知的字符串表 我要做的是生成一个func sarr_make_tokens()来标记任意c字符串,并返回一个c字符串标记表(以NULL结尾) 例如,使用s[]=“你好,残酷的世界!”并调用:char*
tokens[0] : "hello"
tokens[1] : "cruel"
tokens[2] : "world"
tokens[3] : NULL
下面的代码失败(我猜是在realloc()行中),但我想不出应该传递给它的大小,以便保留任何已存储的令牌
是否有可能,或者我必须首先通过s的本地副本上的strok()循环获取令牌数,然后在令牌表中分配尽可能多的插槽,然后应用另一个strok()循环,以便在表中存储实际令牌
在将令牌复制到令牌表之前,我可以使用一个中间链接列表来存储令牌,但是如果有一种方法可以使用具有正确大小的realloc,那就更好了
我将感谢任何帮助!下面是有问题的代码。。。实际上,它可以工作,但在尝试释放()调用者函数中的已获取令牌表时,seg会出错
#define S_FREE(p) \
do \
if ( (p) ) { \
free( (p) ); \
(p) = NULL; \
} \
while (0)
/* --------------------------------------------- */
int sarr_free( char *sarr[] )
{
register char **cpp = sarr;
/* sanity check */
if ( !sarr ) { errno = EFAULT; return 0; }
while ( *cpp )
free( *cpp++ );
free( sarr );
return 1; /* TRUE */
}
/* --------------------------------------------- */
char **sarr_make_tokens( char *s, const char *delims )
{
char **tokens = NULL, **ppchar = NULL;
size_t toksize = 0;
register char *cp = NULL;
register int i=0, j=0;
/* sanity checks */
if ( !s || !delims ) { errno = EFAULT; return NULL; }
if ( !*s || !*delims ) { errno = EINVAL; return NULL; }
i = 0;
cp = strtok( s, delims );
while ( cp != NULL )
{
/* add a new slot in the array */
ppchar = realloc( tokens, (i+1) * sizeof(char *) );
if ( !ppchar ) {
for (j=i-1; j > -1; j--)
free( tokens[j] );
S_FREE( tokens );
errno = ENOMEM;
return NULL;
}
tokens = ppchar;
/* make room for the token & copy it into the slot */
toksize = strlen( cp ) + 1;
tokens[i] = calloc( toksize, sizeof(char) );
if ( !tokens[i] ) {
for (j=i-1; j > -1; j--)
free( tokens[j] );
free( tokens );
errno = ENOMEM;
return NULL;
}
memcpy( tokens[i], cp, toksize );
/* get next token */
cp = strtok( NULL, delims );
i++;
}
if ( i != 0 ) { /* while-loop run at least once */
ppchar = realloc( tokens, (i+1) * sizeof(char *) );
if ( !ppchar )
/* handle error here */
tokens = ppchar;
tokens[ i ] = NULL; /* ... NULL terminate the array of tokens */
}
else /* while-loop did not run at all */
errno = ERANGE; /* ... flag failure of 1st strtok() */
return tokens;
}
这很容易。。。使用
strtok()
、strdup()
和realloc()
,函数相当简单
//EDIT: Now handles errors.
char **tok(char *s, char *delim)
{
char *str, **arr, **ap;
int cap=3, fill=0;
if((str=strdup(s))==NULL) //in case s is read-only.
goto NoMem;
if((arr = malloc(cap*sizeof(char*)))==NULL)
goto NoMem;
for(s=strtok(str, delim); s; s=strtok(NULL, delim)){
if(cap<=fill+1)
if(ap = realloc(arr, (cap=(cap*3)/2)*sizeof(char*)))
arr=ap;
else
goto NoMem;
if((arr[fill++] = strdup(s))==NULL)
goto NoMem;
arr[fill] = NULL;
}
free(str);
return arr;
NoMem:
if(str) free(str);
if(arr){
for(ap=arr; *ap; ap++)
free(*ap);
free(arr);
}
return NULL;
}
//编辑:现在处理错误。
char**tok(char*s,char*delim)
{
字符*str,**arr,**ap;
int cap=3,fill=0;
if((str=strdup(s))==NULL)//如果s是只读的。
后藤诺姆;
if((arr=malloc(cap*sizeof(char*)))==NULL)
后藤诺姆;
for(s=strtok(str,delim);s;s=strtok(NULL,delim)){
if(cap问题毕竟不在realloc()中,而是因为在退出函数之前,我没有为NULL终止分配额外的表槽(我已经更正了它,并删除了原始帖子中的相关文本)
Dave还提出了一个更优雅(但不可行)的解决方案:
感谢大家的反馈!采纳Dave的建议,并对原始函数的功能进行了一点扩展,这样程序员就可以决定s是否应被视为只读(较慢)或不应被视为只读(较快),通过一个extar参数,我得出了以下结论…在错误检查中有点多余,这有点与“更快”的说法相矛盾,但好吧,我还没有仔细考虑…但希望它没有bug
#define S_FREE(p) \
do \
if ( (p) ) { \
free( (p) ); \
(p) = NULL; \
} \
while (0)
#define SARR_BACKFREE(sarr, ifailed) \
do { \
register int i=0; \
for (i=(ifailed)-1; i > -1; i--) \
S_FREE( (sarr)[i] ); \
S_FREE( (sarr) ); \
} while(0)
/* -------------------------------------- */
char *s_strdup( const char *src )
{
char *s = NULL;
size_t ssize = 0;
/* sanity check */
if ( !src ) { errno = EFAULT; return NULL; }
ssize = strlen( src ) + 1;
if ( !(s = malloc(ssize)) ) {
errno = ENOMEM;
return NULL;
}
return memcpy( s, src, ssize );
}
/* -------------------------------------- */
char **sarr_make_tokens( char *s, char *delims, const int readonly )
{
char *str = NULL, *cp = NULL;
int cap = 3, itok = 0;
char **sarr = NULL, **ppchar = NULL;
/* sanity checks */
if ( !s || !delims ) { errno = EFAULT; return NULL; }
if ( !*s || !*delims ) { errno = EINVAL; return NULL; }
/* treat s as a string literal? */
if ( readonly ) {
if ( (str = s_strdup( s )) == NULL )
goto ret_nomem;
}
else str = s;
/* allocate table of strings sarr */
if ( (sarr = malloc( cap * sizeof(char *) )) == NULL )
goto ret_nomem;
/* tokenize str and store tokens in sarr */
for ( cp=strtok(str, delims); cp; cp=strtok(NULL, delims) )
{
if( cap <= itok+1 )
{
ppchar = realloc(sarr, (cap=(cap*3)/2) * sizeof(char *) );
if ( !ppchar )
goto clean_and_ret_nomem;
sarr = ppchar;
}
if ( (sarr[ itok ] = s_strdup( cp )) == NULL )
goto clean_and_ret_nomem;
itok++;
}
/* NULL terminate sarr */
sarr[ itok ] = NULL;
if ( readonly )
S_FREE( str );
return sarr;
clean_and_ret_nomem:
SARR_BACKFREE(sarr, itok);
ret_nomem:
if ( readonly )
S_FREE( str ); /* S_FREE() works ONLY if str != NULL */
errno = ENOMEM;
return NULL;
}
#定义S#U自由(p)\
做\
如果((p)){\
自由((p))\
(p) =零\
} \
而(0)
#定义SARR_回流阀(SARR,如图所示)\
做{\
寄存器int i=0\
对于(i=(ifailed)-1;i>-1;i--)\
S_FREE((sarr)[i])\
无硫((sarr))\
}而(0)
/* -------------------------------------- */
char*s_strdup(const char*src)
{
char*s=NULL;
大小\u t ssize=0;
/*健康检查*/
如果(!src){errno=EFAULT;返回NULL;}
ssize=strlen(src)+1;
如果(!(s=malloc(ssize))){
errno=ENOMEM;
返回NULL;
}
返回memcpy(s、src、ssize);
}
/* -------------------------------------- */
char**sarr\u make\u标记(char*s、char*delims、const int只读)
{
char*str=NULL,*cp=NULL;
int cap=3,itok=0;
字符**sarr=NULL,**ppchar=NULL;
/*健康检查*/
如果(!s | |!delims){errno=EFAULT;返回NULL;}
if(!*s | |!*delims){errno=EINVAL;返回NULL;}
/*是否将s视为字符串文字*/
如果(只读){
如果((str=s_strdup(s))==NULL)
去重新命名;
}
else-str=s;
/*分配字符串表*/
if((sarr=malloc(cap*sizeof(char*)))==NULL)
去重新命名;
/*标记化str并将标记存储在sarr中*/
for(cp=strtok(str,delims);cp;cp=strtok(NULL,delims))
{
如果(cap),你应该将你的解决方案作为答案发布,过一段时间后,将其标记为已接受,而不是将已解决的问题添加到标题中。这不是StackOverflow的“标准”。哦,我明白了,我现在就开始了,谢谢!现在还不能这样做,因为。。。“哦!您的答案无法提交,因为:*声誉低于100的用户在提问后8小时内无法回答自己的问题。您可以在4小时内自行回答。在此之前,请使用评论或编辑您的问题。”这看起来很酷,谢谢!但是,strdup()不是标准的ANSI C func(有很多免费的(str)我想也不见了。)你觉得我忘了把free()
放在哪里了?我指的是你帖子的原始版本:)顺便说一句,我刚刚用一个自定义的strdup(const char src){char*s=NULL;size\u t ssize=0;/sanity check*/if(!src){errno=EFAULT;return NULL;}ssize=strlen(src)+1;if(!(s=malloc(ssize)){errno=ENOMEM;return NULL;}return memcpy(s,src,ssize);}它似乎在循环的最后一行进入了一个无限循环:arr[fill++]=strdup(s);…它正确地迭代了一次,然后进入无限循环…Win7Oh上的mingw gcc和pelles-c都是一样的,您已经将它从strdup(str)更改为要升级…那么,让我再检查一次。现在它很好,很好!添加