简体   繁体   English

在C中对结构数组进行排序

[英]Sorting an array of structs in C

We were given an assignment in class, to sort array of structs. 我们在课堂上被分配了一个任务,以对结构数组进行排序。 After the assignment was handed in we discussed doing the sorting by using pointers of arrays to sort it as it was more efficient than the way most people had done it. 分配完任务后,我们讨论了使用数组指针对列表进行排序的方法,因为它比大多数人的方法更有效。

I decided to try and do it this way as well, however I'm running into some issues that I haven't been able to solve. 我决定也尝试这样做,但是我遇到了一些我无法解决的问题。

http://pastebin.com/Cs3y39yu http://pastebin.com/Cs3y39yu

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

typedef struct stage2{//standard dec of the struct field
     char need;
         double ring;
         char fight[8];
         int32_t uncle;
         char game;
         double war;
         int8_t train;
         uint32_t beds;
         float crook;
         int32_t feast;
         int32_t rabbits;
         int32_t chin;
         int8_t ground;
         char veil;
         uint32_t flowers;
         int8_t adjustment;
         int16_t pets;
} stage2;

void usage(){//usage method to handle areas
        fprintf(stderr,"File not found\n");//prints to stderr
        exit(1);//exits the program
}

int needS(const void *v1, const void *v2)
{
    const stage2 *p1 = v1;
    const stage2 *p2 = v2;
        printf("%c %c \n",p1->need,p2->need);
    return 0;
}


int main(int argc, char** argv){
        if(argc != 3){//checks for a input files, only 1
          usage();//if not runs usage
  }
  int structSize = 60;//size of structs in bytes, sizeof() doesnt return correct val
  char* fileName = argv[1];  //pull input filename
  FILE *file = fopen(fileName, "r");//opens in read mode
  fseek(file, 0, SEEK_END);  //goes to end of file
  long fileSize = ftell(file);  //saves filesize
  char *vals = malloc(fileSize);  //allocates the correct size for array based on its filesize
  fseek(file, 0, SEEK_SET);  //returns to start of file
  fread(vals, 1, fileSize, file);  //reads the file into char array vals
  fclose(file); //closes file
  int structAmount = fileSize/structSize; //determines amount of structs we need
  stage2 mainArray[structAmount]; //makes array of structs correct size


  int j;//loop variables
  int i;


  printf("need, ring, fight, uncle, game, war, train, beds, crook, feast, rabbits, chin, ground, veil, flowers, adjustment, pets\n");//prints our struct names

  for(i = 0; i < structAmount; i ++){//initialises the array vals
        mainArray[i].need = *(&vals[0+(i*60)]);
        mainArray[i].ring = *((double *)&vals[1+(i*60)]);
        for(j = 0;j<9;j++){
          mainArray[i].fight[j] = *(&vals[j+9+(i*60)]);
        }
        mainArray[i].uncle = *((int32_t *)&vals[17+(i*60)]);
        mainArray[i].game = *(&vals[21+(i*60)]);
        mainArray[i].war = *((double *)&vals[22+(i*60)]);
        mainArray[i].train = *((int8_t *)&vals[30+(i*60)]);
        mainArray[i].beds = *((uint32_t *)&vals[31+(i*60)]);   
        mainArray[i].crook = *((float *)&vals[35+(i*60)]);
        mainArray[i].feast = *((int32_t *)&vals[39+(i*60)]);
        mainArray[i].rabbits = *((int32_t *)&vals[43+(i*60)]);
        mainArray[i].chin = *((int32_t *)&vals[47+(i*60)]);
        mainArray[i].ground = *((int8_t *)&vals[51+(i*60)]);
        mainArray[i].veil = *(&vals[52+(i*60)]);
        mainArray[i].flowers = *((uint32_t *)&vals[53+(i*60)]);
        mainArray[i].adjustment = *((int8_t *)&vals[57+(i*60)]);
        mainArray[i].pets = *((int16_t *)&vals[58+(i*60)]);    
  }

  for(i = 0; i < structAmount; i ++){//prints
        printf("%c, %f, %s, %d, %c, %f, %d, %u, %f, %d, %d, %d, %d, %c, %u, %d, %d \n",
        mainArray[i].need,mainArray[i].ring,mainArray[i].fight,mainArray[i].uncle,mainArray[i].game,mainArray[i].war,mainArray[i].train,
        mainArray[i].beds,mainArray[i].crook,mainArray[i].feast,mainArray[i].rabbits,mainArray[i].chin,mainArray[i].ground,mainArray[i].veil,
        mainArray[i].flowers,mainArray[i].adjustment,mainArray[i].pets);//prints

  }
  free(vals);//frees the memory we allocated to vals

  stage2 *array = malloc(structAmount * structSize);

  for(i = 0; i < structAmount; i ++){
        array[i] = mainArray[i];  
  }
  printf("Before Sort\n\n");  
  for(i = 0; i < structAmount; i ++){
          printf("%c, %f, %s, %d, %c, %f, %d, %u, %f, %d, %d, %d, %d, %c, %u, %d, %d \n",
  array[i].need,array[i].ring,array[i].fight,array[i].uncle,array[i].game,array[i].war,array[i].train,
  array[i].beds,array[i].crook,array[i].feast,array[i].rabbits,array[i].chin,array[i].ground,array[i].veil,
  array[i].flowers,array[i].adjustment,array[i].pets);//prints 
  }
  qsort(array, structAmount,structSize,needS);


  printf("After Sort\n\n");  
  for(i = 0; i < structAmount; i ++){
          printf("%c, %f, %s, %d, %c, %f, %d, %u, %f, %d, %d, %d, %d, %c, %u, %d, %d \n",
  array[i].need,array[i].ring,array[i].fight,array[i].uncle,array[i].game,array[i].war,array[i].train,
  array[i].beds,array[i].crook,array[i].feast,array[i].rabbits,array[i].chin,array[i].ground,array[i].veil,
  array[i].flowers,array[i].adjustment,array[i].pets);//prints 
  }

  FILE *my_file = fopen(argv[2], "wb");
  for(i = 0; i < structAmount; i ++){
          fwrite(&mainArray[i].need, sizeof(char), 1, my_file);
          fwrite(&mainArray[i].ring, sizeof(double), 1, my_file);
          fwrite(&mainArray[i].fight, sizeof(char[8]), 1, my_file);
          fwrite(&mainArray[i].uncle, sizeof(int32_t), 1, my_file);
          fwrite(&mainArray[i].game, sizeof(char), 1, my_file);
          fwrite(&mainArray[i].war, sizeof(double), 1, my_file);
          fwrite(&mainArray[i].train, sizeof(int8_t), 1, my_file);
          fwrite(&mainArray[i].beds, sizeof(uint32_t), 1, my_file);
          fwrite(&mainArray[i].crook, sizeof(float), 1, my_file);
          fwrite(&mainArray[i].feast, sizeof(int32_t), 1, my_file);
          fwrite(&mainArray[i].rabbits, sizeof(int32_t), 1, my_file);
          fwrite(&mainArray[i].chin, sizeof(int32_t), 1, my_file);
          fwrite(&mainArray[i].ground, sizeof(int8_t), 1, my_file);
          fwrite(&mainArray[i].veil, sizeof(char), 1, my_file);
          fwrite(&mainArray[i].flowers, sizeof(uint32_t), 1, my_file);
          fwrite(&mainArray[i].adjustment, sizeof(int8_t), 1, my_file);
          fwrite(&mainArray[i].pets, sizeof(int16_t), 1, my_file);
  }

  fclose(my_file);

  return 0;//return statement
}

