返回指向 char* 的指针会破坏数据
Returning pointer to char* ruins data
我正在尝试编写一个函数,它将沿 space (' '
) 拆分字符串而不更改原始字符串,将所有标记放入数组中,然后 return那个数组。我 运行 遇到的问题是 return 指针。下面是我的代码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **split_line(char *ln) {
char **tokens, *tok, line[256];
int j;
strcpy(line, ln);
tokens = calloc(64, sizeof(char*));
for (int i = 0; i < 64; i++)
tokens[i] = calloc(64, sizeof(char));
tokens[0] = strtok(line, " ");
for (j = 1; (tok = strtok(NULL, " ")) != NULL && j < 63; j++)
tokens[j] = tok;
tokens[j] = NULL;
return tokens;
}
int main(void) {
char **cut_ln, *input;
input = "Each word in this string should be an element in cut_ln.";
cut_ln = split_line(input);
printf("`%s`\n", input);
for (int i = 0; cut_ln[i] != NULL; i++)
printf("[%d]: `%s`\n", i, cut_ln[i]);
return 0;
}
当 运行 时,这给出:
`This word in this string should be an element in cut_ln.`
[0]: `This`
[1]: `wo1`
[2]: `�={G+s`
[3]: `G+s`
[4]: `string`
[5]: ``
[6]: `0����`
[7]: `��`
[8]: ``
[9]: ``
[10]: ``
当我尝试在 split_line
函数中打印 tokens
的内容时,它给出了预期的结果。但是,当 tokens
被 returned 并分配给一个变量,然后打印时,它会给出上面显示的结果。我做错了什么?
当您 return tokens
时,它包含从 strtok
编辑的指针 return,这些指针指向 line
。但是line
此时已经不存在了。
您分配了内存并使 tokens
的各种元素指向分配的内存。不要用 strtok
.
中的 return 值覆盖这些值
对于初学者来说,函数的声明至少应该像
char ** split_line( const char *ln );
因为传递的字符串在函数内没有被改变。
如果声明将指定分隔符的第二个参数,该函数将更加灵活。
char ** split_line( const char *ln, const char *delim );
其次,使用像 64
或 256
这样的幻数是个坏主意。如果传递的字符串包含超过 63
个标记,或者当字符串包含至少一个长度大于 63
.
的标记时,该函数将不起作用
您在此循环中动态分配了 64 个数组
for (int i = 0; i < 64; i++)
tokens[i] = calloc(64, sizeof(char));
并将它们的地址分配给变量tokens
指向的数组元素。但是您立即将指针重新分配给本地数组 line
.
内的地址
tokens[0] = strtok(line, " ");
for (j = 1; (tok = strtok(NULL, " ")) != NULL && j < 63; j++)
tokens[j] = tok;
因此该函数会产生大量内存泄漏。并且返回的指针数组将包含无效指针,因为退出函数后局部数组line
将不存在。
还有这个说法
tokens[j] = NULL;
是多余的。使用 calloc
您最初已经将所有指针设置为 NULL
.
函数如下面的演示程序所示。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char ** split_line( const char *s, const char *delim )
{
size_t n = 0;
for ( const char *p = s; *p; )
{
p += strspn( p, delim );
if ( *p )
{
++n;
p += strcspn( p, delim );
}
}
char **tokens = calloc( n + 1, sizeof( char * ) );
if ( tokens )
{
size_t i = 0;
int success = 1;
for ( const char *p = s; success && *p; i += success )
{
p += strspn( p, delim );
if ( *p )
{
const char *q = p;
p += strcspn( p, delim );
tokens[i] = malloc( p - q + 1 );
if ( ( success = tokens[i] != NULL ) )
{
memcpy( tokens[i], q, p - q );
tokens[i][p-q] = '[=15=]';
}
}
}
if ( !success )
{
for ( char **p = tokens; *p; ++p )
{
free( *p );
}
free( tokens );
}
}
return tokens;
}
int main(void)
{
const char *s = "Each word in this string should be an element in cut_ln.";
char **tokens = split_line( s, " " );
if ( tokens )
{
for ( char **p = tokens; *p; ++p )
{
puts( *p );
}
for ( char **p = tokens; *p; ++p )
{
free( *p );
}
}
free( tokens );
return 0;
}
程序输出为
Each
word
in
this
string
should
be
an
element
in
cut_ln.
我正在尝试编写一个函数,它将沿 space (' '
) 拆分字符串而不更改原始字符串,将所有标记放入数组中,然后 return那个数组。我 运行 遇到的问题是 return 指针。下面是我的代码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **split_line(char *ln) {
char **tokens, *tok, line[256];
int j;
strcpy(line, ln);
tokens = calloc(64, sizeof(char*));
for (int i = 0; i < 64; i++)
tokens[i] = calloc(64, sizeof(char));
tokens[0] = strtok(line, " ");
for (j = 1; (tok = strtok(NULL, " ")) != NULL && j < 63; j++)
tokens[j] = tok;
tokens[j] = NULL;
return tokens;
}
int main(void) {
char **cut_ln, *input;
input = "Each word in this string should be an element in cut_ln.";
cut_ln = split_line(input);
printf("`%s`\n", input);
for (int i = 0; cut_ln[i] != NULL; i++)
printf("[%d]: `%s`\n", i, cut_ln[i]);
return 0;
}
当 运行 时,这给出:
`This word in this string should be an element in cut_ln.`
[0]: `This`
[1]: `wo1`
[2]: `�={G+s`
[3]: `G+s`
[4]: `string`
[5]: ``
[6]: `0����`
[7]: `��`
[8]: ``
[9]: ``
[10]: ``
当我尝试在 split_line
函数中打印 tokens
的内容时,它给出了预期的结果。但是,当 tokens
被 returned 并分配给一个变量,然后打印时,它会给出上面显示的结果。我做错了什么?
当您 return tokens
时,它包含从 strtok
编辑的指针 return,这些指针指向 line
。但是line
此时已经不存在了。
您分配了内存并使 tokens
的各种元素指向分配的内存。不要用 strtok
.
对于初学者来说,函数的声明至少应该像
char ** split_line( const char *ln );
因为传递的字符串在函数内没有被改变。
如果声明将指定分隔符的第二个参数,该函数将更加灵活。
char ** split_line( const char *ln, const char *delim );
其次,使用像 64
或 256
这样的幻数是个坏主意。如果传递的字符串包含超过 63
个标记,或者当字符串包含至少一个长度大于 63
.
您在此循环中动态分配了 64 个数组
for (int i = 0; i < 64; i++)
tokens[i] = calloc(64, sizeof(char));
并将它们的地址分配给变量tokens
指向的数组元素。但是您立即将指针重新分配给本地数组 line
.
tokens[0] = strtok(line, " ");
for (j = 1; (tok = strtok(NULL, " ")) != NULL && j < 63; j++)
tokens[j] = tok;
因此该函数会产生大量内存泄漏。并且返回的指针数组将包含无效指针,因为退出函数后局部数组line
将不存在。
还有这个说法
tokens[j] = NULL;
是多余的。使用 calloc
您最初已经将所有指针设置为 NULL
.
函数如下面的演示程序所示。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char ** split_line( const char *s, const char *delim )
{
size_t n = 0;
for ( const char *p = s; *p; )
{
p += strspn( p, delim );
if ( *p )
{
++n;
p += strcspn( p, delim );
}
}
char **tokens = calloc( n + 1, sizeof( char * ) );
if ( tokens )
{
size_t i = 0;
int success = 1;
for ( const char *p = s; success && *p; i += success )
{
p += strspn( p, delim );
if ( *p )
{
const char *q = p;
p += strcspn( p, delim );
tokens[i] = malloc( p - q + 1 );
if ( ( success = tokens[i] != NULL ) )
{
memcpy( tokens[i], q, p - q );
tokens[i][p-q] = '[=15=]';
}
}
}
if ( !success )
{
for ( char **p = tokens; *p; ++p )
{
free( *p );
}
free( tokens );
}
}
return tokens;
}
int main(void)
{
const char *s = "Each word in this string should be an element in cut_ln.";
char **tokens = split_line( s, " " );
if ( tokens )
{
for ( char **p = tokens; *p; ++p )
{
puts( *p );
}
for ( char **p = tokens; *p; ++p )
{
free( *p );
}
}
free( tokens );
return 0;
}
程序输出为
Each
word
in
this
string
should
be
an
element
in
cut_ln.