伙计我用它来从文件中成功读取数据。
void read(FILE *fPtr, int *a, int *b, char **c, int size)
{
char line[100];
int num = 0;
while ( fgets(line, 99, fPtr) && num < size )
{
// This is a names array
c[num] = strtok (line, " ");
// here the name prints out fine
a[num] = atoi(strtok (NULL, " \n"));
b[num] = atoi(strtok (NULL, " \n"));
num++;
}
}
但是我无法从这个char **数组中正确读取。
主要功能:
int main()
{
FILE *fp;
int g[30];
int a[30];
char *names[30];
// Open file
fp = fopen("input.txt", "r");
read(
fp, g, a,
names, 30 );
printf("%s\n", player_names[0]);
printThis(
g, a,
names, 30 );
return 0;
}
打印这个:
void printThis(int* g, int* a, char** n, int s)
{
for (int i = 0; i < 5; ++i)
printf("%s\n", n[i]);
}
这完全没有打印名字!它打印只是@一些空间字符和@。为什么不打印任何东西。这不是访问char数组的正确方法吗?
答案 0 :(得分:2)
正如评论中所述,您希望分配c[num] = strtok (line, " ");
,您发现names
中的值不再指向任何有意义的内容。在函数中声明变量时,除非为变量动态分配存储(例如,使用malloc
),否则该变量仅在函数的生命周期内存活。为什么?
调用函数时,会为执行名为函数堆栈框架的函数创建单独的内存区域。该内存保存在函数内声明的变量。当函数返回时,堆栈帧被销毁(实际上只是根据需要释放以便重用)。声明为函数本地的任何变量都不再可用于访问。
在您的情况下,strtok
会返回指向line
内令牌开头的指针(如果找不到令牌,则返回NULL
)。在函数return
上,strtok
返回的指针指向已释放以供重用的内存,并且无法再在调用函数(main
这里)中访问 - 因此您的问题。
我该如何解决这个问题?嗯,有很多方法。第一个最简单的解决方案是为名称动态分配存储空间,并复制strtok
找到的标记,并将它们存储在已分配的内存中。该内存将存活return
,并且可以在调用者中有效访问。 (你必须记住在不再需要时释放内存)
您还需要让read
(我的readfp
下方)函数返回有意义的值,而不是void
。知道实际填充了多少记录会很高兴。如果您将函数类型更改为int
,则可以返回num
并知道填充了多少记录(例如name[x] a[x] & g[x]
组合)。
等待...... num
被宣布为read
的本地,我为什么要退回呢?每个函数总是可以返回自己的type
值。如果您声明int read (...)
,read
可以返回int
值,并且由于调用方和被调用方之间的调用约定,它的生存返回。
将这些内容组合在一起,并使用strtol
代替atoi
进行int
转换(并通过创建单独的函数来处理并轻松实现验证转换),你可以做类似以下的事情。另请注意,main
使用参数,您可以使用它来传递文件名以打开,而不是硬编码,例如fopen("input.txt", "r")
。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
/* define constants for use below */
enum { BASE = 10, MAXP = 30, MAXL = 100 };
int readfp (FILE *fp, int *a, int *b, char **c, int size);
int xstrtol (char *s, int base);
int main (int argc, char **argv) {
int g[MAXP] = {0},
a[MAXP] = {0},
n = 0;
char *names[MAXP] = {NULL};
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;
}
n = readfp (fp, g, a, names, MAXP );
if (fp != stdin) fclose (fp); /* close file if not stdin */
if (n)
for (int i = 0; i < n; i++) {
printf ("%-30s %8d %8d\n", names[i], g[i], a[i]);
free (names[i]); /* free memory allocated by strdup */
}
return 0;
}
int readfp (FILE *fp, int *a, int *b, char **c, int size)
{
char line[MAXL] = "";
int num = 0;
while (num < size && fgets (line, sizeof line, fp))
{
char *delim = " \t\n",
*tmp = strtok (line, delim);
int itmp = 0;
if (tmp)
c[num] = strdup (tmp); /* allocate storage and copy tmp */
else {
fprintf (stderr, "error: strtok failed - c[%d].\n", num);
break;
}
if ((tmp = strtok (NULL, delim)) && (itmp = xstrtol (tmp, BASE)))
a[num] = itmp;
else {
fprintf (stderr, "error: strtok failed - a[%d].\n", num);
break;
}
if ((tmp = strtok (NULL, delim)) && (itmp = xstrtol (tmp, BASE)))
b[num] = itmp;
else {
fprintf (stderr, "error: strtok failed - b[%d].\n", num);
break;
}
num++;
}
return num;
}
int xstrtol (char *s, int base)
{
errno = 0;
char *endptr = NULL;
long v = strtol (s, &endptr, base);
if (errno) {
fprintf (stderr, "xstrtol() error: over/underflow detected.\n");
exit (EXIT_FAILURE);
}
if (s == endptr && v == 0) {
fprintf (stderr, "xstrtol() error: no digits found.\n");
exit (EXIT_FAILURE);
}
if (v < INT_MIN || INT_MAX < v) {
fprintf (stderr, "xstrtol() error: out of range of integer.\n");
exit (EXIT_FAILURE);
}
return (int)v;
}
示例输入文件
$ cat dat/names.txt
Ryan,Elizabeth 62 325
McIntyre,Osborne 84 326
DuMond,Kristin 18 327
Larson,Lois 42 328
Thorpe,Trinity 15 329
Ruiz,Pedro 35 330
Ali,Mohammed 60 331
Vashti,Indura 20 332
示例使用/输出
$ ./bin/readnames <dat/names.txt
Ryan,Elizabeth 62 325
McIntyre,Osborne 84 326
DuMond,Kristin 18 327
Larson,Lois 42 328
Thorpe,Trinity 15 329
Ruiz,Pedro 35 330
Ali,Mohammed 60 331
Vashti,Indura 20 332
内存使用/错误检查
在你编写的动态分配内存的任何代码中,你有2个职责关于任何分配的内存块:(1)总是保留一个指向起始地址的指针内存块,(2)当不再需要时,它可以释放。
您必须使用内存错误检查程序,以确保您不会尝试在已分配的内存块的范围之外/之外进行写入,尝试读取或基于未初始化值的条件跳转,最后,确认您释放了已分配的所有内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。
$ valgrind ./bin/readnames <dat/names.txt
==11303== Memcheck, a memory error detector
==11303== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==11303== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==11303== Command: ./bin/readnames
==11303==
Ryan,Elizabeth 62 325
McIntyre,Osborne 84 326
DuMond,Kristin 18 327
Larson,Lois 42 328
Thorpe,Trinity 15 329
Ruiz,Pedro 35 330
Ali,Mohammed 60 331
Vashti,Indura 20 332
==11303==
==11303== HEAP SUMMARY:
==11303== in use at exit: 0 bytes in 0 blocks
==11303== total heap usage: 8 allocs, 8 frees, 112 bytes allocated
==11303==
==11303== All heap blocks were freed -- no leaks are possible
==11303==
==11303== For counts of detected and suppressed errors, rerun with: -v
==11303== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认已释放已分配的所有内存并且没有内存错误。
仔细看看,如果您有其他问题,请告诉我。
答案 1 :(得分:1)
您的代码中存在许多问题,正如评论中的人员所提到的那样。但是你应该仔细考虑Weather Vane的评论。现在你的名字数组是char *names[30]
;并为其分配局部变量的地址。您需要以某种方式复制数据(因为函数调用后行超出范围)。所以我建议将names
数组更改为:
char names[30][WORD_LEN];
要复制数据,您可能需要strcpy
之类的内容。我稍微改变了你的代码,并且能够看到所需的结果:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define WORD_LEN 100
void read(FILE *fPtr, int *a, int *b, char c[][WORD_LEN], int size)
{
char line[100];
int num = 0;
while ( fgets(line, 99, fPtr) && num < size ) {
// This is a names array
//strcpy(c[num], strtok (line, " "));
// here the name prints out fine
// !avoid atoi, use sscanf instead
//a[num] = atoi(strtok (NULL, " "));
//b[num] = atoi(strtok (NULL, " "));
sscanf(line, "%s%d%d", c[num], &a[num], &b[num]);
num++;
}
}
void printThis(int* g, int* a, char n[][WORD_LEN], int s)
{
for (int i = 0; i < 2; ++i)
printf("%s %d %d\n", n[i], a[i], g[i]);
}
int main()
{
FILE *fp;
int g[30];
int a[30];
char names[30][WORD_LEN];
// Open file
fp = fopen("input.txt", "r");
read(fp, g, a, names, 30 );
printThis(g, a, names, 30 );
return 0;
}
我将文件结构更改为以空格分隔的标记,我能够看到名称:
~/work : $ cat input.txt
Rohan 120000 4300000
Prashant 12 43
~/work : $ g++ readCharArray.c
~/work : $ ./a.out
Rohan 4300000 120000
Prashant 43 12
~/work : $