There is the link to the code that I have been working on. 有指向我一直在努力的代码的链接。 My main issue is that from what I gather, when using the sorting comparison method needS (line 31) for the first execution of the sort it should return the first field for the first two structs in the array and print them (I am aware that this isn't a valid sorting method but wanted to make sure the variables were what I expected). 我的主要问题是,从我收集的数据来看,当使用排序比较方法needS (第31行)进行排序的第一次执行时,它应该返回数组中前两个结构的第一个字段并打印它们(我知道这不是有效的排序方法,但要确保变量符合我的期望)。 However that's not what occurs; 但是,事实并非如此。 the p1 variable will print out what I expect however the p2 variable will not. p1变量将打印出我期望的结果,但是p2变量不会。 From this point onwards, every use of this will return junk (I think) values. 从现在开始,每次使用此方法都会返回垃圾值(我认为)。

Is there anything that I'm missing or doing wrong? 有什么我想念的或做错的吗?

Your problem is in this line: 您的问题在这一行:

int structSize = 60;  //size of structs in bytes, sizeof() doesn't return correct val

You're wrong; 你错了; sizeof() does return the correct size. sizeof()确实返回正确的大小。 Whatever you're doing is bogus. 无论您做什么,都是虚假的。 You need to go back to the basics of serialization. 您需要回到序列化的基础知识。 You will need to write the data correctly. 您将需要正确写入数据。 You should be able to read all the data into the array in a single read operation. 您应该能够通过一次读取操作将所有数据读取到阵列中。 If you really have a 60 bytes per record in the input, you have serious issues. 如果输入中的每条记录确实有60个字节,则存在严重问题。

Note that your structure layout in memory wastes 7 bytes on most systems (3 on some) between the char need; 请注意,您在存储器中的结构布局在大多数系统之间(在某些char need;之间)浪费了7个字节(某些情况下为3个字节) char need; and double ring; double ring; elements. 元素。 There's a similar gap between the char game; char game;之间也存在类似的差距char game; and double war; double war; , and another gap (usually 3 bytes) between int8_t train; ,以及int8_t train;之间的另一个间隔(通常为3个字节) int8_t train; and uint32_t beds; uint32_t beds; , and another similar gap between (usually 2 bytes this time) between char veil; ,以及char veil;之间的另一个类似间隔(通常是2个字节) char veil; and uint32_t flowers; uint32_t flowers; (because char veil; is preceded by int8_t ground; , and a gap of 1 between int8_t adjustment; and int16_t pets; — I won't guarantee that I've spotted all the gaps). (因为char veil;前面是int8_t ground;并且int8_t adjustment;int16_t pets;之间的间隔为1 int16_t pets; —我不能保证已经发现所有间隔)。

To learn more about structure layouts and padding, see Why isn't sizeof for a struct equal to the sum of sizeof of each member? 要了解有关结构布局和填充的更多信息,请参见为什么结构的sizeof不等于每个成员的sizeof之和? as suggested by paddy . 根据稻田的建议。

To minimize the wasted space, the heuristic is to put the members with bigger base types before those with smaller base types. 为了最大程度地减少浪费的空间,试探法是将具有较大基本类型的成员放在具有较小基本类型的成员之前。 Thus all the double members should come before any of the char members. 因此,所有double成员都应位于任何char成员之前。 If a member is an array type, ignore the array and look at the size of the base type. 如果成员是数组类型,请忽略该数组并查看基本类型的大小。 For example, char fight[8]; 例如char fight[8]; is best put with the other char members at the end, though given that it is a multiple of 8 bytes, it could stay where it is — but it is simpler to be consistent. 最好与其他char成员放在最后,尽管考虑到它是8字节的倍数,它可以保留在原来的位置,但是保持一致更简单。 Pointers need to be treated as 8 bytes on 64-bit systems, or as 4 bytes on 32-bit systems. 在64位系统上,指针应被视为8字节,在32位系统上,指针应被视为4字节。 Place pointers between the non-pointer types such as long long or double (usually 8 bytes each) and the smaller non-pointer types such as int or uint32_t . 将指针放在非指针类型(例如long longdouble (通常每个8个字节)与较小的非指针类型(例如intuint32_t The long type is a nuisance; long类型很麻烦; it can be 4 or 8 bytes, depending (it's 4 bytes on Windows, even Windows 64-bit; it is 8 bytes on 64-bit Unix or 4 bytes on 32-but Unix). 它可以是4或8字节,具体取决于(在Windows上,甚至Windows 64位为4字节;在64位Unix上为8字节,在32-but Unix上为4字节)。

The function needS needs to return a value that is appropriate for sorting the items in the array. needS函数需要返回一个适合于对数组中的项目进行排序的值。

int needS(const void *v1, const void *v2)
{
    const stage2 *p1 = v1;
    const stage2 *p2 = v2;
    printf("%c %c \n",p1->need,p2->need);

    // Something like:
    return (p1->need < p2->need);
}

You have confused the "packed" structure size in your data file with the in-memory structure array that you allocate (by default, struct stage2 will have extra padding to align data types efficiently). 您已经将数据文件中的“打包”结构大小与分配的内存结构数组混淆了(默认情况下, struct stage2将具有额外的填充以有效地对齐数据类型)。 This line here is the problem: 这行是问题所在:

stage2 *array = malloc(structAmount * structSize);

It should be: 它应该是:

stage2 *array = malloc(structAmount * sizeof(stage2));

Your call to qsort needs to be updated accordingly too: 您对qsort调用也需要相应地进行更新:

qsort(array, structAmount, sizeof(stage2), needS);

In addition to other posters' good answers about comparison functions and structure size, you also don't sort by pointers, which was your initial intention. 除了其他张贴者关于比较功能和结构大小的好答案外,您也不会按指针排序,这是您的初衷。

In your code, you sort a copy of the whole array of large structs. 在代码中,对大型结构的整个数组的副本进行排序。 When sorting by pointer, you create an auxiliary array of pointers to the original array elements and then sort these pointers by way of the data they point to. 当按指针排序时,您将创建一个指向原始数组元素的辅助指针数组,然后通过它们所指向的数据对这些指针进行排序。 This will leave the original array intact. 这将保留原始数组的完整性。

Your comparison function must then treat the void * pointers as pointers to pointers to your struct. 然后,您的比较函数必须将void *指针视为指向您的结构的指针。 Example (with heavily abridged structures) below. 下面的示例(具有高度删节的结构)。

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

typedef struct stage2 {
    int need;
} stage2;

int needS(const void *v1, const void *v2)
{
    stage2 *const *p1 = v1;
    stage2 *const *p2 = v2;

    return ((*p1)->need - (*p2)->need) - ((*p2)->need - (*p1)->need);
}

int main(int argc, char **argv)
{
    stage2 mainArray[] = {
        {8}, {3}, {5}, {1}, {19}, {-2}, {8}, {0},{0}, {4}, {5}, {1}, {8}
    };
    int structAmount = sizeof(mainArray) / sizeof(*mainArray);

    int i;

    stage2 **array = malloc(structAmount * sizeof(*array));

    // Assign pointers
    for (i = 0; i < structAmount; i++) {
        array[i] = &mainArray[i];
    }

    qsort(array, structAmount, sizeof(*array), needS);

    puts("Original");
    for (i = 0; i < structAmount; i++) {
        printf("%d\n", mainArray[i].need);
    }

    puts("");
    puts("Sorted");
    for (i = 0; i < structAmount; i++) {
        printf("%d\n", array[i]->need);
    }

    free(array);

    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM