【问题标题】:C programming: Sorting Structures with two parametersC 编程:使用两个参数对结构进行排序
【发布时间】:2017-04-02 22:26:11
【问题描述】:

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

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

【问题讨论】:

标签: c sorting pointers struct qsort


【解决方案1】:

您的比较功能看起来不对。您将获得指向两条记录 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 类型。只需返回qsortstrcmp返回的值即可。

为了概括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);

【讨论】:

  • 我将我的函数更改为您描述的方式并更改了 qsort 但它通过降序排列它们并且它也给我警告警告:初始化从指针目标类型中丢弃'const'限定符[启用默认] 存储 *aa = a; ^ 警告:初始化从指针目标类型中丢弃“const”限定符 [默认启用] store *bb = b;
  • 如果数组是指针数组,则双 * 表示法是正确的。不是,正如你所说,额外的* 是错误的。
  • @AndreD'Souza:在 aa 和 bb 的定义前添加“const”以消除警告。请参阅编辑后的答案。
  • 您更新的程序应该按我现在理解的升序排序。请使用最新版本的代码更新您的问题。如果你发现排序顺序是降序的,那么在返回给 qsort 之前尝试将 strcmp 返回的值乘以 -1。这将反转排序。
  • 我更新了问题,我仍然坚持将姓氏和公司名称作为一个参数进行排序。顺便感谢您到目前为止的帮助
【解决方案2】:

不是一个完整的答案,因为这看起来像家庭作业,但是:如果您的compareStore() 需要比较或者姓氏公司名称,以设置为准,那么它应该包含一个条件。

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

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

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

【讨论】:

    猜你喜欢
    • 2016-07-07
    • 1970-01-01
    • 2013-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-16
    相关资源
    最近更新 更多