c - 使用 txt 文件中的指针在内存中存储和访问数据

标签 c

所以我目前正在从事一个使用 txt 文件中的数据的项目。提示用户输入文件名,txt 文件的前两行是整数,主要包含 txt 文件的行值和列值。 按照我的老师要求的方式编写这个程序时,有两件事让我感到困惑。对于标准,她说:

  1. 读入数据并放入数据数组
  2. 您的代码应该通过指针和指针算法访问内存位置,您提交的代码中没有 []。

最左边的列是一个标识符,而该行的其余部分应被视为该行数据(浮点值)。 该文件可能包含的示例是:

3
4
abc123  8.55    5   0   10
cdef123  83.50 10.5 10  55
hig123   7.30   6   0   1.9

我的代码:

//Creates array for 100 characters for filename
char fileName[100];

printf("Enter the file name to be read from: ");
scanf("%s", fileName);

FILE *myFile;
myFile = fopen(fileName, "r");

//Checks if file opened correctly
if (myFile == NULL)
{
    printf("Error opening file\n"); //full file name must be entered
}
else {
    printf("File opened successfully\n");
}

//gets value of records and value per records from file
//This will be the first 2 lines from line
fscanf(myFile, "%d %d", &records, &valuesPerRecords);

//printf("%d %d\n", records, valuesPerRecords); //Check int values from file
int counter = 0;
char *ptr_data;

ptr_data = (char*)malloc(records*(valuesPerRecords));

int totalElements = records*(valuesPerRecords);

/*If malloc can't allocate enough space, print error*/
if (ptr_data == NULL) {
    printf("Error\n");
    exit(-1);
}

int counter;

for (counter = 0; counter < totalElements; counter++){
    fscanf(myFile, "%s", &ptr_data);
}

所以我想知道到目前为止,我是否在正确的轨道上。我似乎想不出一种方法将第一列作为字符串读入,而其余列作为整数读入。稍后我还必须使用存储的值并对它们进行排序,但这是以后的另一个问题。

最佳答案

首先,您的教授显然希望您熟悉在字符串(标签)和数字(浮点值)的集合中遍历指针) 使用指针算法 而不使用数组索引。扎实的指针熟悉度分配。

要处理标签,您可以使用指向 char 类型指针的指针(双指针),因为每个指针都将指向一个 char 数组。您可以按如下方式为标签声明和分配指针。 (假设您已经从输入文件中读取了 rowscols 值)

    char buf[MAXC] = "",    /* temporary line buffer    */
         **labels = NULL,   /* collection of labels     */
         **lp = NULL;       /* pointers to walk labels  */
