在下面的代码中,我希望你能看到我有一个char*
变量,并且我想从文件中读取一个字符串。然后我想从函数中传回这个字符串。我对指针感到困惑,所以我不太确定我应该做些什么。
这样做的目的是将数组传递给另一个要搜索名称的函数。
不幸的是程序崩溃了,我不知道为什么。
char* ObtainName(FILE *fp)
{
char* temp;
int i = 0;
temp = fgetc(fp);
while(temp != '\n')
{
temp = fgetc(fp);
i++;
}
printf("%s", temp);
return temp;
}
非常感谢任何帮助。
答案 0 :(得分:1)
fgetc
会返回int
,而非char*
。此int
是流中的字符,如果到达文件末尾,则为EOF
。
您隐含地将int
强制转换为char*
,即将其解释为地址(启用警告。)当您致电printf
时,它会读取该地址并继续一次读取一个字符,寻找结束字符串的空终止符,但该地址几乎肯定无效。这是未定义的行为。
答案 1 :(得分:1)
我对你想要完成的事情采取了一些自由。而只需处理指针,只要设置最大长度,就可以使用固定大小的数组。我还包括了几个检查,以便您不会运行缓冲区的末尾或文件的末尾。同样重要的是确保在字符串末尾有一个空终止'\ 0'。
#define MAX_LEN 100
char* ObtainName(FILE *fp)
{
static char temp[MAX_LEN];
int i = 0;
while(i < MAX_LEN-1)
{
if (feof(fp))
{
break;
}
temp[i] = fgetc(fp);
if (temp[i] == '\n')
{
break;
}
i++;
}
temp[i] = '\0';
printf("%s", temp);
return temp;
}
答案 2 :(得分:1)
所以,这里有几个问题:
<强> 1。您没有为字符串内容留出存储空间
该行
char *temp;
将temp
声明为指向char
的指针;它的值将是单个字符值的地址。由于它在没有static
关键字的情况下在本地范围内声明,因此其初始值将是不确定的,并且该值可能与有效的内存地址不对应。
它不会为从fp
读取的字符串内容留出任何存储空间;这必须作为一个单独的步骤完成,我将在下面介绍。
<强> 2。您没有正确存储字符串内容
该行
temp = fgetc(fp);
从fp
读取下一个字符并将其分配给temp
。首先,这意味着您只存储从流中读取的最后一个字符,而不是整个字符串。其次,更重要的是,您将fgetc()
(返回类型int
的值)的结果分配给类型为char *
的对象(被视为地址) )。你基本上是在说#34;我想对待这封信&#39; a&#39;作为记忆的地址。&#34;这带给我们......
第3。您试图阅读不属于您的记忆
在第
行printf("%s", temp);
您尝试打印从temp
中存储的地址开始的字符串。因为你写给temp
的最后一件事很可能是一个值为&lt; 127,你告诉printf
从非常低且很可能无法访问的地址开始,因此崩溃。
<强> 4。您尝试返回字符串的方式可以保证让您心痛
由于您已定义函数以返回char *
,因此您需要执行以下操作之一:
static
关键字声明一个数组,以便数组不会消失&#34;功能退出后;然而,这种方法有严重的缺点; 动态分配内存
您可以使用动态内存分配例程为字符串内容预留一个存储区域,如下所示:
char *temp = malloc( MAX_STRING_LENGTH * sizeof *temp );
或
char *temp = calloc( MAX_STRING_LENGTH, sizeof *temp );
,然后在您撰写时返回temp
。
malloc
和calloc
都预留了您指定的字节数; calloc
会将所有这些字节初始化为0,这会花费更多时间,但可以保存您的培根,尤其是在处理文本时。
问题是当不再需要时,有人必须释放这个记忆;因为你返回指针,现在调用此函数的任何人都有责任在完成该字符串时调用free()
,例如:
void Caller( FILE *fp )
{
...
char *name = ObtainName( fo );
...
free( name );
...
}
这扩展了程序内存管理的责任,增加了有人忘记释放内存的可能性,导致内存泄漏。 理想情况,您希望拥有相同的功能,可以将内存分配给它。
使用静态数组
您可以将temp
声明为char
的数组并使用static
关键字:
static char temp[MAX_STRING_SIZE];
这将在程序启动时在数组中留出MAX_STRING_SIZE
个字符,并且会在调用ObtainName
之间保留。完成后无需致电free
。
此方法的问题在于,通过创建静态缓冲区,代码不是重入;如果ObtainName
调用了另一个函数,而该函数又调用了ObtainName
,则该新调用将破坏之前缓冲区中的任何内容。
为什么不将temp
声明为
char temp[MAX_STRING_SIZE];
没有static
关键字?问题是当ObtainName
退出时,temp
数组不再存在(或者更确切地说,它正在使用的内存可供其他人使用)。您返回的指针不再有效,并且可以覆盖该数组的内容,然后才能再次访问它。
更改功能定义
理想情况下,您希望ObtainName
不必担心它必须写入的内存。实现这一目标的最佳方法是调用者将目标缓冲区作为参数传递,以及缓冲区的大小:
int ObtainName( FILE *fp, char *buffer, size_t bufferSize )
{
...
}
这样,ObtainName
将数据写入调用者指定的位置(如果您想为不同目的获取多个名称,则非常有用)。该函数将返回一个整数值,可以是简单的成功或失败,也可以是一个错误代码,指示函数失败的原因等。
请注意,如果您正在阅读文字,则不必逐字逐句阅读;您可以使用fgets()
或fscanf()
等函数一次读取整个字符串。
如果要读取以空格分隔的字符串,请使用fscanf
(即,如果输入文件包含"This is a test"
,fscanf( fp, "%s", temp);
将只读取"This"
)。如果要读取整行(由换行符分隔),请使用fgets()
。
假设您想一次阅读一个单独的字符串,您可以使用以下内容(假设为C99):
#define FMT_SIZE 20
...
int ObtainName( FILE *fp, char *buffer, size_t bufsize )
{
int result = 1; // assume success
int scanfResult = 0;
char fmt[FMT_SIZE];
sprintf( fmt, "%%%zus", bufsize - 1 );
scanfResult = fscanf( fp, fmt, buffer );
if ( scanfResult == EOF )
{
// hit end-of-file before reading any text
result = 0;
}
else if ( scanfResult == 0 )
{
// did not read anything from input stream
result = 0;
}
else
{
result = 1;
}
return result;
}
那么这个噪音是什么
char fmt[FMT_SIZE];
sprintf( fmt, "%%%zus", bufsize - 1 );
约?当您使用fscanf()
或%s
转换说明符而没有最大长度说明符时,%[
中存在非常讨厌的安全漏洞。 %s
转换说明符告诉fscanf
读取字符,直到它看到空白字符为止;如果流中有更多非空白字符而不是缓冲区大小要容纳,fscanf
会将那些额外字符存储在缓冲区末尾,从而破坏跟随它的内存。这是一种常见的恶意软件攻击。所以我们要指定输入的最大长度;例如,%20s
表示从流中读取不超过20个字符并将它们存储到缓冲区。
不幸的是,由于缓冲区长度作为参数传递,我们不能写%20s
之类的内容,而fscanf
并没有给我们指定长度的方法作为fprintf
的方式论证。所以我们必须创建一个单独的格式字符串,我们将其存储在fmt
中。如果输入缓冲区长度为10,则格式字符串将为%10s
。如果输入缓冲区长度为1000,则格式字符串将为%1000s
。
答案 3 :(得分:0)
以下代码扩展了您的问题中的代码,并返回已分配存储中的字符串:
char* ObtainName(FILE *fp)
{
int temp;
int i = 1;
char *string = malloc(i);
if(NULL == string)
{
fprintf(stderr, "malloc() failed\n");
goto CLEANUP;
}
*string = '\0';
temp = fgetc(fp);
while(temp != '\n')
{
char *newMem;
++i;
newMem=realloc(string, i);
if(NULL==newMem)
{
fprintf(stderr, "realloc() failed.\n");
goto CLEANUP;
}
string=newMem;
string[i-1] = temp;
string[i] = '\0';
temp = fgetc(fp);
}
CLEANUP:
printf("%s", string);
return(string);
}
注意'free()'此函数返回的字符串,否则会发生内存泄漏。