0

I'm Now trying to sort the Last names and Company names. The user has to enter either a last name or company name (only one). this is my code right now:

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));
}

This is my output as of now. It should consider both last name and company name as the same parameters and then sort them:

First Name: Andre
Last Name: D'Souza
Company Name:
Phone Number (enter only numbers): 6474000964
Email: [email protected]
First Name:
Last Name:
Company Name: University of Guelph
Phone Number (enter only numbers): 5192137299
Email: [email protected]
First Name: Raf
Last Name:
Company Name: Raffy Taffy
Phone Number (enter only numbers): 1234567
Email: [email protected]
1

2 Answers 2

2

Your compare function looks wrong. You are passed pointers to two records a and b. These are pointers to your store struct, but you are casting them as store** for some reason, then trying to dereference this as a store*. This has the effect of using the data as pointers, which will certainly cause a segmentation fault.

I suggest:

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

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

Note that strcmp returns exactly the kind of int that qsort is expecting. Just return qsort the value returned by strcmp.

To generalize compareStore to check either Last name or Company name, assume that one of these contains a string, and the other is either NullPtr or a Null string, then the complete solution is:

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));
}

Another bug is found in your call to qsort itself, where you pass the size of your list, but you should be passing the size of each record within the list to be sorted:

qsort (list, count, sizeof(store), compareStore);
Sign up to request clarification or add additional context in comments.

13 Comments

I changed my function to the way you described and changed the qsort but it is arranging them by descending and it is also giving me warnings warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default] store *aa = a; ^ warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default] store *bb = b;
The double * notation would be correct if the array was an array of pointers. It isn't, as you say, and the extra * is erroneous.
@AndreD'Souza: Add "const" in front of the definitions for aa and bb to get rid of the warnings. See edited answer.
Your updated program should sort in ascending order as I now understand it. Please update your question with the lastest version of your code. If you find it that the sorted order is descending, then try mulitplying the value returned by strcmp by -1 before returning it to qsort. This will reverse the sort.
I updated the question, I'm still stuck on ordering the Last name and Company name as one parameter. By the way Thanks for your help so far
|
0

Not a complete answer, because this looks like homework, but: if your compareStore() needs to compare either the last name or the company name, whichever is set, then it should contain a conditional.

In your case, you need a way to tell whether last_name or company_name is set. You can set the unused pointer to NULL and test if (a->last_name). You could also add an enum field.

If you can change the definition of your struct, you don’t really need two char * fields that will be used the same way, only one at a time. You can have one field, interpreted differently.

Finally, (Sorry for the unwarranted criticism.) you normally shouldn’t suppress static type checking like you did with the void* parameters. It’s there to stop you from shooting yourself in the foot. In this case, though, the function is an input to qsort() and therefore one of the rare unavoidable exceptions.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.