...
    /* allocate & validate cols char* pointers */
    if (!(labels = calloc (rows, sizeof *labels))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

您可以为您的指针值做同样的事情,除了您只需要一个指向 double 类型的指针,因为您只需要分配一个 double 的集合。

    double  *mtrx = NULL,   /* collection of numbers    */
            *p;             /* pointers to walk numbers */
...
    nptrs = rows * cols;    /* set number of poiners required */

    /* allocate & validate nptrs doubles */
    if (!(mtrx = calloc (nptrs, sizeof *mtrx))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

指针 lpp 的使用至关重要,因为您不能递增 labelsmtrx (不保存原始地址)因为这样做会丢失指向分配给每个内存的开始的指针,立即导致内存泄漏(您无法释放该 block )并阻止您再次访问开始.每次您需要遍历 labelsmtrx 时,只需将起始地址分配给指针,例如

    p  = mtrx;   /* set pointer p to mtrx */
    lp = labels; /* set poiners lp to labels */

现在您可以自由地以您选择的任何方式读取和解析行,但我强烈建议使用line-oriented-input 函数将每一行读入临时行缓冲区,然后使用 sscanf 解析您需要的值。这比单独使用 fscanf 读取有很多优势。阅读每一行后,您可以在为字符串分配空间并分配值之前解析/验证每个值。

(注意:我在下面使用一个 sscanf 调用作弊,您实际上应该将一个 char* 指针分配给 buf,请阅读label,然后循环 cols 次(可能使用 strtok/strtod)检查每个值并分配给 mtrx , -- 留给你)

    /* read each remaining line, allocate/fill pointers */
    while (ndx < rows && fgets (buf, MAXC, fp)) {
        if (*buf == '\n') continue;      /* skip empty lines */
        char label[MAXC] = "";    /* temp storage for labels */
        double val[cols];        /* temp storage for numbers */
        if (sscanf (buf, "%s %lf %lf %lf %lf", /* parse line */
                label, &val[0], &val[1], &val[2], &val[3]) == 
                (int)(cols + 1)) {
            *lp++ = strdup (label);      /* alloc/copy label */
            for (i = 0; i < cols; i++) /* alloc/copy numbers */
                *p++ = val[i];
            ndx++;                        /* increment index */
        }
    }
    if (fp != stdin) fclose (fp);  /* close file if not stdin */

然后只需再次循环这些值,根据需要使用或输出,然后释放您分配的内存。你可以用类似的东西来做到这一点:

    p  = mtrx;                     /* reset pointer p to mtrx */
    lp = labels;                /* reset poiners lp to labels */

    for (i = 0; i < rows; i++) {
        printf (" %-10s", *lp);
        free (*lp++);
        for (j = 0; j < cols; j++)
            printf (" %7.2lf", *p++);
        putchar ('\n');
    }
    free (mtrx);   /* free pointers */
    free (labels);

这基本上是众多方法中的一种。将它们放在一起,您可以:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum { MAXC = 512 }; /* constants for max chars per-line */

int main (int argc, char **argv) {

    char buf[MAXC] = "",    /* temporary line buffer    */
         **labels = NULL,   /* collection of labels     */
         **lp = NULL;       /* pointers to walk labels  */
    double  *mtrx = NULL,   /* collection of numbers    */
            *p;             /* pointers to walk numbers */
    size_t i, j, ndx = 0, rows = 0, cols = 0, nptrs = 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;
    }

    while (fgets (buf, MAXC, fp))  /* get rows, ignore blank lines */
        if (sscanf (buf, "%zu", &rows) == 1)
            break;

    while (fgets (buf, MAXC, fp))  /* get cols, ignore blank lines */
        if (sscanf (buf, "%zu", &cols) == 1)
            break;

    if (!rows || !cols) {   /* validate rows & cols > 0 */
        fprintf (stderr, "error: rows and cols values not found.\n");
        return 1;
    }
    nptrs = rows * cols;    /* set number of poiners required */

    /* allocate & validate nptrs doubles */
    if (!(mtrx = calloc (nptrs, sizeof *mtrx))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

    /* allocate & validate rows char* pointers */
    if (!(labels = calloc (rows, sizeof *labels))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

    p  = mtrx;   /* set pointer p to mtrx */
    lp = labels; /* set poiners lp to labels */

    /* read each remaining line, allocate/fill pointers */
    while (ndx < rows && fgets (buf, MAXC, fp)) {
        if (*buf == '\n') continue;      /* skip empty lines */
        char label[MAXC] = "";    /* temp storage for labels */
        double val[cols];        /* temp storage for numbers */
        if (sscanf (buf, "%s %lf %lf %lf %lf", /* parse line */
                label, &val[0], &val[1], &val[2], &val[3]) == 
                (int)(cols + 1)) {
            *lp++ = strdup (label);      /* alloc/copy label */
            for (i = 0; i < cols; i++) /* alloc/copy numbers */
                *p++ = val[i];
            ndx++;                        /* increment index */
        }
    }
    if (fp != stdin) fclose (fp);  /* close file if not stdin */

    p  = mtrx;                     /* reset pointer p to mtrx */
    lp = labels;                /* reset poiners lp to labels */

    for (i = 0; i < rows; i++) {
        printf (" %-10s", *lp);
        free (*lp++);
        for (j = 0; j < cols; j++)
            printf (" %7.2lf", *p++);
        putchar ('\n');
    }
    free (mtrx);   /* free pointers */
    free (labels);

    return 0;
}

使用的示例输入文件

$ cat dat/arrinpt.txt
3

4

abc123 8.55 5 0 10

cdef123 83.50 10.5 10 55

hig123 7.30 6 0 1.9

示例使用/输出

$ ./bin/arrayptrs <dat/arrinpt.txt
 abc123        8.55    5.00    0.00   10.00
 cdef123      83.50   10.50   10.00   55.00
 hig123        7.30    6.00    0.00    1.90

内存使用/错误检查

在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 个责任:(1) 始终保留指向起始地址的指针内存块,因此,(2) 它可以在不再需要时被释放

您必须使用内存错误检查程序来确保您没有在分配的内存块之外/之外写入,尝试读取或基于未初始化的值进行跳转,最后确认您已释放所有你分配的内存。对于 Linux valgrind 是正常的选择。

$ valgrind ./bin/arrayptrs <dat/arrinpt.txt
==22210== Memcheck, a memory error detector
==22210== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==22210== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==22210== Command: ./bin/arrayptrs
==22210==
 abc123        8.55    5.00    0.00   10.00
 cdef123      83.50   10.50   10.00   55.00
 hig123        7.30    6.00    0.00    1.90
==22210==
==22210== HEAP SUMMARY:
==22210==     in use at exit: 0 bytes in 0 blocks
==22210==   total heap usage: 5 allocs, 5 frees, 142 bytes allocated
==22210==
==22210== All heap blocks were freed -- no leaks are possible
==22210==
==22210== For counts of detected and suppressed errors, rerun with: -v
==22210== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

始终确认所有堆 block 都已释放——不可能发生泄漏,同样重要的是错误摘要:0 个上下文中的 0 个错误。注意:一些操作系统没有提供足够的泄漏和错误抑制文件(将系统和操作系统内存排除在报告中的文件),这将导致 valgrind 报告一些内存还没有被释放(尽管你已经完成了你的工作并释放了你分配和控制的所有 block )。

检查一下,如果您有任何问题,请告诉我。

关于c - 使用 txt 文件中的指针在内存中存储和访问数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39431880/

相关文章:

c++ - 如何在makefile中添加.c、.cc和.so文件来创建另一个.so?

c - 加密c源文件

c - vim 保持自动完成弹出窗口

c++将dll注入(inject)cmd.exe监控命令

c - 在一个程序中混合使用 SCHED_FIFO 和 SCHED_RR?

c - 如何在c程序中将u-law和a-law编码的wav文件转换为pcm wav文件?

c - C 中的指针,理论方面

c - 如何在c中从字符串中分离和存储元素?

c - 测试文件是否可删除

C:打印二维数组的问题