如何在结构数组中访问struct

时间:2017-12-27 21:49:50

标签: c linux struct

在学校作业中,我必须对位于二进制文件中的struct元素进行排序。我想我已经设法对它进行排序,但是打印结果时遇到问题。我不知道如何访问struct的元素,因为必须从文件中读取数据,所以我只有数组中第一个结构的地址。 (我认为它应该保留在数组中以便我可以使用qsort。)

这是主要代码:

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

typedef struct {
char name[32];
double value;
} record;

int nuvu(record* a, record* b){
    if(a->name < b->name) return -1;
    if(a->name > b->name) return 1;
    if(a->value < b->value) return -1;
    if(a->value > b->value) return 1;
}

int main()
{
    long N;
    unsigned char* p = readfile( "d.bin", &N );
    char* s;
    scanf("%s",&s);

    int k= N/sizeof(record);
    qsort(p,k,sizeof(record),(int(*)(const void*, const void *))nuvu);

    printf???

    free(p);
    return 0;
}

其他: readfile.c

#include "readfile.h"
unsigned char* readfile( char* filename, long* pN )
{
    FILE* f= fopen(filename,"rb");
    if(f==0){
        *pN=-1;
        return 0;
    }
    fseek(f,0,SEEK_END);
    *pN=ftell(f);
    fseek(f,0,SEEK_SET);

    char*p=malloc(*pN);
    if(p==0){
        *pN=-2;
        fclose(f);
        return 0;
    }
    size_t r = fread(p,1,*pN,f);
    if(r!=*pN){
        *pN=-3;
        fclose(f);
        free(p);
        return 0;
    }
    fclose(f);
    return p;
}

readfile.h

#ifndef __READFILE_H
#define __READFILE_H

#include <stdio.h>
#include <stdlib.h>
unsigned char* readfile(char* filename, long* pN);

#endif /* __READFILE_H */

3 个答案:

答案 0 :(得分:1)

您似乎遇到的最大困惑是&#34;我如何阅读我的结构数组?&#34;

unsigned char* p = readfile( "d.bin", &N );

没办法开始。将记录从二进制文件读取到struct数组中的概念是从文件中读取sizeof (struct record)个字节到类型struct record的存储中。 (这将暂时忽略数据的序列化,填充和可移植性问题,以及我们使用typedef 的事实。)

了解文件大小,并且知道sizeof (struct record)允许您(1)验证您将从文件中读取的记录数,例如: (nbytes / sizeof (struct record))和(2)确定是否存在任何不属于读取的杂散字节(例如if (nbytes / sizeof (struct record) != 0)),如果存在,您至少应该警告。

根据您必须读取的记录数以及是否存在该数字的上限,将确定您是否可以使用固定大小的数组(或VLA),或者是否需要动态分配(和重新分配)地址未知数量的记录或阻止StackOverflow ..无论您如何处理为记录创建存储 - 您应该确保不要超出您创建的存储的范围

下面,我们将简单地使用一组100条记录。适合堆栈的内容和需要动态分配的内容之间的分界线将取决于编译器,但是每当您开始考虑成千上万条记录时,您需要查阅编译器文档并开始考虑动态分配。 / p>

fread提供了一种从文件中读取二进制记录的简单方法,并验证您实际读取了要读取的记录数。例如,给定100rec个记录数组的声明,您可以执行以下操作:

enum { MAXC = 32, MAXS = 100 }; /* if you need constants, define them */
...
    record rec[MAXS] = {{ .name = "" }};    /* array of 100 records */
    ...
    nrec = nbytes / sizeof *rec;   /* number of records based on file size */

    /* read / validate nrec records from file */
    if (fread (rec, sizeof *rec, nrec, fp) != nrec) {
        perror (fn);
        return 1;
    }

成功从您的文件中读取您的记录后,使用qsort对记录进行排序(namevalue)要求您了解const void *指针要在比较函数中进行比较将是指向rec 的指针,因此您必须在比较函数中提供适当的强制转换以访问和比较这些值。例如,要对name执行字符串比较,您可以执行类似以下操作:

/** record string comparison on name */
int reccmpname (const void *a, const void *b)
{
    const record *ra = a,
                 *rb = b;
    return strcmp (ra->name, rb->name);
}

