很多 C 语言只是在您的脑海中保持清晰,您需要在代码的什么范围(块)中提供哪些数据。在您的情况下,您正在使用的数据是数据文件每个部分的第一行(或标题)。为此,您有 s1 和 t1,但您没有任何东西可以保留 n,以便它可以与您的数据一起重复使用。由于n 包含每个标题下production_left 和production_right 的预期索引数,因此只需创建一个索引数组,例如int idx[MAXC] = {0}; 存储与每个 s1 和 t1 关联的每个 n。这样您就可以保留该值以供以后在迭代中使用。 (MAXC 只是为20 定义的常量,以防止在您的代码中使用幻数)
接下来,您需要转向您对指针声明及其使用的理解。 char *s1[MAXC], *t1[MAXC], *production_left[MAXC], *production_right[MAXC]; 为 s1、t1、production_left 和 production_right 声明了 4 个 指针数组(每个 20 指针)。指针未初始化和未分配。虽然每个都可以初始化为一个指针值,但没有与它们中的任何一个关联的存储(分配的内存)允许复制数据。您不能简单地使用 fscanf 并为每个分配相同的指针值(它们最终都会指向最后一个值 - 如果它仍在范围内)
所以你有两个选择,(1)要么使用二维数组,要么(2)为每个字符串分配存储空间并将字符串复制到新的内存块,并将指向该块开头的指针分配给(例如s1[x])。 strdup 函数在单个函数调用中提供分配和复制。如果您没有strdup,则使用strlen、malloc 和memcopy 编写一个简单的函数(如果字符串可能重叠,请使用memmove)。
一旦您确定了需要保留哪些值以供以后在代码中使用,您已确保声明的变量具有正确的范围,并且您已确保每个变量都已正确初始化和分配存储,剩下的就是编写逻辑让事情如你所愿。
在转向示例之前,值得注意的是,您对面向行的输入 与您的数据感兴趣。 scanf 系列提供格式化输入,但通常最好对实际输入使用面向行 函数(例如fgets),然后单独解析与,例如sscanf。在这种情况下,这主要是一种清洗,因为您的第一个值是 字符串值,而 %s 格式说明符将跳过干预 空格,但通常情况并非如此。例如,您正在有效地阅读:
char tmp1[MAXC] = "", tmp2[MAXC] = "";
...
if (fscanf (fp, "%s %s %d", tmp1, tmp2, &n) != 3)
break;
这可以很容易地用更健壮的东西代替:
char buf[MAXC] = "", tmp1[MAXC] = "", tmp2[MAXC] = "";
...
if (!fgets (buf, sizeof buf, fp) ||
sscanf (buf, "%s %s %d", tmp1, tmp2, &n) != 3)
break;
(将分别验证读取和转换)
在上面,还要注意临时缓冲区tmp1 和tmp2 的使用(以及buf 与fgets 一起使用)将输入读入临时值通常是有利的,这些临时值可以在最终存储之前进行验证以后再用。
剩下的就是按照正确的顺序将各个部分组合在一起以实现您的目标。下面是一个示例,它从作为第一个参数给出的文件名(如果没有给出文件名,则从stdin)读取数据,然后输出数据并在退出前释放所有分配的内存。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 20
int main (int argc, char **argv) {
char *s1[MAXC], *t1[MAXC],
*production_left[MAXC], *production_right[MAXC];
int idx[MAXC] = {0}, /* storage for 'n' values */
iss = 0, ipp = 0, pidx = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* loop until no header row read, protecting array bounds */
for (; ipp < MAXC && iss < MAXC; iss++) {
char buf[MAXC] = "", tmp1[MAXC] = "", tmp2[MAXC] = "";
int n = 0;
if (!fgets (buf, sizeof buf, fp) ||
sscanf (buf, "%s %s %d", tmp1, tmp2, &n) != 3)
break;
idx[iss] = n;
s1[iss] = strdup (tmp1); /* strdup - allocate & copy */
t1[iss] = strdup (tmp2);
if (!s1[iss] || !t1[iss]) { /* if either is NULL, handle error */
fprintf (stderr, "error: s1 or s1 empty/NULL, iss: %d\n", iss);
return 1;
}
/* read 'n' data lines from file, protecting array bounds */
for (int i = 0; i < n && ipp < MAXC; i++, ipp++) {
char ptmp1[MAXC] = "", ptmp2[MAXC] = "";
if (!fgets (buf, sizeof buf, fp) ||
sscanf (buf, "%s %s", ptmp1, ptmp2) != 2) {
fprintf (stderr, "error: read failure, ipp: %d\n", iss);
return 1;
}
production_left[ipp] = strdup (ptmp1);
production_right[ipp] = strdup (ptmp2);
if (!production_left[ipp] || !production_right[ipp]) {
fprintf (stderr, "error: production_left or "
"production_right empty/NULL, iss: %d\n", iss);
return 1;
}
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (int i = 0; i < iss; i++) {
printf ("%-8s %-8s %2d\n", s1[i], t1[i], idx[i]);
free (s1[i]); /* free s & t allocations */
free (t1[i]);
for (int j = pidx; j < pidx + idx[i]; j++) {
printf (" %-8s %-8s\n", production_left[j], production_right[j]);
free (production_left[j]); /* free production allocations */
free (production_right[j]);
}
pidx += idx[i]; /* increment previous index value */
}
return 0;
}
输入文件示例
$ cat dat/production.txt
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B
使用/输出示例
$ ./bin/production <dat/production.txt
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此 (2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试写入超出/超出分配的内存块的范围,尝试读取或基于未初始化的值进行条件跳转,最后,确认您释放了所有已分配的内存。
对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。
$ valgrind ./bin/production <dat/production.txt
==3946== Memcheck, a memory error detector
==3946== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3946== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==3946== Command: ./bin/production
==3946==
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B
==3946==
==3946== HEAP SUMMARY:
==3946== in use at exit: 0 bytes in 0 blocks
==3946== total heap usage: 18 allocs, 18 frees, 44 bytes allocated
==3946==
==3946== All heap blocks were freed -- no leaks are possible
==3946==
==3946== For counts of detected and suppressed errors, rerun with: -v
==3946== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放已分配的所有内存并且没有内存错误。
查看一下,如果您还有其他问题,请告诉我。