C编程:使用两个参数对结构进行排序

时间:2017-04-02 22:26:12

标签: c sorting pointers struct qsort

我正在尝试对姓氏和公司名称进行排序。用户必须输入姓氏或公司名称(仅一个)。这是我现在的代码:

struct store {
unsigned long phone_num;
char *first_name;
char *last_name;
char *company_name;
char *email;
};
typedef struct store store;


void findContact(FILE *fp, long fileEnd)
{
/*variables*/
char fName [100];
char lName [100];
char cName [100];
char email [100];

int i, length;
int count = 1;
int size = sizeof(long);
int usize = sizeof(unsigned long);

unsigned long phone;

long nextPosition = 0;
long fNamePosition = 0;
long lNamePosition = 0;
long cNamePosition = 0;
long emailPosition = 0;

store *list;
list = malloc(sizeof(store));

/*Search for Contact position in file*/
fseek(fp, 0, SEEK_SET); /*Seeks to beginning of file*/
do {
    i = count - 1;

    fread(&phone, usize, 1, fp); /*reads phonenumber of contact*/
    fread(&fNamePosition, size , 1, fp);
    fread(&lNamePosition, size, 1, fp);
    fread(&cNamePosition, size, 1, fp);
    fread(&emailPosition, size, 1, fp);
    fread(&nextPosition, size, 1, fp);

    if(fNamePosition != 0) {

        fseek(fp,fNamePosition,SEEK_SET);
        if(lNamePosition == 0) {
          length = cNamePosition - fNamePosition;
        } else {
          length = lNamePosition - fNamePosition;
        }
        fread(fName,sizeof(char),length,fp);
    } else {
        strcpy(fName," ");
    }

    if(lNamePosition != 0) {
        fseek(fp,lNamePosition,SEEK_SET);
        if (cNamePosition == 0) {
          length = emailPosition - lNamePosition;
        } else {
          length = cNamePosition - lNamePosition;
        }
        fread(lName,sizeof(char), length,fp);
    } else {
        strcpy(lName," ");
    }

    if(cNamePosition != 0) {
        fseek(fp,cNamePosition,SEEK_SET);
        length = emailPosition-cNamePosition;
        fread(cName,sizeof(char), length,fp);
    } else {
        strcpy(cName," ");
    }

    fseek(fp,emailPosition,SEEK_SET);
    length = nextPosition - emailPosition;
    fread(email,sizeof(char),length,fp);

    list = realloc(list, count * sizeof(store));

    list[i].phone_num = phone;
    list[i].first_name = (char *) malloc(strlen(fName) + 1);
    strcpy(list[i].first_name, fName);
    list[i].last_name = (char *) malloc(strlen(lName) + 1);
    strcpy(list[i].last_name, lName);
    list[i].company_name = (char *) malloc(strlen(cName) + 1);
    strcpy(list[i].company_name, cName);
    list[i].email = (char *) malloc(strlen(email) + 1);
    strcpy(list[i].email, email);

    count++;

} while (ftell(fp) != fileEnd);

count--;

qsort(list, count, sizeof(store), compareStore);

/*Prints output*/
for(i=0;i<count;i++) {
    printf("First Name: %s\n", list[i].first_name);
    printf("Last Name: %s\n", list[i].last_name);
    printf("Company Name: %s\n", list[i].company_name);
    printf("Phone Number (enter only numbers): %ld\n", list[i].phone_num);
    printf("Email: %s\n", list[i].email);

    free(list[i].first_name);
    free(list[i].last_name);
    free(list[i].company_name);
    free(list[i].email);
}

free(list);
return;
}


int compareStore (const void*a, const void *b)
{
    const store *aa = a;
    const store *bb = b;

    return (strcmp(bb->last_name, aa->last_name));
}

这是我现在的输出。它应该将姓氏和公司名称视为相同的参数,然后对它们进行排序:

First Name: Andre
Last Name: D'Souza
Company Name:
Phone Number (enter only numbers): 6474000964
Email: adsouz03@mail.com
First Name:
Last Name:
Company Name: University of Guelph
Phone Number (enter only numbers): 5192137299
Email: uog@mail.com
First Name: Raf
Last Name:
Company Name: Raffy Taffy
Phone Number (enter only numbers): 1234567
Email: raf@mail.com

2 个答案:

答案 0 :(得分:2)

您的比较功能看起来不对。您将传递指向两个记录a和b的指针。这些是指向商店结构的指针,但出于某种原因,您将它们作为商店**进行投射,然后尝试将其取消引用为商店*。这具有将数据用作指针的效果,这肯定会导致分段错误。

我建议:

int compareStore (const void*a, const void *b)
{
    const store *aa = a;
    const store *bb = b;

    return (strcmp(aa->last_name, bb->last_name)); 
}

请注意,strcmp完全返回qsort所期望的int类型。只需返回qsort strcmp返回的值{。}}。

要概括compareStore以检查姓氏或公司名称,假设其中一个包含字符串,另一个是NullPtr或Null字符串,那么完整的解决方案是:

int compareStore (const void*a, const void *b)
{
    const store *aa = a;
    const store *bb = b;

    // This MACRO retrieve ptr to last_name or company_name based
    // on whether last_name is a NULL ptr or a null "" string.
    // If last_name is either, use company_name insteadof last_name
    #define getKey(x) ((((x)->last_name==NULL)||((x)->last_name[0]==0)) ? (x)->company_name : (x)->last_name)

    // Use the Macro getKey to point to appropriate sort key for each record
    const char* keyA = getKey(aa);  
    const char* keyB = getKey(bb);

    return (strcmp(keyA, keyB));
}

在您对qsort本身的调用中发现了另一个错误,您可以在其中传递列表的大小,但是您应该传递列表中每个记录的大小以进行排序:

qsort (list, count, sizeof(store), compareStore);

答案 1 :(得分:0)

不是一个完整的答案,因为这看起来像家庭作业,但是:如果您的compareStore()需要 姓氏公司名称,无论哪个设置后,它应该包含一个条件。

在您的情况下,您需要一种方法来判断是否设置了last_namecompany_name。您可以将未使用的指针设置为NULL并测试if (a->last_name)。您还可以添加enum字段。

如果您可以更改struct的定义,则实际上不需要两个char *字段,这些字段将以相同的方式使用,一次只能使用一个。你可以有一个字段,解释不同。

最后,(抱歉无端的批评。)你通常不应该像使用void*参数那样禁止静态类型检查。它可以阻止你在脚下射击自己。但是,在这种情况下,函数是qsort()的输入,因此是罕见的不可避免的例外之一。