除此之外,您的代码缺少的其余部分是流程中每个步骤的 验证 。始终,始终验证您使用的任何功能的返回并处理您遇到的任何错误。一个最小的例子,没有在单独的源文件之间拆分代码可能类似于以下内容。拆分成单独的源文件留给您。

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

enum { MAXC = 32, MAXS = 100 }; /* if you need constants, define them */

typedef struct {
    char name[MAXC];
    double value;
} record;

/** record string comparison on name */
int reccmpname (const void *a, const void *b)
{
    const record *ra = a,
                 *rb = b;
    return strcmp (ra->name, rb->name);
}

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

    record rec[MAXS] = {{ .name = "" }};    /* array of 100 records */
    size_t nrec = 0;                        /* number of records from file */
    long nbytes = 0;                        /* number of bytes in file */
    char *fn = argc > 1 ? argv[1] : "dat/records.bin";
    FILE *fp = fopen (fn, "rb");

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    if (fseek (fp, 0, SEEK_END) == -1) {    /* validate seek to end */
        perror ("fseek");
        return 1;
    }
    nbytes = ftell (fp);                    /* number of bytes in file */
    if (nbytes == -1) {
        perror ("ftell");
        return 1;
    }
    if (fseek (fp, 0, SEEK_SET) == -1) {    /* validate seek to start */
        perror ("fseek");
        return 1;
    }

    if (nbytes % sizeof *rec != 0) /* does file contain even no. or records? */
        fprintf (stderr, "warning: file size not multiple of record size.\n");

    nrec = nbytes / sizeof *rec;   /* number of records based on file size */

    /* read / validate nrec records from file */
    if (fread (rec, sizeof *rec, nrec, fp) != nrec) {
        perror (fn);
        return 1;
    }
    fclose (fp);    /* close file */

    printf ("\n=== unsorted records ===\n\n");  /* output unsorted */
    for (size_t i = 0; i < nrec; i++)
        printf ("%-32s  %g\n", rec[i].name, rec[i].value);

    qsort (rec, nrec, sizeof *rec, reccmpname); /* qsort records */

    printf ("\n=== sorted records ===\n\n");    /* output sorted */
    for (size_t i = 0; i < nrec; i++)
        printf ("%-32s  %g\n", rec[i].name, rec[i].value);

    return 0;
}

注意:在写入文件之前,使用的数据文件只包含100个结构记录,字典单词为name,随机值为value

示例使用/输出

$ ./bin/struct_rd_name_val_recs

=== unsorted records ===

Abscess                           4.15871e+08
Abject                            3.5743e+08
Abo                               6.87659e+08
Aboard                            2.02028e+09
Abase                             3.34319e+08
...
=== sorted records ===

A                                 3.66907e+08
Aaa                               5.59224e+07
Aaas                              1.45617e+09
Aardvark                          1.72828e+09
Aarhus                            1.95723e+09

如果您有任何问题,请与我们联系。

答案 1 :(得分:0)

您可以将[]运算符与指针一起使用:

struct my_struct {
  int i, j;
};
struct my_struct * ptr = malloc(sizeof(struct my_struct) * 10);
for(int n = 0; 10 > n; ++ n)
{
  ptr[n].i = n;
  ptr[n].j = n*2;
}
free(ptr);

答案 2 :(得分:0)

使用qsort的标准程序,不要更改其签名。如评论中所述,使用strcmp。您必须弄清楚结构如何排序的逻辑。以下示例按record::name排序,如果name相同,则按顺序测试value

int nuvu(const void * a_, const void * b_)
{
    const record* a = a_;
    const record* b = b_;
    if(strcmp(a->name, b->name) == 0)
        return a->value > b->value;
    return strcmp(a->name, b->name);
}

数据作为字节读入p,必须转换为&#34;记录数组&#34; record* arr = (record*)p;。如果一切按计划进行,则数组中的项目数应为filesize/sizeof(record)

int main(void)
{
    long filesize = 0;
    unsigned char* p = readfile("d.bin", &filesize);
    if(!p)
        return 0;

    int count = filesize / sizeof(record);
    record* arr = (record*)p;

    qsort(arr, count, sizeof(record), nuvu);

    for(int i = 0; i < count; i++)
        printf("%s %f\n", arr[i].name, arr[i].value);

    free(p);
    return 0;
}