我正在我的 C 程序中使用两个函数,但无法让它们合作。我的第一个函数迭代 csv 文件并使用 strtok() 根据“,”分隔符分隔标记。每个标记都保存到大小为 3 的字符数组中,如果特定标记与目标标记不匹配,则读取下一行输入,标记化,并用新的输入标记覆盖字符数组。
这是使用的全局变量:
char * stateCityZip[3];
这是第一个函数的代码:
int zipToCity(char * zip)
{
char line[1024];
char * tok = malloc(20 * sizeof(char));
FILE * file = fopen("cityzip.csv", "r");
while (fgets(line, sizeof(line), file))
{
//State
tok = strtok(line, ",");
stateCityZip[0] = tok;
//City
tok = strtok(NULL, ",");
stateCityZip[1] = tok;
//Zip
tok = strtok(NULL, ",");
stateCityZip[2] = tok;
if (strcmp(stateCityZip[2], newZip) == 0) {
//printf("Found %s, %s\n", stateCityZip[0], stateCityZip[1]);
strlen(stateCityZip[1]));
return 1;
}
}
return 0;
}
我的第二个函数只是尝试打印 stateCityZip 的值。然而,当我打印这些值时,它们显示为空白。我发现解决这个问题的唯一方法是取消注释
//printf("Found %s, %s\n", stateCityZip[0], stateCityZip[1]);
线。
这是我的第二个函数的代码:
int main() {
printf("City: [%s]", stateCityZip[1]);
printf("State: [%s]", stateCityZip[0]);
return 0;
}
输出:
City: []
State: []
最佳答案
strtok
返回指向其输入的子字符串的指针。因此,您的所有 tok
值都是指向 line
部分的指针,该部分具有自动存储期限,因此 stateCityZip
中的指针一旦 zipToCity
返回。
(实际上,如果文件中有多于一行,一旦您前进到下一行,tok
指针就会全部失效,因为它们现在指向新行的任意子字符串缓冲区中的行。)
相反,您应该使用 strdup
将 token 的副本分配为新字符串,并将从 strdup
返回的指针保存到 stateCityZip
>.
重要提示:由strdup
分配的字符串需要由free
释放。如果stateCityZip是全局的,那么你可以不释放它们(当你的进程退出时,最后的内存将被释放)。但是,如果再次调用 zipToCity,它将覆盖 stateCityZip 中的指针并泄漏相应的字符串。因此,最安全的做法可能是首先 free()
stateCityZip
中的任何字符串(如果它们为 NULL,那就没问题,因为 free(NULL)
code> 是一个无操作),然后再分配新值。
关于内存分配的主题:您的示例代码有一个对 tok
的 malloc
调用,这是完全多余的,并且肯定会泄漏,因为您覆盖了指针从malloc
返回,而没有free
它。
并且,关于良好的编码实践:fopen
可能会失败。您需要检查返回的 FILE * 是否不为 NULL。 (如果它是NULL,errno
会告诉你原因。)并且你还需要fclose
你没有出现的FILE *
要么做。
更一般地说:这些代码似乎根本没有进行任何错误检查。每次调用任何标准库函数(或任何函数,就此而言!)时,您都需要考虑该函数如何无法完成其应做的事情,您将如何能够告诉(通常失败行为都有详细记录,因此请务必阅读文档),以及如果失败,您希望程序执行什么操作。如果文件不存在或无法读取怎么办?如果它包含不是以逗号分隔的行,或者不包含预期数量的字段?如果在处理文件的过程中内存不足,或者由于堆内存不足而 strdup
无法复制字符串,会发生什么情况?如果你不考虑这些问题,或者忘记处理它们,你可能会在一切正常的“幸福情况”中侥幸逃脱,但迟早它会回来咬你——通常是最坏的情况可能的时间。
关于除非在分配时打印该值,否则 Char 数组值将打印为空 - C,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52390791/